73 lines
3.1 KiB
Python
73 lines
3.1 KiB
Python
"""
|
|
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'<ProjectNotificationRecipient project={self.project_id} {who}>'
|