80 lines
3.2 KiB
Python
80 lines
3.2 KiB
Python
"""
|
|
app/models/notification_recipient.py
|
|
------------------------------------
|
|
Per-contract additional notification recipients.
|
|
|
|
Each row is ONE extra recipient attached to a Contract (Project) who should be
|
|
notified when selected notification events fire within that contract's
|
|
facilities — in ADDITION to whoever the global NotificationMatrix already
|
|
routes to.
|
|
|
|
A recipient is either:
|
|
- an existing app user (user_id set, email NULL) → in-app notification + email
|
|
- a free-form email (email set, user_id NULL) → email only
|
|
|
|
`event_types` is a JSON list of event_type keys (matching MATRIX_EVENTS) that
|
|
this recipient subscribes to for this contract. A recipient is notified only
|
|
when the firing event_type is present in this list. An empty list means the
|
|
recipient receives nothing (the add form requires at least one event).
|
|
|
|
Resolution happens centrally in notify_by_matrix() (app/utils/notifications.py),
|
|
which derives the contract from the event's facility / issue / inspection.
|
|
"""
|
|
|
|
import json
|
|
from app import db
|
|
from app.utils.time_utils import now_eastern
|
|
|
|
|
|
class ContractNotificationRecipient(db.Model):
|
|
__tablename__ = 'contract_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, not the DB).
|
|
user_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey('users.id', ondelete='CASCADE'),
|
|
nullable=True, index=True,
|
|
)
|
|
email = db.Column(db.String(200), nullable=True)
|
|
# JSON list of event_type keys this recipient is subscribed to for this contract.
|
|
event_types = db.Column(db.Text, nullable=True) # JSON-encoded list[str]
|
|
created_at = db.Column(db.DateTime, nullable=False, default=now_eastern)
|
|
|
|
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])
|
|
|
|
def get_event_types(self) -> list:
|
|
"""Return event_types as a Python list of strings."""
|
|
if not self.event_types:
|
|
return []
|
|
try:
|
|
result = json.loads(self.event_types)
|
|
return [e for e in result if isinstance(e, str) and e.strip()]
|
|
except (json.JSONDecodeError, TypeError):
|
|
return []
|
|
|
|
def set_event_types(self, values) -> None:
|
|
"""Store a list of event_type keys as a JSON string."""
|
|
clean = [v.strip() for v in (values or []) if isinstance(v, str) and v.strip()]
|
|
self.event_types = json.dumps(clean)
|
|
|
|
@property
|
|
def display_target(self) -> str:
|
|
"""Human-readable recipient label for the UI."""
|
|
if self.user_id and self.user:
|
|
return f'{self.user.display_name} ({self.user.email})'
|
|
return self.email or '—'
|
|
|
|
def __repr__(self):
|
|
who = f'user={self.user_id}' if self.user_id else f'email={self.email}'
|
|
return f'<ContractNotificationRecipient project={self.project_id} {who}>'
|