Merged in [13957] from rjsparks@nostrum.com:

Moves Important Dates into the datatracker. Fixes #2338.
 - Legacy-Id: 13962
Note: SVN reference [13957] has been migrated to Git commit 2e6f26af23
This commit is contained in:
Henrik Levkowetz 2017-07-21 16:30:04 +00:00
commit 0d1feacb42
18 changed files with 10433 additions and 9758 deletions

View file

@ -2,7 +2,7 @@ from django.contrib import admin
from ietf.meeting.models import (Meeting, Room, Session, TimeSlot, Constraint, Schedule,
SchedTimeSessAssignment, ResourceAssociation, FloorPlan, UrlResource,
SessionPresentation)
SessionPresentation, ImportantDate, )
class UrlResourceAdmin(admin.ModelAdmin):
@ -125,3 +125,10 @@ class SessionPresentationAdmin(admin.ModelAdmin):
list_filter = ['session__meeting', 'document__group__acronym', ]
raw_id_fields = ['document', 'session', ]
admin.site.register(SessionPresentation, SessionPresentationAdmin)
class ImportantDateAdmin(admin.ModelAdmin):
model = ImportantDate
list_display = ['meeting', 'name', 'date']
ordering = ['-meeting__number','date',]
admin.site.register(ImportantDate,ImportantDateAdmin)

View file

@ -22,7 +22,8 @@ from ietf.ietfauth.utils import has_role, user_is_person
from ietf.liaisons.utils import get_person_for_user
from ietf.mailtrigger.utils import gather_address_lists
from ietf.person.models import Person
from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssignment
from ietf.meeting.models import Meeting, Schedule, TimeSlot, SchedTimeSessAssignment, ImportantDate
from ietf.name.models import ImportantDateName
from ietf.utils.history import find_history_active_at, find_history_replacements_active_at
from ietf.utils.mail import send_mail
from ietf.utils.pipe import pipe
@ -128,6 +129,13 @@ def get_meeting(num=None,type_in=['ietf',]):
else:
raise Http404("No such meeting found: %s" % num)
def get_ietf_meeting(num=None):
if num:
meetings = Meeting.objects.filter(number=num)
else:
meetings = Meeting.objects.filter(type='ietf',date__gte=datetime.datetime.today()-datetime.timedelta(days=31)).order_by('date')
return meetings.first()
def get_schedule(meeting, name=None):
if name is None:
schedule = meeting.agenda
@ -608,3 +616,9 @@ def update_interim_session_assignment(form):
timeslot=slot,
session=session,
schedule=session.meeting.agenda)
def populate_important_dates(meeting):
assert ImportantDate.objects.filter(meeting=meeting).exists() is False
assert meeting.type_id=='ietf'
for datename in ImportantDateName.objects.filter(used=True):
ImportantDate.objects.create(meeting=meeting,name=datename,date=meeting.date+datetime.timedelta(days=datename.default_offset_days))

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-20 06:10
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('name', '0025_add-important-dates'),
('meeting', '0052_floorplan_short'),
]
operations = [
migrations.CreateModel(
name='ImportantDate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField()),
('meeting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='meeting.Meeting')),
('name', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='name.ImportantDateName')),
],
options={'ordering': ['-meeting','date']},
),
]

