from __future__ import annotations from sqlmodel import Field, SQLModel class User(SQLModel, table=True): id: str = Field(primary_key=True) private_token: str class UserPreference(SQLModel, table=True): user_id: str = Field(primary_key=True, foreign_key="user.id") show_images: bool = False class PlayPreference(SQLModel, table=True): user_id: str = Field(primary_key=True, foreign_key="user.id") kink_id: str = Field(primary_key=True, foreign_key="kink.id") interest_state: str directions_csv: str = "" class ScenarioPreference(SQLModel, table=True): user_id: str = Field(primary_key=True, foreign_key="user.id") parent_kink_id: str = Field(primary_key=True, foreign_key="kink.id") scenario_kink_id: str = Field(primary_key=True, foreign_key="kink.id") interest_state: str directions_csv: str = "" class RolePreference(SQLModel, table=True): user_id: str = Field(primary_key=True, foreign_key="user.id") kink_id: str = Field(primary_key=True, foreign_key="kink.id") class PartnerLink(SQLModel, table=True): left_user_id: str = Field(primary_key=True, foreign_key="user.id") right_user_id: str = Field(primary_key=True, foreign_key="user.id") class PartnerLinkRequest(SQLModel, table=True): """Pending link: from_user_id asked to connect with to_user_id (must accept).""" from_user_id: str = Field(primary_key=True, foreign_key="user.id") to_user_id: str = Field(primary_key=True, foreign_key="user.id") created_at: str = "" class PartnerGroup(SQLModel, table=True): id: str = Field(primary_key=True) owner_user_id: str = Field(index=True, foreign_key="user.id") name: str match_mode: str = "all_members" updated_at: str = "" class PartnerGroupMember(SQLModel, table=True): group_id: str = Field(primary_key=True, foreign_key="partnergroup.id") member_user_id: str = Field(primary_key=True, foreign_key="user.id") share_full_list: bool = False class PromptDismissal(SQLModel, table=True): user_id: str = Field(primary_key=True, foreign_key="user.id") kink_id: str = Field(primary_key=True, foreign_key="kink.id") class FetlifeCrawlJob(SQLModel, table=True): id: str = Field(primary_key=True) job_type: str target_key: str = Field(index=True) url: str state: str = "queued" attempts: int = 0 last_error: str = "" content_hash: str = "" discovered_from: str = "" updated_at: str = "" class FetlifeSupervisorRun(SQLModel, table=True): id: str = Field(primary_key=True) profile: str = "" pid: int = 0 state: str = "running" started_at: str = "" heartbeat_at: str = "" completed_at: str = "" current_job_id: str = "" current_job_type: str = "" current_target_key: str = "" jobs_claimed: int = 0 jobs_ok: int = 0 jobs_failed: int = 0 challenge_events: int = 0 restart_count: int = 0 last_error: str = "" class FetlifeKinkMeta(SQLModel, table=True): kink_id: str = Field(primary_key=True, foreign_key="kink.id") fetish_id: str = Field(index=True) source_url: str counts_json: str = "" popularity: float = 0.0 has_real_images: bool = False similar_count: int = 0 updated_at: str = "" class FetlifeUserSample(SQLModel, table=True): nickname: str = Field(primary_key=True) profile_url: str sampled_from_fetish_id: str = Field(index=True) state: str = "queued" updated_at: str = "" class FetlifeUserFetish(SQLModel, table=True): nickname: str = Field(primary_key=True) fetish_id: str = Field(primary_key=True) kink_id: str = Field(index=True) bucket: str activity: str = "" updated_at: str = "" class Source(SQLModel, table=True): id: str = Field(primary_key=True) name: str source_type: str base_url: str license_model: str notes: str = "" class Kink(SQLModel, table=True): id: str = Field(primary_key=True) name: str cluster: str short_definition: str notes: str = "" risk_level: str = "general" is_extreme: bool = False class Alias(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) alias: str source_id: str = Field(foreign_key="source.id") class KinkLemma(SQLModel, table=True): """Per-kink cached spaCy lemma signature used by play-dedupe at catalog build. Populated lazily on first build and persisted so subsequent boots avoid the ~55s spaCy pass over the full catalog. `name_hash` is the kink name at the time the signature was computed; a name change forces a recompute via the dedupe loop. """ kink_id: str = Field(primary_key=True, foreign_key="kink.id") name_hash: str signature: str class KinkFacet(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) field_name: str field_value: str class KinkExample(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) text: str kind: str class Definition(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) source_id: str = Field(foreign_key="source.id") text: str tone: str = "neutral" license: str = "" source_url: str = "" class Asset(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) source_id: str = Field(foreign_key="source.id") asset_url: str thumbnail_url: str = "" mime_type: str = "image/jpeg" license: str = "" creator: str = "" attribution_text: str = "" is_explicit: bool = False is_illustration: bool = False click_to_reveal: bool = True class FetlifePictureRef(SQLModel, table=True): id: str = Field(primary_key=True) kink_id: str = Field(foreign_key="kink.id", index=True) fetish_id: str = Field(index=True) attachment_id: str = Field(index=True) picture_page_url: str = "" picture_title: str = "" creator: str = "" caption_text: str = "" class SimilarityEdge(SQLModel, table=True): id: str = Field(primary_key=True) left_kink_id: str = Field(foreign_key="kink.id", index=True) right_kink_id: str = Field(foreign_key="kink.id", index=True) similarity_type: str score: float method: str version: str = "v1" class KinkScenarioParent(SQLModel, table=True): scenario_kink_id: str = Field(primary_key=True, foreign_key="kink.id") parent_kink_id: str = Field(primary_key=True, foreign_key="kink.id") score: float = 0.0 method: str = "" evidence_json: str = "" updated_at: str = "" class KinkContentType(SQLModel, table=True): kink_id: str = Field(primary_key=True, foreign_key="kink.id") content_kind: str = Field(index=True) evidence: str = "" updated_at: str = "" class KinkTypeOverride(SQLModel, table=True): id: str = Field(primary_key=True) normalized_name: str = Field(index=True) cluster: str = Field(index=True) content_kind: str = Field(index=True) note: str = ""