from datetime import date from typing import Optional from slugify import slugify from sqlalchemy.engine.base import Connection from sqlalchemy.event.api import listens_for from sqlalchemy.orm.mapper import Mapper from sqlalchemy.sql.expression import select from sqlalchemy.sql.functions import count from sqlalchemy.sql.schema import Column from sqlalchemy.sql.sqltypes import Unicode from sqlalchemy_utils.types import CountryType from sqlmodel.main import Field, Relationship from compub.utils import multi_max from .base import AbstractBase __all__ = [ 'LegalForm', 'LegalFormSubcategory', 'Industry', 'Company', 'CompanyName', ] class LegalForm(AbstractBase, table=True): __tablename__ = 'legal_form' # Fields short: str = Field(max_length=32, nullable=False, index=True) name: Optional[str] = Field(default=None, max_length=255, sa_column=Column(Unicode(255))) country: str = Field(sa_column=Column(CountryType)) # Relationships subcategories: list['LegalFormSubcategory'] = Relationship( back_populates='legal_form', sa_relationship_kwargs={'lazy': 'selectin'} ) def __str__(self) -> str: return str(self.short) class LegalFormSubcategory(AbstractBase, table=True): __tablename__ = 'legal_form_subcategory' # Fields short: str = Field(max_length=32, nullable=False, index=True) name: Optional[str] = Field(default=None, max_length=255, sa_column=Column(Unicode(255))) # Relationships legal_form_id: Optional[int] = Field( foreign_key='legal_form.id', default=None, nullable=False, index=True ) legal_form: Optional[LegalForm] = Relationship( back_populates='subcategories', sa_relationship_kwargs={'lazy': 'selectin'} ) companies: list['Company'] = Relationship( back_populates='legal_form', sa_relationship_kwargs={'lazy': 'selectin'} ) def __str__(self) -> str: return str(self.short) class CompanyIndustryLink(AbstractBase, table=True): __tablename__ = 'company_industries' # Relationships company_id: Optional[int] = Field(foreign_key='company.id', default=None, nullable=False, primary_key=True) industry_id: Optional[int] = Field(foreign_key='industry.id', default=None, nullable=False, primary_key=True) class Industry(AbstractBase, table=True): __tablename__ = 'industry' # Fields name: str = Field(max_length=255, nullable=False, index=True) # Relationships companies: list['Company'] = Relationship( back_populates='industries', link_model=CompanyIndustryLink, sa_relationship_kwargs={'lazy': 'selectin'} ) def __str__(self) -> str: return str(self.name) class Company(AbstractBase, table=True): __tablename__ = 'company' # Fields visible: bool = Field(default=True, nullable=False, index=True) insolvent: bool = Field(default=False, nullable=False, index=True) founding_data: Optional[date] liquidation_date: Optional[date] city: str = Field(max_length=255, index=True) # TODO: Get rid of city; implement address properly # Relationships legal_form_id: Optional[int] = Field( foreign_key='legal_form_subcategory.id', default=None, index=True ) legal_form: Optional[LegalFormSubcategory] = Relationship( back_populates='companies', sa_relationship_kwargs={'lazy': 'selectin'} ) address_id: Optional[int] = Field( foreign_key='address.id', default=None, index=True ) industries: list[Industry] = Relationship( back_populates='companies', link_model=CompanyIndustryLink, sa_relationship_kwargs={'lazy': 'selectin'} ) names: list['CompanyName'] = Relationship( back_populates='company', sa_relationship_kwargs={'lazy': 'selectin'} ) def __str__(self) -> str: return str(self.current_name or f"") @property def current_name(self) -> Optional['CompanyName']: return multi_max(list(self.names), CompanyName.get_reg_date, 'date_updated', default=None) class CompanyName(AbstractBase, table=True): __tablename__ = 'company_name' __MAX_NAME_LENGTH__: int = 768 __MAX_SLUG_LENGTH__: int = 255 # Fields name: str = Field( max_length=__MAX_NAME_LENGTH__, sa_column=Column(Unicode(__MAX_NAME_LENGTH__), nullable=False, index=True) ) date_registered: Optional[date] slug: Optional[str] = Field(default=None, max_length=__MAX_SLUG_LENGTH__, index=True) # Relationships company_id: Optional[int] = Field( foreign_key='company.id', default=None, nullable=False, index=True ) company: Optional[Company] = Relationship( back_populates='names', sa_relationship_kwargs={'lazy': 'selectin'} ) def __str__(self) -> str: return str(self.name) def get_reg_date(self) -> date: return date(1, 1, 1) if self.date_registered is None else self.date_registered @property def max_slug_length(self) -> int: return self.__MAX_SLUG_LENGTH__ @listens_for(CompanyName, 'before_insert') def generate_company_name_slug(_mapper: Mapper, connection: Connection, target: CompanyName) -> None: if target.slug: return slug = slugify(target.name)[:(target.max_slug_length - 2)] statement = select(count()).select_from(CompanyName).where(CompanyName.slug.startswith(slug)) num = connection.execute(statement).scalar() if num == 0: target.slug = slug else: target.slug = f'{slug}-{str(num + 1)}'