View file

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-20 06:14
from __future__ import unicode_literals
from django.db import migrations
import datetime
def forward(apps,schema_editor):
Meeting = apps.get_model('meeting','Meeting')
ImportantDate = apps.get_model('meeting', 'ImportantDate')
ImportantDateName = apps.get_model('name', 'ImportantDateName')
for num in [98,99]:
meeting = Meeting.objects.get(number=num)
for datename in ImportantDateName.objects.filter(used=True):
ImportantDate.objects.create(meeting=meeting,name=datename,date=meeting.date+datetime.timedelta(days=datename.default_offset_days-1))
ImportantDate.objects.filter(meeting__number=99,name__slug='openreg').update(date='2017-03-27')
ImportantDate.objects.filter(meeting__number=98,name__slug='openreg').update(date='2016-11-28')
ImportantDate.objects.filter(meeting__number=98,name__slug='opensched').update(date='2016-12-19')
def reverse(apps,schema_editor):
ImportantDate = apps.get_model('meeting', 'ImportantDate')
ImportantDate.objects.filter(meeting__number__in=[99,98]).delete()
class Migration(migrations.Migration):
dependencies = [
('meeting', '0053_important_date'),
('name', '0026_popuulate-important-date-names'),
]
operations = [
migrations.RunPython(forward,reverse),
]

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-21 01:35
from __future__ import unicode_literals
from django.db import migrations
import datetime
def forward(apps, schema_editor):
ImportantDate = apps.get_model('meeting','ImportantDate')
Meeting = apps.get_model('meeting','Meeting')
for m in Meeting.objects.filter(type='ietf').exclude(importantdate__isnull=False):
if m.idsubmit_cutoff_day_offset_00 == m.idsubmit_cutoff_day_offset_01 :
ImportantDate.objects.create(
name_id='idcutoff',
meeting=m,
date=m.date-datetime.timedelta(days=m.idsubmit_cutoff_day_offset_00)
)
else:
ImportantDate.objects.create(
name_id='00cutoff',
meeting=m,
date=m.date-datetime.timedelta(days=m.idsubmit_cutoff_day_offset_00)
)
ImportantDate.objects.create(
name_id='01cutoff',
meeting=m,
date=m.date-datetime.timedelta(days=m.idsubmit_cutoff_day_offset_01)
)
def reverse(apps, schema_editor):
Meeting = apps.get_model('meeting','Meeting')
# This is a simple reverse implementation. If a reverse is needed a significant
# time after the forward is run (as in multiple meeting cycles), a more considered
# implemenation should be created reflecting any assumptions that have changed in
# that time.
for m in Meeting.objects.filter(type='ietf'):
if m.importantdate_set.count()==2:
m.importantdate_set.all().delete()
class Migration(migrations.Migration):
dependencies = [
('meeting', '0054_earlier_important_dates'),
]
operations = [
migrations.RunPython(forward,reverse)
]

View file

@ -21,7 +21,7 @@ from ietf.dbtemplate.models import DBTemplate
from ietf.doc.models import Document
from ietf.group.models import Group
from ietf.group.utils import can_manage_materials
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName
from ietf.name.models import MeetingTypeName, TimeSlotTypeName, SessionStatusName, ConstraintName, RoomResourceName, ImportantDateName
from ietf.person.models import Person
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
from ietf.utils.text import xslugify
@ -109,13 +109,25 @@ class Meeting(models.Model):
def get_00_cutoff(self):
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day, tzinfo=pytz.utc)
cutoff_date = start_date - datetime.timedelta(days=self.idsubmit_cutoff_day_offset_00)
importantdate = self.importantdate_set.filter(name_id='idcutoff').first()
if not importantdate:
importantdate = self.importantdate_set.filter(name_id='00cutoff').first()
if importantdate:
cutoff_date = importantdate.date
else:
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
cutoff_time = cutoff_date + self.idsubmit_cutoff_time_utc
return cutoff_time
def get_01_cutoff(self):
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day, tzinfo=pytz.utc)
cutoff_date = start_date - datetime.timedelta(days=self.idsubmit_cutoff_day_offset_01)
importantdate = self.importantdate_set.filter(name_id='idcutoff').first()
if not importantdate:
importantdate = self.importantdate_set.filter(name_id='01cutoff').first()
if importantdate:
cutoff_date = importantdate.date
else:
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
cutoff_time = cutoff_date + self.idsubmit_cutoff_time_utc
return cutoff_time
@ -1100,3 +1112,10 @@ class Session(models.Model):
self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename)
return self._agenda_file
class ImportantDate(models.Model):
meeting = models.ForeignKey(Meeting)
date = models.DateField()
name = models.ForeignKey(ImportantDateName)
class Meta:
ordering = ["-meeting","date", ]

View file

@ -9,7 +9,7 @@ from ietf import api
from ietf.meeting.models import ( Meeting, ResourceAssociation, Constraint, Room, Schedule, Session,
TimeSlot, SchedTimeSessAssignment, SessionPresentation, FloorPlan,
UrlResource)
UrlResource, ImportantDate )
from ietf.name.resources import MeetingTypeNameResource
class MeetingResource(ModelResource):
@ -281,3 +281,21 @@ class UrlResourceResource(ModelResource):
}
api.meeting.register(UrlResourceResource())
from ietf.name.resources import ImportantDateNameResource
class ImportantDateResource(ModelResource):
meeting = ToOneField(MeetingResource, 'meeting')
name = ToOneField(ImportantDateNameResource, 'name')
class Meta:
queryset = ImportantDate.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'importantdate'
filtering = {
"id": ALL,
"date": ALL,
"meeting": ALL_WITH_RELATIONS,
"name": ALL_WITH_RELATIONS,
}
api.meeting.register(ImportantDateResource())

View file

