feat(auth): add local user and household models
This commit is contained in:
parent
e29d62f949
commit
04daadccda
13 changed files with 178 additions and 17 deletions
1
.sisyphus/evidence/task-T1-identity-models.txt
Normal file
1
.sisyphus/evidence/task-T1-identity-models.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
['household_memberships', 'households', 'users']
|
||||||
1
.sisyphus/evidence/task-T1-sharing-default.txt
Normal file
1
.sisyphus/evidence/task-T1-sharing-default.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
False
|
||||||
|
|
@ -6,6 +6,7 @@ from .enums import (
|
||||||
DayTime,
|
DayTime,
|
||||||
EvidenceLevel,
|
EvidenceLevel,
|
||||||
GroomingAction,
|
GroomingAction,
|
||||||
|
HouseholdRole,
|
||||||
IngredientFunction,
|
IngredientFunction,
|
||||||
MedicationKind,
|
MedicationKind,
|
||||||
OverallSkinState,
|
OverallSkinState,
|
||||||
|
|
@ -14,6 +15,7 @@ from .enums import (
|
||||||
ProductCategory,
|
ProductCategory,
|
||||||
RemainingLevel,
|
RemainingLevel,
|
||||||
ResultFlag,
|
ResultFlag,
|
||||||
|
Role,
|
||||||
RoutineRole,
|
RoutineRole,
|
||||||
SexAtBirth,
|
SexAtBirth,
|
||||||
SkinConcern,
|
SkinConcern,
|
||||||
|
|
@ -24,6 +26,8 @@ from .enums import (
|
||||||
UsageFrequency,
|
UsageFrequency,
|
||||||
)
|
)
|
||||||
from .health import LabResult, MedicationEntry, MedicationUsage
|
from .health import LabResult, MedicationEntry, MedicationUsage
|
||||||
|
from .household import Household
|
||||||
|
from .household_membership import HouseholdMembership
|
||||||
from .pricing import PricingRecalcJob
|
from .pricing import PricingRecalcJob
|
||||||
from .product import (
|
from .product import (
|
||||||
ActiveIngredient,
|
ActiveIngredient,
|
||||||
|
|
@ -42,6 +46,7 @@ from .skincare import (
|
||||||
SkinConditionSnapshotBase,
|
SkinConditionSnapshotBase,
|
||||||
SkinConditionSnapshotPublic,
|
SkinConditionSnapshotPublic,
|
||||||
)
|
)
|
||||||
|
from .user import User
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# ai logs
|
# ai logs
|
||||||
|
|
@ -54,6 +59,7 @@ __all__ = [
|
||||||
"DayTime",
|
"DayTime",
|
||||||
"EvidenceLevel",
|
"EvidenceLevel",
|
||||||
"GroomingAction",
|
"GroomingAction",
|
||||||
|
"HouseholdRole",
|
||||||
"IngredientFunction",
|
"IngredientFunction",
|
||||||
"MedicationKind",
|
"MedicationKind",
|
||||||
"OverallSkinState",
|
"OverallSkinState",
|
||||||
|
|
@ -62,6 +68,7 @@ __all__ = [
|
||||||
"RemainingLevel",
|
"RemainingLevel",
|
||||||
"ProductCategory",
|
"ProductCategory",
|
||||||
"ResultFlag",
|
"ResultFlag",
|
||||||
|
"Role",
|
||||||
"RoutineRole",
|
"RoutineRole",
|
||||||
"SexAtBirth",
|
"SexAtBirth",
|
||||||
"SkinConcern",
|
"SkinConcern",
|
||||||
|
|
@ -74,6 +81,8 @@ __all__ = [
|
||||||
"LabResult",
|
"LabResult",
|
||||||
"MedicationEntry",
|
"MedicationEntry",
|
||||||
"MedicationUsage",
|
"MedicationUsage",
|
||||||
|
"Household",
|
||||||
|
"HouseholdMembership",
|
||||||
# product
|
# product
|
||||||
"ActiveIngredient",
|
"ActiveIngredient",
|
||||||
"Product",
|
"Product",
|
||||||
|
|
@ -85,6 +94,7 @@ __all__ = [
|
||||||
"ProductWithInventory",
|
"ProductWithInventory",
|
||||||
"PricingRecalcJob",
|
"PricingRecalcJob",
|
||||||
"UserProfile",
|
"UserProfile",
|
||||||
|
"User",
|
||||||
# routine
|
# routine
|
||||||
"GroomingSchedule",
|
"GroomingSchedule",
|
||||||
"Routine",
|
"Routine",
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,11 @@ from .domain import Domain
|
||||||
|
|
||||||
|
|
||||||
class AICallLog(SQLModel, table=True):
|
class AICallLog(SQLModel, table=True):
|
||||||
__tablename__ = "ai_call_logs"
|
__tablename__ = "ai_call_logs" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset()
|
__domains__: ClassVar[frozenset[Domain]] = frozenset()
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
||||||
endpoint: str = Field(index=True)
|
endpoint: str = Field(index=True)
|
||||||
model: str
|
model: str
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ class UsageFrequency(str, Enum):
|
||||||
AS_NEEDED = "as_needed"
|
AS_NEEDED = "as_needed"
|
||||||
|
|
||||||
|
|
||||||
|
class Role(str, Enum):
|
||||||
|
ADMIN = "admin"
|
||||||
|
MEMBER = "member"
|
||||||
|
|
||||||
|
|
||||||
|
class HouseholdRole(str, Enum):
|
||||||
|
OWNER = "owner"
|
||||||
|
MEMBER = "member"
|
||||||
|
|
||||||
|
|
||||||
class ProductCategory(str, Enum):
|
class ProductCategory(str, Enum):
|
||||||
CLEANSER = "cleanser"
|
CLEANSER = "cleanser"
|
||||||
TONER = "toner"
|
TONER = "toner"
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@ from .enums import MedicationKind, ResultFlag
|
||||||
|
|
||||||
|
|
||||||
class MedicationEntry(SQLModel, table=True):
|
class MedicationEntry(SQLModel, table=True):
|
||||||
__tablename__ = "medication_entries"
|
__tablename__ = "medication_entries" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
||||||
|
|
||||||
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
|
|
||||||
kind: MedicationKind = Field(index=True)
|
kind: MedicationKind = Field(index=True)
|
||||||
|
|
||||||
|
|
@ -43,10 +44,11 @@ class MedicationEntry(SQLModel, table=True):
|
||||||
|
|
||||||
|
|
||||||
class MedicationUsage(SQLModel, table=True):
|
class MedicationUsage(SQLModel, table=True):
|
||||||
__tablename__ = "medication_usages"
|
__tablename__ = "medication_usages" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
||||||
|
|
||||||
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
medication_record_id: UUID = Field(
|
medication_record_id: UUID = Field(
|
||||||
foreign_key="medication_entries.record_id", index=True
|
foreign_key="medication_entries.record_id", index=True
|
||||||
)
|
)
|
||||||
|
|
@ -78,10 +80,11 @@ class MedicationUsage(SQLModel, table=True):
|
||||||
|
|
||||||
|
|
||||||
class LabResult(SQLModel, table=True):
|
class LabResult(SQLModel, table=True):
|
||||||
__tablename__ = "lab_results"
|
__tablename__ = "lab_results" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.HEALTH})
|
||||||
|
|
||||||
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
record_id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
|
|
||||||
collected_at: datetime = Field(index=True)
|
collected_at: datetime = Field(index=True)
|
||||||
test_code: str = Field(index=True, regex=r"^\d+-\d$")
|
test_code: str = Field(index=True, regex=r"^\d+-\d$")
|
||||||
|
|
|
||||||
36
backend/innercontext/models/household.py
Normal file
36
backend/innercontext/models/household.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# pyright: reportImportCycles=false
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import TYPE_CHECKING, ClassVar
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
from sqlalchemy import Column, DateTime
|
||||||
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
|
|
||||||
|
from .base import utc_now
|
||||||
|
from .domain import Domain
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .household_membership import HouseholdMembership # pyright: ignore[reportImportCycles]
|
||||||
|
|
||||||
|
|
||||||
|
class Household(SQLModel, table=True):
|
||||||
|
__tablename__ = "households" # pyright: ignore[reportAssignmentType]
|
||||||
|
__domains__: ClassVar[frozenset[Domain]] = frozenset()
|
||||||
|
|
||||||
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
||||||
|
updated_at: datetime = Field(
|
||||||
|
default_factory=utc_now,
|
||||||
|
sa_column=Column(
|
||||||
|
DateTime(timezone=True),
|
||||||
|
default=utc_now,
|
||||||
|
onupdate=utc_now,
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
memberships: list["HouseholdMembership"] = Relationship(
|
||||||
|
back_populates="household",
|
||||||
|
sa_relationship_kwargs={"cascade": "all, delete-orphan"},
|
||||||
|
)
|
||||||
45
backend/innercontext/models/household_membership.py
Normal file
45
backend/innercontext/models/household_membership.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# pyright: reportImportCycles=false
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import TYPE_CHECKING, ClassVar
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
from sqlalchemy import Column, DateTime, UniqueConstraint
|
||||||
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
|
|
||||||
|
from .base import utc_now
|
||||||
|
from .domain import Domain
|
||||||
|
from .enums import HouseholdRole
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .household import Household # pyright: ignore[reportImportCycles]
|
||||||
|
from .user import User # pyright: ignore[reportImportCycles]
|
||||||
|
|
||||||
|
|
||||||
|
class HouseholdMembership(SQLModel, table=True):
|
||||||
|
__tablename__ = "household_memberships" # pyright: ignore[reportAssignmentType]
|
||||||
|
__domains__: ClassVar[frozenset[Domain]] = frozenset()
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint("user_id", name="uq_household_memberships_user_id"),
|
||||||
|
)
|
||||||
|
|
||||||
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID = Field(foreign_key="users.id", index=True, ondelete="CASCADE")
|
||||||
|
household_id: UUID = Field(
|
||||||
|
foreign_key="households.id", index=True, ondelete="CASCADE"
|
||||||
|
)
|
||||||
|
role: HouseholdRole = Field(default=HouseholdRole.MEMBER, index=True)
|
||||||
|
|
||||||
|
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
||||||
|
updated_at: datetime = Field(
|
||||||
|
default_factory=utc_now,
|
||||||
|
sa_column=Column(
|
||||||
|
DateTime(timezone=True),
|
||||||
|
default=utc_now,
|
||||||
|
onupdate=utc_now,
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
user: "User" = Relationship(back_populates="household_membership")
|
||||||
|
household: "Household" = Relationship(back_populates="memberships")
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import date, datetime
|
from datetime import date, datetime
|
||||||
|
from enum import Enum
|
||||||
from typing import Any, ClassVar, Optional, cast
|
from typing import Any, ClassVar, Optional, cast
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
|
@ -72,7 +73,9 @@ class ProductContext(SQLModel):
|
||||||
|
|
||||||
def _ev(v: object) -> str:
|
def _ev(v: object) -> str:
|
||||||
"""Return enum value or string as-is (handles both DB-loaded dicts and Python enums)."""
|
"""Return enum value or string as-is (handles both DB-loaded dicts and Python enums)."""
|
||||||
return v.value if hasattr(v, "value") else str(v) # type: ignore[union-attr]
|
if isinstance(v, Enum):
|
||||||
|
return str(v.value)
|
||||||
|
return str(v)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -136,10 +139,11 @@ class ProductBase(SQLModel):
|
||||||
|
|
||||||
|
|
||||||
class Product(ProductBase, table=True):
|
class Product(ProductBase, table=True):
|
||||||
__tablename__ = "products"
|
__tablename__ = "products" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
short_id: str = Field(
|
short_id: str = Field(
|
||||||
max_length=8,
|
max_length=8,
|
||||||
unique=True,
|
unique=True,
|
||||||
|
|
@ -230,8 +234,8 @@ class Product(ProductBase, table=True):
|
||||||
*,
|
*,
|
||||||
computed_price_tier: PriceTier | None = None,
|
computed_price_tier: PriceTier | None = None,
|
||||||
price_per_use_pln: float | None = None,
|
price_per_use_pln: float | None = None,
|
||||||
) -> dict:
|
) -> dict[str, Any]:
|
||||||
ctx: dict = {
|
ctx: dict[str, Any] = {
|
||||||
"id": str(self.id),
|
"id": str(self.id),
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"brand": self.brand,
|
"brand": self.brand,
|
||||||
|
|
@ -273,7 +277,7 @@ class Product(ProductBase, table=True):
|
||||||
if isinstance(a, dict):
|
if isinstance(a, dict):
|
||||||
actives_ctx.append(a)
|
actives_ctx.append(a)
|
||||||
else:
|
else:
|
||||||
a_dict: dict = {"name": a.name}
|
a_dict: dict[str, Any] = {"name": a.name}
|
||||||
if a.percent is not None:
|
if a.percent is not None:
|
||||||
a_dict["percent"] = a.percent
|
a_dict["percent"] = a.percent
|
||||||
if a.functions:
|
if a.functions:
|
||||||
|
|
@ -342,8 +346,10 @@ class Product(ProductBase, table=True):
|
||||||
inv for inv in (self.inventory or []) if inv.is_opened and inv.opened_at
|
inv for inv in (self.inventory or []) if inv.is_opened and inv.opened_at
|
||||||
]
|
]
|
||||||
if opened_items:
|
if opened_items:
|
||||||
most_recent = max(opened_items, key=lambda x: x.opened_at)
|
most_recent = max(opened_items, key=lambda x: cast(date, x.opened_at))
|
||||||
ctx["days_since_opened"] = (date.today() - most_recent.opened_at).days
|
ctx["days_since_opened"] = (
|
||||||
|
date.today() - cast(date, most_recent.opened_at)
|
||||||
|
).days
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -351,12 +357,14 @@ class Product(ProductBase, table=True):
|
||||||
|
|
||||||
|
|
||||||
class ProductInventory(SQLModel, table=True):
|
class ProductInventory(SQLModel, table=True):
|
||||||
__tablename__ = "product_inventory"
|
__tablename__ = "product_inventory" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
product_id: UUID = Field(foreign_key="products.id", index=True, ondelete="CASCADE")
|
product_id: UUID = Field(foreign_key="products.id", index=True, ondelete="CASCADE")
|
||||||
|
|
||||||
|
is_household_shared: bool = Field(default=False)
|
||||||
is_opened: bool = Field(default=False)
|
is_opened: bool = Field(default=False)
|
||||||
opened_at: date | None = Field(default=None)
|
opened_at: date | None = Field(default=None)
|
||||||
finished_at: date | None = Field(default=None)
|
finished_at: date | None = Field(default=None)
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,13 @@ from .enums import SexAtBirth
|
||||||
|
|
||||||
|
|
||||||
class UserProfile(SQLModel, table=True):
|
class UserProfile(SQLModel, table=True):
|
||||||
__tablename__ = "user_profiles"
|
__tablename__ = "user_profiles" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset(
|
__domains__: ClassVar[frozenset[Domain]] = frozenset(
|
||||||
{Domain.HEALTH, Domain.SKINCARE}
|
{Domain.HEALTH, Domain.SKINCARE}
|
||||||
)
|
)
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
birth_date: date | None = Field(default=None)
|
birth_date: date | None = Field(default=None)
|
||||||
sex_at_birth: SexAtBirth | None = Field(
|
sex_at_birth: SexAtBirth | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class Routine(SQLModel, table=True):
|
class Routine(SQLModel, table=True):
|
||||||
__tablename__ = "routines"
|
__tablename__ = "routines" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint(
|
UniqueConstraint(
|
||||||
|
|
@ -23,6 +23,7 @@ class Routine(SQLModel, table=True):
|
||||||
)
|
)
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
routine_date: date = Field(index=True)
|
routine_date: date = Field(index=True)
|
||||||
part_of_day: PartOfDay = Field(index=True)
|
part_of_day: PartOfDay = Field(index=True)
|
||||||
notes: str | None = Field(default=None)
|
notes: str | None = Field(default=None)
|
||||||
|
|
@ -45,20 +46,22 @@ class Routine(SQLModel, table=True):
|
||||||
|
|
||||||
|
|
||||||
class GroomingSchedule(SQLModel, table=True):
|
class GroomingSchedule(SQLModel, table=True):
|
||||||
__tablename__ = "grooming_schedule"
|
__tablename__ = "grooming_schedule" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
day_of_week: int = Field(ge=0, le=6, index=True) # 0 = poniedziałek, 6 = niedziela
|
day_of_week: int = Field(ge=0, le=6, index=True) # 0 = poniedziałek, 6 = niedziela
|
||||||
action: GroomingAction
|
action: GroomingAction
|
||||||
notes: str | None = Field(default=None)
|
notes: str | None = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class RoutineStep(SQLModel, table=True):
|
class RoutineStep(SQLModel, table=True):
|
||||||
__tablename__ = "routine_steps"
|
__tablename__ = "routine_steps" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
routine_id: UUID = Field(foreign_key="routines.id", index=True)
|
routine_id: UUID = Field(foreign_key="routines.id", index=True)
|
||||||
product_id: UUID | None = Field(default=None, foreign_key="products.id", index=True)
|
product_id: UUID | None = Field(default=None, foreign_key="products.id", index=True)
|
||||||
order_index: int = Field(ge=0)
|
order_index: int = Field(ge=0)
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,12 @@ class SkinConditionSnapshot(SkinConditionSnapshotBase, table=True):
|
||||||
i kontekstu rutyny. Wszystkie metryki numeryczne w skali 1–5.
|
i kontekstu rutyny. Wszystkie metryki numeryczne w skali 1–5.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "skin_condition_snapshots"
|
__tablename__ = "skin_condition_snapshots" # pyright: ignore[reportAssignmentType]
|
||||||
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
__domains__: ClassVar[frozenset[Domain]] = frozenset({Domain.SKINCARE})
|
||||||
__table_args__ = (UniqueConstraint("snapshot_date", name="uq_skin_snapshot_date"),)
|
__table_args__ = (UniqueConstraint("snapshot_date", name="uq_skin_snapshot_date"),)
|
||||||
|
|
||||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||||
|
|
||||||
# Override: add index for table context
|
# Override: add index for table context
|
||||||
snapshot_date: date = Field(index=True)
|
snapshot_date: date = Field(index=True)
|
||||||
|
|
|
||||||
41
backend/innercontext/models/user.py
Normal file
41
backend/innercontext/models/user.py
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# pyright: reportImportCycles=false
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import TYPE_CHECKING, ClassVar
|
||||||
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
|
from sqlalchemy import Column, DateTime, String, UniqueConstraint
|
||||||
|
from sqlmodel import Field, Relationship, SQLModel
|
||||||
|
|
||||||
|
from .base import utc_now
|
||||||
|
from .domain import Domain
|
||||||
|
from .enums import Role
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .household_membership import HouseholdMembership # pyright: ignore[reportImportCycles]
|
||||||
|
|
||||||
|
|
||||||
|
class User(SQLModel, table=True):
|
||||||
|
__tablename__ = "users" # pyright: ignore[reportAssignmentType]
|
||||||
|
__domains__: ClassVar[frozenset[Domain]] = frozenset()
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint("oidc_issuer", "oidc_subject", name="uq_users_oidc_identity"),
|
||||||
|
)
|
||||||
|
|
||||||
|
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||||
|
oidc_issuer: str = Field(sa_column=Column(String(length=512), nullable=False))
|
||||||
|
oidc_subject: str = Field(sa_column=Column(String(length=512), nullable=False))
|
||||||
|
role: Role = Field(default=Role.MEMBER, index=True)
|
||||||
|
|
||||||
|
created_at: datetime = Field(default_factory=utc_now, nullable=False)
|
||||||
|
updated_at: datetime = Field(
|
||||||
|
default_factory=utc_now,
|
||||||
|
sa_column=Column(
|
||||||
|
DateTime(timezone=True),
|
||||||
|
default=utc_now,
|
||||||
|
onupdate=utc_now,
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
household_membership: "HouseholdMembership" = Relationship(back_populates="user")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue