Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1be098e
feat: Show bluesheets using Attended tables (#6898)
pselkirk Jan 31, 2024
8e14099
feat: Allow users to add themselves to session attendance (#6454)
pselkirk Jan 31, 2024
adb5f50
chore: Correct copyright year
pselkirk Jan 31, 2024
1e7995c
fix: Address review comments
pselkirk Jan 31, 2024
aecfe42
fix: Don't try to generate empty bluesheets
pselkirk Jan 31, 2024
9d256ae
refactor: Complete rewrite of bluesheet.html
pselkirk Jan 31, 2024
0373565
Merge branch 'main' into fix-6898-6454
pselkirk Feb 1, 2024
90feb2b
refactor: Fill in a few gaps, close a few holes
pselkirk Feb 10, 2024
315776d
fix: Report file-save errors to caller
pselkirk Feb 12, 2024
df3a646
fix: Address review comments
pselkirk Feb 14, 2024
8cddcdf
fix: typo
pselkirk Feb 14, 2024
2ae1208
refactor: if instead of except; refactor gently
jennifer-richards Feb 21, 2024
b250298
refactor: Rearrange logic a little, add comment
jennifer-richards Feb 21, 2024
58f3a78
style: Black
jennifer-richards Feb 21, 2024
57c83d2
refactor: auto_now_add->default to allow override
jennifer-richards Feb 22, 2024
e7cadc6
refactor: jsonschema to validate API payload
jennifer-richards Feb 22, 2024
ddeb367
feat: Handle new API data format
jennifer-richards Feb 22, 2024
6a57b01
test: Split test into deprecated/new version
jennifer-richards Feb 22, 2024
96cb93e
style: Black
jennifer-richards Feb 22, 2024
f054c85
test: Test new add_session_attendees API
jennifer-richards Feb 22, 2024
16f508e
fix: Fix bug uncovered by test
jennifer-richards Feb 22, 2024
b69bc7c
refactor: Refactor affiliation lookup a bit
jennifer-richards Feb 22, 2024
4843a30
fix: Order bluesheet by Attended.time
jennifer-richards Feb 22, 2024
f1cb0d9
refactor: Move helpers from views.py to utils.py
jennifer-richards Feb 22, 2024
287ef0b
test: Test that finalize calls generate_bluesheets
jennifer-richards Feb 22, 2024
170ca01
test: test_bluesheet_data()
jennifer-richards Feb 23, 2024
ea0d1dc
Merge branch 'main' into fix-6898-6454-jlr
jennifer-richards Feb 23, 2024
1aa59f8
fix: Clean up merge
jennifer-richards Feb 23, 2024
d0afcdf
fix: Remove debug statement
jennifer-richards Feb 23, 2024
af8c59b
chore: comments
jennifer-richards Feb 23, 2024
8b79e16
refactor: Renumber migrations
jennifer-richards Feb 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 117 additions & 1 deletion ietf/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ def test_api_set_session_video_url(self):
event = doc.latest_event()
self.assertEqual(event.by, recman)

def test_api_add_session_attendees(self):
def test_api_add_session_attendees_deprecated(self):
# Deprecated test - should be removed when we stop accepting a simple list of user PKs in
# the add_session_attendees() view
url = urlreverse('ietf.meeting.views.api_add_session_attendees')
otherperson = PersonFactory()
recmanrole = RoleFactory(group__type_id='ietf', name_id='recman')
Expand Down Expand Up @@ -285,6 +287,120 @@ def test_api_add_session_attendees(self):
self.assertTrue(session.attended_set.filter(person=recman).exists())
self.assertTrue(session.attended_set.filter(person=otherperson).exists())

def test_api_add_session_attendees(self):
url = urlreverse("ietf.meeting.views.api_add_session_attendees")
otherperson = PersonFactory()
recmanrole = RoleFactory(group__type_id="ietf", name_id="recman")
recman = recmanrole.person
meeting = MeetingFactory(type_id="ietf")
session = SessionFactory(group__type_id="wg", meeting=meeting)
apikey = PersonalApiKey.objects.create(endpoint=url, person=recman)

badrole = RoleFactory(group__type_id="ietf", name_id="ad")
badapikey = PersonalApiKey.objects.create(endpoint=url, person=badrole.person)
badrole.person.user.last_login = timezone.now()
badrole.person.user.save()

# Improper credentials, or method
r = self.client.post(url, {})
self.assertContains(r, "Missing apikey parameter", status_code=400)

r = self.client.post(url, {"apikey": badapikey.hash()})
self.assertContains(r, "Restricted to role: Recording Manager", status_code=403)

r = self.client.post(url, {"apikey": apikey.hash()})
self.assertContains(r, "Too long since last regular login", status_code=400)

recman.user.last_login = timezone.now() - datetime.timedelta(days=365)
recman.user.save()
r = self.client.post(url, {"apikey": apikey.hash()})
self.assertContains(r, "Too long since last regular login", status_code=400)

recman.user.last_login = timezone.now()
recman.user.save()
r = self.client.get(url, {"apikey": apikey.hash()})
self.assertContains(r, "Method not allowed", status_code=405)

recman.user.last_login = timezone.now()
recman.user.save()

# Malformed requests
r = self.client.post(url, {"apikey": apikey.hash()})
self.assertContains(r, "Missing attended parameter", status_code=400)

for baddict in (
"{}",
'{"bogons;drop table":"bogons;drop table"}',
'{"session_id":"Not an integer;drop table"}',
f'{{"session_id":{session.pk},"attendees":"not a list;drop table"}}',
f'{{"session_id":{session.pk},"attendees":"not a list;drop table"}}',
f'{{"session_id":{session.pk},"attendees":[1,2,"not an int;drop table",4]}}',
f'{{"session_id":{session.pk},"attendees":["user_id":{recman.user.pk}]}}', # no join_time
f'{{"session_id":{session.pk},"attendees":["user_id":{recman.user.pk},"join_time;drop table":"2024-01-01T00:00:00Z]}}',
f'{{"session_id":{session.pk},"attendees":["user_id":{recman.user.pk},"join_time":"not a time;drop table"]}}',
# next has no time zone indicator
f'{{"session_id":{session.pk},"attendees":["user_id":{recman.user.pk},"join_time":"2024-01-01T00:00:00"]}}',
f'{{"session_id":{session.pk},"attendees":["user_id":"not an int; drop table","join_time":"2024-01-01T00:00:00Z"]}}',
# Uncomment the next one when the _deprecated version of this test is retired
# f'{{"session_id":{session.pk},"attendees":[{recman.user.pk}, {otherperson.user.pk}]}}',
):
r = self.client.post(url, {"apikey": apikey.hash(), "attended": baddict})
self.assertContains(r, "Malformed post", status_code=400)

bad_session_id = Session.objects.order_by("-pk").first().pk + 1
r = self.client.post(
url,
{
"apikey": apikey.hash(),
"attended": f'{{"session_id":{bad_session_id},"attendees":[]}}',
},
)
self.assertContains(r, "Invalid session", status_code=400)
bad_user_id = User.objects.order_by("-pk").first().pk + 1
r = self.client.post(
url,
{
"apikey": apikey.hash(),
"attended": f'{{"session_id":{session.pk},"attendees":[{{"user_id":{bad_user_id}, "join_time":"2024-01-01T00:00:00Z"}}]}}',
},
)
self.assertContains(r, "Invalid attendee", status_code=400)

# Reasonable request
r = self.client.post(
url,
{
"apikey": apikey.hash(),
"attended": json.dumps(
{
"session_id": session.pk,
"attendees": [
{
"user_id": recman.user.pk,
"join_time": "2023-09-03T12:34:56Z",
},
{
"user_id": otherperson.user.pk,
"join_time": "2023-09-03T03:00:19Z",
},
],
}
),
},
)

self.assertEqual(session.attended_set.count(), 2)
self.assertTrue(session.attended_set.filter(person=recman).exists())
self.assertEqual(
session.attended_set.get(person=recman).time,
datetime.datetime(2023, 9, 3, 12, 34, 56, tzinfo=datetime.timezone.utc),
)
self.assertTrue(session.attended_set.filter(person=otherperson).exists())
self.assertEqual(
session.attended_set.get(person=otherperson).time,
datetime.datetime(2023, 9, 3, 3, 0, 19, tzinfo=datetime.timezone.utc),
)

def test_api_upload_polls_and_chatlog(self):
recmanrole = RoleFactory(group__type_id='ietf', name_id='recman')
recmanrole.person.user.last_login = timezone.now()
Expand Down
26 changes: 26 additions & 0 deletions ietf/meeting/migrations/0007_attended_origin_attended_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright The IETF Trust 2024, All Rights Reserved

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
("meeting", "0006_alter_sessionpresentation_document_and_session"),
]

operations = [
migrations.AddField(
model_name="attended",
name="origin",
field=models.CharField(default="datatracker", max_length=32),
),
migrations.AddField(
model_name="attended",
name="time",
field=models.DateTimeField(
blank=True, default=django.utils.timezone.now, null=True
),
),
]
3 changes: 2 additions & 1 deletion ietf/meeting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,6 @@ def can_manage_materials(self, user):
return can_manage_materials(user,self.group)

def is_material_submission_cutoff(self):
debug.say("is_material_submission_cutoff got called")
return date_today(datetime.timezone.utc) > self.meeting.get_submission_correction_date()

def joint_with_groups_acronyms(self):
Expand Down Expand Up @@ -1427,6 +1426,8 @@ class Meta:
class Attended(models.Model):
person = ForeignKey(Person)
session = ForeignKey(Session)
time = models.DateTimeField(default=timezone.now, null=True, blank=True)
origin = models.CharField(max_length=32, default='datatracker')

class Meta:
unique_together = (('person', 'session'),)
Expand Down
Loading