Initial commit: svg backend
This commit is contained in:
18
backend/app/schemas/audit.py
Normal file
18
backend/app/schemas/audit.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AuditEventItem(BaseModel):
|
||||
audit_event_id: str
|
||||
scheme_id: str
|
||||
event_type: str
|
||||
object_type: str
|
||||
object_ref: str | None
|
||||
details_json: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeAuditResponse(BaseModel):
|
||||
items: List[AuditEventItem]
|
||||
total: int
|
||||
28
backend/app/schemas/manifest.py
Normal file
28
backend/app/schemas/manifest.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SvgLimitsManifest(BaseModel):
|
||||
max_file_size_bytes: int
|
||||
max_elements: int
|
||||
|
||||
|
||||
class SanitizationManifest(BaseModel):
|
||||
allow_internal_use_references_only: bool
|
||||
forbid_foreign_object_v1: bool
|
||||
forbid_style_v1: bool
|
||||
forbid_image_v1: bool
|
||||
allowed_data_attributes: list[str]
|
||||
|
||||
|
||||
class ExtractionContractManifest(BaseModel):
|
||||
seat_fields: list[str]
|
||||
priority: list[str]
|
||||
|
||||
|
||||
class ServiceManifestResponse(BaseModel):
|
||||
service: str
|
||||
api_prefix: str
|
||||
auth_header_name: str
|
||||
svg_limits: SvgLimitsManifest
|
||||
sanitization: SanitizationManifest
|
||||
extraction_contract: ExtractionContractManifest
|
||||
179
backend/app/schemas/pricing.py
Normal file
179
backend/app/schemas/pricing.py
Normal file
@@ -0,0 +1,179 @@
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel, field_validator
|
||||
|
||||
|
||||
class PricingCategoryCreateRequest(BaseModel):
|
||||
name: str
|
||||
code: str | None = None
|
||||
|
||||
|
||||
class PricingCategoryUpdateRequest(BaseModel):
|
||||
name: str
|
||||
code: str | None = None
|
||||
|
||||
|
||||
class PricingCategoryCreateResponse(BaseModel):
|
||||
pricing_category_id: str
|
||||
scheme_id: str
|
||||
name: str
|
||||
code: str | None
|
||||
|
||||
|
||||
class PricingCategoryUpdateResponse(BaseModel):
|
||||
pricing_category_id: str
|
||||
scheme_id: str
|
||||
name: str
|
||||
code: str | None
|
||||
|
||||
|
||||
class DeleteResponse(BaseModel):
|
||||
status: str
|
||||
|
||||
|
||||
class PriceRuleCreateRequest(BaseModel):
|
||||
pricing_category_id: str | None = None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str = "RUB"
|
||||
|
||||
@field_validator("target_type")
|
||||
@classmethod
|
||||
def validate_target_type(cls, value: str) -> str:
|
||||
allowed = {"sector", "group", "seat"}
|
||||
if value not in allowed:
|
||||
raise ValueError("Поле target_type должно быть одним из: sector, group, seat")
|
||||
return value
|
||||
|
||||
@field_validator("currency")
|
||||
@classmethod
|
||||
def validate_currency(cls, value: str) -> str:
|
||||
if value != "RUB":
|
||||
raise ValueError("В v1 поддерживается только валюта RUB")
|
||||
return value
|
||||
|
||||
@field_validator("amount", mode="before")
|
||||
@classmethod
|
||||
def parse_amount(cls, value):
|
||||
if value is None:
|
||||
raise ValueError("Поле amount обязательно")
|
||||
text = str(value).strip()
|
||||
if text == "":
|
||||
raise ValueError("Поле amount обязательно")
|
||||
try:
|
||||
return Decimal(text)
|
||||
except (InvalidOperation, ValueError):
|
||||
raise ValueError("Некорректная сумма. Используйте формат 2500.00")
|
||||
|
||||
@field_validator("amount")
|
||||
@classmethod
|
||||
def validate_amount(cls, value: Decimal) -> Decimal:
|
||||
if value < Decimal("0.00"):
|
||||
raise ValueError("Сумма не может быть отрицательной")
|
||||
if value.quantize(Decimal("0.01")) != value:
|
||||
raise ValueError("Сумма должна быть с точностью до 2 знаков после запятой")
|
||||
return value
|
||||
|
||||
|
||||
class PriceRuleUpdateRequest(BaseModel):
|
||||
pricing_category_id: str | None = None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str = "RUB"
|
||||
|
||||
@field_validator("target_type")
|
||||
@classmethod
|
||||
def validate_target_type(cls, value: str) -> str:
|
||||
allowed = {"sector", "group", "seat"}
|
||||
if value not in allowed:
|
||||
raise ValueError("Поле target_type должно быть одним из: sector, group, seat")
|
||||
return value
|
||||
|
||||
@field_validator("currency")
|
||||
@classmethod
|
||||
def validate_currency(cls, value: str) -> str:
|
||||
if value != "RUB":
|
||||
raise ValueError("В v1 поддерживается только валюта RUB")
|
||||
return value
|
||||
|
||||
@field_validator("amount", mode="before")
|
||||
@classmethod
|
||||
def parse_amount(cls, value):
|
||||
if value is None:
|
||||
raise ValueError("Поле amount обязательно")
|
||||
text = str(value).strip()
|
||||
if text == "":
|
||||
raise ValueError("Поле amount обязательно")
|
||||
try:
|
||||
return Decimal(text)
|
||||
except (InvalidOperation, ValueError):
|
||||
raise ValueError("Некорректная сумма. Используйте формат 2500.00")
|
||||
|
||||
@field_validator("amount")
|
||||
@classmethod
|
||||
def validate_amount(cls, value: Decimal) -> Decimal:
|
||||
if value < Decimal("0.00"):
|
||||
raise ValueError("Сумма не может быть отрицательной")
|
||||
if value.quantize(Decimal("0.01")) != value:
|
||||
raise ValueError("Сумма должна быть с точностью до 2 знаков после запятой")
|
||||
return value
|
||||
|
||||
|
||||
class PriceRuleCreateResponse(BaseModel):
|
||||
price_rule_id: str
|
||||
scheme_id: str
|
||||
pricing_category_id: str | None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str
|
||||
|
||||
|
||||
class PriceRuleUpdateResponse(BaseModel):
|
||||
price_rule_id: str
|
||||
scheme_id: str
|
||||
pricing_category_id: str | None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str
|
||||
|
||||
|
||||
class PricingCategoryItem(BaseModel):
|
||||
pricing_category_id: str
|
||||
scheme_id: str
|
||||
name: str
|
||||
code: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class PriceRuleItem(BaseModel):
|
||||
price_rule_id: str
|
||||
scheme_id: str
|
||||
pricing_category_id: str | None
|
||||
target_type: str
|
||||
target_ref: str
|
||||
amount: Decimal
|
||||
currency: str
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemePricingResponse(BaseModel):
|
||||
categories: List[PricingCategoryItem]
|
||||
rules: List[PriceRuleItem]
|
||||
|
||||
|
||||
class EffectiveSeatPriceResponse(BaseModel):
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
seat_id: str
|
||||
sector_id: str | None
|
||||
group_id: str | None
|
||||
matched_rule_level: str
|
||||
matched_target_ref: str
|
||||
pricing_category_id: str | None
|
||||
amount: Decimal
|
||||
currency: str
|
||||
19
backend/app/schemas/scheme_groups.py
Normal file
19
backend/app/schemas/scheme_groups.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SchemeGroupItem(BaseModel):
|
||||
group_record_id: str
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
element_id: str | None
|
||||
group_id: str | None
|
||||
name: str
|
||||
classes_raw: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeGroupListResponse(BaseModel):
|
||||
items: List[SchemeGroupItem]
|
||||
total: int
|
||||
67
backend/app/schemas/scheme_registry.py
Normal file
67
backend/app/schemas/scheme_registry.py
Normal file
@@ -0,0 +1,67 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SchemeListItem(BaseModel):
|
||||
scheme_id: str
|
||||
source_upload_id: str
|
||||
name: str
|
||||
status: str
|
||||
current_version_number: int
|
||||
published_at: str | None
|
||||
normalized_elements_count: int
|
||||
normalized_seats_count: int
|
||||
normalized_groups_count: int
|
||||
normalized_sectors_count: int
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeListResponse(BaseModel):
|
||||
items: List[SchemeListItem]
|
||||
total: int
|
||||
|
||||
|
||||
class SchemeDetailResponse(BaseModel):
|
||||
scheme_id: str
|
||||
source_upload_id: str
|
||||
name: str
|
||||
status: str
|
||||
current_version_number: int
|
||||
published_at: str | None
|
||||
normalized_elements_count: int
|
||||
normalized_seats_count: int
|
||||
normalized_groups_count: int
|
||||
normalized_sectors_count: int
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeCurrentResponse(BaseModel):
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
version_number: int
|
||||
status: str
|
||||
normalized_storage_path: str
|
||||
normalized_elements_count: int
|
||||
normalized_seats_count: int
|
||||
normalized_groups_count: int
|
||||
normalized_sectors_count: int
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemePublishResponse(BaseModel):
|
||||
scheme_id: str
|
||||
status: str
|
||||
current_version_number: int
|
||||
published_at: str | None
|
||||
|
||||
|
||||
class SchemeRollbackRequest(BaseModel):
|
||||
target_version_number: int
|
||||
|
||||
|
||||
class SchemeRollbackResponse(BaseModel):
|
||||
scheme_id: str
|
||||
status: str
|
||||
current_version_number: int
|
||||
published_at: str | None
|
||||
29
backend/app/schemas/scheme_seats.py
Normal file
29
backend/app/schemas/scheme_seats.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SchemeSeatItem(BaseModel):
|
||||
seat_record_id: str
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
element_id: str | None
|
||||
seat_id: str | None
|
||||
sector_id: str | None
|
||||
group_id: str | None
|
||||
row_label: str | None
|
||||
seat_number: str | None
|
||||
tag: str | None
|
||||
classes_raw: str | None
|
||||
x: float | None
|
||||
y: float | None
|
||||
cx: float | None
|
||||
cy: float | None
|
||||
width: float | None
|
||||
height: float | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeSeatListResponse(BaseModel):
|
||||
items: List[SchemeSeatItem]
|
||||
total: int
|
||||
19
backend/app/schemas/scheme_sectors.py
Normal file
19
backend/app/schemas/scheme_sectors.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SchemeSectorItem(BaseModel):
|
||||
sector_record_id: str
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
element_id: str | None
|
||||
sector_id: str | None
|
||||
name: str
|
||||
classes_raw: str | None
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeSectorListResponse(BaseModel):
|
||||
items: List[SchemeSectorItem]
|
||||
total: int
|
||||
29
backend/app/schemas/scheme_versions.py
Normal file
29
backend/app/schemas/scheme_versions.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SchemeVersionListItem(BaseModel):
|
||||
scheme_version_id: str
|
||||
scheme_id: str
|
||||
version_number: int
|
||||
status: str
|
||||
normalized_storage_path: str
|
||||
normalized_elements_count: int
|
||||
normalized_seats_count: int
|
||||
normalized_groups_count: int
|
||||
normalized_sectors_count: int
|
||||
created_at: str
|
||||
|
||||
|
||||
class SchemeVersionListResponse(BaseModel):
|
||||
items: List[SchemeVersionListItem]
|
||||
total: int
|
||||
|
||||
|
||||
class SchemeVersionCreateResponse(BaseModel):
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
version_number: int
|
||||
status: str
|
||||
normalized_storage_path: str
|
||||
21
backend/app/schemas/test_mode.py
Normal file
21
backend/app/schemas/test_mode.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class TestSeatPreviewResponse(BaseModel):
|
||||
scheme_id: str
|
||||
scheme_version_id: str
|
||||
seat_id: str
|
||||
element_id: str | None
|
||||
sector_id: str | None
|
||||
group_id: str | None
|
||||
row_label: str | None
|
||||
seat_number: str | None
|
||||
selectable: bool
|
||||
has_price: bool
|
||||
matched_rule_level: str | None
|
||||
matched_target_ref: str | None
|
||||
pricing_category_id: str | None
|
||||
amount: Decimal | None
|
||||
currency: str | None
|
||||
21
backend/app/schemas/upload.py
Normal file
21
backend/app/schemas/upload.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UploadResponse(BaseModel):
|
||||
upload_id: str
|
||||
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
|
||||
svg_max_file_size_bytes: int
|
||||
svg_max_elements: int
|
||||
original_storage_path: str
|
||||
sanitized_storage_path: str
|
||||
normalized_storage_path: str
|
||||
accepted: bool
|
||||
46
backend/app/schemas/upload_registry.py
Normal file
46
backend/app/schemas/upload_registry.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UploadListItem(BaseModel):
|
||||
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
|
||||
created_at: str
|
||||
|
||||
|
||||
class UploadListResponse(BaseModel):
|
||||
items: List[UploadListItem]
|
||||
total: int
|
||||
|
||||
|
||||
class UploadDetailResponse(BaseModel):
|
||||
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
|
||||
created_at: str
|
||||
Reference in New Issue
Block a user