Initial commit: svg backend

This commit is contained in:
adminko
2026-03-19 13:39:32 +03:00
commit 85fb2f4bb9
78 changed files with 6161 additions and 0 deletions

View File

View File

@@ -0,0 +1,42 @@
import json
from uuid import uuid4
from sqlalchemy import asc, select
from app.db.session import AsyncSessionLocal
from app.models.audit_event import AuditEventRecord
async def create_audit_event(
*,
scheme_id: str,
event_type: str,
object_type: str,
object_ref: str | None = None,
details: dict | None = None,
) -> str:
audit_event_id = uuid4().hex
async with AsyncSessionLocal() as session:
row = AuditEventRecord(
audit_event_id=audit_event_id,
scheme_id=scheme_id,
event_type=event_type,
object_type=object_type,
object_ref=object_ref,
details_json=json.dumps(details, ensure_ascii=False) if details is not None else None,
)
session.add(row)
await session.commit()
return audit_event_id
async def list_audit_events(scheme_id: str) -> list[AuditEventRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(AuditEventRecord)
.where(AuditEventRecord.scheme_id == scheme_id)
.order_by(asc(AuditEventRecord.created_at), asc(AuditEventRecord.id))
)
return list(result.scalars().all())

View File

@@ -0,0 +1,237 @@
from decimal import Decimal
from uuid import uuid4
from fastapi import HTTPException, status
from sqlalchemy import asc, desc, select
from app.db.session import AsyncSessionLocal
from app.models.price_rule import PriceRuleRecord
from app.models.pricing_category import PricingCategoryRecord
async def create_pricing_category(
*,
scheme_id: str,
name: str,
code: str | None,
) -> str:
pricing_category_id = uuid4().hex
async with AsyncSessionLocal() as session:
row = PricingCategoryRecord(
pricing_category_id=pricing_category_id,
scheme_id=scheme_id,
name=name,
code=code,
)
session.add(row)
await session.commit()
return pricing_category_id
async def update_pricing_category(
*,
scheme_id: str,
pricing_category_id: str,
name: str,
code: str | None,
) -> PricingCategoryRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PricingCategoryRecord).where(
PricingCategoryRecord.scheme_id == scheme_id,
PricingCategoryRecord.pricing_category_id == pricing_category_id,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Pricing category not found",
)
row.name = name
row.code = code
await session.commit()
await session.refresh(row)
return row
async def delete_pricing_category(
*,
scheme_id: str,
pricing_category_id: str,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PricingCategoryRecord).where(
PricingCategoryRecord.scheme_id == scheme_id,
PricingCategoryRecord.pricing_category_id == pricing_category_id,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Pricing category not found",
)
await session.delete(row)
await session.commit()
async def create_price_rule(
*,
scheme_id: str,
pricing_category_id: str | None,
target_type: str,
target_ref: str,
amount: Decimal,
currency: str,
) -> str:
price_rule_id = uuid4().hex
async with AsyncSessionLocal() as session:
row = PriceRuleRecord(
price_rule_id=price_rule_id,
scheme_id=scheme_id,
pricing_category_id=pricing_category_id,
target_type=target_type,
target_ref=target_ref,
amount=amount,
currency=currency,
)
session.add(row)
await session.commit()
return price_rule_id
async def update_price_rule(
*,
scheme_id: str,
price_rule_id: str,
pricing_category_id: str | None,
target_type: str,
target_ref: str,
amount: Decimal,
currency: str,
) -> PriceRuleRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PriceRuleRecord).where(
PriceRuleRecord.scheme_id == scheme_id,
PriceRuleRecord.price_rule_id == price_rule_id,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Price rule not found",
)
row.pricing_category_id = pricing_category_id
row.target_type = target_type
row.target_ref = target_ref
row.amount = amount
row.currency = currency
await session.commit()
await session.refresh(row)
return row
async def delete_price_rule(
*,
scheme_id: str,
price_rule_id: str,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PriceRuleRecord).where(
PriceRuleRecord.scheme_id == scheme_id,
PriceRuleRecord.price_rule_id == price_rule_id,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Price rule not found",
)
await session.delete(row)
await session.commit()
async def list_pricing_categories(scheme_id: str) -> list[PricingCategoryRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PricingCategoryRecord)
.where(PricingCategoryRecord.scheme_id == scheme_id)
.order_by(asc(PricingCategoryRecord.created_at), asc(PricingCategoryRecord.id))
)
return list(result.scalars().all())
async def list_price_rules(scheme_id: str) -> list[PriceRuleRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PriceRuleRecord)
.where(PriceRuleRecord.scheme_id == scheme_id)
.order_by(asc(PriceRuleRecord.created_at), asc(PriceRuleRecord.id))
)
return list(result.scalars().all())
async def find_effective_price_rule(
*,
scheme_id: str,
seat_id: str | None,
group_id: str | None,
sector_id: str | None,
) -> tuple[str, dict]:
async with AsyncSessionLocal() as session:
checks = [
("seat", seat_id),
("group", group_id),
("sector", sector_id),
]
for level, ref in checks:
if not ref:
continue
result = await session.execute(
select(PriceRuleRecord)
.where(
PriceRuleRecord.scheme_id == scheme_id,
PriceRuleRecord.target_type == level,
PriceRuleRecord.target_ref == ref,
)
.order_by(desc(PriceRuleRecord.created_at), desc(PriceRuleRecord.id))
.limit(1)
)
row = result.scalar_one_or_none()
if row is not None:
return level, {
"price_rule_id": row.price_rule_id,
"scheme_id": row.scheme_id,
"pricing_category_id": row.pricing_category_id,
"target_type": row.target_type,
"target_ref": row.target_ref,
"amount": row.amount,
"currency": row.currency,
}
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="No pricing rule matched current seat",
)

