From d32bb300716aab349fba5413acdda684fceb5df0 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 5 Dec 2020 03:44:34 +0000 Subject: [PATCH] Add notification types --- app/blueprints/packages/packages.py | 18 ++++----- app/blueprints/packages/releases.py | 4 +- app/blueprints/packages/reviews.py | 10 +++-- app/blueprints/packages/screenshots.py | 2 +- app/blueprints/threads/__init__.py | 12 +++--- app/models.py | 54 +++++++++++++++++++++++++- app/utils.py | 8 ++-- migrations/versions/81de25b72f66_.py | 31 +++++++++++++++ 8 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 migrations/versions/81de25b72f66_.py diff --git a/app/blueprints/packages/packages.py b/app/blueprints/packages/packages.py index 51a5b3c..06fe989 100644 --- a/app/blueprints/packages/packages.py +++ b/app/blueprints/packages/packages.py @@ -309,7 +309,7 @@ def create_edit(author=None, name=None): else: msg = "Edited {}".format(package.title) - addNotification(package.maintainers, current_user, + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, package.getDetailsURL(), package) severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR @@ -391,7 +391,7 @@ def move_to_state(package): msg = "Approved {}".format(package.title) - addNotification(package.maintainers, current_user, msg, package.getDetailsURL(), package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.getDetailsURL(), package) severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR addAuditLog(severity, current_user, msg, package.getDetailsURL(), package) @@ -423,7 +423,7 @@ def remove(package): url = url_for("users.profile", username=package.author.username) msg = "Deleted {}".format(package.title) - addNotification(package.maintainers, current_user, msg, url, package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, url, package) addAuditLog(AuditSeverity.EDITOR, current_user, msg, url) db.session.commit() @@ -438,7 +438,7 @@ def remove(package): package.state = PackageState.WIP msg = "Unapproved {}".format(package.title) - addNotification(package.maintainers, current_user, msg, package.getDetailsURL(), package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_APPROVAL, msg, package.getDetailsURL(), package) addAuditLog(AuditSeverity.EDITOR, current_user, msg, package.getDetailsURL(), package) db.session.commit() @@ -474,12 +474,12 @@ def edit_maintainers(package): for user in users: if not user in package.maintainers: - addNotification(user, current_user, + addNotification(user, current_user, NotificationType.MAINTAINER, "Added you as a maintainer of {}".format(package.title), package.getDetailsURL(), package) for user in package.maintainers: if user != package.author and not user in users: - addNotification(user, current_user, + addNotification(user, current_user, NotificationType.MAINTAINER, "Removed you as a maintainer of {}".format(package.title), package.getDetailsURL(), package) package.maintainers.clear() @@ -488,7 +488,7 @@ def edit_maintainers(package): package.maintainers.append(package.author) msg = "Edited {} maintainers".format(package.title) - addNotification(package.author, current_user, msg, package.getDetailsURL(), package) + addNotification(package.author, current_user, NotificationType.MAINTAINER, msg, package.getDetailsURL(), package) severity = AuditSeverity.NORMAL if current_user == package.author else AuditSeverity.MODERATION addAuditLog(severity, current_user, msg, package.getDetailsURL(), package) @@ -515,7 +515,7 @@ def remove_self_maintainers(package): else: package.maintainers.remove(current_user) - addNotification(package.author, current_user, + addNotification(package.author, current_user, NotificationType.MAINTAINER, "Removed themself as a maintainer of {}".format(package.title), package.getDetailsURL(), package) db.session.commit() @@ -537,7 +537,7 @@ def update_from_release(package): return redirect(package.getDetailsURL()) msg = "Updated meta from latest release" - addNotification(package.maintainers, current_user, + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, package.getDetailsURL(), package) severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR addAuditLog(severity, current_user, msg, package.getDetailsURL(), package) diff --git a/app/blueprints/packages/releases.py b/app/blueprints/packages/releases.py index 594196e..0069b32 100644 --- a/app/blueprints/packages/releases.py +++ b/app/blueprints/packages/releases.py @@ -90,7 +90,7 @@ def create_release(package): makeVCSRelease.apply_async((rel.id, form["vcsLabel"].data), task_id=rel.task_id) msg = "Release {} created".format(rel.title) - addNotification(package.maintainers, current_user, msg, rel.getEditURL(), package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, rel.getEditURL(), package) db.session.commit() return redirect(url_for("tasks.check", id=rel.task_id, r=rel.getEditURL())) @@ -111,7 +111,7 @@ def create_release(package): updateMetaFromRelease.delay(rel.id, uploadedPath) msg = "Release {} created".format(rel.title) - addNotification(package.maintainers, current_user, msg, rel.getEditURL(), package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, rel.getEditURL(), package) db.session.commit() return redirect(url_for("tasks.check", id=rel.task_id, r=rel.getEditURL())) diff --git a/app/blueprints/packages/reviews.py b/app/blueprints/packages/reviews.py index 04bcf41..5294c39 100644 --- a/app/blueprints/packages/reviews.py +++ b/app/blueprints/packages/reviews.py @@ -21,7 +21,7 @@ from flask_login import current_user, login_required from flask_wtf import FlaskForm from wtforms import * from wtforms.validators import * -from app.models import db, PackageReview, Thread, ThreadReply +from app.models import db, PackageReview, Thread, ThreadReply, NotificationType from app.utils import is_package_page, addNotification, get_int_or_abort @@ -98,13 +98,15 @@ def review(package): package.recalcScore() - notif_msg = None if was_new: notif_msg = "New review '{}'".format(form.title.data) + type = NotificationType.NEW_REVIEW else: notif_msg = "Updated review '{}'".format(form.title.data) + type = NotificationType.OTHER - addNotification(package.maintainers, current_user, notif_msg, url_for("threads.view", id=thread.id), package) + addNotification(package.maintainers, current_user, type, notif_msg, + url_for("threads.view", id=thread.id), package) db.session.commit() @@ -133,7 +135,7 @@ def delete_review(package): thread.review = None notif_msg = "Deleted review '{}', comments were kept as a thread".format(thread.title) - addNotification(package.maintainers, current_user, notif_msg, url_for("threads.view", id=thread.id), package) + addNotification(package.maintainers, current_user, NotificationType.OTHER, notif_msg, url_for("threads.view", id=thread.id), package) db.session.delete(review) db.session.commit() diff --git a/app/blueprints/packages/screenshots.py b/app/blueprints/packages/screenshots.py index fa7d205..323aa97 100644 --- a/app/blueprints/packages/screenshots.py +++ b/app/blueprints/packages/screenshots.py @@ -59,7 +59,7 @@ def create_screenshot(package): msg = "Screenshot added {}" \ .format(ss.title) - addNotification(package.maintainers, current_user, msg, package.getDetailsURL(), package) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, package.getDetailsURL(), package) db.session.commit() return redirect(package.getDetailsURL()) diff --git a/app/blueprints/threads/__init__.py b/app/blueprints/threads/__init__.py index 9233fa4..c19d823 100644 --- a/app/blueprints/threads/__init__.py +++ b/app/blueprints/threads/__init__.py @@ -97,8 +97,8 @@ def set_lock(id): msg = "Unlocked thread '{}'".format(thread.title) flash("Unlocked thread", "success") - addNotification(thread.watchers, current_user, msg, thread.getViewURL(), thread.package) - addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.getViewURL(), thread.package) + addNotification(thread.watchers, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) + addAuditLog(AuditSeverity.MODERATION, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) db.session.commit() @@ -168,7 +168,7 @@ def edit_reply(id): msg = "Edited reply by {}".format(reply.author.display_name) severity = AuditSeverity.NORMAL if current_user == reply.author else AuditSeverity.MODERATION - addNotification(reply.author, current_user, msg, thread.getViewURL(), thread.package) + addNotification(reply.author, current_user, NotificationType.OTHER, msg, thread.getViewURL(), thread.package) addAuditLog(severity, current_user, msg, thread.getViewURL(), thread.package, reply.comment) reply.comment = comment @@ -208,7 +208,7 @@ def view(id): thread.watchers.append(current_user) msg = "New comment on '{}'".format(thread.title) - addNotification(thread.watchers, current_user, msg, thread.getViewURL(), thread.package) + addNotification(thread.watchers, current_user, NotificationType.THREAD_REPLY, msg, thread.getViewURL(), thread.package) db.session.commit() return redirect(thread.getViewURL()) @@ -302,10 +302,10 @@ def new(): notif_msg = "New thread '{}'".format(thread.title) if package is not None: - addNotification(package.maintainers, current_user, notif_msg, thread.getViewURL(), package) + addNotification(package.maintainers, current_user, NotificationType.NEW_THREAD, notif_msg, thread.getViewURL(), package) editors = User.query.filter(User.rank >= UserRank.EDITOR).all() - addNotification(editors, current_user, notif_msg, thread.getViewURL(), package) + addNotification(editors, current_user, NotificationType.EDITOR_MISC, notif_msg, thread.getViewURL(), package) db.session.commit() diff --git a/app/models.py b/app/models.py index 9e89b30..75e022d 100644 --- a/app/models.py +++ b/app/models.py @@ -274,6 +274,53 @@ class UserEmailVerification(db.Model): is_password_reset = db.Column(db.Boolean, nullable=False, default=False) +class NotificationType(enum.Enum): + # Any other + OTHER = 0 + + # Package / release / etc + PACKAGE_EDIT = 1 + + # Approval review actions + PACKAGE_APPROVAL = 2 + + # New thread + NEW_THREAD = 3 + + # New Review + NEW_REVIEW = 4 + + # Posted reply to subscribed thread + THREAD_REPLY = 5 + + # Added / removed as maintainer + MAINTAINER = 6 + + # Editor misc + EDITOR_ALERT = 7 + + # Editor misc + EDITOR_MISC = 8 + + + def getTitle(self): + return self.name.replace("_", " ").title() + + def toName(self): + return self.name.lower() + + def __str__(self): + return self.name + + @classmethod + def choices(cls): + return [(choice, choice.getTitle()) for choice in cls] + + @classmethod + def coerce(cls, item): + return item if type(item) == NotificationType else NotificationType[item] + + class Notification(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -283,6 +330,10 @@ class Notification(db.Model): causer_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False) causer = db.relationship("User", foreign_keys=[causer_id]) + type = db.Column(db.Enum(NotificationType), nullable=False, default=NotificationType.OTHER) + + emailed = db.Column(db.Boolean(), nullable=False, default=False) + title = db.Column(db.String(100), nullable=False) url = db.Column(db.String(200), nullable=True) @@ -291,12 +342,13 @@ class Notification(db.Model): created_at = db.Column(db.DateTime, nullable=True, default=datetime.datetime.utcnow) - def __init__(self, user, causer, title, url, package=None): + def __init__(self, user, causer, type, title, url, package=None): if len(title) > 100: title = title[:99] + "…" self.user = user self.causer = causer + self.type = type self.title = title self.url = url self.package = package diff --git a/app/utils.py b/app/utils.py index 4b8bfbe..3a0a292 100644 --- a/app/utils.py +++ b/app/utils.py @@ -235,18 +235,18 @@ def is_package_page(f): return decorated_function -def addNotification(target, causer, title, url, package=None): +def addNotification(target: User, causer: User, type: NotificationType, title: str, url: str, package: Package =None): try: iter(target) for x in target: - addNotification(x, causer, title, url, package) + addNotification(x, causer, type, title, url, package) return except TypeError: pass if target.rank.atLeast(UserRank.NEW_MEMBER) and target != causer: - Notification.query.filter_by(user=target, causer=causer, title=title, url=url, package=package).delete() - notif = Notification(target, causer, title, url, package) + Notification.query.filter_by(user=target, causer=causer, type=type, title=title, url=url, package=package).delete() + notif = Notification(target, causer, type, title, url, package) db.session.add(notif) diff --git a/migrations/versions/81de25b72f66_.py b/migrations/versions/81de25b72f66_.py new file mode 100644 index 0000000..749cb7b --- /dev/null +++ b/migrations/versions/81de25b72f66_.py @@ -0,0 +1,31 @@ +"""empty message + +Revision ID: 81de25b72f66 +Revises: c154912eaa0c +Create Date: 2020-12-05 03:38:42.004388 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +from sqlalchemy.dialects import postgresql + +revision = '81de25b72f66' +down_revision = 'c154912eaa0c' +branch_labels = None +depends_on = None + + +def upgrade(): + status = postgresql.ENUM('OTHER', 'PACKAGE_EDIT', 'PACKAGE_APPROVAL', 'NEW_THREAD', 'NEW_REVIEW', 'THREAD_REPLY', 'MAINTAINER', 'EDITOR_ALERT', 'EDITOR_MISC', name='notificationtype') + status.create(op.get_bind()) + + op.add_column('notification', sa.Column('emailed', sa.Boolean(), nullable=False, server_default="true")) + op.add_column('notification', sa.Column('type', sa.Enum('OTHER', 'PACKAGE_EDIT', 'PACKAGE_APPROVAL', 'NEW_THREAD', 'NEW_REVIEW', 'THREAD_REPLY', 'MAINTAINER', 'EDITOR_ALERT', 'EDITOR_MISC', name='notificationtype'), nullable=False, server_default="OTHER")) + + +def downgrade(): + op.drop_column('notification', 'type') + op.drop_column('notification', 'emailed')