Skip to content
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

feat: session apis #7173

Merged
merged 65 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
59dbfe0
feat: Show bluesheets using Attended tables (#7094)
jennifer-richards Feb 26, 2024
19c73d9
chore: Remove unused import
jennifer-richards Feb 27, 2024
2a72896
Merge pull request #7115 from ietf-tools/main
jennifer-richards Feb 28, 2024
d41a95d
style: Black
jennifer-richards Feb 13, 2024
1ce4172
feat: Stub session update notify API
jennifer-richards Feb 14, 2024
dd0664d
feat: Add order & rev to slides JSON
jennifer-richards Feb 14, 2024
0b8ea53
style: Black
jennifer-richards Feb 14, 2024
b966eb6
feat: Stub actual Meetecho slide deck mgmt API
jennifer-richards Feb 16, 2024
7970c74
refactor: Limit reordering to type="slides"
jennifer-richards Feb 16, 2024
039093f
chore: Remove repository from meetecho API
jennifer-richards Feb 23, 2024
1cad77a
feat: update Meetecho on slide reorder
jennifer-richards Feb 23, 2024
be32f9d
refactor: drop pytz from meetecho.py
jennifer-richards Feb 24, 2024
13816f2
chore: Remove more repository refs
jennifer-richards Feb 24, 2024
9d788c4
refactor: Eliminate more pytz
jennifer-richards Feb 24, 2024
71835e4
test: Test add_slide_deck api
jennifer-richards Feb 24, 2024
a2fab26
fix: Allow 202 status code / absent Content-Type
jennifer-richards Feb 24, 2024
38edcb1
test: Test delete_slide_deck api
jennifer-richards Feb 24, 2024
6ef7c0b
test: Test update_slide_decks api
jennifer-richards Feb 24, 2024
51df73a
refactor: sessionpresentation_set -> presentations
jennifer-richards Feb 27, 2024
848f356
test: Test send_update()
jennifer-richards Feb 27, 2024
6b4cc1f
fix: Debug send_update()
jennifer-richards Feb 27, 2024
b1fdee4
test: ajax_reorder_slides calls Meetecho API
jennifer-richards Feb 27, 2024
6ecca94
test: Test SldesManager.add()
jennifer-richards Feb 28, 2024
3937977
feat: Implement SlidesManager.add()
jennifer-richards Feb 28, 2024
e4a5e4a
test: Test that ajax_add_slides... calls API
jennifer-richards Feb 28, 2024
347ef1f
feat: Call Meetecho API when slides added to session
jennifer-richards Feb 28, 2024
ea21f35
test: Test SlidesManager.delete()
jennifer-richards Feb 28, 2024
ee9f2e7
feat: Implement SlidesManager.delete()
jennifer-richards Feb 28, 2024
438c720
test: ajax_remove_slides... calls Meetecho API
jennifer-richards Feb 28, 2024
481a200
feat: Call Meetecho API when slides removed
jennifer-richards Feb 28, 2024
fd9ce3c
chore: Update docstring
jennifer-richards Feb 28, 2024
52300b3
Merge pull request #7116 from jennifer-richards/slides-api
jennifer-richards Feb 29, 2024
464db35
feat: rudimentary debug mode for Meetecho API
jennifer-richards Mar 5, 2024
bfc0fbc
test: remove_sessionpresentation() calls Meetecho API
jennifer-richards Mar 5, 2024
41de9a2
feat: Call Meetecho API from remove_sessionpresentation()
jennifer-richards Mar 5, 2024
3e1810f
test: upload_slides() calls Meetecho API
jennifer-richards Mar 5, 2024
61647ee
style: Black
jennifer-richards Mar 6, 2024
e90b01a
fix: Refactor/debug upload_session_slides
jennifer-richards Mar 6, 2024
1070ace
test: Fix test bug
jennifer-richards Mar 6, 2024
bea80ba
feat: Call Meetecho API when uploading session slides
jennifer-richards Mar 6, 2024
6662ee7
fix: Only replace slides actually linked to session
jennifer-richards Mar 6, 2024
18d75ca
fix: Delint
jennifer-richards Mar 6, 2024
f46068d
Merge pull request #7146 from jennifer-richards/more-slides-api
jennifer-richards Mar 6, 2024
c9c4fdd
fix: Send get_versionless_href() as url for slides
jennifer-richards Mar 7, 2024
d4fecda
test: TZ-aware timestamps, please
jennifer-richards Mar 7, 2024
1d27b98
chore: Add comments
jennifer-richards Mar 7, 2024
8370136
Merge pull request #7151 from jennifer-richards/yet-more-me-apis
jennifer-richards Mar 7, 2024
22dd81c
feat: Call Meetecho API in edit_sessionpresentation
jennifer-richards Mar 7, 2024
49fdcc8
feat: Call Meetecho API in remove_sessionpresentation
jennifer-richards Mar 7, 2024
3dc4105
feat: Call Meetecho API from add_sessionpresentation
jennifer-richards Mar 7, 2024
73b2c01
fix: Set order in add_sessionpresentation
jennifer-richards Mar 7, 2024
715b311
fix: Restrict API calls to "slides" docs
jennifer-richards Mar 7, 2024
e8fd854
feat: Call Meetecho API on title changes
jennifer-richards Mar 7, 2024
60058d4
test: Check meetecho API calls in test_revise()
jennifer-richards Mar 7, 2024
bacd98b
Merge pull request #7154 from jennifer-richards/the-neverending-api
jennifer-richards Mar 8, 2024
e2e837d
fix: better Meetecho API "order" management
jennifer-richards Mar 8, 2024
5998f63
fix: no PUT if there are no slides after DELETE
jennifer-richards Mar 8, 2024
136cef7
Merge pull request #7157 from jennifer-richards/put-after-delete
jennifer-richards Mar 8, 2024
57d6896
feat: Catch exceptions from SlidesManager
jennifer-richards Mar 12, 2024
9ce507d
feat: Limit which sessions we send notifications for
jennifer-richards Mar 12, 2024
b77e511
Merge pull request #7170 from jennifer-richards/me-api-tweaks
jennifer-richards Mar 12, 2024
4033e58
fix: handle absence of request_timeout in api config
jennifer-richards Mar 12, 2024
538b52c
test: always send slide notifications in tests
jennifer-richards Mar 12, 2024
7787441
Merge pull request #7171 from jennifer-richards/test-fixes
jennifer-richards Mar 12, 2024
2c903c3
fix: save slides before sending notification (#7172)
jennifer-richards Mar 12, 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
89 changes: 83 additions & 6 deletions ietf/doc/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2575,37 +2575,68 @@ def test_view_document_meetings(self):
self.assertFalse(q("#futuremeets a.btn:contains('Remove document')"))
self.assertFalse(q("#pastmeets a.btn:contains('Remove document')"))

def test_edit_document_session(self):
@override_settings(MEETECHO_API_CONFIG="fake settings")
@mock.patch("ietf.doc.views_doc.SlidesManager")
def test_edit_document_session(self, mock_slides_manager_cls):
doc = IndividualDraftFactory.create()
sp = doc.presentations.create(session=self.future,rev=None)

url = urlreverse('ietf.doc.views_doc.edit_sessionpresentation',kwargs=dict(name='no-such-doc',session_id=sp.session_id))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

url = urlreverse('ietf.doc.views_doc.edit_sessionpresentation',kwargs=dict(name=doc.name,session_id=0))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

url = urlreverse('ietf.doc.views_doc.edit_sessionpresentation',kwargs=dict(name=doc.name,session_id=sp.session_id))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

self.client.login(username=self.other_chair.user.username,password='%s+password'%self.other_chair.user.username)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)

self.assertFalse(mock_slides_manager_cls.called)

self.client.login(username=self.group_chair.user.username,password='%s+password'%self.group_chair.user.username)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
q = PyQuery(response.content)
self.assertEqual(2,len(q('select#id_version option')))
self.assertFalse(mock_slides_manager_cls.called)

# edit draft
self.assertEqual(1,doc.docevent_set.count())
response = self.client.post(url,{'version':'00','save':''})
self.assertEqual(response.status_code, 302)
self.assertEqual(doc.presentations.get(pk=sp.pk).rev,'00')
self.assertEqual(2,doc.docevent_set.count())
self.assertFalse(mock_slides_manager_cls.called)

# editing slides should call Meetecho API
slides = SessionPresentationFactory(
session=self.future,
document__type_id="slides",
document__rev="00",
rev=None,
order=1,
).document
url = urlreverse(
"ietf.doc.views_doc.edit_sessionpresentation",
kwargs={"name": slides.name, "session_id": self.future.pk},
)
response = self.client.post(url, {"version": "00", "save": ""})
self.assertEqual(response.status_code, 302)
self.assertEqual(mock_slides_manager_cls.call_count, 1)
self.assertEqual(mock_slides_manager_cls.call_args, mock.call(api_config="fake settings"))
self.assertEqual(mock_slides_manager_cls.return_value.send_update.call_count, 1)
self.assertEqual(
mock_slides_manager_cls.return_value.send_update.call_args,
mock.call(self.future),
)

def test_edit_document_session_after_proceedings_closed(self):
doc = IndividualDraftFactory.create()
Expand All @@ -2622,35 +2653,60 @@ def test_edit_document_session_after_proceedings_closed(self):
q=PyQuery(response.content)
self.assertEqual(1,len(q(".alert-warning:contains('may affect published proceedings')")))

def test_remove_document_session(self):
@override_settings(MEETECHO_API_CONFIG="fake settings")
@mock.patch("ietf.doc.views_doc.SlidesManager")
def test_remove_document_session(self, mock_slides_manager_cls):
doc = IndividualDraftFactory.create()
sp = doc.presentations.create(session=self.future,rev=None)

url = urlreverse('ietf.doc.views_doc.remove_sessionpresentation',kwargs=dict(name='no-such-doc',session_id=sp.session_id))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

url = urlreverse('ietf.doc.views_doc.remove_sessionpresentation',kwargs=dict(name=doc.name,session_id=0))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

url = urlreverse('ietf.doc.views_doc.remove_sessionpresentation',kwargs=dict(name=doc.name,session_id=sp.session_id))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
self.assertFalse(mock_slides_manager_cls.called)

self.client.login(username=self.other_chair.user.username,password='%s+password'%self.other_chair.user.username)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)