@ -22,7 +22,7 @@ from ietf.group.models import Group, Role
from ietf.meeting.helpers import can_approve_interim_request, can_view_interim_request
from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_minutes_reminder
from ietf.meeting.helpers import send_interim_minutes_reminder, populate_important_dates
from ietf.meeting.models import Session, TimeSlot, Meeting
from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting
from ietf.meeting.utils import finalize
@ -408,6 +408,14 @@ class MeetingTests(TestCase):
self.assertTrue("agenda" in unicontent(r))
self.assertTrue(session.group.acronym in unicontent(r))
def test_important_dates(self):
meeting=MeetingFactory(type_id='ietf')
populate_important_dates(meeting)
url = urlreverse('ietf.meeting.views.important_dates',kwargs={'num':meeting.number})
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(str(meeting.importantdate_set.first().date) in unicontent(r))
class EditTests(TestCase):
def setUp(self):
# make sure we have the colors of the area

View file

@ -90,6 +90,8 @@ type_ietf_only_patterns_id_optional = [
url(r'^proceedings/attendees/$', views.proceedings_attendees),
url(r'^proceedings/overview/$', views.proceedings_overview),
url(r'^proceedings/progress-report/$', views.proceedings_progress_report),
url(r'^important-dates/$', views.important_dates),
]
urlpatterns = [

View file

@ -46,7 +46,7 @@ from ietf.meeting.helpers import build_all_agenda_slices, get_wg_name_list
from ietf.meeting.helpers import get_all_assignments_from_schedule
from ietf.meeting.helpers import get_modified_from_assignments
from ietf.meeting.helpers import get_wg_list, find_ads_for_meeting
from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, get_meetings
from ietf.meeting.helpers import get_meeting, get_schedule, agenda_permissions, get_meetings, get_ietf_meeting
from ietf.meeting.helpers import preprocess_assignments_for_agenda, read_agenda_file, read_session_file
from ietf.meeting.helpers import convert_draft_to_pdf, get_earliest_session_date
from ietf.meeting.helpers import can_view_interim_request, can_approve_interim_request
@ -416,13 +416,13 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
".csv": "text/csv; charset=%s"%settings.DEFAULT_CHARSET,
}
meetings = get_meetings(num)
# We do not have the appropriate data in the datatracker for IETF 64 and earlier.
# So that we're not producing misleading pages...
meeting = meetings.first()
if not meetings.exists() or (meeting.number.isdigit() and int(meeting.number) <= 64 and not meeting.agenda.assignments.exists()):
assert num is None or num.isdigit()
meeting = get_ietf_meeting(num)
if not meeting or (meeting.number.isdigit() and int(meeting.number) <= 64 and not meeting.agenda.assignments.exists()):
if ext == '.html':
return HttpResponseRedirect( 'https://www.ietf.org/proceedings/%s' % num )
else:
@ -2202,4 +2202,19 @@ def api_import_recordings(request, number):
else:
return HttpResponse(status=405)
def important_dates(request, num=None):
assert num is None or num.isdigit()
meeting = get_ietf_meeting(num)
if not meeting:
raise Http404
base_num = int(meeting.number)
meetings=[meeting]
for i in range(1,3):
future_meeting = get_ietf_meeting(base_num+i)
if future_meeting:
meetings.append(future_meeting)
context={'meetings':meetings}
return render(request, 'meeting/important-dates.html', context)

View file

@ -5,7 +5,7 @@ from ietf.name.models import (
DBTemplateTypeName, DocRelationshipName,
DocReminderTypeName, DocTagName, DocTypeName, DraftSubmissionStateName,
FeedbackTypeName, FormalLanguageName, GroupMilestoneStateName, GroupStateName, GroupTypeName,
IntendedStdLevelName, IprDisclosureStateName, IprEventTypeName, IprLicenseTypeName,
ImportantDateName, IntendedStdLevelName, IprDisclosureStateName, IprEventTypeName, IprLicenseTypeName,
LiaisonStatementEventTypeName, LiaisonStatementPurposeName, LiaisonStatementState,
LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
@ -40,6 +40,11 @@ class CountryNameAdmin(NameAdmin):
inlines = [CountryAliasInline]
admin.site.register(CountryName, CountryNameAdmin)
class ImportantDateNameAdmin(NameAdmin):
list_display = ["slug", "name", "desc", "used", "default_offset_days"]
ordering = ('-used','default_offset_days',)
admin.site.register(ImportantDateName,ImportantDateNameAdmin)
admin.site.register(BallotPositionName, NameAdmin)
admin.site.register(ConstraintName, NameAdmin)
admin.site.register(ContinentName, NameAdmin)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-19 07:07
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('name', '0024_merge_20170606_1320'),
]
operations = [
migrations.CreateModel(
name='ImportantDateName',
fields=[
('slug', models.CharField(max_length=32, primary_key=True, serialize=False)),
('name', models.CharField(max_length=255)),
('desc', models.TextField(blank=True)),
('used', models.BooleanField(default=True)),
('order', models.IntegerField(default=0)),
('default_offset_days', models.SmallIntegerField()),
],
options={
'ordering': ['order', 'name'],
'abstract': False,
},
),
]

