-
Notifications
You must be signed in to change notification settings - Fork 298
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
User notifications bundle #4457
Changes from 25 commits
0dcad9f
872990c
2ab8315
63bc4d0
7e889fe
59a9664
94c66ee
271c99f
ea06498
cb71560
b0db509
c943841
0b036e6
1145755
4ff84fe
59d9d65
5d34609
a44af00
ef3eecc
5fc5d5e
834f8a2
0b8a91e
95bb29d
e85776b
e0e237d
e2f2727
cc1ede0
d6b9242
0394ea1
4eed7d8
58cf5ef
1431674
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Generated by Django 4.2.10 on 2024-06-20 11:00 | ||
|
||
import apps.base.models.user_notification_policy | ||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('base', '0005_drop_unused_dynamic_settings'), | ||
('user_management', '0022_alter_team_unique_together'), | ||
('alerts', '0051_remove_escalationpolicy_custom_button_trigger'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='UserNotificationBundle', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('important', models.BooleanField()), | ||
('notification_channel', models.PositiveSmallIntegerField(default=None, null=True, validators=[apps.base.models.user_notification_policy.validate_channel_choice])), | ||
('last_notified_at', models.DateTimeField(default=None, null=True)), | ||
('notification_task_id', models.CharField(default=None, max_length=100, null=True)), | ||
('eta', models.DateTimeField(default=None, null=True)), | ||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_bundles', to='user_management.user')), | ||
], | ||
), | ||
migrations.CreateModel( | ||
name='BundledNotification', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('created_at', models.DateTimeField(auto_now_add=True)), | ||
('bundle_uuid', models.CharField(db_index=True, default=None, max_length=100, null=True)), | ||
('alert_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='alerts.alertgroup')), | ||
('alert_receive_channel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='alerts.alertreceivechannel')), | ||
('notification_bundle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='alerts.usernotificationbundle')), | ||
('notification_policy', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='base.usernotificationpolicy')), | ||
], | ||
), | ||
migrations.AddConstraint( | ||
model_name='usernotificationbundle', | ||
constraint=models.UniqueConstraint(fields=('user', 'important', 'notification_channel'), name='unique_user_notification_bundle'), | ||
), | ||
] |
Ferril marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import datetime | ||
|
||
from django.db import models | ||
from django.utils import timezone | ||
|
||
from apps.alerts.constants import BUNDLED_NOTIFICATION_DELAY_SECONDS | ||
from apps.base.models import UserNotificationPolicy | ||
from apps.base.models.user_notification_policy import validate_channel_choice | ||
|
||
|
||
class UserNotificationBundle(models.Model): | ||
NOTIFICATION_CHANNELS_TO_BUNDLE = [ | ||
UserNotificationPolicy.NotificationChannel.SMS, | ||
] | ||
|
||
user = models.ForeignKey("user_management.User", on_delete=models.CASCADE, related_name="notification_bundles") | ||
important = models.BooleanField() | ||
notification_channel = models.PositiveSmallIntegerField( | ||
validators=[validate_channel_choice], null=True, default=None | ||
) | ||
last_notified_at = models.DateTimeField(default=None, null=True) | ||
notification_task_id = models.CharField(max_length=100, null=True, default=None) | ||
# estimated time of arrival for scheduled send_bundled_notification task | ||
eta = models.DateTimeField(default=None, null=True) | ||
|
||
class Meta: | ||
constraints = [ | ||
models.UniqueConstraint( | ||
fields=["user", "important", "notification_channel"], name="unique_user_notification_bundle" | ||
) | ||
] | ||
|
||
def notified_recently(self) -> bool: | ||
return ( | ||
timezone.now() - self.last_notified_at < timezone.timedelta(seconds=BUNDLED_NOTIFICATION_DELAY_SECONDS) | ||
if self.last_notified_at | ||
else False | ||
) | ||
|
||
def eta_is_valid(self) -> bool: | ||
""" | ||
`eta` shows eta of scheduled send_bundled_notification task and should never be less than the current time | ||
(with a 1 minute buffer provided). | ||
`eta` is None means that there is no scheduled task. | ||
""" | ||
if not self.eta or self.eta + timezone.timedelta(minutes=1) >= timezone.now(): | ||
return True | ||
return False | ||
|
||
def get_notification_eta(self) -> datetime.datetime: | ||
last_notified = self.last_notified_at if self.last_notified_at else timezone.now() | ||
return last_notified + timezone.timedelta(seconds=BUNDLED_NOTIFICATION_DELAY_SECONDS) | ||
|
||
def append_notification(self, alert_group, notification_policy): | ||
self.notifications.create( | ||
alert_group=alert_group, notification_policy=notification_policy, alert_receive_channel=alert_group.channel | ||
) | ||
|
||
@classmethod | ||
def notification_is_bundleable(cls, notification_channel): | ||
return notification_channel in cls.NOTIFICATION_CHANNELS_TO_BUNDLE | ||
Ferril marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
class BundledNotification(models.Model): | ||
alert_group = models.ForeignKey("alerts.AlertGroup", on_delete=models.CASCADE) | ||
alert_receive_channel = models.ForeignKey("alerts.AlertReceiveChannel", on_delete=models.CASCADE) | ||
notification_policy = models.ForeignKey("base.UserNotificationPolicy", on_delete=models.SET_NULL, null=True) | ||
notification_bundle = models.ForeignKey( | ||
UserNotificationBundle, on_delete=models.CASCADE, related_name="notifications" | ||
) | ||
created_at = models.DateTimeField(auto_now_add=True) | ||
bundle_uuid = models.CharField(max_length=100, null=True, default=None, db_index=True) | ||
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. I see this is primarly used right now in as the second arg to 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. The idea is that |
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.
Why do we store
task_id
inactive_notification_policy_id
field? Please rename or at least add the commentThere 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.
It's a part of the old model
UserHasNotification
and wasn't added or changed in this PR. I just moved these lines in separate method to makenotify_user_task
more readable. Makes sense to add the comment though.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.
maybe out of scope for this PR but I think this model should be renamed to something like
UserActiveNotificationPolicy
(UserHasNotification
isn't very informative imo 😬 )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.
Agree 💯 I'd propose to do it with
notify_user_task
refactoring (#4629)