self.assertFalse(mock_slides_manager_cls.called)

self.client.login(username=self.group_chair.user.username,password='%s+password'%self.group_chair.user.username)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertFalse(mock_slides_manager_cls.called)

# removing a draft
self.assertEqual(1,doc.docevent_set.count())
response = self.client.post(url,{'remove_session':''})
self.assertEqual(response.status_code, 302)
self.assertFalse(doc.presentations.filter(pk=sp.pk).exists())
self.assertEqual(2,doc.docevent_set.count())
self.assertFalse(mock_slides_manager_cls.called)

# removing slides should call Meetecho API
slides = SessionPresentationFactory(session=self.future, document__type_id="slides", order=1).document
url = urlreverse(
"ietf.doc.views_doc.remove_sessionpresentation",
kwargs={"name": slides.name, "session_id": self.future.pk},
)
response = self.client.post(url, {"remove_session": ""})
self.assertEqual(response.status_code, 302)
self.assertEqual(mock_slides_manager_cls.call_count, 1)
self.assertEqual(mock_slides_manager_cls.call_args, mock.call(api_config="fake settings"))
self.assertEqual(mock_slides_manager_cls.return_value.delete.call_count, 1)
self.assertEqual(
mock_slides_manager_cls.return_value.delete.call_args,
mock.call(self.future, slides),
)

