Skip to content

Commit afa79dc

Browse files
jimfentonrjsparks
andauthored
feat: Warn if uploading minutes before session end (ietf-tools#8700)
* Warn if uploading minutes before sessionn end * Remove extraneous btn-primary for session future Co-authored-by: Robert Sparks <rjsparks@nostrum.com> * fix: guard against unscheduled sessions * fix: test addition of warning * fix: another guard against unscheduled sessions * feat: test future warning on session details pannel --------- Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
1 parent 15ef591 commit afa79dc

4 files changed

Lines changed: 168 additions & 131 deletions

File tree

ietf/meeting/tests_views.py

Lines changed: 156 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -6541,110 +6541,130 @@ def test_upload_bluesheets_interim_chair_access(self):
65416541
self.assertIn('Upload', str(q("title")))
65426542

65436543

6544-
def test_upload_minutes_agenda(self):
6545-
for doctype in ('minutes','agenda'):
6546-
session = SessionFactory(meeting__type_id='ietf')
6547-
if doctype == 'minutes':
6548-
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
6549-
else:
6550-
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
6551-
self.client.logout()
6552-
login_testing_unauthorized(self,"secretary",url)
6553-
r = self.client.get(url)
6554-
self.assertEqual(r.status_code, 200)
6555-
q = PyQuery(r.content)
6556-
self.assertIn('Upload', str(q("Title")))
6557-
self.assertFalse(session.presentations.exists())
6558-
self.assertFalse(q('form input[type="checkbox"]'))
6559-
6560-
session2 = SessionFactory(meeting=session.meeting,group=session.group)
6561-
r = self.client.get(url)
6562-
self.assertEqual(r.status_code, 200)
6563-
q = PyQuery(r.content)
6564-
self.assertTrue(q('form input[type="checkbox"]'))
6565-
6566-
# test not submitting a file
6567-
r = self.client.post(url, dict(submission_method="upload"))
6568-
self.assertEqual(r.status_code, 200)
6569-
q = PyQuery(r.content)
6570-
self.assertTrue(q("form .is-invalid"))
6571-
6572-
test_file = BytesIO(b'this is some text for a test')
6573-
test_file.name = "not_really.json"
6574-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6575-
self.assertEqual(r.status_code, 200)
6576-
q = PyQuery(r.content)
6577-
self.assertTrue(q('form .is-invalid'))
6578-
6579-
test_file = BytesIO(b'this is some text for a test'*1510000)
6580-
test_file.name = "not_really.pdf"
6581-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6582-
self.assertEqual(r.status_code, 200)
6583-
q = PyQuery(r.content)
6584-
self.assertTrue(q('form .is-invalid'))
6585-
6586-
test_file = BytesIO(b'<html><frameset><frame src="foo.html"></frame><frame src="bar.html"></frame></frameset></html>')
6587-
test_file.name = "not_really.html"
6588-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6589-
self.assertEqual(r.status_code, 200)
6590-
q = PyQuery(r.content)
6591-
self.assertTrue(q('form .is-invalid'))
6592-
6593-
# Test html sanitization
6594-
test_file = BytesIO(b'<html><head><title>Title</title></head><body><h1>Title</h1><section>Some text</section></body></html>')
6595-
test_file.name = "some.html"
6596-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6597-
self.assertEqual(r.status_code, 302)
6598-
doc = session.presentations.filter(document__type_id=doctype).first().document
6599-
self.assertEqual(doc.rev,'00')
6600-
text = doc.text()
6601-
self.assertIn('Some text', text)
6602-
self.assertNotIn('<section>', text)
6603-
text = retrieve_str(doctype, f"{doc.name}-{doc.rev}.html")
6604-
self.assertIn('Some text', text)
6605-
self.assertNotIn('<section>', text)
6606-
6607-
# txt upload
6608-
test_bytes = b'This is some text for a test, with the word\nvirtual at the beginning of a line.'
6609-
test_file = BytesIO(test_bytes)
6610-
test_file.name = "some.txt"
6611-
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=False))
6612-
self.assertEqual(r.status_code, 302)
6613-
doc = session.presentations.filter(document__type_id=doctype).first().document
6614-
self.assertEqual(doc.rev,'01')
6615-
self.assertFalse(session2.presentations.filter(document__type_id=doctype))
6616-
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6617-
self.assertEqual(retrieved_bytes, test_bytes)
6618-
6544+
def test_label_future_sessions(self):
6545+
self.client.login(username='secretary', password='secretary+password')
6546+
for future in (True, False):
6547+
mtg_date = date_today()+datetime.timedelta(days=180 if future else -180)
6548+
session = SessionFactory(meeting__type_id='ietf', meeting__date=mtg_date)
6549+
# Verify future warning shows on the session details panel
6550+
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
66196551
r = self.client.get(url)
6620-
self.assertEqual(r.status_code, 200)
6621-
q = PyQuery(r.content)
6622-
self.assertIn('Revise', str(q("Title")))
6623-
test_bytes = b'this is some different text for a test'
6624-
test_file = BytesIO(test_bytes)
6625-
test_file.name = "also_some.txt"
6626-
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=True))
6627-
self.assertEqual(r.status_code, 302)
6628-
doc = Document.objects.get(pk=doc.pk)
6629-
self.assertEqual(doc.rev,'02')
6630-
self.assertTrue(session2.presentations.filter(document__type_id=doctype))
6631-
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6632-
self.assertEqual(retrieved_bytes, test_bytes)
6633-
6634-
# Test bad encoding
6635-
test_file = BytesIO('<html><h1>Title</h1><section>Some\x93text</section></html>'.encode('latin1'))
6636-
test_file.name = "some.html"
6637-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6638-
self.assertContains(r, 'Could not identify the file encoding')
6639-
doc = Document.objects.get(pk=doc.pk)
6640-
self.assertEqual(doc.rev,'02')
6552+
self.assertTrue(r.status_code==200)
6553+
if future:
6554+
self.assertContains(r, "Session has not ended yet")
6555+
else:
6556+
self.assertNotContains(r, "Session has not ended yet")
66416557

