Perplexed7675's picture
Sync from kink_cli (Docker Space)
44370b5 verified
Raw
History Blame Contribute Delete
15.1 kB
"""Backend facade that composes all service modules."""
from __future__ import annotations
from pathlib import Path
import threading
from typing import Any
from sqlalchemy import event as sa_event
from sqlmodel import SQLModel, create_engine
from backend import catalog as _catalog_mod
from backend import content_types as _content_types_mod
from backend import db as _db_mod
from backend import enrichment as _enrichment_mod
from backend import fetlife_data as _fetlife_data_mod
from backend import fetlife_queue as _fetlife_queue_mod
from backend import ingestion as _ingestion_mod
from backend import partners as _partners_mod
from backend import recommendations as _recommendations_mod
from backend import scenarios as _scenarios_mod
from backend import search as _search_mod
from backend import shared_play as _shared_play_mod
from backend import similarity as _similarity_mod
from backend import users as _users_mod
from backend.models import RecsysSettings
from backend.recsys_env_overrides import load_recsys_settings
class Backend:
def __init__(self, path: Path, *, recsys_settings: RecsysSettings | None = None) -> None:
self.path = path
self.recsys_settings = load_recsys_settings(path, recsys_settings)
self.path.parent.mkdir(parents=True, exist_ok=True)
from sqlalchemy.pool import NullPool
self.engine = create_engine(
f"sqlite:///{self.path}",
echo=False,
connect_args={"timeout": 30, "check_same_thread": False},
poolclass=NullPool,
)
@sa_event.listens_for(self.engine, "connect")
def _set_sqlite_pragmas(dbapi_connection, _connection_record) -> None:
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA journal_mode=WAL")
cursor.execute("PRAGMA synchronous=NORMAL")
cursor.execute("PRAGMA busy_timeout=30000")
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
SQLModel.metadata.create_all(self.engine)
self._migrate_user_table()
self._migrate_legacy_ratings()
self._migrate_partner_group_member()
self._migrate_duplicate_pair_groups()
self._seed_type_overrides()
self._ensure_runtime_indexes()
self._catalog_cache: dict[str, Any] | None = None
self._catalog_token: tuple[Any, ...] | None = None
self._catalog_build_lock = threading.Lock()
self._last_cache_invalidation_at = 0.0
self._ppr_graph_cache: Any = None
self._ppr_transition_cache: Any = None
self._top_edges_by_source_cache: dict[str, list[dict[str, Any]]] = {}
self._ensure_fts_schema()
def invalidate_recommendation_cache(self) -> None:
"""Compatibility hook for play mutations.
Recommendations are recomputed against fresh user state on every call; the old
per-process HTTP cache was removed because it could serve stale decks in
multi-process deployments.
"""
return
def peek_catalog_kink_id(self) -> str | None:
"""Return any stored kink id (single-row probe for deploy smoke tests)."""
from sqlmodel import Session, select
from models import Kink
with Session(self.engine) as session:
row = session.exec(select(Kink.id).limit(1)).first()
return str(row) if row is not None else None
# --- db ---
_sqlite = _db_mod._sqlite
_now_iso = _db_mod._now_iso
_ensure_fts_schema = _db_mod._ensure_fts_schema
_ensure_runtime_indexes = _db_mod._ensure_runtime_indexes
_normalize_interest_state = _db_mod._normalize_interest_state
_migrate_user_table = _db_mod._migrate_user_table
_migrate_partner_group_member = _db_mod._migrate_partner_group_member
_migrate_duplicate_pair_groups = _db_mod._migrate_duplicate_pair_groups
_migrate_legacy_ratings = _db_mod._migrate_legacy_ratings
# --- similarity ---
_rebuild_similarity_edges = _similarity_mod._rebuild_similarity_edges
rebuild_similarity_edges = _similarity_mod.rebuild_similarity_edges
_edges_for_kink = _similarity_mod._edges_for_kink
add_similarity_edge = _similarity_mod.add_similarity_edge
# --- content_types ---
_seed_type_overrides = _content_types_mod._seed_type_overrides
_derive_content_kind = _content_types_mod._derive_content_kind
_sync_content_types = _content_types_mod._sync_content_types
_content_type_map = _content_types_mod._content_type_map
_content_kind = _content_types_mod._content_kind
# --- enrichment ---
_is_role_query = _enrichment_mod._is_role_query
_discoverability_penalty = _enrichment_mod._discoverability_penalty
_looks_role_like = _enrichment_mod._looks_role_like
_looks_play_like = _enrichment_mod._looks_play_like
_display_summary = _enrichment_mod._display_summary
_normalized_play_name = _enrichment_mod._normalized_play_name
_canonical_preference_tuple = _enrichment_mod._canonical_preference_tuple
_normalize_directions = _enrichment_mod._normalize_directions
_serialize_directions = _enrichment_mod._serialize_directions
_deserialize_directions = _enrichment_mod._deserialize_directions
_direction_mode_for_kink = _enrichment_mod._direction_mode_for_kink
_default_directions_for_kink = _enrichment_mod._default_directions_for_kink
_parse_count_value = _enrichment_mod._parse_count_value
_starter_score = _enrichment_mod._starter_score
_starter_reason_for_kink = _enrichment_mod._starter_reason_for_kink
_starter_tier_for_kink = _enrichment_mod._starter_tier_for_kink
_starter_tier_rank = _enrichment_mod._starter_tier_rank
_detail_summary_text = _enrichment_mod._detail_summary_text
_image_trust_state = _enrichment_mod._image_trust_state
_image_relevance = _enrichment_mod._image_relevance
_derived_product_flags = _enrichment_mod._derived_product_flags
_starter_candidates = _enrichment_mod._starter_candidates
_extract_popularity_from_notes = _enrichment_mod._extract_popularity_from_notes
slugify = _enrichment_mod.slugify
stable_fragment = _enrichment_mod.stable_fragment
_make_memorable_id = _enrichment_mod._make_memorable_id
_make_memorable_pass = _enrichment_mod._make_memorable_pass
# --- catalog ---
_invalidate_catalog_cache = _catalog_mod._invalidate_catalog_cache
_build_catalog_cache = _catalog_mod._build_catalog_cache
_mark_runtime_duplicates = _catalog_mod._mark_runtime_duplicates
_rebuild_fts_index = _catalog_mod._rebuild_fts_index
_maybe_rebuild_fts_index = _catalog_mod._maybe_rebuild_fts_index
_catalog = _catalog_mod._catalog
catalog_is_ready = _catalog_mod.catalog_is_ready
warm_up_catalog = _catalog_mod.warm_up_catalog
refresh_cache = _catalog_mod.refresh_cache
# --- search ---
_query_tokens = _search_mod._query_tokens
_fts_query = _search_mod._fts_query
_fallback_doc_search = _search_mod._fallback_doc_search
search_kinks = _search_mod.search_kinks
search_roles = _search_mod.search_roles
list_kinks = _search_mod.list_kinks
list_kink_summaries = _search_mod.list_kink_summaries
get_kink = _search_mod.get_kink
get_kinks_batch = _search_mod.get_kinks_batch
similar_kinks = _search_mod.similar_kinks
_card_for_kink = _search_mod._card_for_kink
# --- ingestion ---
upsert_source = _ingestion_mod.upsert_source
match_kink_id = _ingestion_mod.match_kink_id
ensure_kink = _ingestion_mod.ensure_kink
add_alias = _ingestion_mod.add_alias
add_definition = _ingestion_mod.add_definition
add_example = _ingestion_mod.add_example
add_asset = _ingestion_mod.add_asset
upsert_fetlife_picture_ref = _ingestion_mod.upsert_fetlife_picture_ref
catalog_stats = _ingestion_mod.catalog_stats
# --- users ---
create_user = _users_mod.create_user
_load_play_preferences = _users_mod._load_play_preferences
_load_role_preferences = _users_mod._load_role_preferences
get_user = _users_mod.get_user
authenticate_user = _users_mod.authenticate_user
all_users = _users_mod.all_users
delete_user = _users_mod.delete_user
export_user_data = _users_mod.export_user_data
partner_activity_hash = _users_mod.partner_activity_hash
save_preferences = _users_mod.save_preferences
remove_play_preference = _users_mod.remove_play_preference
save_play_preference = _users_mod.save_play_preference
save_role_preference = _users_mod.save_role_preference
role_cards = _users_mod.role_cards
link_users = _users_mod.link_users
request_partner_link = _users_mod.request_partner_link
accept_partner_request = _users_mod.accept_partner_request
decline_partner_request = _users_mod.decline_partner_request
cancel_partner_request = _users_mod.cancel_partner_request
dismiss_prompt = _users_mod.dismiss_prompt
play_board = _users_mod.play_board
# --- partners ---
_linked_partner_ids = _partners_mod._linked_partner_ids
list_partner_groups = _partners_mod.list_partner_groups
ensure_pair_partner_groups = _partners_mod.ensure_pair_partner_groups
create_partner_group = _partners_mod.create_partner_group
add_partner_group_members = _partners_mod.add_partner_group_members
remove_partner_group_member = _partners_mod.remove_partner_group_member
_group_participants = _partners_mod._group_participants
can_access_partner_group = _partners_mod.can_access_partner_group
toggle_share_full_list = _partners_mod.toggle_share_full_list
partner_full_board = _partners_mod.partner_full_board
# --- scenarios ---
_load_scenario_preferences = _scenarios_mod._load_scenario_preferences
scenario_parent_links = _scenarios_mod.scenario_parent_links
list_scenarios_for_parent = _scenarios_mod.list_scenarios_for_parent
scenario_parents_for_user = _scenarios_mod.scenario_parents_for_user
upsert_scenario_parent = _scenarios_mod.upsert_scenario_parent
candidate_scenario_parent_links = _scenarios_mod.candidate_scenario_parent_links
save_scenario_preference = _scenarios_mod.save_scenario_preference
remove_scenario_preference = _scenarios_mod.remove_scenario_preference
# --- recommendations ---
_recommendation_item = _recommendations_mod._recommendation_item
_finalize_reasons = _recommendations_mod._finalize_reasons
_strip_rec_sampler_meta = _recommendations_mod._strip_rec_sampler_meta
_stochastic_select_recommendations = _recommendations_mod._stochastic_select_recommendations
_starter_recommendations = _recommendations_mod._starter_recommendations
_main_recommendation_target = _recommendations_mod._main_recommendation_target
_legacy_edge_sum_scores = _recommendations_mod._legacy_edge_sum_scores
_negative_edge_adjustments = _recommendations_mod._negative_edge_adjustments
_positive_seed_reasons_only = _recommendations_mod._positive_seed_reasons_only
_personalized_recommendations = _recommendations_mod._personalized_recommendations
_dedupe_recommendations = _recommendations_mod._dedupe_recommendations
_take_unique_kink_items = _recommendations_mod._take_unique_kink_items
_explore_backfill = _recommendations_mod._explore_backfill
_blended_recommendations = _recommendations_mod._blended_recommendations
_partner_influenced_recommendations = _recommendations_mod._partner_influenced_recommendations
_load_ots_candidate_cache = _recommendations_mod._load_ots_candidate_cache
_ots_recommendations = _recommendations_mod._ots_recommendations
_graph_recommend = _recommendations_mod._graph_recommend
recommend = _recommendations_mod.recommend
recommendation_debug = _recommendations_mod.recommendation_debug
_group_recommendations = _recommendations_mod._group_recommendations
# --- shared_play ---
_directions_compatible = _shared_play_mod._directions_compatible
_directions_compatible_group = _shared_play_mod._directions_compatible_group
_safe_prompt_candidate = _shared_play_mod._safe_prompt_candidate
_direct_shared_ids = _shared_play_mod._direct_shared_ids
_similarity_edges_by_source = _shared_play_mod._similarity_edges_by_source
_positive_scenario_states = _shared_play_mod._positive_scenario_states
_related_direction_compatible = _shared_play_mod._related_direction_compatible
_related_match_entry = _shared_play_mod._related_match_entry
_related_positive_matches_for_pair = _shared_play_mod._related_positive_matches_for_pair
_related_positive_matches_for_users = _shared_play_mod._related_positive_matches_for_users
_scenario_direct_matches_for_users = _shared_play_mod._scenario_direct_matches_for_users
_scenario_related_matches_for_pair = _shared_play_mod._scenario_related_matches_for_pair
_scenario_related_matches_for_users = _shared_play_mod._scenario_related_matches_for_users
_pair_safe_similarity_floor = _shared_play_mod._pair_safe_similarity_floor
_direct_shared_ids_for_users = _shared_play_mod._direct_shared_ids_for_users
_shared_play_list_for_users = _shared_play_mod._shared_play_list_for_users
shared_play_list = _shared_play_mod.shared_play_list
shared_play_list_for_group = _shared_play_mod.shared_play_list_for_group
prompt_candidates = _shared_play_mod.prompt_candidates
group_prompt_candidates = _shared_play_mod.group_prompt_candidates
# --- fetlife_queue ---
queue_fetlife_job = _fetlife_queue_mod.queue_fetlife_job
start_fetlife_supervisor_run = _fetlife_queue_mod.start_fetlife_supervisor_run
heartbeat_fetlife_supervisor = _fetlife_queue_mod.heartbeat_fetlife_supervisor
finish_fetlife_supervisor_run = _fetlife_queue_mod.finish_fetlife_supervisor_run
_process_is_live = _fetlife_queue_mod._process_is_live
fetlife_supervisor_status = _fetlife_queue_mod.fetlife_supervisor_status
requeue_stale_fetlife_jobs = _fetlife_queue_mod.requeue_stale_fetlife_jobs
claim_fetlife_jobs = _fetlife_queue_mod.claim_fetlife_jobs
complete_fetlife_job = _fetlife_queue_mod.complete_fetlife_job
fetlife_job_stats = _fetlife_queue_mod.fetlife_job_stats
fetlife_jobs = _fetlife_queue_mod.fetlife_jobs
fetlife_queue_preview = _fetlife_queue_mod.fetlife_queue_preview
queue_fetlife_priority_debt = _fetlife_queue_mod.queue_fetlife_priority_debt
# --- fetlife_data ---
upsert_fetlife_kink_meta = _fetlife_data_mod.upsert_fetlife_kink_meta
fetlife_source_coverage = _fetlife_data_mod.fetlife_source_coverage
fetlife_coverage_debt = _fetlife_data_mod.fetlife_coverage_debt
fetlife_data_health = _fetlife_data_mod.fetlife_data_health
content_type_coverage = _fetlife_data_mod.content_type_coverage
upsert_fetlife_user_sample = _fetlife_data_mod.upsert_fetlife_user_sample
replace_fetlife_user_fetishes = _fetlife_data_mod.replace_fetlife_user_fetishes
rebuild_fetlife_collaborative_edges = _fetlife_data_mod.rebuild_fetlife_collaborative_edges
fetlife_sample_coverage = _fetlife_data_mod.fetlife_sample_coverage