Initial commit: svg backend
This commit is contained in:
0
backend/app/repositories/__init__.py
Normal file
0
backend/app/repositories/__init__.py
Normal file
42
backend/app/repositories/audit.py
Normal file
42
backend/app/repositories/audit.py
Normal 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())
|
||||
237
backend/app/repositories/pricing.py
Normal file
237
backend/app/repositories/pricing.py
Normal 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",
|
||||
)
|
||||
74
backend/app/repositories/scheme_groups.py
Normal file
74
backend/app/repositories/scheme_groups.py
Normal 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()
|
||||
118
backend/app/repositories/scheme_seats.py
Normal file
118
backend/app/repositories/scheme_seats.py
Normal 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()
|
||||
74
backend/app/repositories/scheme_sectors.py
Normal file
74
backend/app/repositories/scheme_sectors.py
Normal 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()
|
||||
169
backend/app/repositories/scheme_versions.py
Normal file
169
backend/app/repositories/scheme_versions.py
Normal 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
|
||||
198
backend/app/repositories/schemes.py
Normal file
198
backend/app/repositories/schemes.py
Normal 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
|
||||
79
backend/app/repositories/uploads.py
Normal file
79
backend/app/repositories/uploads.py
Normal 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
|
||||
Reference in New Issue
Block a user