-
Notifications
You must be signed in to change notification settings - Fork 83
Add notifications for chroots marked EOL #4474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 1.8-1 common/ | ||
| 1.8.1-1 common/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,4 +2,4 @@ | |
| Copr Project - generated version file for sub-component | ||
| """ | ||
|
|
||
| __version__ = "1.8" | ||
| __version__ = "1.8.1" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| """ | ||
| Add Notification table | ||
|
|
||
| Revision ID: 86cb0360405d | ||
| Create Date: 2026-08-09 14:20:01.887507 | ||
| """ | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
|
|
||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision = '86cb0360405d' | ||
| down_revision = 'e31b4af2468c' | ||
| branch_labels = None | ||
| depends_on = None | ||
|
|
||
|
|
||
| def upgrade(): | ||
| op.create_table('notification', | ||
| sa.Column('id', sa.Integer(), nullable=False), | ||
| sa.Column('user_id', sa.Integer(), nullable=False), | ||
| sa.Column('notification_type', sa.Integer(), nullable=False), | ||
| sa.Column('subject', sa.String(length=255), nullable=False), | ||
| sa.Column('body', sa.Text(), nullable=False), | ||
| sa.Column('seen_on', sa.Integer(), nullable=True), | ||
| sa.Column('created_on', sa.Integer(), nullable=False), | ||
| sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), | ||
| sa.PrimaryKeyConstraint('id') | ||
| ) | ||
| op.create_index('notification_user_seen_idx', 'notification', ['user_id', 'seen_on'], unique=False) | ||
|
|
||
|
|
||
| def downgrade(): | ||
| op.drop_index('notification_user_seen_idx', table_name='notification') | ||
| op.drop_table('notification') | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| from sqlalchemy.exc import SQLAlchemyError | ||
| from coprs import db, app | ||
| from coprs.logic import coprs_logic | ||
| from coprs.logic.notifications_logic import NotificationsLogic | ||
| from coprs.mail import send_mail, OutdatedChrootMessage | ||
|
|
||
|
|
||
|
|
@@ -113,6 +114,8 @@ def notify(self, user, chroots): | |
| app.logger.exception("Failed to notify %s", user.mail) | ||
| return | ||
|
|
||
| # if the email succeeds, we will create a notification message | ||
| NotificationsLogic.create(user, msg.subject, msg.text, "eol_chroot") | ||
|
Comment on lines
+117
to
+118
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Do not make the inbox depend on email delivery. When 🤖 Prompt for AI Agents |
||
| # If `send_mail` didn't raise any exception, | ||
| # we consider the email to be sent correctly | ||
| for chroot in chroots: | ||
|
Comment on lines
+117
to
121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift Make email delivery retry-safe. 🤖 Prompt for AI Agents |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| """ | ||
| Logic for the notification messages | ||
| """ | ||
|
|
||
| import time | ||
| from coprs import db | ||
| from coprs import models | ||
| from copr_common.enums import NotificationTypeEnum | ||
|
|
||
|
|
||
| class NotificationsLogic: | ||
| """ | ||
| Notification Messags Logic | ||
| """ | ||
|
|
||
| @classmethod | ||
| def create(cls, user, subject, text, notification_type): | ||
| """ | ||
| Create a new notification message for a user | ||
| """ | ||
| notification = models.Notification( | ||
| user_id=user.id, | ||
| notification_type=NotificationTypeEnum(notification_type), | ||
| subject=subject, | ||
| body=text, | ||
| created_on=int(time.time()), | ||
| ) | ||
| db.session.add(notification) | ||
| return notification | ||
|
|
||
| @classmethod | ||
| def get_unseen_user_notifications(cls, user): | ||
| """ | ||
| Query a user's unseen notification messages (newest first) | ||
| """ | ||
| return (models.Notification.query | ||
| .filter(models.Notification.user_id == user.id) | ||
| .filter(models.Notification.seen_on.is_(None)) | ||
| .order_by(models.Notification.created_on.desc(), | ||
| models.Notification.id.desc())) | ||
|
|
||
| @classmethod | ||
| def mark_seen(cls, notification): | ||
| """ | ||
| Mark a single notification message as seen. | ||
| """ | ||
| if not notification.seen_on: | ||
| notification.seen_on = int(time.time()) | ||
|
|
||
| @classmethod | ||
| def mark_seen_by_ids(cls, user, notification_ids): | ||
| """ | ||
| Mark notification messages as seen by id's. | ||
| """ | ||
| notifications = (models.Notification.query | ||
| .filter(models.Notification.user_id == user.id) | ||
| .filter(models.Notification.id.in_(notification_ids))) | ||
| for notification in notifications: | ||
| cls.mark_seen(notification) | ||
|
|
||
| @classmethod | ||
| def delete_user_notifications(cls, user): | ||
| """ | ||
| Delete all notification messages belonging to a user. | ||
| """ | ||
| (models.Notification.query | ||
| .filter(models.Notification.user_id == user.id) | ||
| .delete()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -251,6 +251,15 @@ def coprs_count(self): | |
| filter_by(group_id=None). | ||
| count()) | ||
|
|
||
| @property | ||
| def unseen_notifications_count(self): | ||
| """ | ||
| Get number of unseen notification messages for this user. | ||
| """ | ||
| return (Notification.query.filter_by(user_id=self.id). | ||
| filter(Notification.seen_on.is_(None)). | ||
| count()) | ||
|
|
||
| @property | ||
| def gravatar_url(self): | ||
| """ | ||
|
|
@@ -2718,3 +2727,28 @@ def insert_fedora_distgit(*args, **kwargs): | |
| clone_package_uri="{namespace}/rpms/{pkgname}", | ||
| default_namespace="", | ||
| )) | ||
|
|
||
|
|
||
| class Notification(db.Model): | ||
| """ | ||
| Represents a message in the notification. | ||
| """ | ||
|
|
||
| __table_args__ = ( | ||
| db.Index('notification_user_seen_idx', 'user_id', 'seen_on'), | ||
| ) | ||
|
|
||
| id = db.Column(db.Integer, primary_key=True) | ||
|
|
||
| user_id = db.Column( | ||
| db.Integer, | ||
| db.ForeignKey("user.id"), | ||
| nullable=False | ||
| ) | ||
|
|
||
| notification_type = db.Column(db.Integer, nullable=False) | ||
|
|
||
| subject = db.Column(db.String(255), nullable=False) | ||
| body = db.Column(db.Text, nullable=False) | ||
| seen_on = db.Column(db.Integer) | ||
| created_on = db.Column(db.Integer, nullable=False) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpicking here, but I'd remove all the blank lines. The only use-case for blank lines is for grouping related things together and then visually separating the groups from each other. Here I don't see any reason to it |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: fedora-copr/copr
Length of output: 50373
🏁 Script executed:
Repository: fedora-copr/copr
Length of output: 42135
🏁 Script executed:
Repository: fedora-copr/copr
Length of output: 29930
Clean up notifications in the GDPR deletion path.
UsersLogic.delete_user_dataclears user fields but does not removeNotificationrows. The/user/deleteroute therefore leaves each notification'suser_id,subject, andbodystored after deletion. Delete or anonymize these rows.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sundaram123krishnan The annoying bot has a point here. I don't know if anyone has ever used the feature, but we don't want to mess with the EU law.