Update model translations

This commit is contained in:
qananasikq
2026-07-01 14:35:49 +03:00
commit 3904cd003e
53 changed files with 7629 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# Начальная схема: cars, images, sync_runs
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "001_initial"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Таблица cars — автомобили с Encar
op.create_table(
"cars",
sa.Column("id", sa.BigInteger, primary_key=True, autoincrement=True),
sa.Column("parser_id", sa.String(50), nullable=False, unique=True),
sa.Column("brand", sa.String(50), nullable=False),
sa.Column("model", sa.String(50), nullable=False),
sa.Column("year", sa.Integer, nullable=True),
sa.Column("price", sa.BigInteger, nullable=True),
sa.Column("currency", sa.String(10), nullable=False, server_default="USD"),
sa.Column("mileage", sa.Integer, nullable=False, server_default="0"),
sa.Column("country", sa.String(10), nullable=False, server_default="NA"),
sa.Column("is_sold", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("color", sa.String, nullable=False, server_default="other"),
sa.Column("drive", sa.String(10), nullable=True),
sa.Column("gearbox", sa.String(10), nullable=True),
sa.Column("steering_wheel", sa.String(10), nullable=True),
sa.Column("body_type", sa.String(20), nullable=False, server_default="OTHER"),
sa.Column("engine_volume", sa.Integer, nullable=True),
sa.Column("selling_type", sa.String(20), nullable=False, server_default="NA"),
sa.Column("one_owner", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("new_car", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("is_hidden", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("origin", sa.String(20), nullable=False, server_default="NA"),
sa.Column("origin_url", sa.String, nullable=False),
sa.Column("origin_id", sa.String, nullable=False, unique=True),
sa.Column("is_damaged", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("evaluation", sa.String, nullable=True),
sa.Column("non_smoking", sa.Boolean, nullable=False, server_default=sa.text("true")),
sa.Column("rental", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("repair_history", sa.Boolean, nullable=False, server_default=sa.text("false")),
sa.Column("slug", sa.String, nullable=False),
sa.Column("last_seen_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
)
op.create_index("ix_cars_origin_url", "cars", ["origin_url"])
op.create_index("ix_cars_origin_id", "cars", ["origin_id"], unique=True)
# Таблица images — изображения машин
op.create_table(
"images",
sa.Column("id", sa.BigInteger, primary_key=True, autoincrement=True),
sa.Column("fullres_image", sa.String, nullable=False),
sa.Column("preview_image", sa.String, nullable=False),
sa.Column("order_index", sa.Integer, nullable=False),
sa.Column("car_id", sa.Integer, sa.ForeignKey("cars.id", ondelete="CASCADE"), nullable=False),
)
# Таблица sync_runs — логирование синхронизаций
op.create_table(
"sync_runs",
sa.Column("id", sa.BigInteger, primary_key=True, autoincrement=True),
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.Column("finished_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("status", sa.Text, nullable=False),
sa.Column("lane", sa.Text, nullable=False),
sa.Column("ids_fetched", sa.Integer, nullable=False, server_default="0"),
sa.Column("cars_upserted", sa.Integer, nullable=False, server_default="0"),
sa.Column("cars_failed", sa.Integer, nullable=False, server_default="0"),
sa.Column("images_upserted", sa.Integer, nullable=False, server_default="0"),
sa.Column("error_summary", sa.Text, nullable=True),
)
def downgrade() -> None:
op.drop_table("sync_runs")
op.drop_table("images")
op.drop_table("cars")

View File

@@ -0,0 +1,29 @@
# Индексы производительности
from typing import Sequence, Union
from alembic import op
revision: str = "002_add_indexes"
down_revision: Union[str, None] = "001_initial"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_index("ix_cars_brand", "cars", ["brand"])
op.create_index("ix_cars_brand_model", "cars", ["brand", "model"])
op.create_index("ix_cars_year", "cars", ["year"])
op.create_index("ix_cars_is_sold", "cars", ["is_sold"])
op.create_index("ix_cars_last_seen_at", "cars", ["last_seen_at"])
op.create_index("ix_images_car_id", "images", ["car_id"])
op.create_index("ix_sync_runs_status", "sync_runs", ["status"])
def downgrade() -> None:
op.drop_index("ix_sync_runs_status", table_name="sync_runs")
op.drop_index("ix_images_car_id", table_name="images")
op.drop_index("ix_cars_last_seen_at", table_name="cars")
op.drop_index("ix_cars_is_sold", table_name="cars")
op.drop_index("ix_cars_year", table_name="cars")
op.drop_index("ix_cars_brand_model", table_name="cars")
op.drop_index("ix_cars_brand", table_name="cars")

View File

@@ -0,0 +1,38 @@
# Индексы для масштабированной БД (20k+ записей)
from typing import Sequence, Union
from alembic import op
revision: str = "003_add_composite_indexes"
down_revision: Union[str, None] = "002_add_indexes"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Partial index для активных машин Encar (is_sold = FALSE)
# Ускоряет поиск при mark_sold операциях
op.execute(
"CREATE INDEX IF NOT EXISTS ix_cars_active_encar "
"ON cars (origin_url) "
"WHERE is_sold = FALSE AND origin_id LIKE 'encar:%'"
)
# Composite index для проверки дубликатов изображений
op.create_index(
"ix_images_car_id_fullres",
"images",
["car_id", "fullres_image"],
)
# Index для быстрого поиска existing машин по origin_url
op.execute(
"CREATE INDEX IF NOT EXISTS ix_cars_origin_url_id "
"ON cars (origin_url, origin_id)"
)
def downgrade() -> None:
op.execute("DROP INDEX IF EXISTS ix_cars_origin_url_id")
op.drop_index("ix_images_car_id_fullres", table_name="images")
op.execute("DROP INDEX IF EXISTS ix_cars_active_encar")

View File

@@ -0,0 +1,25 @@
"""widen parser_id brand model columns
Revision ID: 004
Revises: 003_add_composite_indexes
Create Date: 2026-04-15
"""
from alembic import op
import sqlalchemy as sa
revision = "004_widen_string_columns"
down_revision = "003_add_composite_indexes"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.alter_column("cars", "parser_id", type_=sa.String(255), existing_nullable=False)
op.alter_column("cars", "brand", type_=sa.String(100), existing_nullable=False)
op.alter_column("cars", "model", type_=sa.String(500), existing_nullable=False)
def downgrade() -> None:
op.alter_column("cars", "parser_id", type_=sa.String(50), existing_nullable=False)
op.alter_column("cars", "brand", type_=sa.String(50), existing_nullable=False)
op.alter_column("cars", "model", type_=sa.String(50), existing_nullable=False)

View File

@@ -0,0 +1,33 @@
"""add unique constraint on images(car_id, fullres_image)
Revision ID: 005
Revises: 004_widen_string_columns
Create Date: 2026-04-16
"""
from alembic import op
revision = "005_unique_images_constraint"
down_revision = "004_widen_string_columns"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Удаляем дубли через CTE + ROW_NUMBER (быстрее чем NOT IN subquery)
op.execute("""
WITH dupes AS (
SELECT id,
ROW_NUMBER() OVER (PARTITION BY car_id, fullres_image ORDER BY id) AS rn
FROM images
)
DELETE FROM images WHERE id IN (SELECT id FROM dupes WHERE rn > 1)
""")
op.create_unique_constraint(
"uq_images_car_id_fullres",
"images",
["car_id", "fullres_image"],
)
def downgrade() -> None:
op.drop_constraint("uq_images_car_id_fullres", "images", type_="unique")

View File

@@ -0,0 +1,31 @@
"""widen images.car_id from integer to bigint
Revision ID: 006
Revises: 005_unique_images_constraint
Create Date: 2026-04-16
"""
import sqlalchemy as sa
from alembic import op
revision = "006_image_car_id_bigint"
down_revision = "005_unique_images_constraint"
def upgrade() -> None:
op.alter_column(
"images",
"car_id",
existing_type=sa.Integer(),
type_=sa.BigInteger(),
existing_nullable=False,
)
def downgrade() -> None:
op.alter_column(
"images",
"car_id",
existing_type=sa.BigInteger(),
type_=sa.Integer(),
existing_nullable=False,
)