"""Whose a row is, and who an account is to the others — one place for both. **Ownership.** A user-owned table says whose a row is in one column (`user_id`, or `to_user_id` for something handed to them). Every read and write names that column through `owned_by` and fetches a row by id through `owned_row`, never with an inline `Model.user_id == …`: the rule "a row owned by someone else is answered like a missing one" then lives here, and a contract test (`test_unit_fences.py`) holds every module to it, core included. **Recipients.** "An active, non-suspended account" — the download share-by-email and the recipient picker filter on it. Kept here so the two forms (SQL clauses for a query, a Python check for a loaded row) can't drift apart. """ from typing import Any, TypeVar from sqlalchemy import ColumnElement, func, select from sqlalchemy.orm import InstrumentedAttribute, Session from app.collation import collated from app.db import Base from app.models import User M = TypeVar("M", bound=Base) def _owner_id(owner: User | int) -> int: return owner if isinstance(owner, int) else owner.id def owned_by(column: InstrumentedAttribute, owner: User | int) -> ColumnElement[bool]: """`column` — a table's owner column — names `owner`.""" return column == _owner_id(owner) def owned_row( db: Session, model: type[M], id_: Any, owner: User | int, *, owner_attr: str = "user_id", lock: bool = False, ) -> M | None: """The row of `model` with this primary key when `owner` owns it, else None — a row of someone else's is the same answer as a missing one, so a caller can never tell the two apart. `lock` takes it FOR UPDATE (past the identity map).""" row = db.get(model, id_, with_for_update=True if lock else None) if row is None or getattr(row, owner_attr) != _owner_id(owner): return None return row def messageable_clauses() -> list: """SQL clauses for "an active, non-suspended account" — splat into a recipient query.""" return [ User.is_active.is_(True), User.is_suspended.is_(False), ] def is_messageable_user(u: User | None) -> bool: """Python mirror of `messageable_clauses()` for an already-loaded row (a share recipient).""" return bool(u and u.is_active and not u.is_suspended) def user_by_email(db: Session, email: str) -> User | None: """The account with this address, matched without regard to case.""" return db.execute( select(User).where(func.lower(User.email) == email.strip().lower()) ).scalar_one_or_none() def messageable_others(db: Session, user: User, lang: str) -> list[User]: """Every account `user` may hand something to: active, not suspended, not themselves; in the reader's alphabet by address.""" return list( db.execute( select(User) .where(User.id != user.id, *messageable_clauses()) .order_by(collated(User.email, lang)) ).scalars() ) def emails_of(db: Session, ids: set[int] | None = None) -> dict[int, str]: """Each account's address by id, in address order: these ids, or every account.""" stmt = select(User.id, User.email).order_by(User.email) if ids is not None: stmt = stmt.where(User.id.in_(ids)) return dict(db.execute(stmt).tuples().all())