Change notification backend
This commit is contained in:
parent
5f97491f0d
commit
7f854c40dd
15 changed files with 188 additions and 134 deletions
|
@ -18,6 +18,9 @@ class Room(models.Model):
|
||||||
Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE
|
Profile, related_name="user_two", verbose_name="user 2", on_delete=CASCADE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = "chat_box"
|
||||||
|
|
||||||
@cache_wrapper(prefix="Rc")
|
@cache_wrapper(prefix="Rc")
|
||||||
def contain(self, profile):
|
def contain(self, profile):
|
||||||
return self.user_one == profile or self.user_two == profile
|
return self.user_one == profile or self.user_two == profile
|
||||||
|
@ -58,6 +61,7 @@ class Message(models.Model):
|
||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=["hidden", "room", "-id"]),
|
models.Index(fields=["hidden", "room", "-id"]),
|
||||||
]
|
]
|
||||||
|
app_label = "chat_box"
|
||||||
|
|
||||||
|
|
||||||
class UserRoom(models.Model):
|
class UserRoom(models.Model):
|
||||||
|
@ -70,6 +74,7 @@ class UserRoom(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("user", "room")
|
unique_together = ("user", "room")
|
||||||
|
app_label = "chat_box"
|
||||||
|
|
||||||
|
|
||||||
class Ignore(models.Model):
|
class Ignore(models.Model):
|
||||||
|
@ -82,6 +87,9 @@ class Ignore(models.Model):
|
||||||
)
|
)
|
||||||
ignored_users = models.ManyToManyField(Profile)
|
ignored_users = models.ManyToManyField(Profile)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = "chat_box"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_ignored(self, current_user, new_friend):
|
def is_ignored(self, current_user, new_friend):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -25,6 +25,7 @@ from judge.models import (
|
||||||
Solution,
|
Solution,
|
||||||
Notification,
|
Notification,
|
||||||
)
|
)
|
||||||
|
from judge.models.notification import make_notification
|
||||||
from judge.widgets import (
|
from judge.widgets import (
|
||||||
AdminHeavySelect2MultipleWidget,
|
AdminHeavySelect2MultipleWidget,
|
||||||
AdminSelect2MultipleWidget,
|
AdminSelect2MultipleWidget,
|
||||||
|
@ -381,14 +382,7 @@ class ProblemAdmin(CompareVersionAdmin):
|
||||||
category = "Problem public: " + str(obj.is_public)
|
category = "Problem public: " + str(obj.is_public)
|
||||||
if orgs:
|
if orgs:
|
||||||
category += " (" + ", ".join(orgs) + ")"
|
category += " (" + ", ".join(orgs) + ")"
|
||||||
for user in users:
|
make_notification(users, html, category, request.profile)
|
||||||
notification = Notification(
|
|
||||||
owner=user,
|
|
||||||
html_link=html,
|
|
||||||
category=category,
|
|
||||||
author=request.profile,
|
|
||||||
)
|
|
||||||
notification.save()
|
|
||||||
|
|
||||||
def construct_change_message(self, request, form, *args, **kwargs):
|
def construct_change_message(self, request, form, *args, **kwargs):
|
||||||
if form.cleaned_data.get("change_message"):
|
if form.cleaned_data.get("change_message"):
|
||||||
|
|
|
@ -26,21 +26,20 @@ from judge.dblock import LockModel
|
||||||
from judge.models import Comment, Notification
|
from judge.models import Comment, Notification
|
||||||
from judge.widgets import HeavyPreviewPageDownWidget
|
from judge.widgets import HeavyPreviewPageDownWidget
|
||||||
from judge.jinja2.reference import get_user_from_text
|
from judge.jinja2.reference import get_user_from_text
|
||||||
|
from judge.models.notification import make_notification
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_OFFSET = 10
|
DEFAULT_OFFSET = 10
|
||||||
|
|
||||||
|
|
||||||
|
def _get_html_link_notification(comment):
|
||||||
|
return f'<a href="{comment.get_absolute_url()}">{comment.page_title}</a>'
|
||||||
|
|
||||||
|
|
||||||
def add_mention_notifications(comment):
|
def add_mention_notifications(comment):
|
||||||
user_referred = get_user_from_text(comment.body).exclude(id=comment.author.id)
|
users_mentioned = get_user_from_text(comment.body).exclude(id=comment.author.id)
|
||||||
for user in user_referred:
|
link = _get_html_link_notification(comment)
|
||||||
notification_ref = Notification(owner=user, comment=comment, category="Mention")
|
make_notification(users_mentioned, "Mention", link, comment.author)
|
||||||
notification_ref.save()
|
|
||||||
|
|
||||||
|
|
||||||
def del_mention_notifications(comment):
|
|
||||||
query = {"comment": comment, "category": "Mention"}
|
|
||||||
Notification.objects.filter(**query).delete()
|
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(ModelForm):
|
class CommentForm(ModelForm):
|
||||||
|
@ -124,23 +123,17 @@ class CommentedDetailView(TemplateResponseMixin, SingleObjectMixin, View):
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
# add notification for reply
|
# add notification for reply
|
||||||
|
comment_notif_link = _get_html_link_notification(comment)
|
||||||
if comment.parent and comment.parent.author != comment.author:
|
if comment.parent and comment.parent.author != comment.author:
|
||||||
notification_reply = Notification(
|
make_notification(
|
||||||
owner=comment.parent.author, comment=comment, category="Reply"
|
[comment.parent.author], "Reply", comment_notif_link, comment.author
|
||||||
)
|
)
|
||||||
notification_reply.save()
|
|
||||||
|
|
||||||
# add notification for page authors
|
# add notification for page authors
|
||||||
page_authors = comment.linked_object.authors.all()
|
page_authors = comment.linked_object.authors.all()
|
||||||
for user in page_authors:
|
make_notification(
|
||||||
if user == comment.author:
|
page_authors, "Comment", comment_notif_link, comment.author
|
||||||
continue
|
)
|
||||||
notification = Notification(
|
|
||||||
owner=user, comment=comment, category="Comment"
|
|
||||||
)
|
|
||||||
notification.save()
|
|
||||||
# except Exception:
|
|
||||||
# pass
|
|
||||||
|
|
||||||
add_mention_notifications(comment)
|
add_mention_notifications(comment)
|
||||||
|
|
||||||
|
|
68
judge/migrations/0171_update_notification.py
Normal file
68
judge/migrations/0171_update_notification.py
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Generated by Django 3.2.18 on 2023-10-10 21:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
|
# Run this in shell
|
||||||
|
def migrate_notif(apps, schema_editor):
|
||||||
|
Notification = apps.get_model("judge", "Notification")
|
||||||
|
Profile = apps.get_model("judge", "Profile")
|
||||||
|
NotificationProfile = apps.get_model("judge", "NotificationProfile")
|
||||||
|
|
||||||
|
unread_count = defaultdict(int)
|
||||||
|
for c in Notification.objects.all():
|
||||||
|
if c.comment:
|
||||||
|
c.html_link = (
|
||||||
|
f'<a href="{c.comment.get_absolute_url()}">{c.comment.page_title}</a>'
|
||||||
|
)
|
||||||
|
c.author = c.comment.author
|
||||||
|
c.save()
|
||||||
|
if c.read is False:
|
||||||
|
unread_count[c.author] += 1
|
||||||
|
|
||||||
|
for user in unread_count:
|
||||||
|
np = NotificationProfile(user=user)
|
||||||
|
np.unread_count = unread_count[user]
|
||||||
|
np.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("judge", "0170_contests_summary"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="contestssummary",
|
||||||
|
options={
|
||||||
|
"verbose_name": "contests summary",
|
||||||
|
"verbose_name_plural": "contests summaries",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="NotificationProfile",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("unread_count", models.IntegerField(default=0)),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="judge.profile"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -6,7 +6,7 @@ from judge.models.choices import (
|
||||||
MATH_ENGINES_CHOICES,
|
MATH_ENGINES_CHOICES,
|
||||||
TIMEZONE,
|
TIMEZONE,
|
||||||
)
|
)
|
||||||
from judge.models.comment import Comment, CommentLock, CommentVote, Notification
|
from judge.models.comment import Comment, CommentLock, CommentVote
|
||||||
from judge.models.contest import (
|
from judge.models.contest import (
|
||||||
Contest,
|
Contest,
|
||||||
ContestMoss,
|
ContestMoss,
|
||||||
|
@ -58,6 +58,7 @@ from judge.models.volunteer import VolunteerProblemVote
|
||||||
from judge.models.pagevote import PageVote, PageVoteVoter
|
from judge.models.pagevote import PageVote, PageVoteVoter
|
||||||
from judge.models.bookmark import BookMark, MakeBookMark
|
from judge.models.bookmark import BookMark, MakeBookMark
|
||||||
from judge.models.course import Course
|
from judge.models.course import Course
|
||||||
|
from judge.models.notification import Notification, NotificationProfile
|
||||||
|
|
||||||
revisions.register(Profile, exclude=["points", "last_access", "ip", "rating"])
|
revisions.register(Profile, exclude=["points", "last_access", "ip", "rating"])
|
||||||
revisions.register(Problem, follow=["language_limits"])
|
revisions.register(Problem, follow=["language_limits"])
|
||||||
|
|
|
@ -177,29 +177,3 @@ class CommentLock(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.page)
|
return str(self.page)
|
||||||
|
|
||||||
|
|
||||||
class Notification(models.Model):
|
|
||||||
owner = models.ForeignKey(
|
|
||||||
Profile,
|
|
||||||
verbose_name=_("owner"),
|
|
||||||
related_name="notifications",
|
|
||||||
on_delete=CASCADE,
|
|
||||||
)
|
|
||||||
time = models.DateTimeField(verbose_name=_("posted time"), auto_now_add=True)
|
|
||||||
comment = models.ForeignKey(
|
|
||||||
Comment, null=True, verbose_name=_("comment"), on_delete=CASCADE
|
|
||||||
)
|
|
||||||
read = models.BooleanField(verbose_name=_("read"), default=False)
|
|
||||||
category = models.CharField(verbose_name=_("category"), max_length=1000)
|
|
||||||
html_link = models.TextField(
|
|
||||||
default="",
|
|
||||||
verbose_name=_("html link to comments, used for non-comments"),
|
|
||||||
max_length=1000,
|
|
||||||
)
|
|
||||||
author = models.ForeignKey(
|
|
||||||
Profile,
|
|
||||||
null=True,
|
|
||||||
verbose_name=_("who trigger, used for non-comment"),
|
|
||||||
on_delete=CASCADE,
|
|
||||||
)
|
|
||||||
|
|
61
judge/models/notification.py
Normal file
61
judge/models/notification.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.db.models import CASCADE, F
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
|
from judge.models import Profile, Comment
|
||||||
|
from judge.caching import cache_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class Notification(models.Model):
|
||||||
|
owner = models.ForeignKey(
|
||||||
|
Profile,
|
||||||
|
verbose_name=_("owner"),
|
||||||
|
related_name="notifications",
|
||||||
|
on_delete=CASCADE,
|
||||||
|
)
|
||||||
|
time = models.DateTimeField(verbose_name=_("posted time"), auto_now_add=True)
|
||||||
|
category = models.CharField(verbose_name=_("category"), max_length=1000)
|
||||||
|
html_link = models.TextField(
|
||||||
|
default="",
|
||||||
|
verbose_name=_("html link to comments, used for non-comments"),
|
||||||
|
max_length=1000,
|
||||||
|
)
|
||||||
|
author = models.ForeignKey(
|
||||||
|
Profile,
|
||||||
|
null=True,
|
||||||
|
verbose_name=_("who trigger, used for non-comment"),
|
||||||
|
on_delete=CASCADE,
|
||||||
|
)
|
||||||
|
comment = models.ForeignKey(
|
||||||
|
Comment, null=True, verbose_name=_("comment"), on_delete=CASCADE
|
||||||
|
) # deprecated
|
||||||
|
read = models.BooleanField(verbose_name=_("read"), default=False) # deprecated
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationProfile(models.Model):
|
||||||
|
unread_count = models.IntegerField(default=0)
|
||||||
|
user = models.OneToOneField(Profile, on_delete=CASCADE)
|
||||||
|
|
||||||
|
|
||||||
|
def make_notification(to_users, category, html_link, author):
|
||||||
|
for user in to_users:
|
||||||
|
if user == author:
|
||||||
|
continue
|
||||||
|
notif = Notification(
|
||||||
|
owner=user, category=category, html_link=html_link, author=author
|
||||||
|
)
|
||||||
|
notif.save()
|
||||||
|
NotificationProfile.objects.get_or_create(user=user)
|
||||||
|
NotificationProfile.objects.filter(user=user).update(
|
||||||
|
unread_count=F("unread_count") + 1
|
||||||
|
)
|
||||||
|
unseen_notifications_count.dirty(user)
|
||||||
|
|
||||||
|
|
||||||
|
@cache_wrapper(prefix="unc")
|
||||||
|
def unseen_notifications_count(profile):
|
||||||
|
try:
|
||||||
|
return NotificationProfile.objects.get(user=profile).unread_count
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return 0
|
|
@ -5,6 +5,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
from judge.models.profile import Profile
|
from judge.models.profile import Profile
|
||||||
|
from judge.caching import cache_wrapper
|
||||||
|
|
||||||
__all__ = ["PageVote", "PageVoteVoter"]
|
__all__ = ["PageVote", "PageVoteVoter"]
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ class PageVote(models.Model):
|
||||||
]
|
]
|
||||||
unique_together = ("content_type", "object_id")
|
unique_together = ("content_type", "object_id")
|
||||||
|
|
||||||
|
@cache_wrapper(prefix="PVvs")
|
||||||
def vote_score(self, user):
|
def vote_score(self, user):
|
||||||
page_vote = PageVoteVoter.objects.filter(pagevote=self, voter=user)
|
page_vote = PageVoteVoter.objects.filter(pagevote=self, voter=user)
|
||||||
if page_vote.exists():
|
if page_vote.exists():
|
||||||
|
|
|
@ -16,6 +16,7 @@ from sortedm2m.fields import SortedManyToManyField
|
||||||
from judge.models.choices import ACE_THEMES, MATH_ENGINES_CHOICES, TIMEZONE
|
from judge.models.choices import ACE_THEMES, MATH_ENGINES_CHOICES, TIMEZONE
|
||||||
from judge.models.runtime import Language
|
from judge.models.runtime import Language
|
||||||
from judge.ratings import rating_class
|
from judge.ratings import rating_class
|
||||||
|
from judge.caching import cache_wrapper
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Organization", "Profile", "OrganizationRequest", "Friend"]
|
__all__ = ["Organization", "Profile", "OrganizationRequest", "Friend"]
|
||||||
|
@ -142,6 +143,7 @@ class Organization(models.Model):
|
||||||
)
|
)
|
||||||
verbose_name = _("organization")
|
verbose_name = _("organization")
|
||||||
verbose_name_plural = _("organizations")
|
verbose_name_plural = _("organizations")
|
||||||
|
app_label = "judge"
|
||||||
|
|
||||||
|
|
||||||
class Profile(models.Model):
|
class Profile(models.Model):
|
||||||
|
@ -266,10 +268,9 @@ class Profile(models.Model):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def count_unseen_notifications(self):
|
def count_unseen_notifications(self):
|
||||||
query = {
|
from judge.models.notification import unseen_notifications_count
|
||||||
"read": False,
|
|
||||||
}
|
return unseen_notifications_count(self)
|
||||||
return self.notifications.filter(**query).count()
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def count_unread_chat_boxes(self):
|
def count_unread_chat_boxes(self):
|
||||||
|
|
|
@ -27,7 +27,7 @@ from judge.dblock import LockModel
|
||||||
from judge.models import Comment, CommentVote, Notification, BlogPost
|
from judge.models import Comment, CommentVote, Notification, BlogPost
|
||||||
from judge.utils.views import TitleMixin
|
from judge.utils.views import TitleMixin
|
||||||
from judge.widgets import MathJaxPagedownWidget, HeavyPreviewPageDownWidget
|
from judge.widgets import MathJaxPagedownWidget, HeavyPreviewPageDownWidget
|
||||||
from judge.comments import add_mention_notifications, del_mention_notifications
|
from judge.comments import add_mention_notifications
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
@ -240,7 +240,6 @@ class CommentEditAjax(LoginRequiredMixin, CommentMixin, UpdateView):
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# update notifications
|
# update notifications
|
||||||
comment = form.instance
|
comment = form.instance
|
||||||
del_mention_notifications(comment)
|
|
||||||
add_mention_notifications(comment)
|
add_mention_notifications(comment)
|
||||||
|
|
||||||
with transaction.atomic(), revisions.create_revision():
|
with transaction.atomic(), revisions.create_revision():
|
||||||
|
|
|
@ -2,10 +2,9 @@ from django.contrib.auth.decorators import login_required
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.db.models import BooleanField, Value
|
|
||||||
|
|
||||||
from judge.utils.cachedict import CacheDict
|
from judge.models import Profile, Notification, NotificationProfile
|
||||||
from judge.models import Profile, Comment, Notification
|
from judge.models.notification import unseen_notifications_count
|
||||||
|
|
||||||
__all__ = ["NotificationList"]
|
__all__ = ["NotificationList"]
|
||||||
|
|
||||||
|
@ -16,24 +15,11 @@ class NotificationList(ListView):
|
||||||
template_name = "notification/list.html"
|
template_name = "notification/list.html"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.unseen_cnt = self.request.profile.count_unseen_notifications
|
self.unseen_cnt = unseen_notifications_count(self.request.profile)
|
||||||
|
|
||||||
query = {
|
self.queryset = Notification.objects.filter(
|
||||||
"owner": self.request.profile,
|
owner=self.request.profile
|
||||||
}
|
).order_by("-id")[:100]
|
||||||
|
|
||||||
self.queryset = (
|
|
||||||
Notification.objects.filter(**query)
|
|
||||||
.order_by("-time")[:100]
|
|
||||||
.annotate(seen=Value(True, output_field=BooleanField()))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Mark the several first unseen
|
|
||||||
for cnt, q in enumerate(self.queryset):
|
|
||||||
if cnt < self.unseen_cnt:
|
|
||||||
q.seen = False
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
return self.queryset
|
return self.queryset
|
||||||
|
|
||||||
|
@ -46,8 +32,6 @@ class NotificationList(ListView):
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
ret = super().get(request, *args, **kwargs)
|
ret = super().get(request, *args, **kwargs)
|
||||||
|
NotificationProfile.objects.filter(user=request.profile).update(unread_count=0)
|
||||||
# update after rendering
|
unseen_notifications_count.dirty(self.request.profile)
|
||||||
Notification.objects.filter(owner=self.request.profile).update(read=True)
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
|
@ -56,10 +56,10 @@ from judge.models import (
|
||||||
Problem,
|
Problem,
|
||||||
Profile,
|
Profile,
|
||||||
Contest,
|
Contest,
|
||||||
Notification,
|
|
||||||
ContestProblem,
|
ContestProblem,
|
||||||
OrganizationProfile,
|
OrganizationProfile,
|
||||||
)
|
)
|
||||||
|
from judge.models.notification import make_notification
|
||||||
from judge import event_poster as event
|
from judge import event_poster as event
|
||||||
from judge.utils.ranker import ranker
|
from judge.utils.ranker import ranker
|
||||||
from judge.utils.views import (
|
from judge.utils.views import (
|
||||||
|
@ -1019,16 +1019,9 @@ class AddOrganizationBlog(
|
||||||
html = (
|
html = (
|
||||||
f'<a href="{link}">{self.object.title} - {self.organization.name}</a>'
|
f'<a href="{link}">{self.object.title} - {self.organization.name}</a>'
|
||||||
)
|
)
|
||||||
for user in self.organization.admins.all():
|
make_notification(
|
||||||
if user.id == self.request.profile.id:
|
self.organization.admins.all(), "Add blog", html, self.request.profile
|
||||||
continue
|
)
|
||||||
notification = Notification(
|
|
||||||
owner=user,
|
|
||||||
author=self.request.profile,
|
|
||||||
category="Add blog",
|
|
||||||
html_link=html,
|
|
||||||
)
|
|
||||||
notification.save()
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@ -1104,17 +1097,8 @@ class EditOrganizationBlog(
|
||||||
)
|
)
|
||||||
html = f'<a href="{link}">{blog.title} - {self.organization.name}</a>'
|
html = f'<a href="{link}">{blog.title} - {self.organization.name}</a>'
|
||||||
post_authors = blog.authors.all()
|
post_authors = blog.authors.all()
|
||||||
posible_user = self.organization.admins.all() | post_authors
|
posible_users = self.organization.admins.all() | post_authors
|
||||||
for user in posible_user:
|
make_notification(posible_users, action, html, self.request.profile)
|
||||||
if user.id == self.request.profile.id:
|
|
||||||
continue
|
|
||||||
notification = Notification(
|
|
||||||
owner=user,
|
|
||||||
author=self.request.profile,
|
|
||||||
category=action,
|
|
||||||
html_link=html,
|
|
||||||
)
|
|
||||||
notification.save()
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
with transaction.atomic(), revisions.create_revision():
|
with transaction.atomic(), revisions.create_revision():
|
||||||
|
|
|
@ -80,6 +80,7 @@ def vote_page(request, delta):
|
||||||
else:
|
else:
|
||||||
PageVote.objects.filter(id=pagevote_id).update(score=F("score") + delta)
|
PageVote.objects.filter(id=pagevote_id).update(score=F("score") + delta)
|
||||||
break
|
break
|
||||||
|
_dirty_vote_score(pagevote_id, request.profile)
|
||||||
return HttpResponse("success", content_type="text/plain")
|
return HttpResponse("success", content_type="text/plain")
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,3 +104,8 @@ class PageVoteDetailView(TemplateResponseMixin, SingleObjectMixin, View):
|
||||||
context = super(PageVoteDetailView, self).get_context_data(**kwargs)
|
context = super(PageVoteDetailView, self).get_context_data(**kwargs)
|
||||||
context["pagevote"] = self.object.get_or_create_pagevote()
|
context["pagevote"] = self.object.get_or_create_pagevote()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def _dirty_vote_score(pagevote_id, profile):
|
||||||
|
pv = PageVote(id=pagevote_id)
|
||||||
|
pv.vote_score.dirty(pv, profile)
|
||||||
|
|
|
@ -49,16 +49,10 @@ ticket_widget = (
|
||||||
|
|
||||||
def add_ticket_notifications(users, author, link, ticket):
|
def add_ticket_notifications(users, author, link, ticket):
|
||||||
html = f'<a href="{link}">{ticket.linked_item}</a>'
|
html = f'<a href="{link}">{ticket.linked_item}</a>'
|
||||||
|
|
||||||
users = set(users)
|
users = set(users)
|
||||||
if author in users:
|
if author in users:
|
||||||
users.remove(author)
|
users.remove(author)
|
||||||
|
make_notification(users, "Ticket", html, author)
|
||||||
for user in users:
|
|
||||||
notification = Notification(
|
|
||||||
owner=user, html_link=html, category="Ticket", author=author
|
|
||||||
)
|
|
||||||
notification.save()
|
|
||||||
|
|
||||||
|
|
||||||
class TicketForm(forms.Form):
|
class TicketForm(forms.Form):
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
{% if not has_notifications %}
|
{% if not has_notifications %}
|
||||||
|
|
||||||
<h2 style="text-align: center;">{{ _('You have no notifications') }}</h2>
|
<h2 style="text-align: center;">{{ _('You have no notifications') }}</h2>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -17,24 +14,15 @@
|
||||||
{% for notification in notifications %}
|
{% for notification in notifications %}
|
||||||
<tr class="{{ 'highlight' if not notification.seen }}">
|
<tr class="{{ 'highlight' if not notification.seen }}">
|
||||||
<td>
|
<td>
|
||||||
{% if notification.comment %}
|
{{ link_user(notification.author) }}
|
||||||
{{ link_user(notification.comment.author) }}
|
|
||||||
{% else %}
|
|
||||||
{{ link_user(notification.author) }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ notification.category }}
|
{{ notification.category }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if notification.comment %}
|
{% autoescape off %}
|
||||||
<a href="{{ notification.comment.link }}#comment-{{ notification.comment.id }}">{{ notification.comment.page_title }}</a>
|
{{notification.html_link}}
|
||||||
{% else %}
|
{% endautoescape %}
|
||||||
{% autoescape off %}
|
|
||||||
{{notification.html_link}}
|
|
||||||
{% endautoescape %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ relative_time(notification.time) }}
|
{{ relative_time(notification.time) }}
|
||||||
|
@ -43,8 +31,5 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<!--
|
|
||||||
-->
|
|
Loading…
Reference in a new issue