Coverage for portality / bll / services / notifications.py: 68%
80 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-04 09:41 +0100
1# ~~Notifications:Service~~
2from portality import models
3from portality.core import app
4from portality import app_email
5from portality.ui.messages import Messages
6from portality.bll.exceptions import NoSuchObjectException, NoSuchPropertyException
7from datetime import datetime
8from portality.lib import dates
9from portality.ui import templates
11class NotificationsService(object):
12 def notify(self, notification: models.Notification):
13 # first just save the notification
14 notification.save()
16 # now send an email to the notification target
17 acc = models.Account.pull(notification.who)
18 if acc is None:
19 raise NoSuchObjectException(Messages.EXCEPTION_NOTIFICATION_NO_ACCOUNT.format(x=notification.who))
21 if acc.email is None:
22 raise NoSuchPropertyException(Messages.EXCEPTION_NOTIFICATION_NO_EMAIL.format(x=notification.who))
24 to = [acc.email]
25 fro = app.config.get('SYSTEM_EMAIL_FROM', 'helpdesk@doaj.org')
26 subject = app.config.get("SERVICE_NAME", "") + " - " + notification.short
28 action_intro = ""
29 if notification.action is not None:
30 action_intro = self.action_intro(notification.created_by)
32 # ~~-> Email:Library ~~
33 app_email.send_markdown_mail(to=to,
34 fro=fro,
35 subject=subject,
36 template_name=templates.EMAIL_NOTIFICATION,
37 markdown_template_name=templates.EMAIL_NOTIFICATION, # for the moment the markdown and plaintext templates are the same
38 user=acc,
39 message=notification.long,
40 action=notification.action,
41 action_intro=action_intro,
42 url_root=app.config.get("BASE_URL"))
44 return notification
46 def long_notification(self, message_id):
47 # ~~-> Notifications:Data ~~
48 return app.jinja_env.globals["data"]["notifications"].get(message_id, {}).get("long")
50 def short_notification(self, message_id):
51 # ~~-> Notifications:Data ~~
52 return app.jinja_env.globals["data"]["notifications"].get(message_id, {}).get("short", Messages.NOTIFY__DEFAULT_SHORT_NOTIFICATION)
54 def action_intro(self, consumer_id):
55 return app.jinja_env.globals["data"]["notifications"].get(consumer_id, {}).get("action_intro",
56 Messages.NOTIFY__DEFAULT_ACTION_INTRO)
58 def top_notifications(self, account: models.Account, size: int = 10):
59 # ~~-> TopNotifications:Query ~~
60 q = TopNotificationsQuery(account.id, size)
61 top = models.Notification.object_query(q.query())
62 return top
64 def notification_seen(self, account: models.Account, notification_id: str):
65 # ~~-> Notifications:Query ~~
66 note = models.Notification.pull(notification_id)
67 if not note:
68 raise NoSuchObjectException(Messages.EXCEPTION_NOTIFICATION_NO_NOTIFICATION.format(n=notification_id))
69 if account.id == note.who or account.is_super:
70 if not note.is_seen():
71 note.set_seen()
72 note.save()
73 return True
74 return False
76 def mark_all_as_seen(self, account: models.Account=None, until: datetime=None):
77 account_id = None if account is None else account.id
78 q = NotificationsQuery(account_id, until, False)
79 for notification in models.Notification.scroll(q.query()):
80 notification.set_seen()
81 notification.save()
84class TopNotificationsQuery(object):
85 # ~~->$ TopNotifications:Query ~~
86 # ~~^-> Elasticsearch:Technology ~~
87 def __init__(self, account_id, size=10):
88 self.account_id = account_id
89 self.size = size
91 def query(self):
92 return {
93 "query": {
94 "bool": {
95 "must": [
96 {"term": {"who.exact": self.account_id}}
97 ]
98 }
99 },
100 "sort": [{"created_date": {"order": "desc"}}],
101 "size": self.size
102 }
105class NotificationsQuery(object):
106 # ~~->$ Notifications:Query ~~
107 # ~~^-> Elasticsearch:Technology ~~
108 def __init__(self, account_id=None, until=None, seen=None):
109 self._account_id = account_id
110 self._until = until
111 self._seen = seen
113 def query(self):
114 musts = []
115 must_nots = []
116 if self._account_id:
117 musts.append({"term": {"who.exact": self._account_id}})
119 if self._until:
120 musts.append({"range": {"created_date": {"lte": dates.format(self._until, format="%Y-%m-%dT%H:%M:%S")}}})
122 if self._seen is not None:
123 if self._seen is True:
124 musts.append({"exists": {"field": "seen_date"}})
125 else:
126 must_nots.append({"exists": {"field": "seen_date"}})
128 if len(musts) > 0 or len(must_nots) > 0:
129 q = {"query": {"bool": {}}}
130 if len(musts) > 0:
131 q["query"]["bool"]["must"] = musts
132 if len(must_nots) > 0:
133 q["query"]["bool"]["must_not"] = must_nots
134 return q
135 else:
136 return {"query": {"match_all": {}}}