Added 4 new fields to the meeting class, to hold draft submission cutoff information for the meeting. On request, changed the presentation of the cutoff times to show the last submission time, rather than the beginning of the cutoff time. Changed the cutoff hour format to support full timedelta information, not just a given hour (this was necessary in order to be able to set the requested 23:59:59 submission stop time).

- Legacy-Id: 9107
This commit is contained in:
Henrik Levkowetz 2015-02-21 20:48:29 +00:00
parent 01ade795d3
commit 298adf3e20
6 changed files with 156 additions and 40 deletions

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import timedelta.fields
class Migration(migrations.Migration):
dependencies = [
('meeting', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='meeting',
name='idsubmit_cutoff_day_offset_00',
field=models.IntegerField(default=20, help_text=b'The number of days before the meeting start date when the submission of -00 drafts will be closed.'),
preserve_default=True,
),
migrations.AddField(
model_name='meeting',
name='idsubmit_cutoff_day_offset_01',
field=models.IntegerField(default=13, help_text=b'The number of days before the meeting start date when the submission of -01 drafts etc. will be closed.'),
preserve_default=True,
),
migrations.AddField(
model_name='meeting',
name='idsubmit_cutoff_time_utc',
field=timedelta.fields.TimedeltaField(default=86399.0, help_text=b'The time of day (UTC) after which submission will be closed. Use for example 23 hours, 59 minutes, 59 seconds.'),
preserve_default=True,
),
migrations.AddField(
model_name='meeting',
name='idsubmit_cutoff_warning_days',
field=timedelta.fields.TimedeltaField(default=1814400.0, help_text=b'How long before the 00 cutoff to start showing cutoff warnings. Use for example 21 days or 3 weeks.'),
preserve_default=True,
),
]

View file

@ -15,6 +15,7 @@ from django.conf import settings
# mostly used by json_dict()
#from django.template.defaultfilters import slugify, date as date_format, time as time_format
from django.template.defaultfilters import date as date_format
import timedelta
from timedeltafield import TimedeltaField
@ -55,6 +56,19 @@ class Meeting(models.Model):
# more than one timezone, and the pytz module doesn't provide timezone
# lookup information for all relevant city/country combinations.
time_zone = models.CharField(blank=True, max_length=255, choices=timezones)
idsubmit_cutoff_day_offset_00 = models.IntegerField(blank=True,
default=settings.IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_00,
help_text = "The number of days before the meeting start date when the submission of -00 drafts will be closed.")
idsubmit_cutoff_day_offset_01 = models.IntegerField(blank=True,
default=settings.IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_01,
help_text = "The number of days before the meeting start date when the submission of -01 drafts etc. will be closed.")
idsubmit_cutoff_time_utc = timedelta.fields.TimedeltaField(blank=True,
default=settings.IDSUBMIT_DEFAULT_CUTOFF_TIME_UTC,
help_text = "The time of day (UTC) after which submission will be closed. Use for example 23 hours, 59 minutes, 59 seconds.")
idsubmit_cutoff_warning_days = timedelta.fields.TimedeltaField(blank=True,
default=settings.IDSUBMIT_DEFAULT_CUTOFF_WARNING_DAYS,
help_text = "How long before the 00 cutoff to start showing cutoff warnings. Use for example 21 days or 3 weeks.")
#
venue_name = models.CharField(blank=True, max_length=255)
venue_addr = models.TextField(blank=True)
break_area = models.CharField(blank=True, max_length=255)
@ -83,17 +97,38 @@ class Meeting(models.Model):
def end_date(self):
return self.get_meeting_date(5)
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)
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)
cutoff_time = cutoff_date + self.idsubmit_cutoff_time_utc
return cutoff_time
def get_reopen_time(self):
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day)
local_tz = pytz.timezone(self.time_zone)
local_date = local_tz.localize(start_date)
reopen_time = local_date + self.idsubmit_cutoff_time_utc
return reopen_time
@classmethod
def get_current_meeting(cls, type="ietf"):
return cls.objects.all().filter(type=type).order_by('-date').first()
@classmethod
def get_first_cut_off(cls):
date = cls.objects.all().filter(type="ietf").order_by('-date')[0].date
offset = datetime.timedelta(days=settings.FIRST_CUTOFF_DAYS)
return date - offset
meeting = cls.get_current_meeting()
return meeting.get_00_cutoff()
@classmethod
def get_second_cut_off(cls):
date = cls.objects.all().filter(type="ietf").order_by('-date')[0].date
offset = datetime.timedelta(days=settings.SECOND_CUTOFF_DAYS)
return date - offset
meeting = cls.get_current_meeting()
return meeting.get_01_cutoff()
@classmethod
def get_ietf_monday(cls):
@ -115,11 +150,11 @@ class Meeting(models.Model):
# the various dates are currently computed
def get_submission_start_date(self):
return self.date + datetime.timedelta(days=settings.SUBMISSION_START_DAYS)
return self.date + datetime.timedelta(days=settings.MEETING_MATERIALS_SUBMISSION_START_DAYS)
def get_submission_cut_off_date(self):
return self.date + datetime.timedelta(days=settings.SUBMISSION_CUTOFF_DAYS)
return self.date + datetime.timedelta(days=settings.MEETING_MATERIALS_SUBMISSION_CUTOFF_DAYS)
def get_submission_correction_date(self):
return self.date + datetime.timedelta(days=settings.SUBMISSION_CORRECTION_DAYS)
return self.date + datetime.timedelta(days=settings.MEETING_MATERIALS_SUBMISSION_CORRECTION_DAYS)
def get_schedule_by_name(self, name):
return self.schedule_set.filter(name=name).first()