View File

@@ -0,0 +1,74 @@
import json
from uuid import uuid4
from sqlalchemy import asc, delete, select
from app.db.session import AsyncSessionLocal
from app.models.scheme_group import SchemeGroupRecord
async def replace_scheme_version_groups(
*,
scheme_id: str,
scheme_version_id: str,
groups: list[dict],
) -> None:
async with AsyncSessionLocal() as session:
await session.execute(
delete(SchemeGroupRecord).where(
SchemeGroupRecord.scheme_version_id == scheme_version_id
)
)
for item in groups:
row = SchemeGroupRecord(
group_record_id=uuid4().hex,
scheme_id=scheme_id,
scheme_version_id=scheme_version_id,
element_id=item.get("id"),
group_id=item.get("group_id"),
name=item.get("group_id") or item.get("id") or "unnamed-group",
classes_raw=json.dumps(item.get("classes", []), ensure_ascii=False),
)
session.add(row)
await session.commit()
async def list_scheme_version_groups(scheme_version_id: str) -> list[SchemeGroupRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeGroupRecord)
.where(SchemeGroupRecord.scheme_version_id == scheme_version_id)
.order_by(asc(SchemeGroupRecord.id))
)
return list(result.scalars().all())
async def clone_scheme_version_groups(
*,
source_scheme_version_id: str,
target_scheme_version_id: str,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeGroupRecord).where(
SchemeGroupRecord.scheme_version_id == source_scheme_version_id
)
)
rows = list(result.scalars().all())
for row in rows:
session.add(
SchemeGroupRecord(
group_record_id=uuid4().hex,
scheme_id=row.scheme_id,
scheme_version_id=target_scheme_version_id,
element_id=row.element_id,
group_id=row.group_id,
name=row.name,
classes_raw=row.classes_raw,
)
)
await session.commit()

View File

@@ -0,0 +1,118 @@
import json
from uuid import uuid4
from fastapi import HTTPException, status
from sqlalchemy import asc, delete, select
from app.db.session import AsyncSessionLocal
from app.models.scheme_seat import SchemeSeatRecord
async def replace_scheme_version_seats(
*,
scheme_id: str,
scheme_version_id: str,
seats: list[dict],
) -> None:
async with AsyncSessionLocal() as session:
await session.execute(
delete(SchemeSeatRecord).where(
SchemeSeatRecord.scheme_version_id == scheme_version_id
)
)
for item in seats:
row = SchemeSeatRecord(
seat_record_id=uuid4().hex,
scheme_id=scheme_id,
scheme_version_id=scheme_version_id,
element_id=item.get("id"),
seat_id=item.get("seat_id"),
sector_id=item.get("sector_id"),
group_id=item.get("group_id"),
row_label=item.get("row"),
seat_number=item.get("seat_number"),
tag=item.get("tag"),
classes_raw=json.dumps(item.get("classes", []), ensure_ascii=False),
x=item.get("x"),
y=item.get("y"),
cx=item.get("cx"),
cy=item.get("cy"),
width=item.get("width"),
height=item.get("height"),
)
session.add(row)
await session.commit()
async def list_scheme_version_seats(scheme_version_id: str) -> list[SchemeSeatRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeSeatRecord)
.where(SchemeSeatRecord.scheme_version_id == scheme_version_id)
.order_by(asc(SchemeSeatRecord.id))
)
return list(result.scalars().all())
async def get_scheme_version_seat_by_seat_id(
*,
scheme_version_id: str,
seat_id: str,
) -> SchemeSeatRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeSeatRecord).where(
SchemeSeatRecord.scheme_version_id == scheme_version_id,
SchemeSeatRecord.seat_id == seat_id,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Seat not found in current scheme version",
)
return row
async def clone_scheme_version_seats(
*,
source_scheme_version_id: str,
target_scheme_version_id: str,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeSeatRecord).where(
SchemeSeatRecord.scheme_version_id == source_scheme_version_id
)
)
rows = list(result.scalars().all())
for row in rows:
session.add(
SchemeSeatRecord(
seat_record_id=uuid4().hex,
scheme_id=row.scheme_id,
scheme_version_id=target_scheme_version_id,
element_id=row.element_id,
seat_id=row.seat_id,
sector_id=row.sector_id,
group_id=row.group_id,
row_label=row.row_label,
seat_number=row.seat_number,
tag=row.tag,
classes_raw=row.classes_raw,
x=row.x,
y=row.y,
cx=row.cx,
cy=row.cy,
width=row.width,
height=row.height,
)
)
await session.commit()

