Coverage for functions \ flipdare \ firestore \ context \ _model_context_factory.py: 82%
50 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-05-08 12:22 +1000
« prev ^ index » next coverage.py v7.13.0, created at 2026-05-08 12:22 +1000
1#!/usr/bin/env python
2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved.
3#
4# This file is part of Flipdare's proprietary software and contains
5# confidential and copyrighted material. Unauthorised copying,
6# modification, distribution, or use of this file is strictly
7# prohibited without prior written permission from Flipdare Pty Ltd.
8#
9# This software includes third-party components licensed under MIT,
10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details.
11#
13"""
14Base classes for model contexts and their factories.
16Provides common validation, error handling, and factory patterns for
17context objects that wrap multiple related models (e.g., Friend + Users,
18Dare + Users, Group + Members).
20All models are wrapped in PersistedWrapper to guarantee doc_id exists,
21eliminating the need for doc_id validation checks.
22"""
24from __future__ import annotations
26from abc import ABC, abstractmethod
27from typing import TYPE_CHECKING, Any
29from flipdare.app_types import DatabaseDict
30from flipdare.firestore import DareDb, FlagDb, FriendDb, GroupDb, UserDb
31from flipdare.firestore.context._model_context import ModelContext
33from flipdare.wrapper import GroupWrapper, PersistedWrapper, UserWrapper
35__all__ = ["ModelContextFactory"]
37if TYPE_CHECKING:
38 from flipdare.manager.db_manager import DbManager
41# TModel is bound to BaseWrapper - contexts will store PersistedWrapper[BaseWrapper]
42class ModelContextFactory[TModel: PersistedWrapper[Any], TContext: ModelContext](ABC):
43 """
44 Abstract base factory for creating context objects from various input types.
46 Subclasses should implement factory methods to create contexts from:
47 - Model instances
48 - Document IDs (strings)
49 - Raw data dictionaries
50 - Existing context objects
51 """
53 def __init__(self, db_manager: DbManager | None = None) -> None:
54 self._db_manager = db_manager
56 @abstractmethod
57 def create(self, obj: Any) -> TContext | None:
58 """
59 Polymorphic factory method to create context from various types.
61 Args:
62 obj: Can be a model instance, doc ID, dict, or existing context
64 Returns:
65 Context instance or None if creation fails
67 """
68 ...
70 @abstractmethod
71 def _from_id(self, doc_id: str) -> TContext | None:
72 """Create context from document ID. Override in subclass if needed."""
73 ...
75 @abstractmethod
76 def _from_data(self, data: DatabaseDict) -> TContext | None:
77 """Create context from raw data dict. Override in subclass if needed."""
78 ...
80 @abstractmethod
81 def _from_model(self, model: TModel) -> TContext | None:
82 """Create context from raw model instance. Override in subclass if needed."""
83 ...
85 @property
86 def db_manager(self) -> DbManager:
87 from flipdare.services import get_db_manager
89 if self._db_manager is None:
90 self._db_manager = get_db_manager()
91 return self._db_manager
93 @property
94 def dare_db(self) -> DareDb:
95 return self.db_manager.dare_db
97 @property
98 def user_db(self) -> UserDb:
99 return self.db_manager.user_db
101 @property
102 def friend_db(self) -> FriendDb:
103 return self.db_manager.friend_db
105 @property
106 def group_db(self) -> GroupDb:
107 return self.db_manager.group_db
109 @property
110 def flag_db(self) -> FlagDb:
111 return self.db_manager.flag_db
113 def get_user(self, user_id: str) -> UserWrapper | None:
114 """Get user by ID, returns UserWrapper or None."""
115 try:
116 # user_db.get() already returns PersistedWrapper[UserWrapper]
117 return self.user_db.get(user_id)
118 except Exception:
119 return None
121 def get_group(self, gid: str) -> GroupWrapper | None:
122 """Get group by ID, returns GroupWrapper or None."""
123 try:
124 # group_db.get() already returns PersistedWrapper[GroupWrapper]
125 return self.group_db.get(gid)
126 except Exception:
127 return None