diff --git a/docs/api/models.rst b/docs/api/models.rst --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -10,9 +10,6 @@ The :mod:`models` module .. automodule:: kallithea.model.comment :members: -.. automodule:: kallithea.model.notification - :members: - .. automodule:: kallithea.model.permission :members: diff --git a/kallithea/model/db.py b/kallithea/model/db.py --- a/kallithea/model/db.py +++ b/kallithea/model/db.py @@ -452,9 +452,6 @@ class User(Base, BaseDbModel): group_member = relationship('UserGroupMember', cascade='all') - notifications = relationship('UserNotification', cascade='all') - # notifications assigned to this user - user_created_notifications = relationship('Notification', cascade='all') # comments created by this user user_comments = relationship('ChangesetComment', cascade='all') # extra emails for this user @@ -2478,12 +2475,8 @@ class PullRequestReviewer(Base, BaseDbMo ) -class Notification(Base, BaseDbModel): +class Notification(object): __tablename__ = 'notifications' - __table_args__ = ( - Index('notification_type_idx', 'type'), - _table_args_default_dict, - ) TYPE_CHANGESET_COMMENT = u'cs_comment' TYPE_MESSAGE = u'message' @@ -2492,70 +2485,9 @@ class Notification(Base, BaseDbModel): TYPE_PULL_REQUEST = u'pull_request' TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment' - notification_id = Column(Integer(), primary_key=True) - subject = Column(Unicode(512), nullable=False) - body = Column(UnicodeText(), nullable=False) - created_by = Column(Integer(), ForeignKey('users.user_id'), nullable=False) - created_on = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now) - type_ = Column('type', Unicode(255), nullable=False) - - created_by_user = relationship('User') - notifications_to_users = relationship('UserNotification', cascade="all, delete-orphan") - - @property - def recipients(self): - return [x.user for x in UserNotification.query() - .filter(UserNotification.notification == self) - .order_by(UserNotification.user_id.asc()).all()] - - @classmethod - def create(cls, created_by, subject, body, recipients, type_=None): - if type_ is None: - type_ = Notification.TYPE_MESSAGE - - notification = cls() - notification.created_by_user = created_by - notification.subject = subject - notification.body = body - notification.type_ = type_ - notification.created_on = datetime.datetime.now() - - for recipient in recipients: - un = UserNotification() - un.notification = notification - un.user_id = recipient.user_id - # Mark notifications to self "pre-read" - should perhaps just be skipped - if recipient == created_by: - un.read = True - Session().add(un) - - Session().add(notification) - Session().flush() # assign notification.notification_id - return notification - - @property - def description(self): - from kallithea.model.notification import NotificationModel - return NotificationModel().make_description(self) - - -class UserNotification(Base, BaseDbModel): + +class UserNotification(object): __tablename__ = 'user_to_notification' - __table_args__ = ( - UniqueConstraint('user_id', 'notification_id'), - _table_args_default_dict, - ) - - user_id = Column(Integer(), ForeignKey('users.user_id'), primary_key=True) - notification_id = Column(Integer(), ForeignKey('notifications.notification_id'), primary_key=True) - read = Column(Boolean, nullable=False, default=False) - sent_on = Column(DateTime(timezone=False), nullable=True) # FIXME: not nullable? - - user = relationship('User') - notification = relationship('Notification') - - def mark_as_read(self): - self.read = True class Gist(Base, BaseDbModel): diff --git a/kallithea/model/notification.py b/kallithea/model/notification.py --- a/kallithea/model/notification.py +++ b/kallithea/model/notification.py @@ -26,6 +26,7 @@ Original author and date, and relevant c :license: GPLv3, see LICENSE.md for more details. """ +import datetime import logging import traceback @@ -36,7 +37,7 @@ from sqlalchemy.orm import joinedload, s import kallithea from kallithea.lib import helpers as h from kallithea.lib.utils2 import safe_unicode -from kallithea.model.db import Notification, User, UserNotification +from kallithea.model.db import Notification, User from kallithea.model.meta import Session log = logging.getLogger(__name__) @@ -88,14 +89,8 @@ class NotificationModel(object): ) #else: silently skip notification mails? - # TODO: inform user who are notified - notif = Notification.create( - created_by=created_by_obj, subject=subject, - body=body, recipients=recipients_objs, type_=type_ - ) - if not with_email: - return notif + return headers = {} headers['X-Kallithea-Notification-Type'] = type_ @@ -103,18 +98,19 @@ class NotificationModel(object): headers['References'] = ' '.join('<%s>' % x for x in email_kwargs['threading']) # this is passed into template + created_on = h.fmt_date(datetime.datetime.now()) html_kwargs = { 'subject': subject, 'body': h.render_w_mentions(body, repo_name), - 'when': h.fmt_date(notif.created_on), - 'user': notif.created_by_user.username, + 'when': created_on, + 'user': created_by_obj.username, } txt_kwargs = { 'subject': subject, 'body': body, - 'when': h.fmt_date(notif.created_on), - 'user': notif.created_by_user.username, + 'when': created_on, + 'user': created_by_obj.username, } html_kwargs.update(email_kwargs) @@ -134,131 +130,6 @@ class NotificationModel(object): tasks.send_email([rec.email], email_subject, email_txt_body, email_html_body, headers, author=created_by_obj) - return notif - - def delete(self, user, notification): - # we don't want to remove actual notification just the assignment - try: - notification = Notification.guess_instance(notification) - user = User.guess_instance(user) - if notification and user: - obj = UserNotification.query() \ - .filter(UserNotification.user == user) \ - .filter(UserNotification.notification - == notification) \ - .one() - Session().delete(obj) - return True - except Exception: - log.error(traceback.format_exc()) - raise - - def query_for_user(self, user, filter_=None): - """ - Get notifications for given user, filter them if filter dict is given - - :param user: - :param filter: - """ - user = User.guess_instance(user) - - q = UserNotification.query() \ - .filter(UserNotification.user == user) \ - .join((Notification, UserNotification.notification_id == - Notification.notification_id)) \ - .options(joinedload('notification')) \ - .options(subqueryload('notification.created_by_user')) \ - .order_by(Notification.created_on.desc()) - - if filter_: - q = q.filter(Notification.type_.in_(filter_)) - - return q - - def mark_read(self, user, notification): - try: - notification = Notification.guess_instance(notification) - user = User.guess_instance(user) - if notification and user: - obj = UserNotification.query() \ - .filter(UserNotification.user == user) \ - .filter(UserNotification.notification - == notification) \ - .one() - obj.read = True - return True - except Exception: - log.error(traceback.format_exc()) - raise - - def mark_all_read_for_user(self, user, filter_=None): - user = User.guess_instance(user) - q = UserNotification.query() \ - .filter(UserNotification.user == user) \ - .filter(UserNotification.read == False) \ - .join((Notification, UserNotification.notification_id == - Notification.notification_id)) - if filter_: - q = q.filter(Notification.type_.in_(filter_)) - - # this is a little inefficient but sqlalchemy doesn't support - # update on joined tables :( - for obj in q: - obj.read = True - - def get_unread_cnt_for_user(self, user): - user = User.guess_instance(user) - return UserNotification.query() \ - .filter(UserNotification.read == False) \ - .filter(UserNotification.user == user).count() - - def get_unread_for_user(self, user): - user = User.guess_instance(user) - return [x.notification for x in UserNotification.query() \ - .filter(UserNotification.read == False) \ - .filter(UserNotification.user == user).all()] - - def get_user_notification(self, user, notification): - user = User.guess_instance(user) - notification = Notification.guess_instance(notification) - - return UserNotification.query() \ - .filter(UserNotification.notification == notification) \ - .filter(UserNotification.user == user).scalar() - - def make_description(self, notification, show_age=True): - """ - Creates a human readable description based on properties - of notification object - """ - # alias - _n = notification - - if show_age: - return { - _n.TYPE_CHANGESET_COMMENT: _('%(user)s commented on changeset %(age)s'), - _n.TYPE_MESSAGE: _('%(user)s sent message %(age)s'), - _n.TYPE_MENTION: _('%(user)s mentioned you %(age)s'), - _n.TYPE_REGISTRATION: _('%(user)s registered in Kallithea %(age)s'), - _n.TYPE_PULL_REQUEST: _('%(user)s opened new pull request %(age)s'), - _n.TYPE_PULL_REQUEST_COMMENT: _('%(user)s commented on pull request %(age)s'), - }[notification.type_] % dict( - user=notification.created_by_user.username, - age=h.age(notification.created_on), - ) - else: - return { - _n.TYPE_CHANGESET_COMMENT: _('%(user)s commented on changeset at %(when)s'), - _n.TYPE_MESSAGE: _('%(user)s sent message at %(when)s'), - _n.TYPE_MENTION: _('%(user)s mentioned you at %(when)s'), - _n.TYPE_REGISTRATION: _('%(user)s registered in Kallithea at %(when)s'), - _n.TYPE_PULL_REQUEST: _('%(user)s opened new pull request at %(when)s'), - _n.TYPE_PULL_REQUEST_COMMENT: _('%(user)s commented on pull request at %(when)s'), - }[notification.type_] % dict( - user=notification.created_by_user.username, - when=h.fmt_date(notification.created_on), - ) - class EmailNotificationModel(object): diff --git a/kallithea/tests/models/test_notifications.py b/kallithea/tests/models/test_notifications.py --- a/kallithea/tests/models/test_notifications.py +++ b/kallithea/tests/models/test_notifications.py @@ -53,7 +53,7 @@ class TestNotifications(TestController): assert '>hi there<' in html_body assert author.username == 'u1' with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email): - notification = NotificationModel().create(created_by=self.u1, + NotificationModel().create(created_by=self.u1, subject=u'subj', body=u'hi there', recipients=usrs) @@ -151,7 +151,7 @@ class TestNotifications(TestController): for desc, type_, body, kwargs in params: # desc is used as "global" variable - notification = NotificationModel().create(created_by=self.u1, + NotificationModel().create(created_by=self.u1, subject=u'unused', body=body, email_kwargs=kwargs, recipients=[self.u2], type_=type_)