View file

@ -13,6 +13,10 @@
"date": "2010-11-07",
"type": "ietf",
"venue_addr": ""
"idsubmit_cutoff_day_offset_00": 20,
"idsubmit_cutoff_day_offset_01": 13,
"idsubmit_cutoff_time_utc": "23:59:59",
"idsubmit_cutoff_warning_days": "21 days, 0:00:00",
}
},
{
@ -29,6 +33,10 @@
"date": "2011-03-27",
"type": "ietf",
"venue_addr": ""
"idsubmit_cutoff_day_offset_00": 20,
"idsubmit_cutoff_day_offset_01": 13,
"idsubmit_cutoff_time_utc": "23:59:59",
"idsubmit_cutoff_warning_days": "21 days, 0:00:00",
}
},
{
@ -45,6 +53,10 @@
"date": "2011-07-24",
"type": "ietf",
"venue_addr": ""
"idsubmit_cutoff_day_offset_00": 20,
"idsubmit_cutoff_day_offset_01": 13,
"idsubmit_cutoff_time_utc": "23:59:59",
"idsubmit_cutoff_warning_days": "21 days, 0:00:00",
}
},
{
@ -61,6 +73,10 @@
"date": "2011-11-13",
"type": "ietf",
"venue_addr": ""
"idsubmit_cutoff_day_offset_00": 20,
"idsubmit_cutoff_day_offset_01": 13,
"idsubmit_cutoff_time_utc": "23:59:59",
"idsubmit_cutoff_warning_days": "21 days, 0:00:00",
}
},
{
@ -77,6 +93,10 @@
"date": "2012-03-25",
"type": "ietf",
"venue_addr": ""
"idsubmit_cutoff_day_offset_00": 20,
"idsubmit_cutoff_day_offset_01": 13,
"idsubmit_cutoff_time_utc": "23:59:59",
"idsubmit_cutoff_warning_days": "21 days, 0:00:00",
}
}
]

View file

@ -49,7 +49,12 @@ class MainTestCase(TestCase):
url = reverse('meetings_add')
post_data = dict(number=number,city='Toronto',date='2014-07-20',country='CA',
time_zone='America/New_York',venue_name='Hilton',
venue_addr='100 First Ave')
venue_addr='100 First Ave',
idsubmit_cutoff_day_offset_00=13,
idsubmit_cutoff_day_offset_01=20,
idsubmit_cutoff_time_utc =datetime.timedelta(hours=23, minutes=59, seconds=59),
idsubmit_cutoff_warning_days =datetime.timedelta(days=21),
)
self.client.login(username='secretary', password='secretary+password')
response = self.client.post(url, post_data, follow=True)
self.assertEqual(response.status_code, 200)
@ -59,9 +64,15 @@ class MainTestCase(TestCase):
"Edit Meeting"
Meeting.objects.create(number=1,
type_id='ietf',
date=datetime.datetime(2014,7,20))
date=datetime.datetime(2014,7,20),
)
url = reverse('meetings_edit_meeting',kwargs={'meeting_id':1})
post_data = dict(number='1',date='2014-07-20',city='Toronto')
post_data = dict(number='1',date='2014-07-20',city='Toronto',
idsubmit_cutoff_day_offset_00=13,
idsubmit_cutoff_day_offset_01=20,
idsubmit_cutoff_time_utc =datetime.timedelta(hours=23, minutes=59, seconds=59),
idsubmit_cutoff_warning_days =datetime.timedelta(days=21),
)
self.client.login(username="secretary", password="secretary+password")
response = self.client.post(url, post_data,follow=True)
self.assertEqual(response.status_code, 200)

View file

@ -19,6 +19,8 @@ LOG_DIR = '/var/log/datatracker'
import sys
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
import datetime
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@ -362,14 +364,15 @@ IDSUBMIT_TO_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_FROM_EMAIL = 'internet-drafts@ietf.org'
IDSUBMIT_ANNOUNCE_LIST_EMAIL = 'i-d-announce@ietf.org'
FIRST_CUTOFF_DAYS = 19 # Days from meeting to cut off dates on submit
SECOND_CUTOFF_DAYS = 12
CUTOFF_HOUR = 00 # midnight UTC
CUTOFF_WARNING_DAYS = 21 # Number of days before cutoff to start showing the cutoff date
# Days from meeting to day of cut off dates on submit -- cutoff_time_utc is added to this
IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_00 = 20
IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_01 = 13
IDSUBMIT_DEFAULT_CUTOFF_TIME_UTC = datetime.timedelta(hours=23, minutes=59, seconds=59)
IDSUBMIT_DEFAULT_CUTOFF_WARNING_DAYS = datetime.timedelta(days=21)
SUBMISSION_START_DAYS = -90
SUBMISSION_CUTOFF_DAYS = 33
SUBMISSION_CORRECTION_DAYS = 52
MEETING_MATERIALS_SUBMISSION_START_DAYS = -90
MEETING_MATERIALS_SUBMISSION_CUTOFF_DAYS = 33
MEETING_MATERIALS_SUBMISSION_CORRECTION_DAYS = 52
INTERNET_DRAFT_DAYS_TO_EXPIRE = 185

