diff --git a/ietf/meeting/factories.py b/ietf/meeting/factories.py index 94c5a21f5..a4c82275c 100644 --- a/ietf/meeting/factories.py +++ b/ietf/meeting/factories.py @@ -20,7 +20,7 @@ class MeetingFactory(factory.DjangoModelFactory): idsubmit_cutoff_day_offset_00 = 13 idsubmit_cutoff_day_offset_01 = 13 idsubmit_cutoff_time_utc = datetime.timedelta(0, 86399) - idsubmit_cutoff_warning_days = 21 + idsubmit_cutoff_warning_days = datetime.timedelta(days=21) venue_name = factory.Faker('sentence') venue_addr = factory.Faker('address') break_area = factory.Faker('sentence') diff --git a/ietf/meeting/migrations/0043_auto_20161219_1345.py b/ietf/meeting/migrations/0043_auto_20161219_1345.py new file mode 100644 index 000000000..641d64e22 --- /dev/null +++ b/ietf/meeting/migrations/0043_auto_20161219_1345.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0042_auto_20161207_1137'), + ] + + operations = [ + migrations.AddField( + model_name='meeting', + name='xidsubmit_cutoff_time_utc', + field=models.DurationField(default=datetime.timedelta(0, 86399), help_text=b'The time of day (UTC) after which submission will be closed. Use for example 23 hours, 59 minutes, 59 seconds.', blank=True), + ), + migrations.AddField( + model_name='meeting', + name='xidsubmit_cutoff_warning_days', + field=models.DurationField(default=datetime.timedelta(21), help_text=b'How long before the 00 cutoff to start showing cutoff warnings. Use for example 21 days or 3 weeks.', blank=True), + ), + migrations.AddField( + model_name='session', + name='xrequested_duration', + field=models.DurationField(default=datetime.timedelta(0)), + ), + migrations.AddField( + model_name='timeslot', + name='xduration', + field=models.DurationField(default=datetime.timedelta(0)), + ), + ] diff --git a/ietf/meeting/migrations/0044_convert_timedelta_data_to_duration.py b/ietf/meeting/migrations/0044_convert_timedelta_data_to_duration.py new file mode 100644 index 000000000..385dfc040 --- /dev/null +++ b/ietf/meeting/migrations/0044_convert_timedelta_data_to_duration.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + +def forward(apps, schema_editor): + Meeting = apps.get_model('meeting', 'Meeting') + TimeSlot = apps.get_model('meeting', 'TimeSlot') + Session = apps.get_model('meeting', 'Session') + import sys + sys.stderr.write("\n") + sys.stderr.write("Setting duration fields in Meeting objects...\n") + for m in Meeting.objects.all(): + if m.xidsubmit_cutoff_time_utc != m.idsubmit_cutoff_time_utc: + m.xidsubmit_cutoff_time_utc = m.idsubmit_cutoff_time_utc + m.xidsubmit_cutoff_warning_days = m.idsubmit_cutoff_warning_days + m.save() + sys.stderr.write("Setting duration fields in TimeSlot objects...\n") + for t in TimeSlot.objects.all(): + if t.xduration != t.duration: + t.xduration = t.duration + t.save() + sys.stderr.write("Setting duration fields in Session objects...\n") + for s in Session.objects.all(): + if s.xrequested_duration != s.requested_duration: + s.xrequested_duration = s.requested_duration + s.save() + +def backward(apps, schema_editor): + Meeting = apps.get_model('meeting', 'Meeting') + TimeSlot = apps.get_model('meeting', 'TimeSlot') + Session = apps.get_model('meeting', 'Session') + import sys + sys.stderr.write("\n") + sys.stderr.write("Setting timedelta fields in Meeting objects...\n") + for m in Meeting.objects.all(): + if m.idsubmit_cutoff_time_utc != m.xidsubmit_cutoff_time_utc: + m.idsubmit_cutoff_time_utc = m.xidsubmit_cutoff_time_utc + m.idsubmit_cutoff_warning_days = m.xidsubmit_cutoff_warning_days + m.save() + sys.stderr.write("Setting timedelta fields in TimeSlot objects...\n") + for t in TimeSlot.objects.all(): + if t.duration != t.xduration: + t.duration = t.xduration + t.save() + sys.stderr.write("Setting timedelta fields in Session objects...\n") + for s in Session.objects.all(): + if s.requested_duration != s.xrequested_duration: + s.requested_duration = s.xrequested_duration + s.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0043_auto_20161219_1345'), + ] + + operations = [ + migrations.RunPython(forward, backward) + ] diff --git a/ietf/meeting/migrations/0045_switch_timedelta_fields_to_duration.py b/ietf/meeting/migrations/0045_switch_timedelta_fields_to_duration.py new file mode 100644 index 000000000..cd7e6cb94 --- /dev/null +++ b/ietf/meeting/migrations/0045_switch_timedelta_fields_to_duration.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('meeting', '0044_convert_timedelta_data_to_duration'), + ] + + operations = [ + migrations.RemoveField( + model_name='meeting', + name='idsubmit_cutoff_time_utc', + ), + migrations.RenameField( + model_name='meeting', + old_name='xidsubmit_cutoff_time_utc', + new_name='idsubmit_cutoff_time_utc', + ), + # + migrations.RemoveField( + model_name='meeting', + name='idsubmit_cutoff_warning_days', + ), + migrations.RenameField( + model_name='meeting', + old_name='xidsubmit_cutoff_warning_days', + new_name='idsubmit_cutoff_warning_days', + ), + # + migrations.RemoveField( + model_name='timeslot', + name='duration', + ), + migrations.RenameField( + model_name='timeslot', + old_name='xduration', + new_name='duration', + ), + # + migrations.RemoveField( + model_name='session', + name='requested_duration', + ), + migrations.RenameField( + model_name='session', + old_name='xrequested_duration', + new_name='requested_duration', + ), + ] diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 2062901f7..e36884b6a 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -8,8 +8,6 @@ import os import sys import re import string -import timedelta -from timedeltafield import TimedeltaField import debug # pyflakes:ignore @@ -68,10 +66,10 @@ class Meeting(models.Model): 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, + idsubmit_cutoff_time_utc = models.DurationField(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, + idsubmit_cutoff_warning_days = models.DurationField(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.") submission_start_day_offset = models.IntegerField(blank=True, @@ -415,7 +413,7 @@ class TimeSlot(models.Model): type = models.ForeignKey(TimeSlotTypeName) name = models.CharField(max_length=255) time = models.DateTimeField() - duration = TimedeltaField() + duration = models.DurationField(default=datetime.timedelta(0)) location = models.ForeignKey(Room, blank=True, null=True) show_location = models.BooleanField(default=True, help_text="Show location in agenda.") sessions = models.ManyToManyField('Session', related_name='slots', through='SchedTimeSessAssignment', blank=True, help_text=u"Scheduled session, if any.") @@ -986,7 +984,7 @@ class Session(models.Model): agenda_note = models.CharField(blank=True, max_length=255) requested = models.DateTimeField(default=datetime.datetime.now) requested_by = models.ForeignKey(Person) - requested_duration = TimedeltaField(default=0) + requested_duration = models.DurationField(default=datetime.timedelta(0)) comments = models.TextField(blank=True) status = models.ForeignKey(SessionStatusName) scheduled = models.DateTimeField(null=True, blank=True) diff --git a/ietf/meeting/test_data.py b/ietf/meeting/test_data.py index f8589729d..3ef1a99cc 100644 --- a/ietf/meeting/test_data.py +++ b/ietf/meeting/test_data.py @@ -14,8 +14,8 @@ def make_interim_meeting(group,date,status='sched'): time = datetime.datetime.combine(date, datetime.time(9)) meeting = create_interim_meeting(group=group,date=date) session = Session.objects.create(meeting=meeting, group=group, - attendees=10, requested_by=system_person, - requested_duration=20, status_id=status, + attendees=10, requested_by=system_person, status_id=status, + requested_duration=datetime.timedelta(minutes=20), remote_instructions='http://webex.com', scheduled=datetime.datetime.now(),type_id="session") slot = TimeSlot.objects.create( @@ -56,35 +56,37 @@ def make_meeting_test_data(meeting=None): # slots session_date = meeting.date + datetime.timedelta(days=1) - slot1 = TimeSlot.objects.create(meeting=meeting, type_id="session", duration=30 * 60, location=room, + slot1 = TimeSlot.objects.create(meeting=meeting, type_id="session", location=room, + duration=datetime.timedelta(minutes=30), time=datetime.datetime.combine(session_date, datetime.time(9, 30))) - slot2 = TimeSlot.objects.create(meeting=meeting, type_id="session", duration=30 * 60, location=room, + slot2 = TimeSlot.objects.create(meeting=meeting, type_id="session", location=room, + duration=datetime.timedelta(minutes=30), time=datetime.datetime.combine(session_date, datetime.time(10, 30))) - breakfast_slot = TimeSlot.objects.create(meeting=meeting, type_id="lead", duration=90 * 60, - location=breakfast_room, - time=datetime.datetime.combine(session_date, datetime.time(7,0))) + breakfast_slot = TimeSlot.objects.create(meeting=meeting, type_id="lead", location=breakfast_room, + duration=datetime.timedelta(minutes=90), + time=datetime.datetime.combine(session_date, datetime.time(7,0))) # mars WG mars = Group.objects.get(acronym='mars') mars_session = Session.objects.create(meeting=meeting, group=mars, - attendees=10, requested_by=system_person, - requested_duration=20, status_id="schedw", + attendees=10, requested_by=system_person, status_id="schedw", + requested_duration=datetime.timedelta(minutes=20), scheduled=datetime.datetime.now(),type_id="session") SchedTimeSessAssignment.objects.create(timeslot=slot1, session=mars_session, schedule=schedule) SchedTimeSessAssignment.objects.create(timeslot=slot2, session=mars_session, schedule=unofficial_schedule) # ames WG ames_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="ames"), - attendees=10, requested_by=system_person, - requested_duration=20, status_id="schedw", + attendees=10, requested_by=system_person, status_id="schedw", + requested_duration=datetime.timedelta(minutes=20), scheduled=datetime.datetime.now(),type_id="session") SchedTimeSessAssignment.objects.create(timeslot=slot2, session=ames_session, schedule=schedule) SchedTimeSessAssignment.objects.create(timeslot=slot1, session=ames_session, schedule=unofficial_schedule) # IESG breakfast iesg_session = Session.objects.create(meeting=meeting, group=Group.objects.get(acronym="iesg"), - name="IESG Breakfast", - attendees=25, requested_by=system_person, - requested_duration=20, status_id="schedw", + name="IESG Breakfast", attendees=25, + requested_by=system_person, status_id="schedw", + requested_duration=datetime.timedelta(minutes=20), scheduled=datetime.datetime.now(),type_id="lead") SchedTimeSessAssignment.objects.create(timeslot=breakfast_slot, session=iesg_session, schedule=schedule) # No breakfast on unofficial schedule diff --git a/ietf/secr/meetings/forms.py b/ietf/secr/meetings/forms.py index 95faba693..390b9bc8b 100644 --- a/ietf/secr/meetings/forms.py +++ b/ietf/secr/meetings/forms.py @@ -5,7 +5,6 @@ from django import forms from ietf.group.models import Group from ietf.meeting.models import Meeting, Room, TimeSlot, Session, SchedTimeSessAssignment -from ietf.meeting.timedeltafield import TimedeltaFormField, TimedeltaWidget from ietf.name.models import TimeSlotTypeName @@ -175,7 +174,7 @@ class NonSessionEditForm(forms.Form): class TimeSlotForm(forms.Form): day = forms.ChoiceField(choices=DAYS_CHOICES) time = forms.TimeField() - duration = TimedeltaFormField(widget=TimedeltaWidget(attrs={'inputs':['hours','minutes']})) + duration = forms.DurationField(help_text="Enter duration as 'DD HH:MM:SS', or parts thereof. '3:42' means 3 minutes, 42 seconds, not 3 hours 42 minutes.") name = forms.CharField(help_text='Name that appears on the agenda') class NonSessionForm(TimeSlotForm): diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py index cb890f7e5..3d55fc74d 100644 --- a/ietf/secr/meetings/tests.py +++ b/ietf/secr/meetings/tests.py @@ -229,7 +229,7 @@ class SecrMeetingTestCase(TestCase): def test_meetings_times_edit(self): meeting = make_meeting_test_data() timeslot = TimeSlot.objects.filter(meeting=meeting,type='session').first() - url = reverse('meetings_times_edit',kwargs={ + url = reverse('ietf.secr.meetings.views.times_edit',kwargs={ 'meeting_id':42, 'schedule_name':'test-agenda', 'time':timeslot.time.strftime("%Y:%m:%d:%H:%M") @@ -238,8 +238,7 @@ class SecrMeetingTestCase(TestCase): response = self.client.post(url, { 'day':'1', 'time':'08:00', - 'duration_hours':'1', - 'duration_minutes':'0', + 'duration':'1 0:0:0', 'name':'Testing' }) self.assertEqual(response.status_code, 302) diff --git a/ietf/secr/sreq/views.py b/ietf/secr/sreq/views.py index aa72fb7fb..884388178 100644 --- a/ietf/secr/sreq/views.py +++ b/ietf/secr/sreq/views.py @@ -616,7 +616,7 @@ def no_session(request, acronym): meeting=meeting, requested=datetime.datetime.now(), requested_by=login, - requested_duration=0, + requested_duration=datetime.timedelta(0), status=SessionStatusName.objects.get(slug='notmeet'), type_id='session', ) diff --git a/ietf/settings.py b/ietf/settings.py index f82887165..8a6e64726 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -398,13 +398,14 @@ TEST_CODE_COVERAGE_EXCLUDE = [ "idindex/generate_all_id_txt.py", "idindex/generate_id_abstracts_txt.py", "idindex/generate_id_index_txt.py", + "ietf/checks.py", + "ietf/meeting/timedeltafield.py", # Dead code, kept for a migration include + "ietf/review/import_from_review_tool.py", + "ietf/settings*", + "ietf/utils/templatetags/debug_filters.py", + "ietf/utils/test_runner.py", "name/generate_fixtures.py", "review/import_from_review_tool.py", - "ietf/settings*", - "ietf/utils/test_runner.py", - "ietf/checks.py", - "ietf/utils/templatetags/debug_filters.py", - "ietf/review/import_from_review_tool.py", ] # These are filename globs. They are used by test_parse_templates() and