6642-
# Verify that we don't have dead links
6643-
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
6644-
top = '/meeting/%s/' % session.meeting.number
6645-
self.requests_mock.get(f'{session.notes_url()}/download', text='markdown notes')
6646-
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
6647-
self.crawl_materials(url=url, top=top)
6558+
def test_upload_minutes_agenda(self):
6559+
for doctype in ('minutes','agenda'):
6560+
for future in (True, False):
6561+
mtg_date = date_today()+datetime.timedelta(days=180 if future else -180)
6562+
session = SessionFactory(meeting__type_id='ietf', meeting__date=mtg_date)
6563+
if doctype == 'minutes':
6564+
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
6565+
else:
6566+
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
6567+
self.client.logout()
6568+
login_testing_unauthorized(self,"secretary",url)
6569+
r = self.client.get(url)
6570+
self.assertEqual(r.status_code, 200)
6571+
q = PyQuery(r.content)
6572+
self.assertIn('Upload', str(q("Title")))
6573+
self.assertFalse(session.presentations.exists())
6574+
self.assertFalse(q('form input[type="checkbox"]'))
6575+
if future and doctype == "minutes":
6576+
self.assertContains(r, "Session has not ended yet")
6577+
else:
6578+
self.assertNotContains(r, "Session has not ended yet")
6579+
6580+
session2 = SessionFactory(meeting=session.meeting,group=session.group)
6581+
r = self.client.get(url)
6582+
self.assertEqual(r.status_code, 200)
6583+
q = PyQuery(r.content)
6584+
self.assertTrue(q('form input[type="checkbox"]'))
6585+
6586+
# test not submitting a file
6587+
r = self.client.post(url, dict(submission_method="upload"))
6588+
self.assertEqual(r.status_code, 200)
6589+
q = PyQuery(r.content)
6590+
self.assertTrue(q("form .is-invalid"))
6591+
6592+
test_file = BytesIO(b'this is some text for a test')
6593+
test_file.name = "not_really.json"
6594+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6595+
self.assertEqual(r.status_code, 200)
6596+
q = PyQuery(r.content)
6597+
self.assertTrue(q('form .is-invalid'))
6598+
6599+
test_file = BytesIO(b'this is some text for a test'*1510000)
6600+
test_file.name = "not_really.pdf"
6601+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6602+
self.assertEqual(r.status_code, 200)
6603+
q = PyQuery(r.content)
6604+
self.assertTrue(q('form .is-invalid'))
6605+
6606+
test_file = BytesIO(b'<html><frameset><frame src="foo.html"></frame><frame src="bar.html"></frame></frameset></html>')
6607+
test_file.name = "not_really.html"
6608+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6609+
self.assertEqual(r.status_code, 200)
6610+
q = PyQuery(r.content)
6611+
self.assertTrue(q('form .is-invalid'))
6612+
6613+
# Test html sanitization
6614+
test_file = BytesIO(b'<html><head><title>Title</title></head><body><h1>Title</h1><section>Some text</section></body></html>')
6615+
test_file.name = "some.html"
6616+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6617+
self.assertEqual(r.status_code, 302)
6618+
doc = session.presentations.filter(document__type_id=doctype).first().document
6619+
self.assertEqual(doc.rev,'00')
6620+
text = doc.text()
6621+
self.assertIn('Some text', text)
6622+
self.assertNotIn('<section>', text)
6623+
text = retrieve_str(doctype, f"{doc.name}-{doc.rev}.html")
6624+
self.assertIn('Some text', text)
6625+
self.assertNotIn('<section>', text)
6626+
6627+
# txt upload
6628+
test_bytes = b'This is some text for a test, with the word\nvirtual at the beginning of a line.'
6629+
test_file = BytesIO(test_bytes)
6630+
test_file.name = "some.txt"
6631+
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=False))
6632+
self.assertEqual(r.status_code, 302)
6633+
doc = session.presentations.filter(document__type_id=doctype).first().document
6634+
self.assertEqual(doc.rev,'01')
6635+
self.assertFalse(session2.presentations.filter(document__type_id=doctype))
6636+
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6637+
self.assertEqual(retrieved_bytes, test_bytes)
6638+
6639+
r = self.client.get(url)
6640+
self.assertEqual(r.status_code, 200)
6641+
q = PyQuery(r.content)
6642+
self.assertIn('Revise', str(q("Title")))
6643+
test_bytes = b'this is some different text for a test'
6644+
test_file = BytesIO(test_bytes)
6645+
test_file.name = "also_some.txt"
6646+
r = self.client.post(url,dict(submission_method="upload",file=test_file,apply_to_all=True))
6647+
self.assertEqual(r.status_code, 302)
6648+
doc = Document.objects.get(pk=doc.pk)
6649+
self.assertEqual(doc.rev,'02')
6650+
self.assertTrue(session2.presentations.filter(document__type_id=doctype))
6651+
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6652+
self.assertEqual(retrieved_bytes, test_bytes)
6653+
6654+
# Test bad encoding
6655+
test_file = BytesIO('<html><h1>Title</h1><section>Some\x93text</section></html>'.encode('latin1'))
6656+
test_file.name = "some.html"
6657+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6658+
self.assertContains(r, 'Could not identify the file encoding')
6659+
doc = Document.objects.get(pk=doc.pk)
6660+
self.assertEqual(doc.rev,'02')
6661+
6662+
# Verify that we don't have dead links
6663+
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
6664+
top = '/meeting/%s/' % session.meeting.number
6665+
self.requests_mock.get(f'{session.notes_url()}/download', text='markdown notes')
6666+
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
6667+
self.crawl_materials(url=url, top=top)
66486668