View File

@@ -0,0 +1,74 @@
import json
from uuid import uuid4
from sqlalchemy import asc, delete, select
from app.db.session import AsyncSessionLocal
from app.models.scheme_sector import SchemeSectorRecord
async def replace_scheme_version_sectors(
*,
scheme_id: str,
scheme_version_id: str,
sectors: list[dict],
) -> None:
async with AsyncSessionLocal() as session:
await session.execute(
delete(SchemeSectorRecord).where(
SchemeSectorRecord.scheme_version_id == scheme_version_id
)
)
for item in sectors:
row = SchemeSectorRecord(
sector_record_id=uuid4().hex,
scheme_id=scheme_id,
scheme_version_id=scheme_version_id,
element_id=item.get("id"),
sector_id=item.get("sector_id"),
name=item.get("sector_id") or item.get("id") or "unnamed-sector",
classes_raw=json.dumps(item.get("classes", []), ensure_ascii=False),
)
session.add(row)
await session.commit()
async def list_scheme_version_sectors(scheme_version_id: str) -> list[SchemeSectorRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeSectorRecord)
.where(SchemeSectorRecord.scheme_version_id == scheme_version_id)
.order_by(asc(SchemeSectorRecord.id))
)
return list(result.scalars().all())
async def clone_scheme_version_sectors(
*,
source_scheme_version_id: str,
target_scheme_version_id: str,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeSectorRecord).where(
SchemeSectorRecord.scheme_version_id == source_scheme_version_id
)
)
rows = list(result.scalars().all())
for row in rows:
session.add(
SchemeSectorRecord(
sector_record_id=uuid4().hex,
scheme_id=row.scheme_id,
scheme_version_id=target_scheme_version_id,
element_id=row.element_id,
sector_id=row.sector_id,
name=row.name,
classes_raw=row.classes_raw,
)
)
await session.commit()

View File