View file

@ -1,5 +1,6 @@
import os
import datetime
import pytz
from django import forms
from django.conf import settings
@ -40,29 +41,36 @@ class UploadForm(forms.Form):
self.parsed_draft = None
def set_cutoff_warnings(self):
from datetime import timedelta
now = datetime.datetime.utcnow()
first_cut_off = Meeting.get_first_cut_off()
second_cut_off = Meeting.get_second_cut_off()
ietf_monday = Meeting.get_ietf_monday()
if now.date() >= (first_cut_off-timedelta(days=settings.CUTOFF_WARNING_DAYS)) and now.date() < first_cut_off:
self.cutoff_warning = ( 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s at %02sh UTC.<br/>' % (first_cut_off, settings.CUTOFF_HOUR) +
'The pre-meeting cut-off date for revisions to existing documents is %s at %02sh UTC.<br/>' % (second_cut_off, settings.CUTOFF_HOUR) )
elif now.date() >= first_cut_off and now.date() < second_cut_off: # We are in the first_cut_off
if now.date() == first_cut_off and now.hour < settings.CUTOFF_HOUR:
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) is %s, at %02sh UTC. After that, you will not be able to submit a new document until %s, at %sh UTC' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, )
now = datetime.datetime.now(pytz.utc)
meeting = Meeting.get_current_meeting()
#
cutoff_00 = meeting.get_00_cutoff()
cutoff_01 = meeting.get_01_cutoff()
reopen = meeting.get_reopen_time()
#
cutoff_00_str = cutoff_00.strftime("%Y-%m-%d %H:%M %Z")
cutoff_01_str = cutoff_01.strftime("%Y-%m-%d %H:%M %Z")
reopen_str = reopen.strftime("%Y-%m-%d %H:%M %Z")
if now.date() >= (cutoff_00.date() - meeting.idsubmit_cutoff_warning_days) and now <= cutoff_00:
self.cutoff_warning = ( 'The last submission time for new documents (i.e., version -00 Internet-Drafts) before %s is %s.<br/><br/>' % (meeting, cutoff_00_str) +
'The last submission time for revisions to existing documents before %s is %s.<br/>' % (meeting, cutoff_01_str) )
elif now.date() >= cutoff_00.date() and now <= cutoff_01:
# We are in the first_cut_off
if now < cutoff_00:
self.cutoff_warning = (
'The last submission time for new documents (i.e., version -00 Internet-Drafts) before the meeting is %s.<br/>'
'After that, you will not be able to submit a new document until after %s (IETF-meeting local time)' % (cutoff_00_str, reopen_str, ))
else: # No 00 version allowed
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %sh UTC. You will not be able to submit a new document until %s, at %sh UTC.<br>You can still submit a version -01 or higher Internet-Draft until %sh UTC, %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, second_cut_off, )
self.cutoff_warning = (
'The last submission time for new documents (i.e., version -00 Internet-Drafts) was %s.<br/>'
'You will not be able to submit a new document until after %s (IETF-meeting local time).<br/><br>'
'You can still submit a version -01 or higher Internet-Draft until %s' % (cutoff_00_str, reopen_str, cutoff_01_str, ))
self.in_first_cut_off = True
elif now.date() >= second_cut_off and now.date() < ietf_monday:
if now.date() == second_cut_off and now.hour < settings.CUTOFF_HOUR: # We are in the first_cut_off yet
self.cutoff_warning = 'The pre-meeting cut-off date for new documents (i.e., version -00 Internet-Drafts) was %s at %02sh UTC. You will not be able to submit a new document until %s, at %02sh UTC.<br>The I-D submission tool will be shut down at %02sh UTC today, and reopened at %02sh UTC on %s' % (first_cut_off, settings.CUTOFF_HOUR, ietf_monday, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, settings.CUTOFF_HOUR, ietf_monday)
self.in_first_cut_off = True
else: # Completely shut down of the tool
self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.<br>The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday)
self.shutdown = True
elif now > cutoff_01 and now < reopen:
self.cutoff_warning = (
'The last submission time for the I-D submission was %s.<br/><br>'
'The I-D submission tool will be reopened after %s (IETF-meeting local time).' % (cutoff_01_str, reopen_str))
self.shutdown = True
def clean_file(self, field_name, parser_class):
f = self.cleaned_data[field_name]
if not f: