Add notification on ticket messages
This commit is contained in:
parent
aa43b26683
commit
7065d9ce6b
8 changed files with 90 additions and 10 deletions
24
judge/migrations/0109_auto_20201017_1151.py
Normal file
24
judge/migrations/0109_auto_20201017_1151.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Generated by Django 2.2.12 on 2020-10-17 04:51
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('judge', '0108_submission_judged_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='notification',
|
||||
name='html_link',
|
||||
field=models.TextField(default='', max_length=1000, verbose_name='html link to comments, used for non-comments'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notification',
|
||||
name='comment',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='judge.Comment', verbose_name='comment'),
|
||||
),
|
||||
]
|
19
judge/migrations/0110_notification_author.py
Normal file
19
judge/migrations/0110_notification_author.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.2.12 on 2020-10-17 05:10
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('judge', '0109_auto_20201017_1151'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='notification',
|
||||
name='author',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='judge.Profile', verbose_name='who trigger, used for non-comment'),
|
||||
),
|
||||
]
|
|
@ -189,6 +189,8 @@ class CommentLock(models.Model):
|
|||
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, verbose_name=_('comment'), on_delete=CASCADE)
|
||||
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=10)
|
||||
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)
|
|
@ -129,7 +129,6 @@ class Profile(models.Model):
|
|||
def count_unseen_notifications(self):
|
||||
query = {
|
||||
'read': False,
|
||||
'comment__hidden': False,
|
||||
}
|
||||
return self.notifications.filter(**query).count()
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@ class NotificationList(ListView):
|
|||
|
||||
query = {
|
||||
'owner': self.request.profile,
|
||||
'comment__hidden': False,
|
||||
}
|
||||
|
||||
self.queryset = Notification.objects.filter(**query)\
|
||||
.order_by('-time')[:100] \
|
||||
.annotate(seen=Value(True, output_field=BooleanField()))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import json
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied, ValidationError
|
||||
|
@ -17,7 +19,7 @@ from django.views.generic import ListView
|
|||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from judge import event_poster as event
|
||||
from judge.models import Problem, Profile, Ticket, TicketMessage
|
||||
from judge.models import Problem, Profile, Ticket, TicketMessage, Notification
|
||||
from judge.utils.diggpaginator import DiggPaginator
|
||||
from judge.utils.tickets import filter_visible_tickets, own_ticket_filter
|
||||
from judge.utils.views import SingleObjectFormView, TitleMixin, paginate_query_context
|
||||
|
@ -29,6 +31,16 @@ ticket_widget = (forms.Textarea() if HeavyPreviewPageDownWidget is None else
|
|||
preview_timeout=1000, hide_preview_button=True))
|
||||
|
||||
|
||||
def add_ticket_notifications(users, author, link, ticket):
|
||||
html = f"<a href=\"{link}\">{ticket.linked_item}</a>"
|
||||
for user in users:
|
||||
notification = Notification(owner=user,
|
||||
html_link=html,
|
||||
category='Ticket',
|
||||
author=author)
|
||||
notification.save()
|
||||
|
||||
|
||||
class TicketForm(forms.Form):
|
||||
title = forms.CharField(max_length=100, label=gettext_lazy('Ticket title'))
|
||||
body = forms.CharField(widget=ticket_widget)
|
||||
|
@ -66,13 +78,18 @@ class NewTicketView(LoginRequiredMixin, SingleObjectFormView):
|
|||
message = TicketMessage(ticket=ticket, user=ticket.user, body=form.cleaned_data['body'])
|
||||
message.save()
|
||||
ticket.assignees.set(self.get_assignees())
|
||||
|
||||
link = reverse('ticket', args=[ticket.id])
|
||||
|
||||
add_ticket_notifications(ticket.assignees.all(), ticket.user, link, ticket)
|
||||
|
||||
if event.real:
|
||||
event.post('tickets', {
|
||||
'type': 'new-ticket', 'id': ticket.id,
|
||||
'message': message.id, 'user': ticket.user_id,
|
||||
'assignees': list(ticket.assignees.values_list('id', flat=True)),
|
||||
})
|
||||
return HttpResponseRedirect(reverse('ticket', args=[ticket.id]))
|
||||
return HttpResponseRedirect(link)
|
||||
|
||||
|
||||
class NewProblemTicketView(ProblemMixin, TitleMixin, NewTicketView):
|
||||
|
@ -127,6 +144,13 @@ class TicketView(TitleMixin, LoginRequiredMixin, TicketMixin, SingleObjectFormVi
|
|||
body=form.cleaned_data['body'],
|
||||
ticket=self.object)
|
||||
message.save()
|
||||
|
||||
link = '%s#message-%d' % (reverse('ticket', args=[self.object.id]), message.id)
|
||||
|
||||
notify_list = list(chain(self.object.assignees.all(), [self.object.user]))
|
||||
notify_list.remove(message.user)
|
||||
add_ticket_notifications(notify_list, message.user, link, self.object)
|
||||
|
||||
if event.real:
|
||||
event.post('tickets', {
|
||||
'type': 'ticket-message', 'id': self.object.id,
|
||||
|
@ -136,7 +160,7 @@ class TicketView(TitleMixin, LoginRequiredMixin, TicketMixin, SingleObjectFormVi
|
|||
event.post('ticket-%d' % self.object.id, {
|
||||
'type': 'ticket-message', 'message': message.id,
|
||||
})
|
||||
return HttpResponseRedirect('%s#message-%d' % (reverse('ticket', args=[self.object.id]), message.id))
|
||||
return HttpResponseRedirect(link)
|
||||
|
||||
def get_title(self):
|
||||
return _('%(title)s - Ticket %(id)d') % {'title': self.object.title, 'id': self.object.id}
|
||||
|
|
|
@ -214,6 +214,7 @@
|
|||
<a href="{{ ticket.linked_item.get_absolute_url() }}">
|
||||
{{ ticket.linked_item|item_title }}</a>
|
||||
</div>
|
||||
<div>{{ link_user(ticket.user) }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -11,19 +11,30 @@
|
|||
<tr>
|
||||
<th>{{ _('User') }}</th>
|
||||
<th>{{ _('Activity') }}</th>
|
||||
<th>{{ _('Comment') }}</th>
|
||||
<th>{{ _('Link') }}</th>
|
||||
<th>{{ _('Time') }}</th>
|
||||
</tr>
|
||||
{% for notification in notifications %}
|
||||
<tr class="{{ 'highlight' if not notification.seen }}">
|
||||
<td>
|
||||
{% if notification.comment %}
|
||||
{{ link_user(notification.comment.author) }}
|
||||
{% else %}
|
||||
{{ link_user(notification.author) }}
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{ notification.category }}
|
||||
</td>
|
||||
<td>
|
||||
{% if notification.comment %}
|
||||
<a href="{{ notification.comment.link }}#comment-{{ notification.comment.id }}">{{ page_titles[notification.comment.page] }}</a>
|
||||
{% else %}
|
||||
{% autoescape off %}
|
||||
{{notification.html_link}}
|
||||
{% endautoescape %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ relative_time(notification.time) }}
|
||||
|
|
Loading…
Reference in a new issue