@@ -0,0 +1,169 @@
from datetime import datetime
from uuid import uuid4
from fastapi import HTTPException, status
from sqlalchemy import asc, desc, func, select
from app.db.session import AsyncSessionLocal
from app.models.scheme import SchemeRecord
from app.models.scheme_version import SchemeVersionRecord
async def create_initial_scheme_version(
*,
scheme_id: str,
normalized_storage_path: str,
normalized_elements_count: int,
normalized_seats_count: int,
normalized_groups_count: int,
normalized_sectors_count: int,
display_svg_storage_path: str | None = None,
display_svg_status: str = "pending",
display_svg_generated_at: datetime | None = None,
) -> str:
scheme_version_id = uuid4().hex
async with AsyncSessionLocal() as session:
row = SchemeVersionRecord(
scheme_version_id=scheme_version_id,
scheme_id=scheme_id,
version_number=1,
status="draft",
normalized_storage_path=normalized_storage_path,
normalized_elements_count=normalized_elements_count,
normalized_seats_count=normalized_seats_count,
normalized_groups_count=normalized_groups_count,
normalized_sectors_count=normalized_sectors_count,
display_svg_storage_path=display_svg_storage_path,
display_svg_status=display_svg_status,
display_svg_generated_at=display_svg_generated_at,
)
session.add(row)
await session.commit()
return scheme_version_id
async def list_scheme_versions(scheme_id: str, limit: int = 100, offset: int = 0) -> list[SchemeVersionRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeVersionRecord)
.where(SchemeVersionRecord.scheme_id == scheme_id)
.order_by(asc(SchemeVersionRecord.version_number), desc(SchemeVersionRecord.id))
.limit(limit)
.offset(offset)
)
return list(result.scalars().all())
async def count_scheme_versions(scheme_id: str) -> int:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(func.count()).select_from(SchemeVersionRecord).where(SchemeVersionRecord.scheme_id == scheme_id)
)
return int(result.scalar_one())
async def get_current_scheme_version(scheme_id: str, current_version_number: int) -> SchemeVersionRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme_id,
SchemeVersionRecord.version_number == current_version_number,
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Current scheme version not found",
)
return row
async def update_scheme_version_display_artifact(
*,
scheme_version_id: str,
display_svg_storage_path: str,
display_svg_status: str,
display_svg_generated_at: datetime,
) -> None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_version_id == scheme_version_id
)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme version not found",
)
row.display_svg_storage_path = display_svg_storage_path
row.display_svg_status = display_svg_status
row.display_svg_generated_at = display_svg_generated_at
await session.commit()
async def create_next_scheme_version_from_current(scheme_id: str) -> SchemeVersionRecord:
async with AsyncSessionLocal() as session:
scheme_result = await session.execute(
select(SchemeRecord).where(SchemeRecord.scheme_id == scheme_id)
)
scheme = scheme_result.scalar_one_or_none()
if scheme is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme not found",
)
current_result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme.scheme_id,
SchemeVersionRecord.version_number == scheme.current_version_number,
)
)
current_version = current_result.scalar_one_or_none()
if current_version is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Current scheme version not found",
)
next_version_number = current_version.version_number + 1
new_version = SchemeVersionRecord(
scheme_version_id=uuid4().hex,
scheme_id=scheme.scheme_id,
version_number=next_version_number,
status="draft",
normalized_storage_path=current_version.normalized_storage_path,
normalized_elements_count=current_version.normalized_elements_count,
normalized_seats_count=current_version.normalized_seats_count,
normalized_groups_count=current_version.normalized_groups_count,
normalized_sectors_count=current_version.normalized_sectors_count,
display_svg_storage_path=current_version.display_svg_storage_path,
display_svg_status=current_version.display_svg_status,
display_svg_generated_at=current_version.display_svg_generated_at,
)
session.add(new_version)
scheme.current_version_number = next_version_number
scheme.status = "draft"
scheme.published_at = None
scheme.normalized_elements_count = current_version.normalized_elements_count
scheme.normalized_seats_count = current_version.normalized_seats_count
scheme.normalized_groups_count = current_version.normalized_groups_count
scheme.normalized_sectors_count = current_version.normalized_sectors_count
await session.commit()
await session.refresh(new_version)
return new_version

View File

@@ -0,0 +1,198 @@
from uuid import uuid4
from fastapi import HTTPException, status
from sqlalchemy import desc, func, select
from app.db.session import AsyncSessionLocal
from app.models.scheme import SchemeRecord
from app.models.scheme_version import SchemeVersionRecord
async def create_scheme_from_upload(
*,
source_upload_id: str,
name: str,
normalized_elements_count: int,
normalized_seats_count: int,
normalized_groups_count: int,
normalized_sectors_count: int,
) -> str:
scheme_id = uuid4().hex
async with AsyncSessionLocal() as session:
row = SchemeRecord(
scheme_id=scheme_id,
source_upload_id=source_upload_id,
name=name,
status="draft",
current_version_number=1,
normalized_elements_count=normalized_elements_count,
normalized_seats_count=normalized_seats_count,
normalized_groups_count=normalized_groups_count,
normalized_sectors_count=normalized_sectors_count,
)
session.add(row)
await session.commit()
return scheme_id
async def list_scheme_records(limit: int = 50, offset: int = 0) -> list[SchemeRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeRecord)
.order_by(desc(SchemeRecord.created_at), desc(SchemeRecord.id))
.limit(limit)
.offset(offset)
)
return list(result.scalars().all())
async def count_scheme_records() -> int:
async with AsyncSessionLocal() as session:
result = await session.execute(select(func.count()).select_from(SchemeRecord))
return int(result.scalar_one())
async def get_scheme_record_by_scheme_id(scheme_id: str) -> SchemeRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SchemeRecord).where(SchemeRecord.scheme_id == scheme_id)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme not found",
)
return row
async def publish_scheme(scheme_id: str) -> SchemeRecord:
async with AsyncSessionLocal() as session:
scheme_result = await session.execute(
select(SchemeRecord).where(SchemeRecord.scheme_id == scheme_id)
)
scheme = scheme_result.scalar_one_or_none()
if scheme is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme not found",
)
version_result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme.scheme_id,
SchemeVersionRecord.version_number == scheme.current_version_number,
)
)
version = version_result.scalar_one_or_none()
if version is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Current scheme version not found",
)
scheme.status = "published"
scheme.published_at = func.now()
version.status = "published"
await session.commit()
await session.refresh(scheme)
return scheme
async def unpublish_scheme(scheme_id: str) -> SchemeRecord:
async with AsyncSessionLocal() as session:
scheme_result = await session.execute(
select(SchemeRecord).where(SchemeRecord.scheme_id == scheme_id)
)
scheme = scheme_result.scalar_one_or_none()
if scheme is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme not found",
)
version_result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme.scheme_id,
SchemeVersionRecord.version_number == scheme.current_version_number,
)
)
version = version_result.scalar_one_or_none()
if version is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Current scheme version not found",
)
scheme.status = "draft"
scheme.published_at = None
version.status = "draft"
await session.commit()
await session.refresh(scheme)
return scheme
async def rollback_scheme_to_version(scheme_id: str, target_version_number: int) -> SchemeRecord:
async with AsyncSessionLocal() as session:
scheme_result = await session.execute(
select(SchemeRecord).where(SchemeRecord.scheme_id == scheme_id)
)
scheme = scheme_result.scalar_one_or_none()
if scheme is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Scheme not found",
)
target_result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme.scheme_id,
SchemeVersionRecord.version_number == target_version_number,
)
)
target_version = target_result.scalar_one_or_none()
if target_version is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Target scheme version not found",
)
current_result = await session.execute(
select(SchemeVersionRecord).where(
SchemeVersionRecord.scheme_id == scheme.scheme_id,
SchemeVersionRecord.version_number == scheme.current_version_number,
)
)
current_version = current_result.scalar_one_or_none()
if current_version is not None:
current_version.status = "draft"
target_version.status = "draft"
scheme.current_version_number = target_version.version_number
scheme.status = "draft"
scheme.published_at = None
scheme.normalized_elements_count = target_version.normalized_elements_count
scheme.normalized_seats_count = target_version.normalized_seats_count
scheme.normalized_groups_count = target_version.normalized_groups_count
scheme.normalized_sectors_count = target_version.normalized_sectors_count
await session.commit()
await session.refresh(scheme)
return scheme

View File

@@ -0,0 +1,79 @@
from fastapi import HTTPException, status
from sqlalchemy import desc, func, select
from app.db.session import AsyncSessionLocal
from app.models.upload import UploadRecord
async def create_upload_record(
*,
upload_id: str,
original_filename: str,
content_type: str,
size_bytes: int,
element_count: int,
removed_elements_count: int,
removed_attributes_count: int,
normalized_elements_count: int,
normalized_seats_count: int,
normalized_groups_count: int,
normalized_sectors_count: int,
original_storage_path: str,
sanitized_storage_path: str,
normalized_storage_path: str,
processing_status: str = "completed",
) -> None:
async with AsyncSessionLocal() as session:
row = UploadRecord(
upload_id=upload_id,
original_filename=original_filename,
content_type=content_type,
size_bytes=size_bytes,
element_count=element_count,
removed_elements_count=removed_elements_count,
removed_attributes_count=removed_attributes_count,
normalized_elements_count=normalized_elements_count,
normalized_seats_count=normalized_seats_count,
normalized_groups_count=normalized_groups_count,
normalized_sectors_count=normalized_sectors_count,
original_storage_path=original_storage_path,
sanitized_storage_path=sanitized_storage_path,
normalized_storage_path=normalized_storage_path,
processing_status=processing_status,
)
session.add(row)
await session.commit()
async def list_upload_records(limit: int = 50, offset: int = 0) -> list[UploadRecord]:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(UploadRecord)
.order_by(desc(UploadRecord.created_at), desc(UploadRecord.id))
.limit(limit)
.offset(offset)
)
return list(result.scalars().all())
async def count_upload_records() -> int:
async with AsyncSessionLocal() as session:
result = await session.execute(select(func.count()).select_from(UploadRecord))
value = result.scalar_one()
return int(value)
async def get_upload_record_by_upload_id(upload_id: str) -> UploadRecord:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(UploadRecord).where(UploadRecord.upload_id == upload_id)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Upload not found",
)
return row