View file

@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-07-19 07:07
from __future__ import unicode_literals
from django.db import migrations
def forward(apps,schema_editor):
ImportantDateName = apps.get_model('name','ImportantDateName')
# offsets below assume meetings start on Saturday
ImportantDateName.objects.create(
slug='openreg',
name='Registration Opens',
desc='IETF Online Registration Opens',
used=True,
order=0,
default_offset_days= - (13*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='opensched',
name='Scheduling Opens',
desc='Working Group and BOF scheduling begins',
used=True,
order=0,
default_offset_days= - (13*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='cutoffwgreq',
name='Cut-off WG scheduling Requests',
desc='Cut-off date for requests to schedule Working Group Meetings at UTC 23:59',
used=True,
order=0,
default_offset_days= - (6*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='cutoffbofreq',
name='Cut-off BOF scheduling Requests',
desc='Cut-off date for BOF proposal requests to Area Directors at UTC 23:59',
used=True,
order=0,
default_offset_days= - (6*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='cutoffbofapprove',
name='Cut-off BOF approval',
desc='Cut-off date for Area Directors to approve BOFs at UTC 23:59',
used=True,
order=0,
default_offset_days= - (5*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='prelimagenda',
name='Preliminary Agenda',
desc='Preliminary Agenda published for comment',
used=True,
order=0,
default_offset_days= - (4*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='cutoffresched',
name='Cut-off Reschedule Requests',
desc='Cut-off date for requests to reschedule Working Group or BOF meetings UTC 23:59',
used=True,
order=0,
default_offset_days= - (4*7 - 4), # Wednesday
)
ImportantDateName.objects.create(
slug='finalagenda',
name='Final Agenda',
desc='Final agenda to be published',
used=True,
order=0,
default_offset_days= - (3*7 + 1), # Friday
)
# NOTE idcutoff is targetted for the current situation where we
# only have one draft cutoff date.. If we ever go back to a separate
# -01 vs -00 cutoff, this one will need to be marked unused and the
# two at then end should be marked used.
ImportantDateName.objects.create(
slug='idcutoff',
name='ID Cutoff',
desc='Internet Draft submission cut-off (for all drafts, including -00) by UTC 23:59', # TODO - get these times out of there? What if the policy changes? Old meetings should show the time used for that meeting.
used=True,
order=0,
default_offset_days= - (2*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='draftwgagenda',
name='Draft Working Group Agendas',
desc='Draft Working Group agendas due by UTC 23:59',
used=True,
order=0,
default_offset_days= - (2*7 - 4), # Wednesday
)
ImportantDateName.objects.create(
slug='earlybird',
name='Earlybird cutoff',
desc='Early Bird registration and payment cut-off at UTC 23:59',
used=True,
order=0,
default_offset_days= - (1*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='revwgagenda',
name='Revised Working Group Agendas',
desc='Revised Working Group agendas due by UTC 23:59',
used=True,
order=0,
default_offset_days= - (1*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='cutoffcancel',
name='Registration Cancellation Cut-off',
desc='Registration cancellation cut-off at UTC 23:59',
used=True,
order=0,
default_offset_days= - (1*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='cutoffpre',
name='Pre-Registration Cutoff',
desc='Final Pre-Registration and Pre-Payment cut-off at 17:00 local meeting time',
used=True,
order=0,
default_offset_days= - (0*7 + 1), # Friday
)
ImportantDateName.objects.create(
slug='procsub',
name='Proceedings Submission Cut-off',
desc='Proceedings submission cutoff date by UTC 23:59',
used=True,
order=0,
default_offset_days= (6 + 3*7), # Friday
)
ImportantDateName.objects.create(
slug='revsub',
name='Proceedings Submission Revision Cut-off',
desc='Proceedings submission corrections cutoff date by UTC 23:59',
used=True,
order=0,
default_offset_days= (2 + 7*7), # Monday
)
ImportantDateName.objects.create(
slug='00cutoff',
name='00 ID Cutoff',
desc='Internet Draft submission cut-off for -00 drafts by UTC 23:59',
used=False,
order=0,
default_offset_days= - (3*7 - 2), # Monday
)
ImportantDateName.objects.create(
slug='01cutoff',
name='01 ID Cutoff',
desc='Internet Draft submission cut-off for revised (-01 and above) drafts by UTC 23:59',
used=False,
order=0,
default_offset_days= - (2*7 - 2), # Monday
)
def reverse(apps,schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('name', '0025_add-important-dates'),
('meeting','0053_important_date')
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -108,3 +108,5 @@ class CountryName(NameModel):
continent = models.ForeignKey(ContinentName)
in_eu = models.BooleanField(verbose_name="In EU", default=False)
class ImportantDateName(NameModel):
default_offset_days = models.SmallIntegerField()

View file

@ -15,7 +15,7 @@ from ietf.name.models import (TimeSlotTypeName, GroupStateName, DocTagName, Inte
LiaisonStatementTagName, FeedbackTypeName, LiaisonStatementState, StreamName,
BallotPositionName, DBTemplateTypeName, NomineePositionStateName,
ReviewRequestStateName, ReviewTypeName, ReviewResultName,
TopicAudienceName, FormalLanguageName, ContinentName, CountryName)
TopicAudienceName, FormalLanguageName, ContinentName, CountryName, ImportantDateName)
class TimeSlotTypeNameResource(ModelResource):
@ -519,3 +519,20 @@ class CountryNameResource(ModelResource):
}
api.name.register(CountryNameResource())
class ImportantDateNameResource(ModelResource):
class Meta:
queryset = ImportantDateName.objects.all()
serializer = api.Serializer()
cache = SimpleCache()
#resource_name = 'importantdatename'
filtering = {
"slug": ALL,
"name": ALL,
"desc": ALL,
"used": ALL,
"order": ALL,
"default_offset_days": ALL,
}
api.name.register(ImportantDateNameResource())

View file

@ -15,7 +15,7 @@ from django.utils.functional import curry
from ietf.ietfauth.utils import role_required
from ietf.utils.mail import send_mail
from ietf.meeting.forms import duration_string
from ietf.meeting.helpers import get_meeting, make_materials_directories
from ietf.meeting.helpers import get_meeting, make_materials_directories, populate_important_dates
from ietf.meeting.models import Meeting, Session, Room, TimeSlot, SchedTimeSessAssignment, Schedule
from ietf.name.models import SessionStatusName
from ietf.group.models import Group, GroupEvent
@ -316,6 +316,8 @@ def add(request):
meeting.session_request_lock_message = previous_meeting.session_request_lock_message
meeting.save()
populate_important_dates(meeting)
# copy special sessions from previous meeting
build_nonsession(meeting,schedule)

View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2017, All Rights Reserved #}
{% load origin %}
{% load ietf_filters staticfiles %}
{% block title %}IETF {{meeting.number}} : Important Dates{% endblock %}
{% block content %}
{% origin %}
<h2>Important Dates</h2>
{% for meeting in meetings %}
<h3>IETF {{meeting.number}} : {{ meeting.date}}, {{meeting.city}}, {{meeting.country}}</h3>
<ul>
{% for d in meeting.importantdate_set.all %}
<li> <strong>{{d.date}} ({% if d.name.slug == 'openreg' %}Week of{% else %}{{d.date|date:'l'}}{% endif %}):</strong> {{d.name.desc}}.
{% if d.name.slug == 'opensched' or d.name.slug == 'cutoffwgreq' %}
To request a Working Group session, use the <a href="{% url 'ietf.secr.sreq.views.main' %}">IETF Meeting Session Request Tool</a>.
{% endif %}
{% if d.name.slug == 'cutoffbofreq' %}
To request a BOF, please see instructions on <a href="https://www.ietf.org/iesg/bof-procedures.html">Requesting a BOF</a>.
{% endif %}
{% if d.name.slug == 'idcutoff' %}
Upload using the <a href="{% url 'ietf.submit.views.upload_submission' %}">ID Submission Tool</a>.
{% endif %}
{% if d.name.slug == 'draftwgagenda' or d.name.slug == 'revwgagenda' or d.name.slug == 'procsub' or d.name.slug == 'revslug' %}
Upload using the <a href="{% url 'ietf.meeting.views.materials' num=meeting.number %}">Meeting Materials Management Tool</a>.
{% endif %}
</li>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}