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

76
backend/alembic/env.py Normal file
View File

@@ -0,0 +1,76 @@
from logging.config import fileConfig
from alembic import context
from sqlalchemy import pool
from sqlalchemy.engine import Connection
from sqlalchemy.ext.asyncio import async_engine_from_config
from app.core.config import settings
from app.db.base import Base
from app.models.audit_event import AuditEventRecord
from app.models.price_rule import PriceRuleRecord
from app.models.pricing_category import PricingCategoryRecord
from app.models.scheme import SchemeRecord
from app.models.scheme_group import SchemeGroupRecord
from app.models.scheme_seat import SchemeSeatRecord
from app.models.scheme_sector import SchemeSectorRecord
from app.models.scheme_version import SchemeVersionRecord
from app.models.upload import UploadRecord
config = context.config
config.set_main_option("sqlalchemy.url", settings.database_url)
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
def run_migrations_offline() -> None:
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
compare_type=True,
)
with context.begin_transaction():
context.run_migrations()
def do_run_migrations(connection: Connection) -> None:
context.configure(
connection=connection,
target_metadata=target_metadata,
compare_type=True,
)
with context.begin_transaction():
context.run_migrations()
async def run_migrations_online() -> None:
connectable = async_engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
async with connectable.connect() as connection:
await connection.run_sync(do_run_migrations)
await connectable.dispose()
def main() -> None:
if context.is_offline_mode():
run_migrations_offline()
else:
import asyncio
asyncio.run(run_migrations_online())
main()

View File

@@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = None
depends_on = None
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1,44 @@
"""create uploads table
Revision ID: 20260316_01
Revises: None
Create Date: 2026-03-16 14:50:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_01"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"uploads",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("upload_id", sa.String(length=32), nullable=False),
sa.Column("original_filename", sa.String(length=512), nullable=False),
sa.Column("content_type", sa.String(length=255), nullable=False),
sa.Column("size_bytes", sa.BigInteger(), nullable=False),
sa.Column("element_count", sa.Integer(), nullable=False),
sa.Column("removed_elements_count", sa.Integer(), nullable=False),
sa.Column("removed_attributes_count", sa.Integer(), nullable=False),
sa.Column("normalized_elements_count", sa.Integer(), nullable=False),
sa.Column("normalized_seats_count", sa.Integer(), nullable=False),
sa.Column("normalized_groups_count", sa.Integer(), nullable=False),
sa.Column("normalized_sectors_count", sa.Integer(), nullable=False),
sa.Column("original_storage_path", sa.Text(), nullable=False),
sa.Column("sanitized_storage_path", sa.Text(), nullable=False),
sa.Column("normalized_storage_path", sa.Text(), nullable=False),
sa.Column("processing_status", sa.String(length=32), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
)
op.create_index("ix_uploads_upload_id", "uploads", ["upload_id"], unique=True)
def downgrade() -> None:
op.drop_index("ix_uploads_upload_id", table_name="uploads")
op.drop_table("uploads")

View File

@@ -0,0 +1,40 @@
"""create schemes table
Revision ID: 20260316_02
Revises: 20260316_01
Create Date: 2026-03-16 15:15:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_02"
down_revision = "20260316_01"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"schemes",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("source_upload_id", sa.String(length=32), nullable=False),
sa.Column("name", sa.String(length=512), nullable=False),
sa.Column("status", sa.String(length=32), nullable=False),
sa.Column("normalized_elements_count", sa.Integer(), nullable=False),
sa.Column("normalized_seats_count", sa.Integer(), nullable=False),
sa.Column("normalized_groups_count", sa.Integer(), nullable=False),
sa.Column("normalized_sectors_count", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["source_upload_id"], ["uploads.upload_id"], ondelete="RESTRICT"),
)
op.create_index("ix_schemes_scheme_id", "schemes", ["scheme_id"], unique=True)
op.create_index("ix_schemes_source_upload_id", "schemes", ["source_upload_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_schemes_source_upload_id", table_name="schemes")
op.drop_index("ix_schemes_scheme_id", table_name="schemes")
op.drop_table("schemes")

View File

@@ -0,0 +1,35 @@
"""create scheme_versions table
Revision ID: 20260316_03
Revises: 20260316_02
Create Date: 2026-03-16 15:25:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_03"
down_revision = "20260316_02"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"scheme_versions",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("scheme_version_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("version_number", sa.Integer(), nullable=False),
sa.Column("status", sa.String(length=32), nullable=False),
sa.Column("normalized_storage_path", sa.Text(), nullable=False),
sa.Column("normalized_elements_count", sa.Integer(), nullable=False),
sa.Column("normalized_seats_count", sa.Integer(), nullable=False),
sa.Column("normalized_groups_count", sa.Integer(), nullable=False),
sa.Column("normalized_sectors_count", sa.Integer(), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
)
op.create_index("ix_scheme_versions_scheme_version_id", "scheme_versions", ["scheme_version_id"], unique=True)
op.create_index("ix_scheme_versions_scheme_id", "scheme_versions", ["scheme_id"], unique=False)

View File

@@ -0,0 +1,27 @@
"""add current_version_number to schemes
Revision ID: 20260316_04
Revises: 20260316_03
Create Date: 2026-03-16 15:30:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_04"
down_revision = "20260316_03"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"schemes",
sa.Column("current_version_number", sa.Integer(), nullable=False, server_default="1"),
)
op.alter_column("schemes", "current_version_number", server_default=None)
def downgrade() -> None:
op.drop_column("schemes", "current_version_number")

View File

@@ -0,0 +1,26 @@
"""add published_at to schemes
Revision ID: 20260316_05
Revises: 20260316_04
Create Date: 2026-03-16 15:40:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_05"
down_revision = "20260316_04"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"schemes",
sa.Column("published_at", sa.DateTime(timezone=True), nullable=True),
)
def downgrade() -> None:
op.drop_column("schemes", "published_at")

View File

@@ -0,0 +1,42 @@
"""create scheme_sectors table
Revision ID: 20260316_06
Revises: 20260316_05
Create Date: 2026-03-16 15:55:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_06"
down_revision = "20260316_05"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"scheme_sectors",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("sector_record_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("scheme_version_id", sa.String(length=32), nullable=False),
sa.Column("element_id", sa.String(length=512), nullable=True),
sa.Column("sector_id", sa.String(length=255), nullable=True),
sa.Column("name", sa.String(length=512), nullable=False),
sa.Column("classes_raw", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
sa.ForeignKeyConstraint(["scheme_version_id"], ["scheme_versions.scheme_version_id"], ondelete="RESTRICT"),
)
op.create_index("ix_scheme_sectors_sector_record_id", "scheme_sectors", ["sector_record_id"], unique=True)
op.create_index("ix_scheme_sectors_scheme_id", "scheme_sectors", ["scheme_id"], unique=False)
op.create_index("ix_scheme_sectors_scheme_version_id", "scheme_sectors", ["scheme_version_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_scheme_sectors_scheme_version_id", table_name="scheme_sectors")
op.drop_index("ix_scheme_sectors_scheme_id", table_name="scheme_sectors")
op.drop_index("ix_scheme_sectors_sector_record_id", table_name="scheme_sectors")
op.drop_table("scheme_sectors")

View File

@@ -0,0 +1,42 @@
"""create scheme_groups table
Revision ID: 20260316_07
Revises: 20260316_06
Create Date: 2026-03-16 16:05:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_07"
down_revision = "20260316_06"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"scheme_groups",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("group_record_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("scheme_version_id", sa.String(length=32), nullable=False),
sa.Column("element_id", sa.String(length=512), nullable=True),
sa.Column("group_id", sa.String(length=255), nullable=True),
sa.Column("name", sa.String(length=512), nullable=False),
sa.Column("classes_raw", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
sa.ForeignKeyConstraint(["scheme_version_id"], ["scheme_versions.scheme_version_id"], ondelete="RESTRICT"),
)
op.create_index("ix_scheme_groups_group_record_id", "scheme_groups", ["group_record_id"], unique=True)
op.create_index("ix_scheme_groups_scheme_id", "scheme_groups", ["scheme_id"], unique=False)
op.create_index("ix_scheme_groups_scheme_version_id", "scheme_groups", ["scheme_version_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_scheme_groups_scheme_version_id", table_name="scheme_groups")
op.drop_index("ix_scheme_groups_scheme_id", table_name="scheme_groups")
op.drop_index("ix_scheme_groups_group_record_id", table_name="scheme_groups")
op.drop_table("scheme_groups")

View File

@@ -0,0 +1,52 @@
"""create scheme_seats table
Revision ID: 20260316_08
Revises: 20260316_07
Create Date: 2026-03-16 16:20:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_08"
down_revision = "20260316_07"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"scheme_seats",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("seat_record_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("scheme_version_id", sa.String(length=32), nullable=False),
sa.Column("element_id", sa.String(length=512), nullable=True),
sa.Column("seat_id", sa.String(length=255), nullable=True),
sa.Column("sector_id", sa.String(length=255), nullable=True),
sa.Column("group_id", sa.String(length=255), nullable=True),
sa.Column("row_label", sa.String(length=255), nullable=True),
sa.Column("seat_number", sa.String(length=255), nullable=True),
sa.Column("tag", sa.String(length=64), nullable=True),
sa.Column("classes_raw", sa.Text(), nullable=True),
sa.Column("x", sa.Float(), nullable=True),
sa.Column("y", sa.Float(), nullable=True),
sa.Column("cx", sa.Float(), nullable=True),
sa.Column("cy", sa.Float(), nullable=True),
sa.Column("width", sa.Float(), nullable=True),
sa.Column("height", sa.Float(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
sa.ForeignKeyConstraint(["scheme_version_id"], ["scheme_versions.scheme_version_id"], ondelete="RESTRICT"),
)
op.create_index("ix_scheme_seats_seat_record_id", "scheme_seats", ["seat_record_id"], unique=True)
op.create_index("ix_scheme_seats_scheme_id", "scheme_seats", ["scheme_id"], unique=False)
op.create_index("ix_scheme_seats_scheme_version_id", "scheme_seats", ["scheme_version_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_scheme_seats_scheme_version_id", table_name="scheme_seats")
op.drop_index("ix_scheme_seats_scheme_id", table_name="scheme_seats")
op.drop_index("ix_scheme_seats_seat_record_id", table_name="scheme_seats")
op.drop_table("scheme_seats")

View File

@@ -0,0 +1,59 @@
"""create pricing tables
Revision ID: 20260316_09
Revises: 20260316_08
Create Date: 2026-03-16 16:40:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_09"
down_revision = "20260316_08"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"pricing_categories",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("pricing_category_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("name", sa.String(length=255), nullable=False),
sa.Column("code", sa.String(length=128), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
)
op.create_index("ix_pricing_categories_pricing_category_id", "pricing_categories", ["pricing_category_id"], unique=True)
op.create_index("ix_pricing_categories_scheme_id", "pricing_categories", ["scheme_id"], unique=False)
op.create_table(
"price_rules",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("price_rule_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("pricing_category_id", sa.String(length=32), nullable=True),
sa.Column("target_type", sa.String(length=32), nullable=False),
sa.Column("target_ref", sa.String(length=255), nullable=False),
sa.Column("amount", sa.Numeric(12, 2), nullable=False),
sa.Column("currency", sa.String(length=3), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
sa.ForeignKeyConstraint(["pricing_category_id"], ["pricing_categories.pricing_category_id"], ondelete="SET NULL"),
)
op.create_index("ix_price_rules_price_rule_id", "price_rules", ["price_rule_id"], unique=True)
op.create_index("ix_price_rules_scheme_id", "price_rules", ["scheme_id"], unique=False)
op.create_index("ix_price_rules_pricing_category_id", "price_rules", ["pricing_category_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_price_rules_pricing_category_id", table_name="price_rules")
op.drop_index("ix_price_rules_scheme_id", table_name="price_rules")
op.drop_index("ix_price_rules_price_rule_id", table_name="price_rules")
op.drop_table("price_rules")
op.drop_index("ix_pricing_categories_scheme_id", table_name="pricing_categories")
op.drop_index("ix_pricing_categories_pricing_category_id", table_name="pricing_categories")
op.drop_table("pricing_categories")

View File

@@ -0,0 +1,38 @@
"""create audit_events table
Revision ID: 20260316_10
Revises: 20260316_09
Create Date: 2026-03-16 17:00:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260316_10"
down_revision = "20260316_09"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"audit_events",
sa.Column("id", sa.BigInteger(), primary_key=True, autoincrement=True),
sa.Column("audit_event_id", sa.String(length=32), nullable=False),
sa.Column("scheme_id", sa.String(length=32), nullable=False),
sa.Column("event_type", sa.String(length=64), nullable=False),
sa.Column("object_type", sa.String(length=64), nullable=False),
sa.Column("object_ref", sa.String(length=255), nullable=True),
sa.Column("details_json", sa.Text(), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
sa.ForeignKeyConstraint(["scheme_id"], ["schemes.scheme_id"], ondelete="RESTRICT"),
)
op.create_index("ix_audit_events_audit_event_id", "audit_events", ["audit_event_id"], unique=True)
op.create_index("ix_audit_events_scheme_id", "audit_events", ["scheme_id"], unique=False)
def downgrade() -> None:
op.drop_index("ix_audit_events_scheme_id", table_name="audit_events")
op.drop_index("ix_audit_events_audit_event_id", table_name="audit_events")
op.drop_table("audit_events")

View File

@@ -0,0 +1,27 @@
"""add display svg fields to scheme_versions
Revision ID: 20260318_11
Revises: 20260316_10
Create Date: 2026-03-18 12:30:00
"""
from alembic import op
import sqlalchemy as sa
revision = "20260318_11"
down_revision = "20260316_10"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("scheme_versions", sa.Column("display_svg_storage_path", sa.Text(), nullable=True))
op.add_column("scheme_versions", sa.Column("display_svg_status", sa.String(length=32), nullable=False, server_default="pending"))
op.add_column("scheme_versions", sa.Column("display_svg_generated_at", sa.DateTime(timezone=True), nullable=True))
def downgrade() -> None:
op.drop_column("scheme_versions", "display_svg_generated_at")
op.drop_column("scheme_versions", "display_svg_status")
op.drop_column("scheme_versions", "display_svg_storage_path")