66496669
def test_upload_minutes_agenda_unscheduled(self):
66506670
for doctype in ('minutes','agenda'):
@@ -6661,6 +6681,7 @@ def test_upload_minutes_agenda_unscheduled(self):
66616681
self.assertIn('Upload', str(q("Title")))
66626682
self.assertFalse(session.presentations.exists())
66636683
self.assertFalse(q('form input[type="checkbox"]'))
6684+
self.assertNotContains(r, "Session has not ended yet")
66646685

66656686
test_file = BytesIO(b'this is some text for a test')
66666687
test_file.name = "not_really.txt"
@@ -6669,35 +6690,40 @@ def test_upload_minutes_agenda_unscheduled(self):
66696690

66706691
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=True)
66716692
def test_upload_minutes_agenda_interim(self):
6672-
session=SessionFactory(meeting__type_id='interim')
66736693
for doctype in ('minutes','agenda'):
6674-
if doctype=='minutes':
6675-
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
6676-
else:
6677-
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
6678-
self.client.logout()
6679-
login_testing_unauthorized(self,"secretary",url)
6680-
r = self.client.get(url)
6681-
self.assertEqual(r.status_code, 200)
6682-
q = PyQuery(r.content)
6683-
self.assertIn('Upload', str(q("title")))
6684-
self.assertFalse(session.presentations.filter(document__type_id=doctype))
6685-
test_bytes = b'this is some text for a test'
6686-
test_file = BytesIO(test_bytes)
6687-
test_file.name = "not_really.txt"
6688-
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6689-
self.assertEqual(r.status_code, 302)
6690-
doc = session.presentations.filter(document__type_id=doctype).first().document
6691-
self.assertEqual(doc.rev,'00')
6692-
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6693-
self.assertEqual(retrieved_bytes, test_bytes)
6694-
6695-
# Verify that we don't have dead links
6696-
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
6697-
top = '/meeting/%s/' % session.meeting.number
6698-
self.requests_mock.get(f'{session.notes_url()}/download', text='markdown notes')
6699-
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
6700-
self.crawl_materials(url=url, top=top)
6694+
for future in (True, False):
6695+
session=SessionFactory(meeting__type_id='interim', meeting__date = date_today()+datetime.timedelta(days=180 if future else -180))
6696+
if doctype=='minutes':
6697+
url = urlreverse('ietf.meeting.views.upload_session_minutes',kwargs={'num':session.meeting.number,'session_id':session.id})
6698+
else:
6699+
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
6700+
self.client.logout()
6701+
login_testing_unauthorized(self,"secretary",url)
6702+
r = self.client.get(url)
6703+
self.assertEqual(r.status_code, 200)
6704+
q = PyQuery(r.content)
6705+
self.assertIn('Upload', str(q("title")))
6706+
self.assertFalse(session.presentations.filter(document__type_id=doctype))
6707+
if future and doctype == "minutes":
6708+
self.assertContains(r, "Session has not ended yet")
6709+
else:
6710+
self.assertNotContains(r, "Session has not ended yet")
6711+
test_bytes = b'this is some text for a test'
6712+
test_file = BytesIO(test_bytes)
6713+
test_file.name = "not_really.txt"
6714+
r = self.client.post(url,dict(submission_method="upload",file=test_file))
6715+
self.assertEqual(r.status_code, 302)
6716+
doc = session.presentations.filter(document__type_id=doctype).first().document
6717+
self.assertEqual(doc.rev,'00')
6718+
retrieved_bytes = retrieve_bytes(doctype, f"{doc.name}-{doc.rev}.txt")
6719+
self.assertEqual(retrieved_bytes, test_bytes)
6720+
6721+
# Verify that we don't have dead links
6722+
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
6723+
top = '/meeting/%s/' % session.meeting.number
6724+
self.requests_mock.get(f'{session.notes_url()}/download', text='markdown notes')
6725+
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
6726+
self.crawl_materials(url=url, top=top)
67016727

