Added an API endpoint for bluesheet information upload. Factored out code in common with manual upload_session_bluesheets(). Tweaked ietf.secr.proceedings.utils.handle_upload_file() to handle temp files in addition to http file objects, to support the API endpoint.
- Legacy-Id: 18067
This commit is contained in:
parent
729b52fb36
commit
3a76e4a935
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
@ -21,7 +22,7 @@ import debug # pyflakes:ignore
|
|||
from ietf.group.factories import RoleFactory
|
||||
from ietf.meeting.factories import MeetingFactory, SessionFactory
|
||||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
from ietf.person.factories import PersonFactory
|
||||
from ietf.person.factories import PersonFactory, random_faker
|
||||
from ietf.person.models import PersonalApiKey
|
||||
from ietf.stats.models import MeetingRegistration
|
||||
from ietf.utils.mail import outbox, get_payload_text
|
||||
|
@ -135,6 +136,93 @@ class CustomApiTests(TestCase):
|
|||
event = doc.latest_event()
|
||||
self.assertEqual(event.by, recman)
|
||||
|
||||
def test_api_upload_bluesheet(self):
|
||||
url = urlreverse('ietf.meeting.views.api_upload_bluesheet')
|
||||
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)
|
||||
group = session.group
|
||||
apikey = PersonalApiKey.objects.create(endpoint=url, person=recman)
|
||||
|
||||
people = [
|
||||
{"name":"Andrea Andreotti", "affiliation": "Azienda"},
|
||||
{"name":"Bosse Bernadotte", "affiliation": "Bolag"},
|
||||
{"name":"Charles Charlemagne", "affiliation": "Compagnie"},
|
||||
]
|
||||
for i in range(3):
|
||||
faker = random_faker()
|
||||
people.append(dict(name=faker.name(), affiliation=faker.company()))
|
||||
bluesheet = json.dumps(people)
|
||||
|
||||
# error cases
|
||||
r = self.client.post(url, {})
|
||||
self.assertContains(r, "Missing apikey parameter", status_code=400)
|
||||
|
||||
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()
|
||||
r = self.client.post(url, {'apikey': badapikey.hash()} )
|
||||
self.assertContains(r, "Restricted to roles Recording Manager, Secretariat", 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()
|
||||
recman.user.save()
|
||||
|
||||
r = self.client.get(url, {'apikey': apikey.hash()} )
|
||||
self.assertContains(r, "Method not allowed", status_code=405)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash()} )
|
||||
self.assertContains(r, "Missing meeting parameter", status_code=400)
|
||||
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, } )
|
||||
self.assertContains(r, "Missing group parameter", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym} )
|
||||
self.assertContains(r, "Missing item parameter", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym, 'item': '1'} )
|
||||
self.assertContains(r, "Missing bluesheet parameter", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': '1', 'group': group.acronym,
|
||||
'item': '1', 'bluesheet': bluesheet, })
|
||||
self.assertContains(r, "No sessions found for meeting", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': 'bogous',
|
||||
'item': '1', 'bluesheet': bluesheet, })
|
||||
self.assertContains(r, "No sessions found in meeting '%s' for group 'bogous'"%meeting.number, status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym,
|
||||
'item': '1', 'bluesheet': "foobar", })
|
||||
self.assertContains(r, "Invalid json value: 'foobar'", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym,
|
||||
'item': '5', 'bluesheet': bluesheet, })
|
||||
self.assertContains(r, "No item '5' found in list of sessions for group", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym,
|
||||
'item': 'foo', 'bluesheet': bluesheet, })
|
||||
self.assertContains(r, "Expected a numeric value for 'item', found 'foo'", status_code=400)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym,
|
||||
'item': '1', 'bluesheet': bluesheet, })
|
||||
self.assertContains(r, "Done", status_code=200)
|
||||
|
||||
r = self.client.post(url, {'apikey': apikey.hash(), 'meeting': meeting.number, 'group': group.acronym,
|
||||
'item': '1', 'bluesheet': bluesheet, })
|
||||
debug.show('bluesheet')
|
||||
self.assertContains(r, "Done", status_code=200)
|
||||
|
||||
bluesheet = session.sessionpresentation_set.filter(document__type__slug='bluesheets').first().document
|
||||
with open(bluesheet.get_file_name()) as file:
|
||||
text = file.read()
|
||||
for p in people:
|
||||
self.assertIn(p['name'], text)
|
||||
self.assertIn(p['affiliation'], text)
|
||||
|
||||
def test_person_export(self):
|
||||
person = PersonFactory()
|
||||
url = urlreverse('ietf.api.views.PersonalInformationExportView')
|
||||
|
|
|
@ -29,7 +29,9 @@ urlpatterns = [
|
|||
# Let Meetecho trigger recording imports
|
||||
url(r'^notify/meeting/import_recordings/(?P<number>[a-z0-9-]+)/?$', meeting_views.api_import_recordings),
|
||||
# Let the registration system notify us about registrations
|
||||
url(r'^notify/meeting/registration', api_views.api_new_meeting_registration),
|
||||
url(r'^notify/meeting/bluesheet/?$', meeting_views.api_upload_bluesheet),
|
||||
# Let the registration system notify us about registrations
|
||||
url(r'^notify/meeting/registration/?', api_views.api_new_meeting_registration),
|
||||
# OpenID authentication provider
|
||||
url(r'^openid/', include('oidc_provider.urls', namespace='oidc_provider')),
|
||||
# For meetecho access
|
||||
|
|
|
@ -13,6 +13,7 @@ import os
|
|||
import pytz
|
||||
import re
|
||||
import tarfile
|
||||
import tempfile
|
||||
import markdown2
|
||||
|
||||
|
||||
|
@ -1702,55 +1703,27 @@ def upload_session_bluesheets(request, session_id, num):
|
|||
if len(sessions) > 1:
|
||||
session_number = 1 + sessions.index(session)
|
||||
|
||||
bluesheet_sp = session.sessionpresentation_set.filter(document__type='bluesheets').first()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = UploadBlueSheetForm(request.POST,request.FILES)
|
||||
if form.is_valid():
|
||||
file = request.FILES['file']
|
||||
_, ext = os.path.splitext(file.name)
|
||||
if bluesheet_sp:
|
||||
doc = bluesheet_sp.document
|
||||
doc.rev = '%02d' % (int(doc.rev)+1)
|
||||
bluesheet_sp.rev = doc.rev
|
||||
bluesheet_sp.save()
|
||||
else:
|
||||
ota = session.official_timeslotassignment()
|
||||
sess_time = ota and ota.timeslot.time
|
||||
if not sess_time:
|
||||
return HttpResponse("Cannot receive uploads for an unscheduled session. Please check the session ID.", status=410, content_type="text/plain")
|
||||
if session.meeting.type_id=='ietf':
|
||||
name = 'bluesheets-%s-%s-%s' % (session.meeting.number,
|
||||
session.group.acronym,
|
||||
sess_time.strftime("%Y%m%d%H%M"))
|
||||
title = 'Bluesheets IETF%s: %s : %s' % (session.meeting.number,
|
||||
session.group.acronym,
|
||||
sess_time.strftime("%a %H:%M"))
|
||||
else:
|
||||
name = 'bluesheets-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
|
||||
title = 'Bluesheets %s: %s' % (session.meeting.number, sess_time.strftime("%a %H:%M"))
|
||||
doc = Document.objects.create(
|
||||
name = name,
|
||||
type_id = 'bluesheets',
|
||||
title = title,
|
||||
group = session.group,
|
||||
rev = '00',
|
||||
)
|
||||
doc.states.add(State.objects.get(type_id='bluesheets',slug='active'))
|
||||
DocAlias.objects.create(name=doc.name).docs.add(doc)
|
||||
session.sessionpresentation_set.create(document=doc,rev='00')
|
||||
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
|
||||
doc.uploaded_filename = filename
|
||||
e = NewRevisionDocEvent.objects.create(doc=doc, rev=doc.rev, by=request.user.person, type='new_revision', desc='New revision available: %s'%doc.rev)
|
||||
save_error = handle_upload_file(file, filename, session.meeting, 'bluesheets', request=request, encoding=form.file_encoding[file.name])
|
||||
|
||||
ota = session.official_timeslotassignment()
|
||||
sess_time = ota and ota.timeslot.time
|
||||
if not sess_time:
|
||||
return HttpResponse("Cannot receive uploads for an unscheduled session. Please check the session ID.", status=410, content_type="text/plain")
|
||||
|
||||
|
||||
save_error = save_bluesheet(request, session, file, encoding=form.file_encoding[file.name])
|
||||
if save_error:
|
||||
form.add_error(None, save_error)
|
||||
else:
|
||||
doc.save_with_history([e])
|
||||
return redirect('ietf.meeting.views.session_details',num=num,acronym=session.group.acronym)
|
||||
else:
|
||||
form = UploadBlueSheetForm()
|
||||
|
||||
bluesheet_sp = session.sessionpresentation_set.filter(document__type='bluesheets').first()
|
||||
|
||||
return render(request, "meeting/upload_session_bluesheets.html",
|
||||
{'session': session,
|
||||
'session_number': session_number,
|
||||
|
@ -1759,6 +1732,47 @@ def upload_session_bluesheets(request, session_id, num):
|
|||
})
|
||||
|
||||
|
||||
def save_bluesheet(request, session, file, encoding='utf-8'):
|
||||
bluesheet_sp = session.sessionpresentation_set.filter(document__type='bluesheets').first()
|
||||
_, ext = os.path.splitext(file.name)
|
||||
|
||||
if bluesheet_sp:
|
||||
doc = bluesheet_sp.document
|
||||
doc.rev = '%02d' % (int(doc.rev)+1)
|
||||
bluesheet_sp.rev = doc.rev
|
||||
bluesheet_sp.save()
|
||||
else:
|
||||
ota = session.official_timeslotassignment()
|
||||
sess_time = ota and ota.timeslot.time
|
||||
|
||||
if session.meeting.type_id=='ietf':
|
||||
name = 'bluesheets-%s-%s-%s' % (session.meeting.number,
|
||||
session.group.acronym,
|
||||
sess_time.strftime("%Y%m%d%H%M"))
|
||||
title = 'Bluesheets IETF%s: %s : %s' % (session.meeting.number,
|
||||
session.group.acronym,
|
||||
sess_time.strftime("%a %H:%M"))
|
||||
else:
|
||||
name = 'bluesheets-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
|
||||
title = 'Bluesheets %s: %s' % (session.meeting.number, sess_time.strftime("%a %H:%M"))
|
||||
doc = Document.objects.create(
|
||||
name = name,
|
||||
type_id = 'bluesheets',
|
||||
title = title,
|
||||
group = session.group,
|
||||
rev = '00',
|
||||
)
|
||||
doc.states.add(State.objects.get(type_id='bluesheets',slug='active'))
|
||||
DocAlias.objects.create(name=doc.name).docs.add(doc)
|
||||
session.sessionpresentation_set.create(document=doc,rev='00')
|
||||
filename = '%s-%s%s'% ( doc.name, doc.rev, ext)
|
||||
doc.uploaded_filename = filename
|
||||
e = NewRevisionDocEvent.objects.create(doc=doc, rev=doc.rev, by=request.user.person, type='new_revision', desc='New revision available: %s'%doc.rev)
|
||||
save_error = handle_upload_file(file, filename, session.meeting, 'bluesheets', request=request, encoding=encoding)
|
||||
if not save_error:
|
||||
doc.save_with_history([e])
|
||||
return save_error
|
||||
|
||||
class UploadMinutesForm(FileUploadForm):
|
||||
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=True,required=False)
|
||||
|
||||
|
@ -3024,6 +3038,64 @@ def api_set_session_video_url(request):
|
|||
return HttpResponse("Done", status=200, content_type='text/plain')
|
||||
|
||||
|
||||
@require_api_key
|
||||
@role_required('Recording Manager', 'Secretariat')
|
||||
@csrf_exempt
|
||||
def api_upload_bluesheet(request):
|
||||
def err(code, text):
|
||||
return HttpResponse(text, status=code, content_type='text/plain')
|
||||
if request.method == 'POST':
|
||||
# parameters:
|
||||
# apikey: the poster's personal API key
|
||||
# meeting: number as string, i.e., '101', or 'interim-2018-quic-02'
|
||||
# group: acronym or special, i.e., 'quic' or 'plenary'
|
||||
# item: '1', '2', '3' (the group's first, second, third etc.
|
||||
# session during the week)
|
||||
# bluesheet: json blob with [{'name': 'Name', 'affiliation': 'Organization', }, ...]
|
||||
for item in ['meeting', 'group', 'item', 'bluesheet',]:
|
||||
value = request.POST.get(item)
|
||||
if not value:
|
||||
return err(400, "Missing %s parameter" % item)
|
||||
number = request.POST.get('meeting')
|
||||
sessions = Session.objects.filter(meeting__number=number)
|
||||
if not sessions.exists():
|
||||
return err(400, "No sessions found for meeting '%s'" % (number, ))
|
||||
acronym = request.POST.get('group')
|
||||
sessions = sessions.filter(group__acronym=acronym)
|
||||
if not sessions.exists():
|
||||
return err(400, "No sessions found in meeting '%s' for group '%s'" % (number, acronym))
|
||||
session_times = [ (s.official_timeslotassignment().timeslot.time, s) for s in sessions if s.official_timeslotassignment() ]
|
||||
session_times.sort()
|
||||
item = request.POST.get('item')
|
||||
if not item.isdigit():
|
||||
return err(400, "Expected a numeric value for 'item', found '%s'" % (item, ))
|
||||
n = int(item)-1 # change 1-based to 0-based
|
||||
try:
|
||||
time, session = session_times[n]
|
||||
except IndexError:
|
||||
return err(400, "No item '%s' found in list of sessions for group" % (item, ))
|
||||
bjson = request.POST.get('bluesheet')
|
||||
try:
|
||||
data = json.loads(bjson)
|
||||
except json.decoder.JSONDecodeError:
|
||||
return err(400, "Invalid json value: '%s'" % (bjson, ))
|
||||
|
||||
fd, name = tempfile.mkstemp(suffix=".txt", text=True)
|
||||
os.close(fd)
|
||||
with open(name, "w") as file:
|
||||
file.write("Bluesheets for %s\n\n" % session)
|
||||
for item in data:
|
||||
file.write("{name}\t{affiliation}\n".format(**item))
|
||||
with open(name, "br") as file:
|
||||
save_err = save_bluesheet(request, session, file)
|
||||
if save_err:
|
||||
return err(400, save_err)
|
||||
else:
|
||||
return err(405, "Method not allowed")
|
||||
|
||||
return HttpResponse("Done", status=200, content_type='text/plain')
|
||||
|
||||
|
||||
def important_dates(request, num=None):
|
||||
assert num is None or num.isdigit()
|
||||
preview_roles = ['Area Director', 'Secretariat', 'IETF Chair', 'IAD', ]
|
||||
|
|
|
@ -59,8 +59,11 @@ def handle_upload_file(file,filename,meeting,subdir, request=None, encoding=None
|
|||
"Your upload %s was changed by the sanitization; please check the "
|
||||
"resulting content. " % (filename, ))
|
||||
else:
|
||||
for chunk in file.chunks():
|
||||
destination.write(chunk)
|
||||
if hasattr(file, 'chunks'):
|
||||
for chunk in file.chunks():
|
||||
destination.write(chunk)
|
||||
else:
|
||||
destination.write(file.read())
|
||||
destination.close()
|
||||
|
||||
# unzip zipfile
|
||||
|
|
Loading…
Reference in a new issue