feat: upload narrative minutes (#7125)
* feat: upload narrative minutes * chore: cover other new URL path
This commit is contained in:
parent
aaf402fd1f
commit
7287e98709
|
@ -472,6 +472,9 @@ class ApplyToAllFileUploadForm(FileUploadForm):
|
||||||
class UploadMinutesForm(ApplyToAllFileUploadForm):
|
class UploadMinutesForm(ApplyToAllFileUploadForm):
|
||||||
doc_type = 'minutes'
|
doc_type = 'minutes'
|
||||||
|
|
||||||
|
class UploadNarrativeMinutesForm(ApplyToAllFileUploadForm):
|
||||||
|
doc_type = 'narrativeminutes'
|
||||||
|
|
||||||
|
|
||||||
class UploadAgendaForm(ApplyToAllFileUploadForm):
|
class UploadAgendaForm(ApplyToAllFileUploadForm):
|
||||||
doc_type = 'agenda'
|
doc_type = 'agenda'
|
||||||
|
|
|
@ -1147,7 +1147,6 @@ class Session(models.Model):
|
||||||
return can_manage_materials(user,self.group)
|
return can_manage_materials(user,self.group)
|
||||||
|
|
||||||
def is_material_submission_cutoff(self):
|
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()
|
return date_today(datetime.timezone.utc) > self.meeting.get_submission_correction_date()
|
||||||
|
|
||||||
def joint_with_groups_acronyms(self):
|
def joint_with_groups_acronyms(self):
|
||||||
|
|
|
@ -6233,6 +6233,33 @@ class MaterialsTests(TestCase):
|
||||||
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
|
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
|
||||||
self.crawl_materials(url=url, top=top)
|
self.crawl_materials(url=url, top=top)
|
||||||
|
|
||||||
|
@override_settings(MEETING_MATERIALS_SERVE_LOCALLY=True)
|
||||||
|
def test_upload_narrativeminutes(self):
|
||||||
|
for type_id in ["interim","ietf"]:
|
||||||
|
session=SessionFactory(meeting__type_id=type_id,group__acronym='iesg')
|
||||||
|
doctype='narrativeminutes'
|
||||||
|
url = urlreverse('ietf.meeting.views.upload_session_narrativeminutes',kwargs={'num':session.meeting.number,'session_id':session.id})
|
||||||
|
self.client.logout()
|
||||||
|
login_testing_unauthorized(self,"secretary",url)
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
q = PyQuery(r.content)
|
||||||
|
self.assertIn('Upload', str(q("title")))
|
||||||
|
self.assertFalse(session.presentations.filter(document__type_id=doctype))
|
||||||
|
test_file = BytesIO(b'this is some text for a test')
|
||||||
|
test_file.name = "not_really.txt"
|
||||||
|
r = self.client.post(url,dict(submission_method="upload",file=test_file))
|
||||||
|
self.assertEqual(r.status_code, 302)
|
||||||
|
doc = session.presentations.filter(document__type_id=doctype).first().document
|
||||||
|
self.assertEqual(doc.rev,'00')
|
||||||
|
|
||||||
|
# Verify that we don't have dead links
|
||||||
|
url = urlreverse('ietf.meeting.views.session_details', kwargs={'num':session.meeting.number, 'acronym': session.group.acronym})
|
||||||
|
top = '/meeting/%s/' % session.meeting.number
|
||||||
|
self.requests_mock.get(f'{session.notes_url()}/download', text='markdown notes')
|
||||||
|
self.requests_mock.get(f'{session.notes_url()}/info', text=json.dumps({'title': 'title', 'updatetime': '2021-12-01T17:11:00z'}))
|
||||||
|
self.crawl_materials(url=url, top=top)
|
||||||
|
|
||||||
def test_enter_agenda(self):
|
def test_enter_agenda(self):
|
||||||
session = SessionFactory(meeting__type_id='ietf')
|
session = SessionFactory(meeting__type_id='ietf')
|
||||||
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
|
url = urlreverse('ietf.meeting.views.upload_session_agenda',kwargs={'num':session.meeting.number,'session_id':session.id})
|
||||||
|
|
|
@ -18,6 +18,7 @@ safe_for_all_meeting_types = [
|
||||||
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
|
url(r'^session/(?P<session_id>\d+)/drafts$', views.add_session_drafts),
|
||||||
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
|
url(r'^session/(?P<session_id>\d+)/bluesheets$', views.upload_session_bluesheets),
|
||||||
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
|
url(r'^session/(?P<session_id>\d+)/minutes$', views.upload_session_minutes),
|
||||||
|
url(r'^session/(?P<session_id>\d+)/narrativeminutes$', views.upload_session_narrativeminutes),
|
||||||
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
|
url(r'^session/(?P<session_id>\d+)/agenda$', views.upload_session_agenda),
|
||||||
url(r'^session/(?P<session_id>\d+)/import/minutes$', views.import_session_minutes),
|
url(r'^session/(?P<session_id>\d+)/import/minutes$', views.import_session_minutes),
|
||||||
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
|
url(r'^session/(?P<session_id>\d+)/propose_slides$', views.propose_session_slides),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright The IETF Trust 2016-2020, All Rights Reserved
|
# Copyright The IETF Trust 2016-2024, All Rights Reserved
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
|
@ -555,7 +555,7 @@ class SaveMaterialsError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def save_session_minutes_revision(session, file, ext, request, encoding=None, apply_to_all=False):
|
def save_session_minutes_revision(session, file, ext, request, encoding=None, apply_to_all=False, narrative=False):
|
||||||
"""Creates or updates session minutes records
|
"""Creates or updates session minutes records
|
||||||
|
|
||||||
This updates the database models to reflect a new version. It does not handle
|
This updates the database models to reflect a new version. It does not handle
|
||||||
|
@ -568,7 +568,8 @@ def save_session_minutes_revision(session, file, ext, request, encoding=None, ap
|
||||||
Returns (Document, [DocEvents]), which should be passed to doc.save_with_history()
|
Returns (Document, [DocEvents]), which should be passed to doc.save_with_history()
|
||||||
if the file contents are stored successfully.
|
if the file contents are stored successfully.
|
||||||
"""
|
"""
|
||||||
minutes_sp = session.presentations.filter(document__type='minutes').first()
|
document_type = DocTypeName.objects.get(slug= 'narrativeminutes' if narrative else 'minutes')
|
||||||
|
minutes_sp = session.presentations.filter(document__type=document_type).first()
|
||||||
if minutes_sp:
|
if minutes_sp:
|
||||||
doc = minutes_sp.document
|
doc = minutes_sp.document
|
||||||
doc.rev = '%02d' % (int(doc.rev)+1)
|
doc.rev = '%02d' % (int(doc.rev)+1)
|
||||||
|
@ -580,28 +581,26 @@ def save_session_minutes_revision(session, file, ext, request, encoding=None, ap
|
||||||
if not sess_time:
|
if not sess_time:
|
||||||
raise SessionNotScheduledError
|
raise SessionNotScheduledError
|
||||||
if session.meeting.type_id=='ietf':
|
if session.meeting.type_id=='ietf':
|
||||||
name = 'minutes-%s-%s' % (session.meeting.number,
|
name = f"{document_type.prefix}-{session.meeting.number}-{session.group.acronym}"
|
||||||
session.group.acronym)
|
title = f"{document_type.name} IETF{session.meeting.number}: {session.group.acronym}"
|
||||||
title = 'Minutes IETF%s: %s' % (session.meeting.number,
|
|
||||||
session.group.acronym)
|
|
||||||
if not apply_to_all:
|
if not apply_to_all:
|
||||||
name += '-%s' % (sess_time.strftime("%Y%m%d%H%M"),)
|
name += '-%s' % (sess_time.strftime("%Y%m%d%H%M"),)
|
||||||
title += ': %s' % (sess_time.strftime("%a %H:%M"),)
|
title += ': %s' % (sess_time.strftime("%a %H:%M"),)
|
||||||
else:
|
else:
|
||||||
name = 'minutes-%s-%s' % (session.meeting.number, sess_time.strftime("%Y%m%d%H%M"))
|
name =f"{document_type.prefix}-{session.meeting.number}-{sess_time.strftime('%Y%m%d%H%M')}"
|
||||||
title = 'Minutes %s: %s' % (session.meeting.number, sess_time.strftime("%a %H:%M"))
|
title = f"{document_type.name} {session.meeting.number}: {sess_time.strftime('%a %H:%M')}"
|
||||||
if Document.objects.filter(name=name).exists():
|
if Document.objects.filter(name=name).exists():
|
||||||
doc = Document.objects.get(name=name)
|
doc = Document.objects.get(name=name)
|
||||||
doc.rev = '%02d' % (int(doc.rev)+1)
|
doc.rev = '%02d' % (int(doc.rev)+1)
|
||||||
else:
|
else:
|
||||||
doc = Document.objects.create(
|
doc = Document.objects.create(
|
||||||
name = name,
|
name = name,
|
||||||
type_id = 'minutes',
|
type = document_type,
|
||||||
title = title,
|
title = title,
|
||||||
group = session.group,
|
group = session.group,
|
||||||
rev = '00',
|
rev = '00',
|
||||||
)
|
)
|
||||||
doc.states.add(State.objects.get(type_id='minutes',slug='active'))
|
doc.states.add(State.objects.get(type_id=document_type.slug,slug='active'))
|
||||||
if session.presentations.filter(document=doc).exists():
|
if session.presentations.filter(document=doc).exists():
|
||||||
sp = session.presentations.get(document=doc)
|
sp = session.presentations.get(document=doc)
|
||||||
sp.rev = doc.rev
|
sp.rev = doc.rev
|
||||||
|
@ -611,7 +610,7 @@ def save_session_minutes_revision(session, file, ext, request, encoding=None, ap
|
||||||
if apply_to_all:
|
if apply_to_all:
|
||||||
for other_session in get_meeting_sessions(session.meeting.number, session.group.acronym):
|
for other_session in get_meeting_sessions(session.meeting.number, session.group.acronym):
|
||||||
if other_session != session:
|
if other_session != session:
|
||||||
other_session.presentations.filter(document__type='minutes').delete()
|
other_session.presentations.filter(document__type=document_type).delete()
|
||||||
other_session.presentations.create(document=doc,rev=doc.rev)
|
other_session.presentations.create(document=doc,rev=doc.rev)
|
||||||
filename = f'{doc.name}-{doc.rev}{ext}'
|
filename = f'{doc.name}-{doc.rev}{ext}'
|
||||||
doc.uploaded_filename = filename
|
doc.uploaded_filename = filename
|
||||||
|
@ -628,7 +627,7 @@ def save_session_minutes_revision(session, file, ext, request, encoding=None, ap
|
||||||
file=file,
|
file=file,
|
||||||
filename=doc.uploaded_filename,
|
filename=doc.uploaded_filename,
|
||||||
meeting=session.meeting,
|
meeting=session.meeting,
|
||||||
subdir='minutes',
|
subdir=document_type.slug,
|
||||||
request=request,
|
request=request,
|
||||||
encoding=encoding,
|
encoding=encoding,
|
||||||
)
|
)
|
||||||
|
|
|
@ -102,7 +102,8 @@ from ietf.utils.timezone import datetime_today, date_today
|
||||||
|
|
||||||
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
|
||||||
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
|
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
|
||||||
UploadAgendaForm, UploadBlueSheetForm, UploadMinutesForm, UploadSlidesForm)
|
UploadAgendaForm, UploadBlueSheetForm, UploadMinutesForm, UploadSlidesForm,
|
||||||
|
UploadNarrativeMinutesForm)
|
||||||
|
|
||||||
request_summary_exclude_group_types = ['team']
|
request_summary_exclude_group_types = ['team']
|
||||||
|
|
||||||
|
@ -2662,6 +2663,61 @@ def upload_session_minutes(request, session_id, num):
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@role_required("Secretariat")
|
||||||
|
def upload_session_narrativeminutes(request, session_id, num):
|
||||||
|
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
|
||||||
|
session = get_object_or_404(Session,pk=session_id)
|
||||||
|
if session.group.acronym != "iesg":
|
||||||
|
raise Http404()
|
||||||
|
|
||||||
|
session_number = None
|
||||||
|
sessions = get_sessions(session.meeting.number,session.group.acronym)
|
||||||
|
show_apply_to_all_checkbox = len(sessions) > 1 if session.type_id == 'regular' else False
|
||||||
|
if len(sessions) > 1:
|
||||||
|
session_number = 1 + sessions.index(session)
|
||||||
|
|
||||||
|
narrativeminutes_sp = session.presentations.filter(document__type='narrativeminutes').first()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = UploadNarrativeMinutesForm(show_apply_to_all_checkbox,request.POST,request.FILES)
|
||||||
|
if form.is_valid():
|
||||||
|
file = request.FILES['file']
|
||||||
|
_, ext = os.path.splitext(file.name)
|
||||||
|
apply_to_all = session.type_id == 'regular'
|
||||||
|
if show_apply_to_all_checkbox:
|
||||||
|
apply_to_all = form.cleaned_data['apply_to_all']
|
||||||
|
|
||||||
|
# Set up the new revision
|
||||||
|
try:
|
||||||
|
save_session_minutes_revision(
|
||||||
|
session=session,
|
||||||
|
apply_to_all=apply_to_all,
|
||||||
|
file=file,
|
||||||
|
ext=ext,
|
||||||
|
encoding=form.file_encoding[file.name],
|
||||||
|
request=request,
|
||||||
|
narrative=True
|
||||||
|
)
|
||||||
|
except SessionNotScheduledError:
|
||||||
|
return HttpResponseGone(
|
||||||
|
"Cannot receive uploads for an unscheduled session. Please check the session ID.",
|
||||||
|
content_type="text/plain",
|
||||||
|
)
|
||||||
|
except SaveMaterialsError as err:
|
||||||
|
form.add_error(None, str(err))
|
||||||
|
else:
|
||||||
|
# no exception -- success!
|
||||||
|
messages.success(request, f'Successfully uploaded narrative minutes as revision {session.narrative_minutes().rev}.')
|
||||||
|
return redirect('ietf.meeting.views.session_details', num=num, acronym=session.group.acronym)
|
||||||
|
else:
|
||||||
|
form = UploadMinutesForm(show_apply_to_all_checkbox)
|
||||||
|
|
||||||
|
return render(request, "meeting/upload_session_narrativeminutes.html",
|
||||||
|
{'session': session,
|
||||||
|
'session_number': session_number,
|
||||||
|
'minutes_sp' : narrativeminutes_sp,
|
||||||
|
'form': form,
|
||||||
|
})
|
||||||
|
|
||||||
class UploadOrEnterAgendaForm(UploadAgendaForm):
|
class UploadOrEnterAgendaForm(UploadAgendaForm):
|
||||||
ACTIONS = [
|
ACTIONS = [
|
||||||
|
|
|
@ -2578,6 +2578,32 @@
|
||||||
"model": "doc.state",
|
"model": "doc.state",
|
||||||
"pk": 177
|
"pk": 177
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "Active",
|
||||||
|
"next_states": [],
|
||||||
|
"order": 0,
|
||||||
|
"slug": "active",
|
||||||
|
"type": "narrativeminutes",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "doc.state",
|
||||||
|
"pk": 178
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "Deleted",
|
||||||
|
"next_states": [],
|
||||||
|
"order": 1,
|
||||||
|
"slug": "deleted",
|
||||||
|
"type": "narrativeminutes",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "doc.state",
|
||||||
|
"pk": 179
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"label": "State"
|
"label": "State"
|
||||||
|
@ -2739,6 +2765,13 @@
|
||||||
"model": "doc.statetype",
|
"model": "doc.statetype",
|
||||||
"pk": "minutes"
|
"pk": "minutes"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"label": "State"
|
||||||
|
},
|
||||||
|
"model": "doc.statetype",
|
||||||
|
"pk": "narrativeminutes"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"label": "State"
|
"label": "State"
|
||||||
|
@ -10763,6 +10796,17 @@
|
||||||
"model": "name.doctypename",
|
"model": "name.doctypename",
|
||||||
"pk": "minutes"
|
"pk": "minutes"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "Narrative Minutes",
|
||||||
|
"order": 0,
|
||||||
|
"prefix": "narrative-minutes",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "name.doctypename",
|
||||||
|
"pk": "narrativeminutes"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"desc": "",
|
"desc": "",
|
||||||
|
@ -16734,7 +16778,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"command": "xym",
|
"command": "xym",
|
||||||
"switch": "--version",
|
"switch": "--version",
|
||||||
"time": "2023-11-21T08:09:45.989Z",
|
"time": "2024-02-21T08:06:28.313Z",
|
||||||
"used": true,
|
"used": true,
|
||||||
"version": "xym 0.7.0"
|
"version": "xym 0.7.0"
|
||||||
},
|
},
|
||||||
|
@ -16745,7 +16789,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"command": "pyang",
|
"command": "pyang",
|
||||||
"switch": "--version",
|
"switch": "--version",
|
||||||
"time": "2023-11-21T08:09:46.322Z",
|
"time": "2024-02-21T08:06:28.663Z",
|
||||||
"used": true,
|
"used": true,
|
||||||
"version": "pyang 2.6.0"
|
"version": "pyang 2.6.0"
|
||||||
},
|
},
|
||||||
|
@ -16756,7 +16800,7 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"command": "yanglint",
|
"command": "yanglint",
|
||||||
"switch": "--version",
|
"switch": "--version",
|
||||||
"time": "2023-11-21T08:09:46.338Z",
|
"time": "2024-02-21T08:06:28.685Z",
|
||||||
"used": true,
|
"used": true,
|
||||||
"version": "yanglint SO 1.9.2"
|
"version": "yanglint SO 1.9.2"
|
||||||
},
|
},
|
||||||
|
@ -16767,9 +16811,9 @@
|
||||||
"fields": {
|
"fields": {
|
||||||
"command": "xml2rfc",
|
"command": "xml2rfc",
|
||||||
"switch": "--version",
|
"switch": "--version",
|
||||||
"time": "2023-11-21T08:09:47.251Z",
|
"time": "2024-02-21T08:06:29.492Z",
|
||||||
"used": true,
|
"used": true,
|
||||||
"version": "xml2rfc 3.18.2"
|
"version": "xml2rfc 3.19.4"
|
||||||
},
|
},
|
||||||
"model": "utils.versioninfo",
|
"model": "utils.versioninfo",
|
||||||
"pk": 4
|
"pk": 4
|
||||||
|
|
|
@ -913,7 +913,7 @@ MEETING_VALID_UPLOAD_EXTENSIONS = {
|
||||||
MEETING_VALID_UPLOAD_MIME_TYPES = {
|
MEETING_VALID_UPLOAD_MIME_TYPES = {
|
||||||
'agenda': ['text/plain', 'text/html', 'text/markdown', 'text/x-markdown', ],
|
'agenda': ['text/plain', 'text/html', 'text/markdown', 'text/x-markdown', ],
|
||||||
'minutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
|
'minutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
|
||||||
'narrative-minutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
|
'narrativeminutes': ['text/plain', 'text/html', 'application/pdf', 'text/markdown', 'text/x-markdown', ],
|
||||||
'slides': [],
|
'slides': [],
|
||||||
'bluesheets': ['application/pdf', 'text/plain', ],
|
'bluesheets': ['application/pdf', 'text/plain', ],
|
||||||
'procmaterials':['application/pdf', ],
|
'procmaterials':['application/pdf', ],
|
||||||
|
|
|
@ -73,6 +73,8 @@
|
||||||
{% if user|has_role:"Secretariat" or can_manage_materials %}
|
{% if user|has_role:"Secretariat" or can_manage_materials %}
|
||||||
{% if pres.document.type.slug == 'minutes' %}
|
{% if pres.document.type.slug == 'minutes' %}
|
||||||
{% url 'ietf.meeting.views.upload_session_minutes' session_id=session.pk num=session.meeting.number as upload_url %}
|
{% url 'ietf.meeting.views.upload_session_minutes' session_id=session.pk num=session.meeting.number as upload_url %}
|
||||||
|
{% elif pres.document.type.slug == 'narrativeminutes' %}
|
||||||
|
{% url 'ietf.meeting.views.upload_session_narrativeminutes' session_id=session.pk num=session.meeting.number as upload_url %}
|
||||||
{% elif pres.document.type.slug == 'agenda' %}
|
{% elif pres.document.type.slug == 'agenda' %}
|
||||||
{% url 'ietf.meeting.views.upload_session_agenda' session_id=session.pk num=session.meeting.number as upload_url %}
|
{% url 'ietf.meeting.views.upload_session_agenda' session_id=session.pk num=session.meeting.number as upload_url %}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -106,6 +108,12 @@
|
||||||
Upload minutes
|
Upload minutes
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if not session.type_counter.narrativeminutes and session.group.acronym == "iesg" %}
|
||||||
|
<a class="btn btn-primary"
|
||||||
|
href="{% url 'ietf.meeting.views.upload_session_narrativeminutes' session_id=session.pk num=session.meeting.number %}">
|
||||||
|
Upload narrative minutes
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user|has_role:"Secretariat" and not session.type_counter.bluesheets or meeting.type.slug == 'interim' and can_manage_materials and not session.type_counter.bluesheets %}
|
{% if user|has_role:"Secretariat" and not session.type_counter.bluesheets or meeting.type.slug == 'interim' and can_manage_materials and not session.type_counter.bluesheets %}
|
||||||
<a class="btn btn-primary"
|
<a class="btn btn-primary"
|
||||||
|
|
34
ietf/templates/meeting/upload_session_narrativeminutes.html
Normal file
34
ietf/templates/meeting/upload_session_narrativeminutes.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{# Copyright The IETF Trust 2024, All Rights Reserved #}
|
||||||
|
{% load origin static django_bootstrap5 tz %}
|
||||||
|
{% block title %}
|
||||||
|
{% if narrativeminutes_sp %}
|
||||||
|
Revise
|
||||||
|
{% else %}
|
||||||
|
Upload
|
||||||
|
{% endif %}
|
||||||
|
Narrative Minutes for {{ session.meeting }} : {{ session.group.acronym }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% origin %}
|
||||||
|
<h1>
|
||||||
|
{% if narrativeminutes_sp %}
|
||||||
|
Revise
|
||||||
|
{% else %}
|
||||||
|
Upload
|
||||||
|
{% endif %}
|
||||||
|
Narrative Minutes for {{ session.meeting }}
|
||||||
|
<br>
|
||||||
|
<small class="text-body-secondary">{{ session.group.acronym }}
|
||||||
|
{% if session.name %}: {{ session.name }}{% endif %}
|
||||||
|
</small>
|
||||||
|
</h1>
|
||||||
|
{% if session_number %}
|
||||||
|
<h2>Session {{ session_number }} : {{ session.official_timeslotassignment.timeslot.time|timezone:session.meeting.time_zone|date:"D M-d-Y Hi" }}</h2>
|
||||||
|
{% endif %}
|
||||||
|
<form enctype="multipart/form-data" method="post" class="my-3">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
<button type="submit" class="btn btn-primary">Upload</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
Loading…
Reference in a new issue