67026728
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=True)
67036729
def test_upload_narrativeminutes(self):

ietf/meeting/views.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,8 @@ def session_details(request, num, acronym):
25222522
else:
25232523
pending_suggestions = SlideSubmission.objects.none()
25242524

2525+
tsa = session.official_timeslotassignment()
2526+
future = tsa is not None and timezone.now() < tsa.timeslot.end_time()
25252527
return render(request, "meeting/session_details.html",
25262528
{ 'scheduled_sessions':scheduled_sessions ,
25272529
'unscheduled_sessions':unscheduled_sessions ,
@@ -2532,6 +2534,7 @@ def session_details(request, num, acronym):
25322534
'can_manage_materials' : can_manage,
25332535
'can_view_request': can_view_request,
25342536
'thisweek': datetime_today()-datetime.timedelta(days=7),
2537+
'future': future,
25352538
})
25362539

25372540
class SessionDraftsForm(forms.Form):
@@ -2823,11 +2826,14 @@ def upload_session_minutes(request, session_id, num):
28232826
else:
28242827
form = UploadMinutesForm(show_apply_to_all_checkbox)
28252828

2829+
tsa = session.official_timeslotassignment()
2830+
future = tsa is not None and timezone.now() < tsa.timeslot.end_time()
28262831
return render(request, "meeting/upload_session_minutes.html",
28272832
{'session': session,
28282833
'session_number': session_number,
28292834
'minutes_sp' : minutes_sp,
28302835
'form': form,
2836+
'future': future,
28312837
})
28322838

28332839
@role_required("Secretariat")

0 commit comments

Comments
 (0)