Skip to content

Commit 4f53cad

Browse files
authored
ci: merge main to release (#8344)
2 parents def346e + 1fb70d8 commit 4f53cad

File tree

21 files changed

+341
-214
lines changed

21 files changed

+341
-214
lines changed

client/agenda/AgendaScheduleList.vue

+13-1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ const meetingEvents = computed(() => {
284284
const purposesWithoutLinks = ['admin', 'closed_meeting', 'officehours', 'social']
285285
if (item.flags.showAgenda || (typesWithLinks.includes(item.type) && !purposesWithoutLinks.includes(item.purpose))) {
286286
if (item.flags.agenda) {
287+
// -> Meeting Materials
287288
links.push({
288289
id: `lnk-${item.id}-tar`,
289290
label: 'Download meeting materials as .tar archive',
@@ -305,7 +306,18 @@ const meetingEvents = computed(() => {
305306
color: 'red'
306307
})
307308
}
308-
if (agendaStore.usesNotes) {
309+
// -> Point to Wiki for Hackathon sessions, HedgeDocs otherwise
310+
if (item.name.toLowerCase().includes('hackathon')) {
311+
links.push({
312+
id: `lnk-${item.id}-wiki`,
313+
label: 'Wiki',
314+
icon: 'book',
315+
href: getUrl('hackathonWiki', {
316+
meetingNumber: agendaStore.meeting.number
317+
}),
318+
color: 'blue'
319+
})
320+
} else if (agendaStore.usesNotes) {
309321
links.push({
310322
id: `lnk-${item.id}-note`,
311323
label: 'Notepad for note-takers',

client/shared/urls.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"bofDefinition": "https://www.ietf.org/how/bofs/",
3+
"hackathonWiki": "https://wiki.ietf.org/meeting/{meetingNumber}/hackathon",
34
"meetingCalIcs": "/meeting/{meetingNumber}/agenda.ics",
45
"meetingDetails": "/meeting/{meetingNumber}/session/{eventAcronym}/",
56
"meetingMaterialsPdf": "/meeting/{meetingNumber}/agenda/{eventAcronym}-drafts.pdf",

dev/deploy-to-container/package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dev/deploy-to-container/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"dependencies": {
55
"dockerode": "^4.0.2",
66
"fs-extra": "^11.2.0",
7-
"nanoid": "5.0.8",
7+
"nanoid": "5.0.9",
88
"nanoid-dictionary": "5.0.0-beta.1",
99
"slugify": "1.6.6",
1010
"tar": "^7.4.3",

ietf/community/apps.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright The IETF Trust 2024, All Rights Reserved
2+
3+
from django.apps import AppConfig
4+
5+
6+
class CommunityConfig(AppConfig):
7+
name = "ietf.community"
8+
9+
def ready(self):
10+
"""Initialize the app after the registry is populated"""
11+
# implicitly connects @receiver-decorated signals
12+
from . import signals # pyflakes: ignore

ietf/community/models.py

+2-33
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
# Copyright The IETF Trust 2012-2020, All Rights Reserved
22
# -*- coding: utf-8 -*-
33

4-
5-
from django.conf import settings
6-
from django.db import models, transaction
7-
from django.db.models import signals
4+
from django.db import models
85
from django.urls import reverse as urlreverse
96

10-
from ietf.doc.models import Document, DocEvent, State
7+
from ietf.doc.models import Document, State
118
from ietf.group.models import Group
129
from ietf.person.models import Person, Email
1310
from ietf.utils.models import ForeignKey
1411

15-
from .tasks import notify_event_to_subscribers_task
16-
1712

1813
class CommunityList(models.Model):
1914
person = ForeignKey(Person, blank=True, null=True)
@@ -98,29 +93,3 @@ class EmailSubscription(models.Model):
9893

9994
def __str__(self):
10095
return "%s to %s (%s changes)" % (self.email, self.community_list, self.notify_on)
101-
102-
103-
def notify_events(sender, instance, **kwargs):
104-
if not isinstance(instance, DocEvent):
105-
return
106-
107-
if not kwargs.get("created", False):
108-
return # only notify on creation
109-
110-
if instance.doc.type_id != 'draft':
111-
return
112-
113-
if getattr(instance, "skip_community_list_notification", False):
114-
return
115-
116-
# kludge alert: queuing a celery task in response to a signal can cause unexpected attempts to
117-
# start a Celery task during tests. To prevent this, don't queue a celery task if we're running
118-
# tests.
119-
if settings.SERVER_MODE != "test":
120-
# Wrap in on_commit in case a transaction is open
121-
transaction.on_commit(
122-
lambda: notify_event_to_subscribers_task.delay(event_id=instance.pk)
123-
)
124-
125-
126-
signals.post_save.connect(notify_events)

ietf/community/signals.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright The IETF Trust 2024, All Rights Reserved
2+
3+
from django.conf import settings
4+
from django.db import transaction
5+
from django.db.models.signals import post_save
6+
from django.dispatch import receiver
7+
8+
from ietf.doc.models import DocEvent
9+
from .tasks import notify_event_to_subscribers_task
10+
11+
12+
def notify_of_event(event: DocEvent):
13+
"""Send subscriber notification emails for a 'draft'-related DocEvent
14+
15+
If the event is attached to a draft of type 'doc', queues a task to send notification emails to
16+
community list subscribers. No emails will be sent when SERVER_MODE is 'test'.
17+
"""
18+
if event.doc.type_id != "draft":
19+
return
20+
21+
if getattr(event, "skip_community_list_notification", False):
22+
return
23+
24+
# kludge alert: queuing a celery task in response to a signal can cause unexpected attempts to
25+
# start a Celery task during tests. To prevent this, don't queue a celery task if we're running
26+
# tests.
27+
if settings.SERVER_MODE != "test":
28+
# Wrap in on_commit in case a transaction is open
29+
transaction.on_commit(
30+
lambda: notify_event_to_subscribers_task.delay(event_id=event.pk)
31+
)
32+
33+
34+
# dispatch_uid ensures only a single signal receiver binding is made
35+
@receiver(post_save, dispatch_uid="notify_of_events_receiver_uid")
36+
def notify_of_events_receiver(sender, instance, **kwargs):
37+
"""Call notify_of_event after saving a new DocEvent"""
38+
if not isinstance(instance, DocEvent):
39+
return
40+
41+
if not kwargs.get("created", False):
42+
return # only notify on creation
43+
44+
notify_of_event(instance)

ietf/community/tests.py

+36-31
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright The IETF Trust 2016-2023, All Rights Reserved
22
# -*- coding: utf-8 -*-
33

4-
54
import mock
65
from pyquery import PyQuery
76

@@ -11,6 +10,7 @@
1110
import debug # pyflakes:ignore
1211

1312
from ietf.community.models import CommunityList, SearchRule, EmailSubscription
13+
from ietf.community.signals import notify_of_event
1414
from ietf.community.utils import docs_matching_community_list_rule, community_list_rules_matching_doc
1515
from ietf.community.utils import reset_name_contains_index_for_rule, notify_event_to_subscribers
1616
from ietf.community.tasks import notify_event_to_subscribers_task
@@ -431,53 +431,58 @@ def test_subscription_for_group(self):
431431
r = self.client.get(url)
432432
self.assertEqual(r.status_code, 200)
433433

434-
# Mock out the on_commit call so we can tell whether the task was actually queued
435-
@mock.patch("ietf.submit.views.transaction.on_commit", side_effect=lambda x: x())
436-
@mock.patch("ietf.community.models.notify_event_to_subscribers_task")
437-
def test_notification_signal_receiver(self, mock_notify_task, mock_on_commit):
438-
"""Saving a DocEvent should notify subscribers
434+
@mock.patch("ietf.community.signals.notify_of_event")
435+
def test_notification_signal_receiver(self, mock_notify_of_event):
436+
"""Saving a newly created DocEvent should notify subscribers
439437
440-
This implicitly tests that notify_events is hooked up to the post_save signal.
438+
This implicitly tests that notify_of_event_receiver is hooked up to the post_save signal.
441439
"""
442440
# Arbitrary model that's not a DocEvent
443-
person = PersonFactory()
444-
mock_notify_task.reset_mock() # clear any calls that resulted from the factories
445-
# be careful overriding SERVER_MODE - we do it here because the method
446-
# under test does not make this call when in "test" mode
447-
with override_settings(SERVER_MODE="not-test"):
448-
person.save()
449-
self.assertFalse(mock_notify_task.delay.called)
450-
441+
person = PersonFactory.build() # builds but does not save...
442+
mock_notify_of_event.reset_mock() # clear any calls that resulted from the factories
443+
person.save()
444+
self.assertFalse(mock_notify_of_event.called)
445+
451446
# build a DocEvent that is not yet persisted
452447
doc = DocumentFactory()
453-
d = DocEventFactory.build(by=person, doc=doc)
454-
# mock_notify_task.reset_mock() # clear any calls that resulted from the factories
448+
event = DocEventFactory.build(by=person, doc=doc) # builds but does not save...
449+
mock_notify_of_event.reset_mock() # clear any calls that resulted from the factories
450+
event.save()
451+
self.assertEqual(mock_notify_of_event.call_count, 1, "notify_task should be run on creation of DocEvent")
452+
self.assertEqual(mock_notify_of_event.call_args, mock.call(event))
453+
454+
# save the existing DocEvent and see that no notification is sent
455+
mock_notify_of_event.reset_mock()
456+
event.save()
457+
self.assertFalse(mock_notify_of_event.called, "notify_task should not be run save of on existing DocEvent")
458+
459+
# Mock out the on_commit call so we can tell whether the task was actually queued
460+
@mock.patch("ietf.submit.views.transaction.on_commit", side_effect=lambda x: x())
461+
@mock.patch("ietf.community.signals.notify_event_to_subscribers_task")
462+
def test_notify_of_event(self, mock_notify_task, mock_on_commit):
463+
"""The community notification task should be called as intended"""
464+
person = PersonFactory() # builds but does not save...
465+
doc = DocumentFactory()
466+
event = DocEventFactory(by=person, doc=doc)
455467
# be careful overriding SERVER_MODE - we do it here because the method
456468
# under test does not make this call when in "test" mode
457469
with override_settings(SERVER_MODE="not-test"):
458-
d.save()
459-
self.assertEqual(mock_notify_task.delay.call_count, 1, "notify_task should be run on creation of DocEvent")
460-
self.assertEqual(mock_notify_task.delay.call_args, mock.call(event_id = d.pk))
461-
462-
mock_notify_task.reset_mock()
463-
with override_settings(SERVER_MODE="not-test"):
464-
d.save()
465-
self.assertFalse(mock_notify_task.delay.called, "notify_task should not be run save of on existing DocEvent")
466-
470+
notify_of_event(event)
471+
self.assertTrue(mock_notify_task.delay.called, "notify_task should run for a DocEvent on a draft")
467472
mock_notify_task.reset_mock()
468-
d = DocEventFactory.build(by=person, doc=doc)
469-
d.skip_community_list_notification = True
473+
474+
event.skip_community_list_notification = True
470475
# be careful overriding SERVER_MODE - we do it here because the method
471476
# under test does not make this call when in "test" mode
472477
with override_settings(SERVER_MODE="not-test"):
473-
d.save()
478+
notify_of_event(event)
474479
self.assertFalse(mock_notify_task.delay.called, "notify_task should not run when skip_community_list_notification is set")
475480

476-
d = DocEventFactory.build(by=person, doc=DocumentFactory(type_id="rfc"))
481+
event = DocEventFactory.build(by=person, doc=DocumentFactory(type_id="rfc"))
477482
# be careful overriding SERVER_MODE - we do it here because the method
478483
# under test does not make this call when in "test" mode
479484
with override_settings(SERVER_MODE="not-test"):
480-
d.save()
485+
notify_of_event(event)
481486
self.assertFalse(mock_notify_task.delay.called, "notify_task should not run on a document with type 'rfc'")
482487

483488
@mock.patch("ietf.utils.mail.send_mail_text")

0 commit comments

Comments
 (0)