""" app/models/project_recipient.py ------------------------------- Per-contract (Project) additional notification recipients. The global notification matrix controls WHICH ROLES receive each event org-wide. This model adds contract-scoped recipients on top of that: each row subscribes one recipient to a chosen set of matrix event types, but ONLY for events that occur in facilities belonging to that contract. Recipient kinds --------------- staff — user_id set, email NULL. Gets an in-app Notification + email via notify() (matrix-authority mode, preferences not consulted). external — email set, user_id NULL. Gets a plain email only (no account, no in-app record) via _send_custom_email(). `events` stores a JSON list of MATRIX_EVENTS keys (see app/models/notification_matrix.py). Dispatch happens inside notify_by_matrix() → _notify_project_recipients() after the matrix roles and global custom emails are processed. """ import json from app import db from app.utils.time_utils import now_eastern class ProjectNotificationRecipient(db.Model): """A contract-scoped additional notification recipient.""" __tablename__ = 'project_notification_recipients' id = db.Column(db.Integer, primary_key=True) project_id = db.Column(db.Integer, db.ForeignKey('projects.id', ondelete='CASCADE'), nullable=False, index=True) # Exactly one of (user_id, email) is set — enforced in the route layer. user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=True, index=True) email = db.Column(db.String(255), nullable=True) events = db.Column(db.Text, nullable=False, default='[]') # JSON list of event keys created_at = db.Column(db.DateTime, default=now_eastern, nullable=False) # Relationships project = db.relationship('Project', foreign_keys=[project_id], backref=db.backref('notification_recipients', lazy='dynamic', cascade='all, delete-orphan')) user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('project_notification_subscriptions', lazy='dynamic')) @property def is_staff(self): """True when the recipient is an application user (in-app + email).""" return self.user_id is not None def get_events(self): """Return the subscribed event keys as a Python list.""" if not self.events: return [] try: result = json.loads(self.events) return [e for e in result if isinstance(e, str) and e.strip()] except (json.JSONDecodeError, TypeError): return [] def __repr__(self): who = f'user={self.user_id}' if self.user_id else f'email={self.email}' return f''