"""initial
Revision ID: 33cad32b9bbd
Revises:
Create Date: 2026-06-11 22:57:15.838928
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '33cad32b9bbd'
down_revision: Union[str, Sequence[str], None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('data_sources',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('slug', sa.String(length=64), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('url_pattern', sa.String(length=512), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('slug')
)
op.create_table('property_types',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('slug', sa.String(length=64), nullable=False),
sa.Column('name', sa.String(length=128), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('slug')
)
op.create_table('raw_parsing_data',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('source_id', sa.Integer(), nullable=True),
sa.Column('external_id', sa.String(length=255), nullable=True),
sa.Column('payload', sa.JSON(), nullable=False),
sa.Column('status', sa.Enum('pending', 'processing', 'completed', 'failed', 'invalid', name='rawdatastatus'), nullable=False),
sa.Column('validation_result', sa.Enum('valid', 'invalid', 'uncertain', name='validationresult'), nullable=True),
sa.Column('error_message', sa.Text(), nullable=True),
sa.Column('received_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('processed_at', sa.TIMESTAMP(timezone=True), nullable=True),
sa.ForeignKeyConstraint(['source_id'], ['data_sources.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('source_id', 'external_id')
)
op.create_table('property_listings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('raw_data_id', sa.Integer(), nullable=True),
sa.Column('source_id', sa.Integer(), nullable=True),
sa.Column('external_id', sa.String(length=255), nullable=True),
sa.Column('title', sa.String(length=512), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('generated_description', sa.Text(), nullable=True),
sa.Column('deal_type', sa.Enum('sale', 'rent_long', 'rent_short', name='dealtype'), nullable=True),
sa.Column('property_type_id', sa.Integer(), nullable=True),
sa.Column('price', sa.Numeric(precision=15, scale=2), nullable=True),
sa.Column('currency', sa.String(length=3), nullable=True),
sa.Column('original_price', sa.Numeric(precision=15, scale=2), nullable=True),
sa.Column('original_currency', sa.String(length=3), nullable=True),
sa.Column('price_per_sqm', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('total_area', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('living_area', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('kitchen_area', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('land_area', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('rooms_count', sa.SmallInteger(), nullable=True),
sa.Column('bedrooms_count', sa.SmallInteger(), nullable=True),
sa.Column('bathrooms_count', sa.SmallInteger(), nullable=True),
sa.Column('layout', sa.Enum('studio', 'separate', 'adjacent', name='layouttype'), nullable=True),
sa.Column('floor', sa.SmallInteger(), nullable=True),
sa.Column('floors_total', sa.SmallInteger(), nullable=True),
sa.Column('building_year', sa.SmallInteger(), nullable=True),
sa.Column('building_type', sa.Enum('brick', 'panel', 'monolith', 'gas_block', 'wood', name='buildingtype'), nullable=True),
sa.Column('renovation_status', sa.Enum('cosmetic', 'euro', 'designer', 'none', 'construction', name='renovationstatus'), nullable=True),
sa.Column('ceiling_height', sa.Numeric(precision=4, scale=2), nullable=True),
sa.Column('material', sa.String(length=128), nullable=True),
sa.Column('has_balcony', sa.Boolean(), nullable=True),
sa.Column('has_loggia', sa.Boolean(), nullable=True),
sa.Column('balcony_count', sa.SmallInteger(), nullable=True),
sa.Column('loggia_count', sa.SmallInteger(), nullable=True),
sa.Column('bathroom_type', sa.Enum('combined', 'separate', 'multiple', name='bathroomtype'), nullable=True),
sa.Column('elevator_count', sa.SmallInteger(), nullable=True),
sa.Column('has_freight_elevator', sa.Boolean(), nullable=True),
sa.Column('parking_type', sa.Enum('ground', 'underground', 'none', 'garage', name='parkingtype'), nullable=True),
sa.Column('heating_type', sa.Enum('central', 'autonomous', 'floor', 'none', name='heatingtype'), nullable=True),
sa.Column('internet', sa.Boolean(), nullable=True),
sa.Column('security', sa.Boolean(), nullable=True),
sa.Column('windows_direction', sa.String(length=128), nullable=True),
sa.Column('window_view', sa.Enum('yard', 'street', 'park', 'water', 'forest', name='windowview'), nullable=True),
sa.Column('address_raw', sa.Text(), nullable=True),
sa.Column('city', sa.String(length=128), nullable=True),
sa.Column('district', sa.String(length=128), nullable=True),
sa.Column('micro_district', sa.String(length=128), nullable=True),
sa.Column('street', sa.String(length=128), nullable=True),
sa.Column('house_number', sa.String(length=32), nullable=True),
sa.Column('metro_station', sa.String(length=128), nullable=True),
sa.Column('metro_distance_min', sa.SmallInteger(), nullable=True),
sa.Column('metro_distance_type', sa.Enum('walk', 'transport', name='metrodistancetype'), nullable=True),
sa.Column('latitude', sa.Numeric(precision=10, scale=8), nullable=True),
sa.Column('longitude', sa.Numeric(precision=11, scale=8), nullable=True),
sa.Column('contact_phone', sa.String(length=64), nullable=True),
sa.Column('contact_name', sa.String(length=255), nullable=True),
sa.Column('contact_email', sa.String(length=255), nullable=True),
sa.Column('is_agent', sa.Boolean(), nullable=True),
sa.Column('agency_name', sa.String(length=255), nullable=True),
sa.Column('publish_date', sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column('url_source', sa.Text(), nullable=True),
sa.Column('listing_status', sa.Enum('active', 'sold', 'rented', 'removed', 'archived', name='listingstatus'), nullable=True),
sa.Column('images_count', sa.Integer(), nullable=True),
sa.Column('listing_quality_score', sa.SmallInteger(), nullable=True),
sa.Column('reliability_rating', sa.SmallInteger(), nullable=True),
sa.Column('sentiment_score', sa.Numeric(precision=3, scale=2), nullable=True),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.ForeignKeyConstraint(['property_type_id'], ['property_types.id'], ),
sa.ForeignKeyConstraint(['raw_data_id'], ['raw_parsing_data.id'], ),
sa.ForeignKeyConstraint(['source_id'], ['data_sources.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('raw_data_id'),
sa.UniqueConstraint('source_id', 'external_id')
)
op.create_table('ai_enrichments',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('property_id', sa.Integer(), nullable=False),
sa.Column('extracted_features', sa.JSON(), nullable=False),
sa.Column('price_assessment', sa.JSON(), nullable=False),
sa.Column('listing_quality_score', sa.SmallInteger(), nullable=True),
sa.Column('reliability_rating', sa.SmallInteger(), nullable=True),
sa.Column('sentiment_score', sa.Numeric(precision=3, scale=2), nullable=True),
sa.Column('classification', sa.String(length=64), nullable=True),
sa.Column('image_analysis_results', sa.JSON(), nullable=False),
sa.Column('generated_description', sa.Text(), nullable=True),
sa.Column('summary', sa.Text(), nullable=True),
sa.Column('model_version', sa.String(length=64), nullable=True),
sa.Column('processing_time_ms', sa.Integer(), nullable=True),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.ForeignKeyConstraint(['property_id'], ['property_listings.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('property_id')
)
op.create_table('property_custom_fields',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('property_id', sa.Integer(), nullable=False),
sa.Column('field_name', sa.String(length=128), nullable=False),
sa.Column('field_value', sa.Text(), nullable=False),
sa.Column('field_type', sa.Enum('str', 'int', 'float', 'bool', 'date', 'json', name='customfieldtype'), nullable=False),
sa.ForeignKeyConstraint(['property_id'], ['property_listings.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('property_id', 'field_name')
)
op.create_table('property_images',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('property_id', sa.Integer(), nullable=False),
sa.Column('url', sa.Text(), nullable=False),
sa.Column('local_path', sa.String(length=512), nullable=True),
sa.Column('hash', sa.String(length=64), nullable=True),
sa.Column('file_size', sa.Integer(), nullable=True),
sa.Column('width', sa.SmallInteger(), nullable=True),
sa.Column('height', sa.SmallInteger(), nullable=True),
sa.Column('download_status', sa.Enum('pending', 'downloaded', 'failed', name='imagedownloadstatus'), nullable=False),
sa.Column('ai_description', sa.Text(), nullable=True),
sa.Column('analysis_status', sa.Enum('pending', 'completed', 'failed', name='imageanalysisstatus'), nullable=False),
sa.Column('order_index', sa.SmallInteger(), nullable=False),
sa.ForeignKeyConstraint(['property_id'], ['property_listings.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
op.create_table('property_snapshots',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('property_id', sa.Integer(), nullable=False),
sa.Column('snapshot_data', sa.JSON(), nullable=False),
sa.Column('changed_fields', sa.JSON(), nullable=False),
sa.Column('created_at', sa.TIMESTAMP(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.ForeignKeyConstraint(['property_id'], ['property_listings.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('property_snapshots')
op.drop_table('property_images')
op.drop_table('property_custom_fields')
op.drop_table('ai_enrichments')
op.drop_table('property_listings')
op.drop_table('raw_parsing_data')
op.drop_table('property_types')
op.drop_table('data_sources')
# ### end Alembic commands ###