def test_remove_document_session_after_proceedings_closed(self):
doc = IndividualDraftFactory.create()
Expand All @@ -2667,28 +2723,49 @@ def test_remove_document_session_after_proceedings_closed(self):
q=PyQuery(response.content)
self.assertEqual(1,len(q(".alert-warning:contains('may affect published proceedings')")))

def test_add_document_session(self):
@override_settings(MEETECHO_API_CONFIG="fake settings")
@mock.patch("ietf.doc.views_doc.SlidesManager")
def test_add_document_session(self, mock_slides_manager_cls):
doc = IndividualDraftFactory.create()

url = urlreverse('ietf.doc.views_doc.add_sessionpresentation',kwargs=dict(name=doc.name))
login_testing_unauthorized(self,self.group_chair.user.username,url)
response = self.client.get(url)
self.assertEqual(response.status_code,200)

self.assertFalse(mock_slides_manager_cls.called)

response = self.client.post(url,{'session':0,'version':'current'})
self.assertEqual(response.status_code,200)
q=PyQuery(response.content)
self.assertTrue(q('.form-select.is-invalid'))
self.assertFalse(mock_slides_manager_cls.called)

response = self.client.post(url,{'session':self.future.pk,'version':'bogus version'})
self.assertEqual(response.status_code,200)
q=PyQuery(response.content)
self.assertTrue(q('.form-select.is-invalid'))
self.assertFalse(mock_slides_manager_cls.called)

# adding a draft
self.assertEqual(1,doc.docevent_set.count())
response = self.client.post(url,{'session':self.future.pk,'version':'current'})
self.assertEqual(response.status_code,302)
self.assertEqual(2,doc.docevent_set.count())
self.assertEqual(doc.presentations.get(session__pk=self.future.pk).order, 0)
self.assertFalse(mock_slides_manager_cls.called)

# adding slides should set order / call Meetecho API
slides = DocumentFactory(type_id="slides")
url = urlreverse("ietf.doc.views_doc.add_sessionpresentation", kwargs=dict(name=slides.name))
response = self.client.post(url, {"session": self.future.pk, "version": "current"})
self.assertEqual(response.status_code,302)
self.assertEqual(slides.presentations.get(session__pk=self.future.pk).order, 1)
self.assertEqual(mock_slides_manager_cls.call_args, mock.call(api_config="fake settings"))
self.assertEqual(mock_slides_manager_cls.return_value.add.call_count, 1)
self.assertEqual(
mock_slides_manager_cls.return_value.add.call_args,
mock.call(self.future, slides, order=1),
)

def test_get_related_meeting(self):
"""Should be able to retrieve related meeting"""
Expand Down
Loading
Loading