fix: fix failing tests and eliminate naive datetime warnings (#4402)
* test: fix timestamp construction in several doc tests * refactor: rename date2datetime to datetime_from_date and clarify code * chore: helper to get tzinfo for PRODUCTION_TIMEZONE * fix: fix timezone handling in make_last_call() * test: fix datetime generation in doc.tests_charter * refactor: remove PRODUCTION_TIMEZONE setting Replaces the PRODUCTION_TIMEZONE setting with a constant, DEADLINE_TZINFO, in ietf.utils.timezone. * test: be more careful about timezone in tests_charter.py * test: be more careful about timezone in doc/tests.py * fix: fix timezone handling affecting doc.tests_draft * fix: fix timezone handling affecting tests_irsg_ballot.py * fix: fix timezone handling affecting tests_review.py * fix: fix timezone handling affecting last ietf.doc tests * fix: fix timezone handling affecting last ietf.group tests * fix: fix timezone handling affecting ietf.iesg tests * fix: handle timezones in get_8989_eligibility_querysets * fix: handle timezones affecting ietfauth tests * fix: return tz-aware datetime from utc_from_string * fix: specify timezone for constants in ipr_rfc_number() * fix: specify tz for ipr deadlines * fix: handle timezones affecting liaisons tests * fix: treat leap day in get_8989_eligibility_querysets() Manual cherry-pick of 248d6474 * test: treat leap day properly in nomcom tests * fix: fix timezone handling affecting nomcom tests * test: fix timezone handling in review tests * fix: fix timezone handling affecting secr.meetings tests * fix: handle both pytz and zoneinfo timezones in ietf.utils.timezone * fix: fix timezone handling affecting secr.proceedings tests * refactor: use make_aware() helper in secr.meetings tests * test: fix timezone handling in secr.telechat tests * fix: fix timezone handling affecting stats tests * fix: eliminate tz-naive helpers affecting sync email parsing * fix: include timezone data when serializing DeletedEvent data * fix: fix timezone handling affecting sync tests * style: remove unused import
This commit is contained in:
parent
a4ecccb061
commit
32054111df
|
@ -18,6 +18,7 @@ from ietf.person.models import Person
|
||||||
from ietf.meeting.models import Meeting
|
from ietf.meeting.models import Meeting
|
||||||
from ietf.doc.utils import add_state_change_event, update_action_holders
|
from ietf.doc.utils import add_state_change_event, update_action_holders
|
||||||
from ietf.mailtrigger.utils import gather_address_lists
|
from ietf.mailtrigger.utils import gather_address_lists
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
nonexpirable_states: Optional[List[State]] = None
|
nonexpirable_states: Optional[List[State]] = None
|
||||||
|
@ -53,13 +54,13 @@ def expirable_drafts(queryset=None):
|
||||||
|
|
||||||
|
|
||||||
def get_soon_to_expire_drafts(days_of_warning):
|
def get_soon_to_expire_drafts(days_of_warning):
|
||||||
start_date = datetime.date.today() - datetime.timedelta(1)
|
start_date = datetime_today(DEADLINE_TZINFO) - datetime.timedelta(1)
|
||||||
end_date = start_date + datetime.timedelta(days_of_warning)
|
end_date = start_date + datetime.timedelta(days_of_warning)
|
||||||
|
|
||||||
return expirable_drafts().filter(expires__gte=start_date, expires__lt=end_date)
|
return expirable_drafts().filter(expires__gte=start_date, expires__lt=end_date)
|
||||||
|
|
||||||
def get_expired_drafts():
|
def get_expired_drafts():
|
||||||
return expirable_drafts().filter(expires__lt=datetime.date.today() + datetime.timedelta(1))
|
return expirable_drafts().filter(expires__lt=datetime_today(DEADLINE_TZINFO) + datetime.timedelta(1))
|
||||||
|
|
||||||
def in_draft_expire_freeze(when=None):
|
def in_draft_expire_freeze(when=None):
|
||||||
if when == None:
|
if when == None:
|
||||||
|
|
61
ietf/doc/migrations/0046_tzaware_deletedevents.py
Normal file
61
ietf/doc/migrations/0046_tzaware_deletedevents.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Generated by Django 2.2.28 on 2022-08-31 20:26
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
TZ_BEFORE = ZoneInfo('PST8PDT')
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
|
||||||
|
for deleted_event in DeletedEvent.objects.all():
|
||||||
|
fields = json.loads(deleted_event.json)
|
||||||
|
replacements = {}
|
||||||
|
for k, v in fields.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
try:
|
||||||
|
dt = datetime.datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
replacements[k] = dt.replace(tzinfo=TZ_BEFORE).astimezone(datetime.timezone.utc).isoformat()
|
||||||
|
if len(replacements) > 0:
|
||||||
|
fields.update(replacements)
|
||||||
|
deleted_event.json = json.dumps(fields)
|
||||||
|
deleted_event.save()
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
|
||||||
|
for deleted_event in DeletedEvent.objects.all():
|
||||||
|
fields = json.loads(deleted_event.json)
|
||||||
|
replacements = {}
|
||||||
|
for k, v in fields.items():
|
||||||
|
if isinstance(v, str) and 'T' in v:
|
||||||
|
try:
|
||||||
|
dt = datetime.datetime.fromisoformat(v)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
replacements[k] = dt.astimezone(TZ_BEFORE).replace(tzinfo=None).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
if len(replacements) > 0:
|
||||||
|
fields.update(replacements)
|
||||||
|
deleted_event.json = json.dumps(fields)
|
||||||
|
deleted_event.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('doc', '0045_use_timezone_now_for_doc_models'),
|
||||||
|
('utils', '0003_pause_to_change_use_tz'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
]
|
|
@ -18,6 +18,7 @@ from pyquery import PyQuery
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
|
@ -57,6 +58,8 @@ from ietf.utils.mail import outbox, empty_outbox
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
|
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
from ietf.utils.text import normalize_text
|
from ietf.utils.text import normalize_text
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
class SearchTests(TestCase):
|
class SearchTests(TestCase):
|
||||||
def test_search(self):
|
def test_search(self):
|
||||||
|
@ -1428,6 +1431,8 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
|
|
||||||
def test_draft_group_link(self):
|
def test_draft_group_link(self):
|
||||||
"""Link to group 'about' page should have correct format"""
|
"""Link to group 'about' page should have correct format"""
|
||||||
|
event_datetime = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles'))
|
||||||
|
|
||||||
for group_type_id in ['wg', 'rg', 'ag']:
|
for group_type_id in ['wg', 'rg', 'ag']:
|
||||||
group = GroupFactory(type_id=group_type_id)
|
group = GroupFactory(type_id=group_type_id)
|
||||||
draft = WgDraftFactory(name='draft-document-%s' % group_type_id, group=group)
|
draft = WgDraftFactory(name='draft-document-%s' % group_type_id, group=group)
|
||||||
|
@ -1436,7 +1441,7 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assert_correct_wg_group_link(r, group)
|
self.assert_correct_wg_group_link(r, group)
|
||||||
|
|
||||||
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
||||||
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
|
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
||||||
# get the rfc name to avoid a redirect
|
# get the rfc name to avoid a redirect
|
||||||
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
||||||
|
@ -1451,7 +1456,7 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assert_correct_non_wg_group_link(r, group)
|
self.assert_correct_non_wg_group_link(r, group)
|
||||||
|
|
||||||
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
||||||
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
|
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
||||||
# get the rfc name to avoid a redirect
|
# get the rfc name to avoid a redirect
|
||||||
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
||||||
|
@ -1837,7 +1842,7 @@ class DocTestCase(TestCase):
|
||||||
desc="Last call\x0b", # include a control character to be sure it does not break anything
|
desc="Last call\x0b", # include a control character to be sure it does not break anything
|
||||||
type="sent_last_call",
|
type="sent_last_call",
|
||||||
by=Person.objects.get(user__username="secretary"),
|
by=Person.objects.get(user__username="secretary"),
|
||||||
expires=datetime.date.today() + datetime.timedelta(days=7))
|
expires=datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=7))
|
||||||
|
|
||||||
r = self.client.get("/feed/last-call/")
|
r = self.client.get("/feed/last-call/")
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -1885,10 +1890,14 @@ class DocTestCase(TestCase):
|
||||||
#other_aliases = ['rfc6020',],
|
#other_aliases = ['rfc6020',],
|
||||||
states = [('draft','rfc'),('draft-iesg','pub')],
|
states = [('draft','rfc'),('draft-iesg','pub')],
|
||||||
std_level_id = 'ps',
|
std_level_id = 'ps',
|
||||||
time = datetime.datetime(2010,10,10),
|
time = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles')),
|
||||||
)
|
)
|
||||||
num = rfc.rfc_number()
|
num = rfc.rfc_number()
|
||||||
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
|
DocEventFactory.create(
|
||||||
|
doc=rfc,
|
||||||
|
type='published_rfc',
|
||||||
|
time=datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles')),
|
||||||
|
)
|
||||||
#
|
#
|
||||||
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=rfc.name))
|
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=rfc.name))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -1906,10 +1915,14 @@ class DocTestCase(TestCase):
|
||||||
stream_id = 'ise',
|
stream_id = 'ise',
|
||||||
states = [('draft','rfc'),('draft-iesg','pub')],
|
states = [('draft','rfc'),('draft-iesg','pub')],
|
||||||
std_level_id = 'inf',
|
std_level_id = 'inf',
|
||||||
time = datetime.datetime(1990,0o4,0o1),
|
time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo('America/Los_Angeles')),
|
||||||
)
|
)
|
||||||
num = april1.rfc_number()
|
num = april1.rfc_number()
|
||||||
DocEventFactory.create(doc=april1, type='published_rfc', time = '1990-04-01')
|
DocEventFactory.create(
|
||||||
|
doc=april1,
|
||||||
|
type='published_rfc',
|
||||||
|
time=datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo('America/Los_Angeles')),
|
||||||
|
)
|
||||||
#
|
#
|
||||||
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=april1.name))
|
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=april1.name))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -2044,7 +2057,9 @@ class GenerateDraftAliasesTests(TestCase):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
def testManagementCommand(self):
|
def testManagementCommand(self):
|
||||||
a_month_ago = timezone.now() - datetime.timedelta(30)
|
tz = ZoneInfo('America/Los_Angeles')
|
||||||
|
a_month_ago = (timezone.now() - datetime.timedelta(30)).astimezone(tz)
|
||||||
|
a_month_ago = a_month_ago.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person
|
ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person
|
||||||
shepherd = PersonFactory()
|
shepherd = PersonFactory()
|
||||||
author1 = PersonFactory()
|
author1 = PersonFactory()
|
||||||
|
@ -2059,9 +2074,9 @@ class GenerateDraftAliasesTests(TestCase):
|
||||||
doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad)
|
doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad)
|
||||||
doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad)
|
doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad)
|
||||||
doc3 = WgRfcFactory.create(name='draft-ietf-mars-finished', group__acronym='mars', authors=[author3], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=a_month_ago)
|
doc3 = WgRfcFactory.create(name='draft-ietf-mars-finished', group__acronym='mars', authors=[author3], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=a_month_ago)
|
||||||
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago.strftime("%Y-%m-%d"))
|
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago)
|
||||||
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10))
|
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10, tzinfo=tz))
|
||||||
DocEventFactory.create(doc=doc4, type='published_rfc', time = '2010-10-10')
|
DocEventFactory.create(doc=doc4, type='published_rfc', time=datetime.datetime(2010, 10, 10, tzinfo=tz))
|
||||||
doc5 = IndividualDraftFactory(authors=[author6])
|
doc5 = IndividualDraftFactory(authors=[author6])
|
||||||
|
|
||||||
args = [ ]
|
args = [ ]
|
||||||
|
|
|
@ -26,6 +26,8 @@ from ietf.person.models import Person
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized
|
from ietf.utils.test_utils import login_testing_unauthorized
|
||||||
|
from ietf.utils.timezone import datetime_today, date_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
class ViewCharterTests(TestCase):
|
class ViewCharterTests(TestCase):
|
||||||
def test_view_revisions(self):
|
def test_view_revisions(self):
|
||||||
|
@ -402,7 +404,7 @@ class EditCharterTests(TestCase):
|
||||||
|
|
||||||
# Make it so that the charter has been through internal review, and passed its external review
|
# Make it so that the charter has been through internal review, and passed its external review
|
||||||
# ballot on a previous telechat
|
# ballot on a previous telechat
|
||||||
last_week = datetime.date.today()-datetime.timedelta(days=7)
|
last_week = datetime_today(DEADLINE_TZINFO) - datetime.timedelta(days=7)
|
||||||
BallotDocEvent.objects.create(type='created_ballot',by=login,doc=charter, rev=charter.rev,
|
BallotDocEvent.objects.create(type='created_ballot',by=login,doc=charter, rev=charter.rev,
|
||||||
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
||||||
time=last_week)
|
time=last_week)
|
||||||
|
@ -746,7 +748,7 @@ class EditCharterTests(TestCase):
|
||||||
|
|
||||||
charter.set_state(State.objects.get(used=True, type="charter", slug="iesgrev"))
|
charter.set_state(State.objects.get(used=True, type="charter", slug="iesgrev"))
|
||||||
|
|
||||||
due_date = datetime.date.today() + datetime.timedelta(days=180)
|
due_date = date_today(DEADLINE_TZINFO) + datetime.timedelta(days=180)
|
||||||
m1 = GroupMilestone.objects.create(group=group,
|
m1 = GroupMilestone.objects.create(group=group,
|
||||||
state_id="active",
|
state_id="active",
|
||||||
desc="Has been copied",
|
desc="Has been copied",
|
||||||
|
@ -826,7 +828,7 @@ class EditCharterTests(TestCase):
|
||||||
m = GroupMilestone.objects.create(group=charter.group,
|
m = GroupMilestone.objects.create(group=charter.group,
|
||||||
state_id="active",
|
state_id="active",
|
||||||
desc="Test milestone",
|
desc="Test milestone",
|
||||||
due=datetime.date.today(),
|
due=date_today(DEADLINE_TZINFO),
|
||||||
resolved="")
|
resolved="")
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_charter.charter_with_milestones_txt', kwargs=dict(name=charter.name, rev=charter.rev))
|
url = urlreverse('ietf.doc.views_charter.charter_with_milestones_txt', kwargs=dict(name=charter.name, rev=charter.rev))
|
||||||
|
|
|
@ -34,6 +34,7 @@ from ietf.iesg.models import TelechatDate
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized
|
from ietf.utils.test_utils import login_testing_unauthorized
|
||||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
|
from ietf.utils.timezone import date_today, datetime_from_date
|
||||||
|
|
||||||
|
|
||||||
class ChangeStateTests(TestCase):
|
class ChangeStateTests(TestCase):
|
||||||
|
@ -402,11 +403,11 @@ class EditInfoTests(TestCase):
|
||||||
|
|
||||||
# change to a telechat that should cause returning item to be auto-detected
|
# change to a telechat that should cause returning item to be auto-detected
|
||||||
# First, make it appear that the previous telechat has already passed
|
# First, make it appear that the previous telechat has already passed
|
||||||
telechat_event.telechat_date = datetime.date.today()-datetime.timedelta(days=7)
|
telechat_event.telechat_date = date_today() - datetime.timedelta(days=7)
|
||||||
telechat_event.save()
|
telechat_event.save()
|
||||||
ad = Person.objects.get(user__username="ad")
|
ad = Person.objects.get(user__username="ad")
|
||||||
ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
|
ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
|
||||||
ballot.time = telechat_event.telechat_date
|
ballot.time = datetime_from_date(telechat_event.telechat_date)
|
||||||
ballot.save()
|
ballot.save()
|
||||||
|
|
||||||
r = self.client.post(url, data)
|
r = self.client.post(url, data)
|
||||||
|
@ -429,7 +430,7 @@ class EditInfoTests(TestCase):
|
||||||
self.assertTrue("Telechat update" in outbox[-1]['Subject'])
|
self.assertTrue("Telechat update" in outbox[-1]['Subject'])
|
||||||
|
|
||||||
# Put it on an agenda that's very soon from now
|
# Put it on an agenda that's very soon from now
|
||||||
next_week = datetime.date.today()+datetime.timedelta(days=7)
|
next_week = date_today() + datetime.timedelta(days=7)
|
||||||
td = TelechatDate.objects.active()[0]
|
td = TelechatDate.objects.active()[0]
|
||||||
td.date = next_week
|
td.date = next_week
|
||||||
td.save()
|
td.save()
|
||||||
|
|
|
@ -19,6 +19,7 @@ from ietf.doc.utils import create_ballot_if_not_open, close_ballot
|
||||||
from ietf.person.utils import get_active_irsg, get_active_ads
|
from ietf.person.utils import get_active_irsg, get_active_ads
|
||||||
from ietf.group.factories import RoleFactory
|
from ietf.group.factories import RoleFactory
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
class IssueIRSGBallotTests(TestCase):
|
class IssueIRSGBallotTests(TestCase):
|
||||||
|
@ -254,7 +255,7 @@ class IssueIRSGBallotTests(TestCase):
|
||||||
irsgmember = get_active_irsg()[0]
|
irsgmember = get_active_irsg()[0]
|
||||||
secr = RoleFactory(group__acronym='secretariat',name_id='secr')
|
secr = RoleFactory(group__acronym='secretariat',name_id='secr')
|
||||||
wg_ballot = create_ballot_if_not_open(None, wg_draft, ad.person, 'approve')
|
wg_ballot = create_ballot_if_not_open(None, wg_draft, ad.person, 'approve')
|
||||||
due = datetime.date.today()+datetime.timedelta(days=14)
|
due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=14)
|
||||||
rg_ballot = create_ballot_if_not_open(None, rg_draft, secr.person, 'irsg-approve', due)
|
rg_ballot = create_ballot_if_not_open(None, rg_draft, secr.person, 'irsg-approve', due)
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=wg_draft.name, ballot_id=wg_ballot.pk))
|
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=wg_draft.name, ballot_id=wg_ballot.pk))
|
||||||
|
|
|
@ -10,7 +10,6 @@ import email.mime.multipart, email.mime.text, email.utils
|
||||||
from mock import patch
|
from mock import patch
|
||||||
from requests import Response
|
from requests import Response
|
||||||
|
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -39,6 +38,7 @@ from ietf.utils.mail import outbox, empty_outbox, parseaddr, on_behalf_of, get_p
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized, reload_db_objects
|
from ietf.utils.test_utils import login_testing_unauthorized, reload_db_objects
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
from ietf.utils.text import strip_prefix, xslugify
|
from ietf.utils.text import strip_prefix, xslugify
|
||||||
|
from ietf.utils.timezone import DEADLINE_TZINFO
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
||||||
class ReviewTests(TestCase):
|
class ReviewTests(TestCase):
|
||||||
|
@ -734,7 +734,7 @@ class ReviewTests(TestCase):
|
||||||
# The secretary is allowed to set a custom completion date (#2590)
|
# The secretary is allowed to set a custom completion date (#2590)
|
||||||
assignment = reload_db_objects(assignment)
|
assignment = reload_db_objects(assignment)
|
||||||
self.assertEqual(assignment.state_id, "completed")
|
self.assertEqual(assignment.state_id, "completed")
|
||||||
self.assertEqual(assignment.completed_on, datetime.datetime(2012, 12, 24, 12, 13, 14))
|
self.assertEqual(assignment.completed_on, datetime.datetime(2012, 12, 24, 12, 13, 14, tzinfo=DEADLINE_TZINFO))
|
||||||
|
|
||||||
# There should be two events:
|
# There should be two events:
|
||||||
# - the event logging when the change when it was entered, i.e. very close to now.
|
# - the event logging when the change when it was entered, i.e. very close to now.
|
||||||
|
@ -742,7 +742,7 @@ class ReviewTests(TestCase):
|
||||||
events = ReviewAssignmentDocEvent.objects.filter(doc=assignment.review_request.doc).order_by('-time')
|
events = ReviewAssignmentDocEvent.objects.filter(doc=assignment.review_request.doc).order_by('-time')
|
||||||
event0_time_diff = timezone.now() - events[0].time
|
event0_time_diff = timezone.now() - events[0].time
|
||||||
self.assertLess(event0_time_diff, datetime.timedelta(seconds=10))
|
self.assertLess(event0_time_diff, datetime.timedelta(seconds=10))
|
||||||
self.assertEqual(events[1].time, datetime.datetime(2012, 12, 24, 12, 13, 14))
|
self.assertEqual(events[1].time, datetime.datetime(2012, 12, 24, 12, 13, 14, tzinfo=DEADLINE_TZINFO))
|
||||||
|
|
||||||
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
|
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
|
||||||
self.assertEqual(f.read(), "This is a review\nwith two lines")
|
self.assertEqual(f.read(), "This is a review\nwith two lines")
|
||||||
|
|
|
@ -143,7 +143,7 @@ class ActionHoldersTests(TestCase):
|
||||||
doc = self.doc_in_iesg_state('pub-req')
|
doc = self.doc_in_iesg_state('pub-req')
|
||||||
doc.action_holders.set([self.ad])
|
doc.action_holders.set([self.ad])
|
||||||
dah = doc.documentactionholder_set.get(person=self.ad)
|
dah = doc.documentactionholder_set.get(person=self.ad)
|
||||||
dah.time_added = datetime.datetime(2020, 1, 1) # arbitrary date in the past
|
dah.time_added = datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc) # arbitrary date in the past
|
||||||
dah.save()
|
dah.save()
|
||||||
|
|
||||||
self.assertNotEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), datetime.date.today())
|
self.assertNotEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), datetime.date.today())
|
||||||
|
|
|
@ -39,6 +39,7 @@ from ietf.review.models import ReviewWish
|
||||||
from ietf.utils import draft, log
|
from ietf.utils import draft, log
|
||||||
from ietf.utils.mail import send_mail
|
from ietf.utils.mail import send_mail
|
||||||
from ietf.mailtrigger.utils import gather_address_lists
|
from ietf.mailtrigger.utils import gather_address_lists
|
||||||
|
from ietf.utils.timezone import date_today, datetime_from_date, datetime_today, DEADLINE_TZINFO
|
||||||
from ietf.utils.xmldraft import XMLDraft
|
from ietf.utils.xmldraft import XMLDraft
|
||||||
|
|
||||||
|
|
||||||
|
@ -637,10 +638,21 @@ def has_same_ballot(doc, date1, date2=None):
|
||||||
""" Test if the most recent ballot created before the end of date1
|
""" Test if the most recent ballot created before the end of date1
|
||||||
is the same as the most recent ballot created before the
|
is the same as the most recent ballot created before the
|
||||||
end of date 2. """
|
end of date 2. """
|
||||||
|
datetime1 = datetime_from_date(date1, DEADLINE_TZINFO)
|
||||||
if date2 is None:
|
if date2 is None:
|
||||||
date2 = datetime.date.today()
|
datetime2 = datetime_today(DEADLINE_TZINFO)
|
||||||
ballot1 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date1+datetime.timedelta(days=1))
|
else:
|
||||||
ballot2 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date2+datetime.timedelta(days=1))
|
datetime2 = datetime_from_date(date2, DEADLINE_TZINFO)
|
||||||
|
ballot1 = doc.latest_event(
|
||||||
|
BallotDocEvent,
|
||||||
|
type='created_ballot',
|
||||||
|
time__lt=datetime1 + datetime.timedelta(days=1),
|
||||||
|
)
|
||||||
|
ballot2 = doc.latest_event(
|
||||||
|
BallotDocEvent,
|
||||||
|
type='created_ballot',
|
||||||
|
time__lt=datetime2 + datetime.timedelta(days=1),
|
||||||
|
)
|
||||||
return ballot1 == ballot2
|
return ballot1 == ballot2
|
||||||
|
|
||||||
def make_notify_changed_event(request, doc, by, new_notify, time=None):
|
def make_notify_changed_event(request, doc, by, new_notify, time=None):
|
||||||
|
@ -687,7 +699,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
|
||||||
and on_agenda
|
and on_agenda
|
||||||
and prev_agenda
|
and prev_agenda
|
||||||
and new_telechat_date != prev_telechat
|
and new_telechat_date != prev_telechat
|
||||||
and prev_telechat < datetime.date.today()
|
and prev_telechat < date_today(DEADLINE_TZINFO)
|
||||||
and has_same_ballot(doc,prev.telechat_date)
|
and has_same_ballot(doc,prev.telechat_date)
|
||||||
):
|
):
|
||||||
returning = True
|
returning = True
|
||||||
|
|
|
@ -40,6 +40,8 @@ from ietf.person.models import Person
|
||||||
from ietf.utils.mail import send_mail_text, send_mail_preformatted
|
from ietf.utils.mail import send_mail_text, send_mail_preformatted
|
||||||
from ietf.utils.decorators import require_api_key
|
from ietf.utils.decorators import require_api_key
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
|
from ietf.utils.timezone import date_today, datetime_from_date, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
BALLOT_CHOICES = (("yes", "Yes"),
|
BALLOT_CHOICES = (("yes", "Yes"),
|
||||||
("noobj", "No Objection"),
|
("noobj", "No Objection"),
|
||||||
|
@ -1055,9 +1057,11 @@ def make_last_call(request, name):
|
||||||
e.desc = "The following Last Call announcement was sent out (ends %s):<br><br>" % expiration_date
|
e.desc = "The following Last Call announcement was sent out (ends %s):<br><br>" % expiration_date
|
||||||
e.desc += announcement
|
e.desc += announcement
|
||||||
|
|
||||||
if form.cleaned_data['last_call_sent_date'] != e.time.date():
|
e_production_time = e.time.astimezone(DEADLINE_TZINFO)
|
||||||
e.time = datetime.datetime.combine(form.cleaned_data['last_call_sent_date'], e.time.time())
|
if form.cleaned_data['last_call_sent_date'] != e_production_time.date():
|
||||||
e.expires = expiration_date
|
lcsd = form.cleaned_data['last_call_sent_date']
|
||||||
|
e.time = e_production_time.replace(year=lcsd.year, month=lcsd.month, day=lcsd.day) # preserves tzinfo
|
||||||
|
e.expires = datetime_from_date(expiration_date, DEADLINE_TZINFO)
|
||||||
e.save()
|
e.save()
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
|
@ -1108,7 +1112,7 @@ def issue_irsg_ballot(request, name):
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
by = request.user.person
|
by = request.user.person
|
||||||
fillerdate = datetime.date.today() + datetime.timedelta(weeks=2)
|
fillerdate = date_today(DEADLINE_TZINFO) + datetime.timedelta(weeks=2)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
button = request.POST.get("irsg_button")
|
button = request.POST.get("irsg_button")
|
||||||
|
@ -1117,7 +1121,7 @@ def issue_irsg_ballot(request, name):
|
||||||
e = IRSGBallotDocEvent(doc=doc, rev=doc.rev, by=request.user.person)
|
e = IRSGBallotDocEvent(doc=doc, rev=doc.rev, by=request.user.person)
|
||||||
if (duedate == None or duedate==""):
|
if (duedate == None or duedate==""):
|
||||||
duedate = str(fillerdate)
|
duedate = str(fillerdate)
|
||||||
e.duedate = datetime.datetime.strptime(duedate, '%Y-%m-%d')
|
e.duedate = datetime_from_date(datetime.datetime.strptime(duedate, '%Y-%m-%d'), DEADLINE_TZINFO)
|
||||||
e.type = "created_ballot"
|
e.type = "created_ballot"
|
||||||
e.desc = "Created IRSG Ballot"
|
e.desc = "Created IRSG Ballot"
|
||||||
ballot_type = BallotType.objects.get(doc_type=doc.type, slug="irsg-approve")
|
ballot_type = BallotType.objects.get(doc_type=doc.type, slug="irsg-approve")
|
||||||
|
|
|
@ -53,6 +53,8 @@ from ietf.utils.mail import send_mail, send_mail_message, on_behalf_of
|
||||||
from ietf.utils.textupload import get_cleaned_text_file_content
|
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||||
from ietf.utils import log
|
from ietf.utils import log
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
class ChangeStateForm(forms.Form):
|
class ChangeStateForm(forms.Form):
|
||||||
state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg"), empty_label=None, required=True)
|
state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg"), empty_label=None, required=True)
|
||||||
|
@ -1477,7 +1479,7 @@ def adopt_draft(request, name):
|
||||||
|
|
||||||
due_date = None
|
due_date = None
|
||||||
if form.cleaned_data["weeks"] != None:
|
if form.cleaned_data["weeks"] != None:
|
||||||
due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"])
|
due_date = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(weeks=form.cleaned_data["weeks"])
|
||||||
|
|
||||||
update_reminder(doc, "stream-s", e, due_date)
|
update_reminder(doc, "stream-s", e, due_date)
|
||||||
|
|
||||||
|
@ -1668,7 +1670,7 @@ def change_stream_state(request, name, state_type):
|
||||||
|
|
||||||
due_date = None
|
due_date = None
|
||||||
if form.cleaned_data["weeks"] != None:
|
if form.cleaned_data["weeks"] != None:
|
||||||
due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"])
|
due_date = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(weeks=form.cleaned_data["weeks"])
|
||||||
|
|
||||||
update_reminder(doc, "stream-s", e, due_date)
|
update_reminder(doc, "stream-s", e, due_date)
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ from ietf.utils.mail import send_mail_message
|
||||||
from ietf.mailtrigger.utils import gather_address_lists
|
from ietf.mailtrigger.utils import gather_address_lists
|
||||||
from ietf.utils.fields import MultiEmailField
|
from ietf.utils.fields import MultiEmailField
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
|
from ietf.utils.timezone import DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
def clean_doc_revision(doc, rev):
|
def clean_doc_revision(doc, rev):
|
||||||
if rev:
|
if rev:
|
||||||
|
@ -768,7 +770,11 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
||||||
|
|
||||||
completion_datetime = timezone.now()
|
completion_datetime = timezone.now()
|
||||||
if "completion_date" in form.cleaned_data:
|
if "completion_date" in form.cleaned_data:
|
||||||
completion_datetime = datetime.datetime.combine(form.cleaned_data["completion_date"], form.cleaned_data.get("completion_time") or datetime.time.min)
|
completion_datetime = datetime.datetime.combine(
|
||||||
|
form.cleaned_data["completion_date"],
|
||||||
|
form.cleaned_data.get("completion_time") or datetime.time.min,
|
||||||
|
tzinfo=DEADLINE_TZINFO,
|
||||||
|
)
|
||||||
|
|
||||||
# complete assignment
|
# complete assignment
|
||||||
assignment.state = form.cleaned_data["state"]
|
assignment.state = form.cleaned_data["state"]
|
||||||
|
|
|
@ -26,6 +26,7 @@ from ietf.person.factories import PersonFactory, EmailFactory
|
||||||
from ietf.doc.factories import DocumentFactory
|
from ietf.doc.factories import DocumentFactory
|
||||||
from ietf.group.factories import RoleFactory, ReviewTeamFactory, GroupFactory
|
from ietf.group.factories import RoleFactory, ReviewTeamFactory, GroupFactory
|
||||||
from ietf.review.factories import ReviewRequestFactory, ReviewerSettingsFactory, ReviewAssignmentFactory
|
from ietf.review.factories import ReviewRequestFactory, ReviewerSettingsFactory, ReviewAssignmentFactory
|
||||||
|
from ietf.utils.timezone import date_today, datetime_today, DEADLINE_TZINFO
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
||||||
class ReviewTests(TestCase):
|
class ReviewTests(TestCase):
|
||||||
|
@ -156,7 +157,7 @@ class ReviewTests(TestCase):
|
||||||
review_request__doc=review_req1.doc,
|
review_request__doc=review_req1.doc,
|
||||||
review_request__team=review_req1.team,
|
review_request__team=review_req1.team,
|
||||||
review_request__type_id="early",
|
review_request__type_id="early",
|
||||||
review_request__deadline=datetime.date.today() + datetime.timedelta(days=30),
|
review_request__deadline=date_today(DEADLINE_TZINFO) + datetime.timedelta(days=30),
|
||||||
review_request__state_id="assigned",
|
review_request__state_id="assigned",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "accepted",
|
state_id = "accepted",
|
||||||
|
@ -166,7 +167,7 @@ class ReviewTests(TestCase):
|
||||||
UnavailablePeriod.objects.create(
|
UnavailablePeriod.objects.create(
|
||||||
team=review_req1.team,
|
team=review_req1.team,
|
||||||
person=reviewer,
|
person=reviewer,
|
||||||
start_date=datetime.date.today() - datetime.timedelta(days=10),
|
start_date=date_today() - datetime.timedelta(days=10),
|
||||||
availability="unavailable",
|
availability="unavailable",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -211,7 +212,7 @@ class ReviewTests(TestCase):
|
||||||
review_request__doc=review_req2.doc,
|
review_request__doc=review_req2.doc,
|
||||||
review_request__team=review_req2.team,
|
review_request__team=review_req2.team,
|
||||||
review_request__type_id="lc",
|
review_request__type_id="lc",
|
||||||
review_request__deadline=datetime.date.today() - datetime.timedelta(days=30),
|
review_request__deadline=date_today(DEADLINE_TZINFO) - datetime.timedelta(days=30),
|
||||||
review_request__state_id="assigned",
|
review_request__state_id="assigned",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "no-response",
|
state_id = "no-response",
|
||||||
|
@ -232,15 +233,15 @@ class ReviewTests(TestCase):
|
||||||
review_req3 = ReviewRequestFactory(state_id='completed', team=team)
|
review_req3 = ReviewRequestFactory(state_id='completed', team=team)
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
review_request__doc=review_req3.doc,
|
review_request__doc=review_req3.doc,
|
||||||
review_request__time=datetime.date.today() - datetime.timedelta(days=30),
|
review_request__time=datetime_today() - datetime.timedelta(days=30),
|
||||||
review_request__team=review_req3.team,
|
review_request__team=review_req3.team,
|
||||||
review_request__type_id="telechat",
|
review_request__type_id="telechat",
|
||||||
review_request__deadline=datetime.date.today() - datetime.timedelta(days=25),
|
review_request__deadline=date_today(DEADLINE_TZINFO) - datetime.timedelta(days=25),
|
||||||
review_request__state_id="completed",
|
review_request__state_id="completed",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "completed",
|
state_id = "completed",
|
||||||
reviewer=reviewer.email_set.first(),
|
reviewer=reviewer.email_set.first(),
|
||||||
assigned_on=datetime.date.today() - datetime.timedelta(days=30)
|
assigned_on=datetime_today() - datetime.timedelta(days=30)
|
||||||
)
|
)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -253,15 +254,15 @@ class ReviewTests(TestCase):
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
review_request__doc=reqs[i].doc,
|
review_request__doc=reqs[i].doc,
|
||||||
review_request__time=datetime.date.today() - datetime.timedelta(days=i*30),
|
review_request__time=datetime_today() - datetime.timedelta(days=i*30),
|
||||||
review_request__team=reqs[i].team,
|
review_request__team=reqs[i].team,
|
||||||
review_request__type_id="telechat",
|
review_request__type_id="telechat",
|
||||||
review_request__deadline=datetime.date.today() - datetime.timedelta(days=i*20),
|
review_request__deadline=date_today(DEADLINE_TZINFO) - datetime.timedelta(days=i*20),
|
||||||
review_request__state_id="completed",
|
review_request__state_id="completed",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "completed",
|
state_id = "completed",
|
||||||
reviewer=reviewer.email_set.first(),
|
reviewer=reviewer.email_set.first(),
|
||||||
assigned_on=datetime.date.today() - datetime.timedelta(days=i*30)
|
assigned_on=datetime_today() - datetime.timedelta(days=i*30)
|
||||||
)
|
)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -305,28 +306,28 @@ class ReviewTests(TestCase):
|
||||||
review_req4 = ReviewRequestFactory(state_id='completed', team=team)
|
review_req4 = ReviewRequestFactory(state_id='completed', team=team)
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
review_request__doc=review_req4.doc,
|
review_request__doc=review_req4.doc,
|
||||||
review_request__time=datetime.date.today() - datetime.timedelta(days=80),
|
review_request__time=datetime_today() - datetime.timedelta(days=80),
|
||||||
review_request__team=review_req4.team,
|
review_request__team=review_req4.team,
|
||||||
review_request__type_id="lc",
|
review_request__type_id="lc",
|
||||||
review_request__deadline=datetime.date.today() - datetime.timedelta(days=60),
|
review_request__deadline=date_today(DEADLINE_TZINFO) - datetime.timedelta(days=60),
|
||||||
review_request__state_id="assigned",
|
review_request__state_id="assigned",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "accepted",
|
state_id = "accepted",
|
||||||
reviewer=reviewer.email_set.first(),
|
reviewer=reviewer.email_set.first(),
|
||||||
assigned_on=datetime.date.today() - datetime.timedelta(days=80)
|
assigned_on=datetime_today() - datetime.timedelta(days=80)
|
||||||
)
|
)
|
||||||
review_req5 = ReviewRequestFactory(state_id='completed', team=team)
|
review_req5 = ReviewRequestFactory(state_id='completed', team=team)
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
review_request__doc=review_req5.doc,
|
review_request__doc=review_req5.doc,
|
||||||
review_request__time=datetime.date.today() - datetime.timedelta(days=120),
|
review_request__time=datetime_today() - datetime.timedelta(days=120),
|
||||||
review_request__team=review_req5.team,
|
review_request__team=review_req5.team,
|
||||||
review_request__type_id="lc",
|
review_request__type_id="lc",
|
||||||
review_request__deadline=datetime.date.today() - datetime.timedelta(days=100),
|
review_request__deadline=date_today(DEADLINE_TZINFO) - datetime.timedelta(days=100),
|
||||||
review_request__state_id="assigned",
|
review_request__state_id="assigned",
|
||||||
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
review_request__requested_by=Person.objects.get(user__username="reviewer"),
|
||||||
state_id = "accepted",
|
state_id = "accepted",
|
||||||
reviewer=reviewer.email_set.first(),
|
reviewer=reviewer.email_set.first(),
|
||||||
assigned_on=datetime.date.today() - datetime.timedelta(days=120)
|
assigned_on=datetime_today() - datetime.timedelta(days=120)
|
||||||
)
|
)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
|
@ -119,6 +119,7 @@ from ietf.settings import MAILING_LIST_INFO_URL
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
from ietf.utils.text import strip_suffix
|
from ietf.utils.text import strip_suffix
|
||||||
from ietf.utils import markdown
|
from ietf.utils import markdown
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
# --- Helpers ----------------------------------------------------------
|
# --- Helpers ----------------------------------------------------------
|
||||||
|
@ -1421,11 +1422,14 @@ def review_requests(request, acronym, group_type=None):
|
||||||
}[since]
|
}[since]
|
||||||
|
|
||||||
closed_review_requests = closed_review_requests.filter(
|
closed_review_requests = closed_review_requests.filter(
|
||||||
Q(reviewrequestdocevent__type='closed_review_request', reviewrequestdocevent__time__gte=datetime.date.today() - date_limit)
|
Q(reviewrequestdocevent__type='closed_review_request',
|
||||||
| Q(reviewrequestdocevent__isnull=True, time__gte=datetime.date.today() - date_limit)
|
reviewrequestdocevent__time__gte=datetime_today(DEADLINE_TZINFO) - date_limit)
|
||||||
|
| Q(reviewrequestdocevent__isnull=True, time__gte=datetime_today(DEADLINE_TZINFO) - date_limit)
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
closed_review_assignments = closed_review_assignments.filter(completed_on__gte = datetime.date.today() - date_limit)
|
closed_review_assignments = closed_review_assignments.filter(
|
||||||
|
completed_on__gte = datetime_today(DEADLINE_TZINFO) - date_limit,
|
||||||
|
)
|
||||||
|
|
||||||
return render(request, 'group/review_requests.html',
|
return render(request, 'group/review_requests.html',
|
||||||
construct_group_menu_context(request, group, "review requests", group_type, {
|
construct_group_menu_context(request, group, "review requests", group_type, {
|
||||||
|
|
|
@ -63,6 +63,7 @@ from ietf.iesg.utils import telechat_page_count
|
||||||
from ietf.ietfauth.utils import has_role, role_required, user_is_person
|
from ietf.ietfauth.utils import has_role, role_required, user_is_person
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.doc.utils_search import fill_in_document_table_attributes, fill_in_telechat_date
|
from ietf.doc.utils_search import fill_in_document_table_attributes, fill_in_telechat_date
|
||||||
|
from ietf.utils.timezone import date_today, datetime_from_date
|
||||||
|
|
||||||
def review_decisions(request, year=None):
|
def review_decisions(request, year=None):
|
||||||
events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved"))
|
events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved"))
|
||||||
|
@ -73,9 +74,9 @@ def review_decisions(request, year=None):
|
||||||
year = int(year)
|
year = int(year)
|
||||||
events = events.filter(time__year=year)
|
events = events.filter(time__year=year)
|
||||||
else:
|
else:
|
||||||
d = datetime.date.today() - datetime.timedelta(days=185)
|
d = date_today() - datetime.timedelta(days=185)
|
||||||
d = datetime.date(d.year, d.month, 1)
|
d = datetime.date(d.year, d.month, 1)
|
||||||
events = events.filter(time__gte=d)
|
events = events.filter(time__gte=datetime_from_date(d))
|
||||||
|
|
||||||
events = events.select_related("doc", "doc__intended_std_level").order_by("-time", "-id")
|
events = events.select_related("doc", "doc__intended_std_level").order_by("-time", "-id")
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,9 @@
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
import datetime
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
from datetime import date as Date, datetime as DateTime
|
|
||||||
# needed if we revert to higher barrier for account creation
|
# needed if we revert to higher barrier for account creation
|
||||||
#from datetime import datetime as DateTime, timedelta as TimeDelta, date as Date
|
#from datetime import datetime as DateTime, timedelta as TimeDelta, date as Date
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
@ -79,6 +79,7 @@ from ietf.doc.fields import SearchableDocumentField
|
||||||
from ietf.utils.decorators import person_required
|
from ietf.utils.decorators import person_required
|
||||||
from ietf.utils.mail import send_mail
|
from ietf.utils.mail import send_mail
|
||||||
from ietf.utils.validators import validate_external_resource_value
|
from ietf.utils.validators import validate_external_resource_value
|
||||||
|
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
# These are needed if we revert to the higher bar for account creation
|
# These are needed if we revert to the higher bar for account creation
|
||||||
|
|
||||||
|
@ -224,7 +225,7 @@ def profile(request):
|
||||||
emails = Email.objects.filter(person=person).exclude(address__startswith='unknown-email-').order_by('-active','-time')
|
emails = Email.objects.filter(person=person).exclude(address__startswith='unknown-email-').order_by('-active','-time')
|
||||||
new_email_forms = []
|
new_email_forms = []
|
||||||
|
|
||||||
nc = NomCom.objects.filter(group__acronym__icontains=Date.today().year).first()
|
nc = NomCom.objects.filter(group__acronym__icontains=date_today().year).first()
|
||||||
if nc and nc.volunteer_set.filter(person=person).exists():
|
if nc and nc.volunteer_set.filter(person=person).exists():
|
||||||
volunteer_status = 'volunteered'
|
volunteer_status = 'volunteered'
|
||||||
elif nc and nc.is_accepting_volunteers:
|
elif nc and nc.is_accepting_volunteers:
|
||||||
|
@ -456,7 +457,7 @@ def confirm_password_reset(request, auth):
|
||||||
password = data['password']
|
password = data['password']
|
||||||
last_login = None
|
last_login = None
|
||||||
if data['last_login']:
|
if data['last_login']:
|
||||||
last_login = DateTime.fromtimestamp(data['last_login'])
|
last_login = datetime.datetime.fromtimestamp(data['last_login'], datetime.timezone.utc)
|
||||||
except django.core.signing.BadSignature:
|
except django.core.signing.BadSignature:
|
||||||
raise Http404("Invalid or expired auth")
|
raise Http404("Invalid or expired auth")
|
||||||
|
|
||||||
|
@ -558,7 +559,7 @@ def review_overview(request):
|
||||||
reviewer__person__user=request.user,
|
reviewer__person__user=request.user,
|
||||||
state__in=["assigned", "accepted"],
|
state__in=["assigned", "accepted"],
|
||||||
)
|
)
|
||||||
today = Date.today()
|
today = date_today(DEADLINE_TZINFO)
|
||||||
for r in open_review_assignments:
|
for r in open_review_assignments:
|
||||||
r.due = max(0, (today - r.review_request.deadline).days)
|
r.due = max(0, (today - r.review_request.deadline).days)
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,14 @@
|
||||||
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import email
|
|
||||||
import datetime
|
import datetime
|
||||||
from dateutil.tz import tzoffset
|
from dateutil.tz import tzoffset
|
||||||
import os
|
import os
|
||||||
import pytz
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from email import message_from_bytes
|
||||||
|
from email.utils import parsedate_tz
|
||||||
|
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.encoding import force_text, force_bytes
|
from django.utils.encoding import force_text, force_bytes
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ def parsedate_to_datetime(date):
|
||||||
http://python.readthedocs.org/en/latest/library/email.util.html
|
http://python.readthedocs.org/en/latest/library/email.util.html
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
tuple = email.utils.parsedate_tz(date)
|
tuple = parsedate_tz(date)
|
||||||
if not tuple:
|
if not tuple:
|
||||||
return None
|
return None
|
||||||
tz = tuple[-1]
|
tz = tuple[-1]
|
||||||
|
@ -62,10 +63,12 @@ def parsedate_to_datetime(date):
|
||||||
|
|
||||||
def utc_from_string(s):
|
def utc_from_string(s):
|
||||||
date = parsedate_to_datetime(s)
|
date = parsedate_to_datetime(s)
|
||||||
if is_aware(date):
|
if date is None:
|
||||||
return date.astimezone(pytz.utc).replace(tzinfo=None)
|
return None
|
||||||
|
elif is_aware(date):
|
||||||
|
return date.astimezone(datetime.timezone.utc)
|
||||||
else:
|
else:
|
||||||
return date
|
return date.replace(tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
# Email Functions
|
# Email Functions
|
||||||
|
@ -174,7 +177,7 @@ def process_response_email(msg):
|
||||||
a matching value in the reply_to field, associated to an IPR disclosure through
|
a matching value in the reply_to field, associated to an IPR disclosure through
|
||||||
IprEvent. Create a Message object for the incoming message and associate it to
|
IprEvent. Create a Message object for the incoming message and associate it to
|
||||||
the original message via new IprEvent"""
|
the original message via new IprEvent"""
|
||||||
message = email.message_from_bytes(force_bytes(msg))
|
message = message_from_bytes(force_bytes(msg))
|
||||||
to = message.get('To', '')
|
to = message.get('To', '')
|
||||||
|
|
||||||
# exit if this isn't a response we're interested in (with plus addressing)
|
# exit if this isn't a response we're interested in (with plus addressing)
|
||||||
|
|
|
@ -14,7 +14,6 @@ from django.http import HttpResponse, Http404, HttpResponseRedirect
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
from django.utils import timezone
|
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
@ -44,6 +43,7 @@ from ietf.utils.draft_search import normalize_draftname
|
||||||
from ietf.utils.mail import send_mail, send_mail_message
|
from ietf.utils.mail import send_mail, send_mail_message
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
from ietf.utils.text import text_to_dict
|
from ietf.utils.text import text_to_dict
|
||||||
|
from ietf.utils.timezone import datetime_from_date, datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
# Globals
|
# Globals
|
||||||
|
@ -145,15 +145,14 @@ def ipr_rfc_number(disclosureDate, thirdPartyDisclosureFlag):
|
||||||
# made on 1993-07-23, which is more than a year after RFC 1310.
|
# made on 1993-07-23, which is more than a year after RFC 1310.
|
||||||
|
|
||||||
# RFC publication date comes from the RFC Editor announcement
|
# RFC publication date comes from the RFC Editor announcement
|
||||||
# TODO: These times are tzinfo=pytz.utc, but disclosure times are offset-naive
|
|
||||||
ipr_rfc_pub_datetime = {
|
ipr_rfc_pub_datetime = {
|
||||||
1310 : datetime.datetime(1992, 3, 13, 0, 0),
|
1310 : datetime.datetime(1992, 3, 13, 0, 0, tzinfo=datetime.timezone.utc),
|
||||||
1802 : datetime.datetime(1994, 3, 23, 0, 0),
|
1802 : datetime.datetime(1994, 3, 23, 0, 0, tzinfo=datetime.timezone.utc),
|
||||||
2026 : datetime.datetime(1996, 10, 29, 0, 0),
|
2026 : datetime.datetime(1996, 10, 29, 0, 0, tzinfo=datetime.timezone.utc),
|
||||||
3668 : datetime.datetime(2004, 2, 18, 0, 0),
|
3668 : datetime.datetime(2004, 2, 18, 0, 0, tzinfo=datetime.timezone.utc),
|
||||||
3979 : datetime.datetime(2005, 3, 2, 2, 23),
|
3979 : datetime.datetime(2005, 3, 2, 2, 23, tzinfo=datetime.timezone.utc),
|
||||||
4879 : datetime.datetime(2007, 4, 10, 18, 21),
|
4879 : datetime.datetime(2007, 4, 10, 18, 21, tzinfo=datetime.timezone.utc),
|
||||||
8179 : datetime.datetime(2017, 5, 31, 23, 1),
|
8179 : datetime.datetime(2017, 5, 31, 23, 1, tzinfo=datetime.timezone.utc),
|
||||||
}
|
}
|
||||||
|
|
||||||
if disclosureDate < ipr_rfc_pub_datetime[1310]:
|
if disclosureDate < ipr_rfc_pub_datetime[1310]:
|
||||||
|
@ -394,7 +393,7 @@ def email(request, id):
|
||||||
type_id = 'msgout',
|
type_id = 'msgout',
|
||||||
by = request.user.person,
|
by = request.user.person,
|
||||||
disclosure = ipr,
|
disclosure = ipr,
|
||||||
response_due = form.cleaned_data['response_due'],
|
response_due = datetime_from_date(form.cleaned_data['response_due'], DEADLINE_TZINFO),
|
||||||
message = msg,
|
message = msg,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -588,7 +587,7 @@ def notify(request, id, type):
|
||||||
type_id = form.cleaned_data['type'],
|
type_id = form.cleaned_data['type'],
|
||||||
by = request.user.person,
|
by = request.user.person,
|
||||||
disclosure = ipr,
|
disclosure = ipr,
|
||||||
response_due = timezone.now().date() + datetime.timedelta(days=30),
|
response_due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=30),
|
||||||
message = message,
|
message = message,
|
||||||
)
|
)
|
||||||
messages.success(request,'Notifications sent')
|
messages.success(request,'Notifications sent')
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import datetime, os
|
import os
|
||||||
import operator
|
import operator
|
||||||
|
|
||||||
from typing import Union # pyflakes:ignore
|
from typing import Union # pyflakes:ignore
|
||||||
|
@ -34,6 +34,7 @@ from ietf.person.models import Email
|
||||||
from ietf.person.fields import SearchableEmailField
|
from ietf.person.fields import SearchableEmailField
|
||||||
from ietf.doc.models import Document, DocAlias
|
from ietf.doc.models import Document, DocAlias
|
||||||
from ietf.utils.fields import DatepickerDateField
|
from ietf.utils.fields import DatepickerDateField
|
||||||
|
from ietf.utils.timezone import date_today, datetime_from_date, DEADLINE_TZINFO
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -185,9 +186,12 @@ class SearchLiaisonForm(forms.Form):
|
||||||
end_date = self.cleaned_data.get('end_date')
|
end_date = self.cleaned_data.get('end_date')
|
||||||
events = None
|
events = None
|
||||||
if start_date:
|
if start_date:
|
||||||
events = LiaisonStatementEvent.objects.filter(type='posted', time__gte=start_date)
|
events = LiaisonStatementEvent.objects.filter(
|
||||||
|
type='posted',
|
||||||
|
time__gte=datetime_from_date(start_date, DEADLINE_TZINFO),
|
||||||
|
)
|
||||||
if end_date:
|
if end_date:
|
||||||
events = events.filter(time__lte=end_date)
|
events = events.filter(time__lte=datetime_from_date(end_date, DEADLINE_TZINFO))
|
||||||
elif end_date:
|
elif end_date:
|
||||||
events = LiaisonStatementEvent.objects.filter(type='posted', time__lte=end_date)
|
events = LiaisonStatementEvent.objects.filter(type='posted', time__lte=end_date)
|
||||||
if events:
|
if events:
|
||||||
|
@ -222,7 +226,7 @@ class LiaisonModelForm(BetterModelForm):
|
||||||
to_groups.widget.attrs['data-minimum-input-length'] = 0
|
to_groups.widget.attrs['data-minimum-input-length'] = 0
|
||||||
deadline = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
|
deadline = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
|
||||||
related_to = SearchableLiaisonStatementsField(label='Related Liaison Statement', required=False)
|
related_to = SearchableLiaisonStatementsField(label='Related Liaison Statement', required=False)
|
||||||
submitted_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Submission date', required=True, initial=datetime.date.today())
|
submitted_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Submission date', required=True, initial=date_today(DEADLINE_TZINFO))
|
||||||
attachments = CustomModelMultipleChoiceField(queryset=Document.objects,label='Attachments', widget=ShowAttachmentsWidget, required=False)
|
attachments = CustomModelMultipleChoiceField(queryset=Document.objects,label='Attachments', widget=ShowAttachmentsWidget, required=False)
|
||||||
attach_title = forms.CharField(label='Title', required=False)
|
attach_title = forms.CharField(label='Title', required=False)
|
||||||
attach_file = forms.FileField(label='File', required=False)
|
attach_file = forms.FileField(label='File', required=False)
|
||||||
|
@ -538,7 +542,7 @@ class EditLiaisonForm(LiaisonModelForm):
|
||||||
super(EditLiaisonForm, self).save(*args,**kwargs)
|
super(EditLiaisonForm, self).save(*args,**kwargs)
|
||||||
if self.has_changed() and 'submitted_date' in self.changed_data:
|
if self.has_changed() and 'submitted_date' in self.changed_data:
|
||||||
event = self.instance.liaisonstatementevent_set.filter(type='submitted').first()
|
event = self.instance.liaisonstatementevent_set.filter(type='submitted').first()
|
||||||
event.time = self.cleaned_data.get('submitted_date')
|
event.time = datetime_from_date(self.cleaned_data.get('submitted_date'), DEADLINE_TZINFO)
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
return self.instance
|
return self.instance
|
||||||
|
|
|
@ -1023,7 +1023,7 @@ class LiaisonManagementTests(TestCase):
|
||||||
LiaisonStatementEventFactory(type_id='posted', statement__body="Has recently in its body",statement__from_groups=[GroupFactory(type_id='sdo',acronym='ulm'),])
|
LiaisonStatementEventFactory(type_id='posted', statement__body="Has recently in its body",statement__from_groups=[GroupFactory(type_id='sdo',acronym='ulm'),])
|
||||||
# Statement 2
|
# Statement 2
|
||||||
s2 = LiaisonStatementEventFactory(type_id='posted', statement__body="That word does not occur here", statement__title="Nor does it occur here")
|
s2 = LiaisonStatementEventFactory(type_id='posted', statement__body="That word does not occur here", statement__title="Nor does it occur here")
|
||||||
s2.time=datetime.datetime(2010,1,1)
|
s2.time=datetime.datetime(2010, 1, 1, tzinfo=datetime.timezone.utc)
|
||||||
s2.save()
|
s2.save()
|
||||||
|
|
||||||
# test list only, no search filters
|
# test list only, no search filters
|
||||||
|
|
|
@ -41,7 +41,7 @@ from ietf.person.models import Person
|
||||||
from ietf.utils.decorators import memoize
|
from ietf.utils.decorators import memoize
|
||||||
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
|
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
|
||||||
from ietf.utils.text import xslugify
|
from ietf.utils.text import xslugify
|
||||||
from ietf.utils.timezone import date2datetime
|
from ietf.utils.timezone import datetime_from_date
|
||||||
from ietf.utils.models import ForeignKey
|
from ietf.utils.models import ForeignKey
|
||||||
from ietf.utils.validators import (
|
from ietf.utils.validators import (
|
||||||
MaxImageSizeValidator, WrappedValidator, validate_file_size, validate_mime_type,
|
MaxImageSizeValidator, WrappedValidator, validate_file_size, validate_mime_type,
|
||||||
|
@ -152,7 +152,7 @@ class Meeting(models.Model):
|
||||||
cutoff_date = importantdate.date
|
cutoff_date = importantdate.date
|
||||||
else:
|
else:
|
||||||
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||||
cutoff_time = date2datetime(cutoff_date) + self.idsubmit_cutoff_time_utc
|
cutoff_time = datetime_from_date(cutoff_date) + self.idsubmit_cutoff_time_utc
|
||||||
return cutoff_time
|
return cutoff_time
|
||||||
|
|
||||||
def get_01_cutoff(self):
|
def get_01_cutoff(self):
|
||||||
|
@ -164,7 +164,7 @@ class Meeting(models.Model):
|
||||||
cutoff_date = importantdate.date
|
cutoff_date = importantdate.date
|
||||||
else:
|
else:
|
||||||
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||||
cutoff_time = date2datetime(cutoff_date) + self.idsubmit_cutoff_time_utc
|
cutoff_time = datetime_from_date(cutoff_date) + self.idsubmit_cutoff_time_utc
|
||||||
return cutoff_time
|
return cutoff_time
|
||||||
|
|
||||||
def get_reopen_time(self):
|
def get_reopen_time(self):
|
||||||
|
|
|
@ -50,6 +50,8 @@ from ietf.stats.models import MeetingRegistration
|
||||||
from ietf.stats.factories import MeetingRegistrationFactory
|
from ietf.stats.factories import MeetingRegistrationFactory
|
||||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized, TestCase, unicontent
|
from ietf.utils.test_utils import login_testing_unauthorized, TestCase, unicontent
|
||||||
|
from ietf.utils.timezone import datetime_today, datetime_from_date, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
client_test_cert_files = None
|
client_test_cert_files = None
|
||||||
|
|
||||||
|
@ -1092,7 +1094,7 @@ class ReminderTest(TestCase):
|
||||||
rai = Position.objects.get(nomcom=self.nomcom,name='RAI')
|
rai = Position.objects.get(nomcom=self.nomcom,name='RAI')
|
||||||
iab = Position.objects.get(nomcom=self.nomcom,name='IAB')
|
iab = Position.objects.get(nomcom=self.nomcom,name='IAB')
|
||||||
|
|
||||||
today = datetime.date.today()
|
today = datetime_today()
|
||||||
t_minus_3 = today - datetime.timedelta(days=3)
|
t_minus_3 = today - datetime.timedelta(days=3)
|
||||||
t_minus_4 = today - datetime.timedelta(days=4)
|
t_minus_4 = today - datetime.timedelta(days=4)
|
||||||
e1 = EmailFactory(address="nominee1@example.org", person=PersonFactory(name="Nominee 1"), origin='test')
|
e1 = EmailFactory(address="nominee1@example.org", person=PersonFactory(name="Nominee 1"), origin='test')
|
||||||
|
@ -2418,7 +2420,8 @@ class rfc8989EligibilityTests(TestCase):
|
||||||
|
|
||||||
nobody=PersonFactory()
|
nobody=PersonFactory()
|
||||||
for nomcom in self.nomcoms:
|
for nomcom in self.nomcoms:
|
||||||
before_elig_date = nomcom.first_call_for_volunteers - datetime.timedelta(days=5)
|
elig_datetime = datetime_from_date(nomcom.first_call_for_volunteers, DEADLINE_TZINFO)
|
||||||
|
before_elig_date = elig_datetime - datetime.timedelta(days=5)
|
||||||
|
|
||||||
chair = RoleFactory(name_id='chair',group__time=before_elig_date).person
|
chair = RoleFactory(name_id='chair',group__time=before_elig_date).person
|
||||||
|
|
||||||
|
@ -2436,7 +2439,7 @@ class rfc8989EligibilityTests(TestCase):
|
||||||
def test_elig_by_office_edge(self):
|
def test_elig_by_office_edge(self):
|
||||||
|
|
||||||
for nomcom in self.nomcoms:
|
for nomcom in self.nomcoms:
|
||||||
elig_date=get_eligibility_date(nomcom)
|
elig_date = datetime_from_date(get_eligibility_date(nomcom), DEADLINE_TZINFO)
|
||||||
day_after = elig_date + datetime.timedelta(days=1)
|
day_after = elig_date + datetime.timedelta(days=1)
|
||||||
two_days_after = elig_date + datetime.timedelta(days=2)
|
two_days_after = elig_date + datetime.timedelta(days=2)
|
||||||
|
|
||||||
|
@ -2451,10 +2454,15 @@ class rfc8989EligibilityTests(TestCase):
|
||||||
def test_elig_by_office_closed_groups(self):
|
def test_elig_by_office_closed_groups(self):
|
||||||
|
|
||||||
for nomcom in self.nomcoms:
|
for nomcom in self.nomcoms:
|
||||||
elig_date=get_eligibility_date(nomcom)
|
elig_date=datetime_from_date(get_eligibility_date(nomcom), DEADLINE_TZINFO)
|
||||||
day_before = elig_date-datetime.timedelta(days=1)
|
day_before = elig_date-datetime.timedelta(days=1)
|
||||||
year_before = datetime.date(elig_date.year-1,elig_date.month,elig_date.day)
|
# special case for Feb 29
|
||||||
three_years_before = datetime.date(elig_date.year-3,elig_date.month,elig_date.day)
|
if elig_date.month == 2 and elig_date.day == 29:
|
||||||
|
year_before = elig_date.replace(year=elig_date.year - 1, day=28)
|
||||||
|
three_years_before = elig_date.replace(year=elig_date.year - 3, day=28)
|
||||||
|
else:
|
||||||
|
year_before = elig_date.replace(year=elig_date.year - 1)
|
||||||
|
three_years_before = elig_date.replace(year=elig_date.year - 3)
|
||||||
just_after_three_years_before = three_years_before + datetime.timedelta(days=1)
|
just_after_three_years_before = three_years_before + datetime.timedelta(days=1)
|
||||||
just_before_three_years_before = three_years_before - datetime.timedelta(days=1)
|
just_before_three_years_before = three_years_before - datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
@ -2513,11 +2521,16 @@ class rfc8989EligibilityTests(TestCase):
|
||||||
for nomcom in self.nomcoms:
|
for nomcom in self.nomcoms:
|
||||||
elig_date = get_eligibility_date(nomcom)
|
elig_date = get_eligibility_date(nomcom)
|
||||||
|
|
||||||
last_date = elig_date
|
last_date = datetime_from_date(elig_date, DEADLINE_TZINFO)
|
||||||
first_date = datetime.date(last_date.year-5,last_date.month,last_date.day)
|
# special case for Feb 29
|
||||||
|
if last_date.month == 2 and last_date.day == 29:
|
||||||
|
first_date = last_date.replace(year = last_date.year - 5, day=28)
|
||||||
|
middle_date = last_date.replace(year=first_date.year - 3, day=28)
|
||||||
|
else:
|
||||||
|
first_date = last_date.replace(year=last_date.year - 5)
|
||||||
|
middle_date = last_date.replace(year=first_date.year - 3)
|
||||||
day_after_last_date = last_date+datetime.timedelta(days=1)
|
day_after_last_date = last_date+datetime.timedelta(days=1)
|
||||||
day_before_first_date = first_date-datetime.timedelta(days=1)
|
day_before_first_date = first_date-datetime.timedelta(days=1)
|
||||||
middle_date = datetime.date(last_date.year-3,last_date.month,last_date.day)
|
|
||||||
|
|
||||||
eligible = set()
|
eligible = set()
|
||||||
ineligible = set()
|
ineligible = set()
|
||||||
|
@ -2655,7 +2668,7 @@ class VolunteerDecoratorUnitTests(TestCase):
|
||||||
office_person = PersonFactory()
|
office_person = PersonFactory()
|
||||||
RoleHistoryFactory(
|
RoleHistoryFactory(
|
||||||
name_id='chair',
|
name_id='chair',
|
||||||
group__time= elig_date - datetime.timedelta(days=365),
|
group__time=datetime_from_date(elig_date) - datetime.timedelta(days=365),
|
||||||
group__group__state_id='conclude',
|
group__group__state_id='conclude',
|
||||||
person=office_person,
|
person=office_person,
|
||||||
)
|
)
|
||||||
|
@ -2664,7 +2677,16 @@ class VolunteerDecoratorUnitTests(TestCase):
|
||||||
author_person = PersonFactory()
|
author_person = PersonFactory()
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
da = WgDocumentAuthorFactory(person=author_person)
|
da = WgDocumentAuthorFactory(person=author_person)
|
||||||
DocEventFactory(type='published_rfc',doc=da.document,time=datetime.date(elig_date.year-3,elig_date.month,elig_date.day))
|
DocEventFactory(
|
||||||
|
type='published_rfc',
|
||||||
|
doc=da.document,
|
||||||
|
time=datetime.datetime(
|
||||||
|
elig_date.year - 3,
|
||||||
|
elig_date.month,
|
||||||
|
28 if elig_date.month == 2 and elig_date.day == 29 else elig_date.day,
|
||||||
|
tzinfo=datetime.timezone.utc,
|
||||||
|
)
|
||||||
|
)
|
||||||
nomcom.volunteer_set.create(person=author_person)
|
nomcom.volunteer_set.create(person=author_person)
|
||||||
|
|
||||||
volunteers = nomcom.volunteer_set.all()
|
volunteers = nomcom.volunteer_set.all()
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ietf.utils.pipe import pipe
|
||||||
from ietf.utils.mail import send_mail_text, send_mail, get_payload_text
|
from ietf.utils.mail import send_mail_text, send_mail, get_payload_text
|
||||||
from ietf.utils.log import log
|
from ietf.utils.log import log
|
||||||
from ietf.person.name import unidecode_name
|
from ietf.person.name import unidecode_name
|
||||||
|
from ietf.utils.timezone import datetime_from_date, datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
|
@ -536,28 +537,35 @@ def get_8989_eligibility_querysets(date, base_qs):
|
||||||
base_qs = Person.objects.all()
|
base_qs = Person.objects.all()
|
||||||
|
|
||||||
previous_five = previous_five_meetings(date)
|
previous_five = previous_five_meetings(date)
|
||||||
|
date_as_dt = datetime_from_date(date, DEADLINE_TZINFO)
|
||||||
three_of_five_qs = new_three_of_five_eligible(previous_five=previous_five, queryset=base_qs)
|
three_of_five_qs = new_three_of_five_eligible(previous_five=previous_five, queryset=base_qs)
|
||||||
|
|
||||||
three_years_ago = datetime.date(date.year-3,date.month,date.day)
|
# If date is Feb 29, neither 3 nor 5 years ago has a Feb 29. Use Feb 28 instead.
|
||||||
|
if date.month == 2 and date.day == 29:
|
||||||
|
three_years_ago = datetime.datetime(date.year - 3, 2, 28, tzinfo=DEADLINE_TZINFO)
|
||||||
|
five_years_ago = datetime.datetime(date.year - 5, 2, 28, tzinfo=DEADLINE_TZINFO)
|
||||||
|
else:
|
||||||
|
three_years_ago = datetime.datetime(date.year - 3, date.month, date.day, tzinfo=DEADLINE_TZINFO)
|
||||||
|
five_years_ago = datetime.datetime(date.year - 5, date.month, date.day, tzinfo=DEADLINE_TZINFO)
|
||||||
|
|
||||||
officer_qs = base_qs.filter(
|
officer_qs = base_qs.filter(
|
||||||
# is currently an officer
|
# is currently an officer
|
||||||
Q(role__name_id__in=('chair','secr'),
|
Q(role__name_id__in=('chair','secr'),
|
||||||
role__group__state_id='active',
|
role__group__state_id='active',
|
||||||
role__group__type_id='wg',
|
role__group__type_id='wg',
|
||||||
role__group__time__lte=date,
|
role__group__time__lte=date_as_dt,
|
||||||
)
|
)
|
||||||
# was an officer since the given date (I think this is wrong - it looks at when roles _start_, not when roles end)
|
# was an officer since the given date (I think this is wrong - it looks at when roles _start_, not when roles end)
|
||||||
| Q(rolehistory__group__time__gte=three_years_ago,
|
| Q(rolehistory__group__time__gte=three_years_ago,
|
||||||
rolehistory__group__time__lte=date,
|
rolehistory__group__time__lte=date_as_dt,
|
||||||
rolehistory__name_id__in=('chair','secr'),
|
rolehistory__name_id__in=('chair','secr'),
|
||||||
rolehistory__group__state_id='active',
|
rolehistory__group__state_id='active',
|
||||||
rolehistory__group__type_id='wg',
|
rolehistory__group__type_id='wg',
|
||||||
)
|
)
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|
||||||
five_years_ago = datetime.date(date.year-5,date.month,date.day)
|
rfc_pks = set(DocEvent.objects.filter(type='published_rfc', time__gte=five_years_ago, time__lte=date_as_dt).values_list('doc__pk', flat=True))
|
||||||
rfc_pks = set(DocEvent.objects.filter(type='published_rfc',time__gte=five_years_ago,time__lte=date).values_list('doc__pk',flat=True))
|
iesgappr_pks = set(DocEvent.objects.filter(type='iesg_approved', time__gte=five_years_ago, time__lte=date_as_dt).values_list('doc__pk',flat=True))
|
||||||
iesgappr_pks = set(DocEvent.objects.filter(type='iesg_approved',time__gte=five_years_ago,time__lte=date).values_list('doc__pk',flat=True))
|
|
||||||
qualifying_pks = rfc_pks.union(iesgappr_pks.difference(rfc_pks))
|
qualifying_pks = rfc_pks.union(iesgappr_pks.difference(rfc_pks))
|
||||||
author_qs = base_qs.filter(
|
author_qs = base_qs.filter(
|
||||||
documentauthor__document__pk__in=qualifying_pks
|
documentauthor__document__pk__in=qualifying_pks
|
||||||
|
@ -598,7 +606,7 @@ def get_eligibility_date(nomcom=None, date=None):
|
||||||
else:
|
else:
|
||||||
return datetime.date(next_nomcom_year,5,1)
|
return datetime.date(next_nomcom_year,5,1)
|
||||||
else:
|
else:
|
||||||
return datetime.date(datetime.date.today().year,5,1)
|
return datetime.date(datetime_today().year,5,1)
|
||||||
|
|
||||||
def previous_five_meetings(date = None):
|
def previous_five_meetings(date = None):
|
||||||
if date is None:
|
if date is None:
|
||||||
|
|
|
@ -951,7 +951,7 @@ def view_feedback_topic(request, year, topic_id):
|
||||||
feedback_types = FeedbackTypeName.objects.filter(slug__in=['comment',])
|
feedback_types = FeedbackTypeName.objects.filter(slug__in=['comment',])
|
||||||
|
|
||||||
last_seen = TopicFeedbackLastSeen.objects.filter(reviewer=request.user.person,topic=topic).first()
|
last_seen = TopicFeedbackLastSeen.objects.filter(reviewer=request.user.person,topic=topic).first()
|
||||||
last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1,month=1,day=1)
|
last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1, month=1, day=1, tzinfo=datetime.timezone.utc)
|
||||||
if last_seen:
|
if last_seen:
|
||||||
last_seen.save()
|
last_seen.save()
|
||||||
else:
|
else:
|
||||||
|
@ -973,7 +973,7 @@ def view_feedback_nominee(request, year, nominee_id):
|
||||||
feedback_types = FeedbackTypeName.objects.filter(slug__in=settings.NOMINEE_FEEDBACK_TYPES)
|
feedback_types = FeedbackTypeName.objects.filter(slug__in=settings.NOMINEE_FEEDBACK_TYPES)
|
||||||
|
|
||||||
last_seen = FeedbackLastSeen.objects.filter(reviewer=request.user.person,nominee=nominee).first()
|
last_seen = FeedbackLastSeen.objects.filter(reviewer=request.user.person,nominee=nominee).first()
|
||||||
last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1,month=1,day=1)
|
last_seen_time = (last_seen and last_seen.time) or datetime.datetime(year=1, month=1, day=1, tzinfo=datetime.timezone.utc)
|
||||||
if last_seen:
|
if last_seen:
|
||||||
last_seen.save()
|
last_seen.save()
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -5,6 +5,7 @@ import datetime
|
||||||
from ietf.group.factories import RoleFactory
|
from ietf.group.factories import RoleFactory
|
||||||
from ietf.utils.mail import empty_outbox, get_payload_text, outbox
|
from ietf.utils.mail import empty_outbox, get_payload_text, outbox
|
||||||
from ietf.utils.test_utils import TestCase, reload_db_objects
|
from ietf.utils.test_utils import TestCase, reload_db_objects
|
||||||
|
from ietf.utils.timezone import datetime_from_date
|
||||||
from .factories import ReviewAssignmentFactory, ReviewRequestFactory, ReviewerSettingsFactory
|
from .factories import ReviewAssignmentFactory, ReviewRequestFactory, ReviewerSettingsFactory
|
||||||
from .mailarch import hash_list_message_id
|
from .mailarch import hash_list_message_id
|
||||||
from .models import ReviewerSettings, ReviewSecretarySettings, ReviewTeamSettings, UnavailablePeriod
|
from .models import ReviewerSettings, ReviewSecretarySettings, ReviewTeamSettings, UnavailablePeriod
|
||||||
|
@ -408,7 +409,7 @@ class ReviewAssignmentReminderTests(TestCase):
|
||||||
review_request__state_id='assigned',
|
review_request__state_id='assigned',
|
||||||
review_request__deadline=self.deadline,
|
review_request__deadline=self.deadline,
|
||||||
state_id='assigned',
|
state_id='assigned',
|
||||||
assigned_on=self.deadline,
|
assigned_on=datetime_from_date(self.deadline),
|
||||||
reviewer=self.reviewer.email_set.first(),
|
reviewer=self.reviewer.email_set.first(),
|
||||||
).review_request.team
|
).review_request.team
|
||||||
second_team.reviewteamsettings.delete() # prevent it from being sent reminders
|
second_team.reviewteamsettings.delete() # prevent it from being sent reminders
|
||||||
|
@ -420,7 +421,7 @@ class ReviewAssignmentReminderTests(TestCase):
|
||||||
review_request__state_id='assigned',
|
review_request__state_id='assigned',
|
||||||
review_request__deadline=not_overdue,
|
review_request__deadline=not_overdue,
|
||||||
state_id='assigned',
|
state_id='assigned',
|
||||||
assigned_on=not_overdue,
|
assigned_on=datetime_from_date(not_overdue),
|
||||||
reviewer=self.reviewer.email_set.first(),
|
reviewer=self.reviewer.email_set.first(),
|
||||||
)
|
)
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
|
@ -428,7 +429,7 @@ class ReviewAssignmentReminderTests(TestCase):
|
||||||
review_request__state_id='assigned',
|
review_request__state_id='assigned',
|
||||||
review_request__deadline=not_overdue,
|
review_request__deadline=not_overdue,
|
||||||
state_id='assigned',
|
state_id='assigned',
|
||||||
assigned_on=not_overdue,
|
assigned_on=datetime_from_date(not_overdue),
|
||||||
reviewer=self.reviewer.email_set.first(),
|
reviewer=self.reviewer.email_set.first(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -439,7 +440,7 @@ class ReviewAssignmentReminderTests(TestCase):
|
||||||
review_request__state_id='assigned',
|
review_request__state_id='assigned',
|
||||||
review_request__deadline=in_grace_period,
|
review_request__deadline=in_grace_period,
|
||||||
state_id='assigned',
|
state_id='assigned',
|
||||||
assigned_on=in_grace_period,
|
assigned_on=datetime_from_date(in_grace_period),
|
||||||
reviewer=self.reviewer.email_set.first(),
|
reviewer=self.reviewer.email_set.first(),
|
||||||
)
|
)
|
||||||
ReviewAssignmentFactory(
|
ReviewAssignmentFactory(
|
||||||
|
@ -447,7 +448,7 @@ class ReviewAssignmentReminderTests(TestCase):
|
||||||
review_request__state_id='assigned',
|
review_request__state_id='assigned',
|
||||||
review_request__deadline=in_grace_period,
|
review_request__deadline=in_grace_period,
|
||||||
state_id='assigned',
|
state_id='assigned',
|
||||||
assigned_on=in_grace_period,
|
assigned_on=datetime_from_date(in_grace_period),
|
||||||
reviewer=self.reviewer.email_set.first(),
|
reviewer=self.reviewer.email_set.first(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ from ietf.review.models import (ReviewRequest, ReviewAssignment, ReviewRequestSt
|
||||||
from ietf.utils.mail import send_mail
|
from ietf.utils.mail import send_mail
|
||||||
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
|
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
|
||||||
from ietf.utils import log
|
from ietf.utils import log
|
||||||
|
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
# The origin date is used to have a single reference date for "every X days".
|
# The origin date is used to have a single reference date for "every X days".
|
||||||
# This date is arbitrarily chosen and has no special meaning, but should be consistent.
|
# This date is arbitrarily chosen and has no special meaning, but should be consistent.
|
||||||
|
@ -194,7 +196,10 @@ def extract_review_assignment_data(teams=None, reviewers=None, time_from=None, t
|
||||||
assigned_time = assigned_on
|
assigned_time = assigned_on
|
||||||
closed_time = completed_on
|
closed_time = completed_on
|
||||||
|
|
||||||
late_days = positive_days(datetime.datetime.combine(deadline, datetime.time.max), closed_time)
|
late_days = positive_days(
|
||||||
|
datetime.datetime.combine(deadline, datetime.time.max, tzinfo=DEADLINE_TZINFO),
|
||||||
|
closed_time,
|
||||||
|
)
|
||||||
request_to_assignment_days = positive_days(requested_time, assigned_time)
|
request_to_assignment_days = positive_days(requested_time, assigned_time)
|
||||||
assignment_to_closure_days = positive_days(assigned_time, closed_time)
|
assignment_to_closure_days = positive_days(assigned_time, closed_time)
|
||||||
request_to_closure_days = positive_days(requested_time, closed_time)
|
request_to_closure_days = positive_days(requested_time, closed_time)
|
||||||
|
@ -285,7 +290,7 @@ def latest_review_assignments_for_reviewers(team, days_back=365):
|
||||||
|
|
||||||
extracted_data = extract_review_assignment_data(
|
extracted_data = extract_review_assignment_data(
|
||||||
teams=[team],
|
teams=[team],
|
||||||
time_from=datetime.date.today() - datetime.timedelta(days=days_back),
|
time_from=datetime_today(DEADLINE_TZINFO) - datetime.timedelta(days=days_back),
|
||||||
ordering=["reviewer"],
|
ordering=["reviewer"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,10 @@ class SecrMeetingTestCase(TestCase):
|
||||||
"Edit Meeting"
|
"Edit Meeting"
|
||||||
meeting = make_meeting_test_data()
|
meeting = make_meeting_test_data()
|
||||||
url = reverse('ietf.secr.meetings.views.edit_meeting',kwargs={'meeting_id':meeting.number})
|
url = reverse('ietf.secr.meetings.views.edit_meeting',kwargs={'meeting_id':meeting.number})
|
||||||
post_data = dict(number=meeting.number,date='2014-07-20',city='Toronto',
|
post_data = dict(number=meeting.number,
|
||||||
|
date='2014-07-20',
|
||||||
|
city='Toronto',
|
||||||
|
time_zone='America/Toronto',
|
||||||
days=7,
|
days=7,
|
||||||
idsubmit_cutoff_day_offset_00=13,
|
idsubmit_cutoff_day_offset_00=13,
|
||||||
idsubmit_cutoff_day_offset_01=20,
|
idsubmit_cutoff_day_offset_01=20,
|
||||||
|
@ -286,7 +289,7 @@ class SecrMeetingTestCase(TestCase):
|
||||||
url = reverse('ietf.secr.meetings.views.times_delete',kwargs={
|
url = reverse('ietf.secr.meetings.views.times_delete',kwargs={
|
||||||
'meeting_id':meeting.number,
|
'meeting_id':meeting.number,
|
||||||
'schedule_name':meeting.schedule.name,
|
'schedule_name':meeting.schedule.name,
|
||||||
'time':qs.first().time.strftime("%Y:%m:%d:%H:%M")
|
'time':qs.first().time.astimezone(meeting.tz()).strftime("%Y:%m:%d:%H:%M")
|
||||||
})
|
})
|
||||||
redirect_url = reverse('ietf.secr.meetings.views.times',kwargs={
|
redirect_url = reverse('ietf.secr.meetings.views.times',kwargs={
|
||||||
'meeting_id':meeting.number,
|
'meeting_id':meeting.number,
|
||||||
|
@ -306,7 +309,7 @@ class SecrMeetingTestCase(TestCase):
|
||||||
url = reverse('ietf.secr.meetings.views.times_edit',kwargs={
|
url = reverse('ietf.secr.meetings.views.times_edit',kwargs={
|
||||||
'meeting_id':72,
|
'meeting_id':72,
|
||||||
'schedule_name':'test-schedule',
|
'schedule_name':'test-schedule',
|
||||||
'time':timeslot.time.strftime("%Y:%m:%d:%H:%M")
|
'time':timeslot.time.astimezone(meeting.tz()).strftime("%Y:%m:%d:%H:%M")
|
||||||
})
|
})
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
response = self.client.post(url, {
|
response = self.client.post(url, {
|
||||||
|
@ -372,7 +375,7 @@ class SecrMeetingTestCase(TestCase):
|
||||||
timeslot = session.official_timeslotassignment().timeslot
|
timeslot = session.official_timeslotassignment().timeslot
|
||||||
url = reverse('ietf.secr.meetings.views.misc_session_edit',kwargs={'meeting_id':72,'schedule_name':meeting.schedule.name,'slot_id':timeslot.pk})
|
url = reverse('ietf.secr.meetings.views.misc_session_edit',kwargs={'meeting_id':72,'schedule_name':meeting.schedule.name,'slot_id':timeslot.pk})
|
||||||
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
redirect_url = reverse('ietf.secr.meetings.views.misc_sessions',kwargs={'meeting_id':72,'schedule_name':'test-schedule'})
|
||||||
new_time = timeslot.time + datetime.timedelta(days=1)
|
new_time = (timeslot.time + datetime.timedelta(days=1)).astimezone(meeting.tz())
|
||||||
self.client.login(username="secretary", password="secretary+password")
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
|
@ -31,6 +31,7 @@ from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm,
|
||||||
from ietf.secr.sreq.views import get_initial_session
|
from ietf.secr.sreq.views import get_initial_session
|
||||||
from ietf.secr.utils.meeting import get_session, get_timeslot
|
from ietf.secr.utils.meeting import get_session, get_timeslot
|
||||||
from ietf.mailtrigger.utils import gather_address_lists
|
from ietf.mailtrigger.utils import gather_address_lists
|
||||||
|
from ietf.utils.timezone import make_aware
|
||||||
|
|
||||||
|
|
||||||
# prep for agenda changes
|
# prep for agenda changes
|
||||||
|
@ -342,7 +343,7 @@ def edit_meeting(request, meeting_id):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = MeetingModelForm(instance=meeting)
|
form = MeetingModelForm(instance=meeting)
|
||||||
|
debug.show('form.errors')
|
||||||
return render(request, 'meetings/edit_meeting.html', {
|
return render(request, 'meetings/edit_meeting.html', {
|
||||||
'meeting': meeting,
|
'meeting': meeting,
|
||||||
'form' : form, },
|
'form' : form, },
|
||||||
|
@ -799,7 +800,8 @@ def get_timeslot_time(form, meeting):
|
||||||
day = form.cleaned_data['day']
|
day = form.cleaned_data['day']
|
||||||
|
|
||||||
date = meeting.date + datetime.timedelta(days=int(day))
|
date = meeting.date + datetime.timedelta(days=int(day))
|
||||||
return datetime.datetime(date.year,date.month,date.day,time.hour,time.minute)
|
return make_aware(datetime.datetime(date.year,date.month,date.day,time.hour,time.minute), meeting.tz())
|
||||||
|
|
||||||
|
|
||||||
@role_required('Secretariat')
|
@role_required('Secretariat')
|
||||||
def times_edit(request, meeting_id, schedule_name, time):
|
def times_edit(request, meeting_id, schedule_name, time):
|
||||||
|
@ -810,7 +812,7 @@ def times_edit(request, meeting_id, schedule_name, time):
|
||||||
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
|
||||||
|
|
||||||
parts = [ int(x) for x in time.split(':') ]
|
parts = [ int(x) for x in time.split(':') ]
|
||||||
dtime = datetime.datetime(*parts)
|
dtime = make_aware(datetime.datetime(*parts), tzinfo=meeting.tz())
|
||||||
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
|
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
@ -861,7 +863,7 @@ def times_delete(request, meeting_id, schedule_name, time):
|
||||||
meeting = get_object_or_404(Meeting, number=meeting_id)
|
meeting = get_object_or_404(Meeting, number=meeting_id)
|
||||||
|
|
||||||
parts = [ int(x) for x in time.split(':') ]
|
parts = [ int(x) for x in time.split(':') ]
|
||||||
dtime = datetime.datetime(*parts)
|
dtime = make_aware(datetime.datetime(*parts), tzinfo=meeting.tz())
|
||||||
status = SessionStatusName.objects.get(slug='schedw')
|
status = SessionStatusName.objects.get(slug='schedw')
|
||||||
|
|
||||||
if request.method == 'POST' and request.POST['post'] == 'yes':
|
if request.method == 'POST' and request.POST['post'] == 'yes':
|
||||||
|
|
|
@ -25,6 +25,7 @@ from ietf.meeting.models import Meeting, SessionPresentation, TimeSlot, SchedTim
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.utils.log import log
|
from ietf.utils.log import log
|
||||||
from ietf.utils.mail import send_mail
|
from ietf.utils.mail import send_mail
|
||||||
|
from ietf.utils.timezone import make_aware
|
||||||
|
|
||||||
AUDIO_FILE_RE = re.compile(r'ietf(?P<number>[\d]+)-(?P<room>.*)-(?P<time>[\d]{8}-[\d]{4})')
|
AUDIO_FILE_RE = re.compile(r'ietf(?P<number>[\d]+)-(?P<room>.*)-(?P<time>[\d]{8}-[\d]{4})')
|
||||||
VIDEO_TITLE_RE = re.compile(r'IETF(?P<number>[\d]+)-(?P<name>.*)-(?P<date>\d{8})-(?P<time>\d{4})')
|
VIDEO_TITLE_RE = re.compile(r'IETF(?P<number>[\d]+)-(?P<name>.*)-(?P<date>\d{8})-(?P<time>\d{4})')
|
||||||
|
@ -33,7 +34,7 @@ VIDEO_TITLE_RE = re.compile(r'IETF(?P<number>[\d]+)-(?P<name>.*)-(?P<date>\d{8})
|
||||||
def _get_session(number,name,date,time):
|
def _get_session(number,name,date,time):
|
||||||
'''Lookup session using data from video title'''
|
'''Lookup session using data from video title'''
|
||||||
meeting = Meeting.objects.get(number=number)
|
meeting = Meeting.objects.get(number=number)
|
||||||
timeslot_time = datetime.datetime.strptime(date + time,'%Y%m%d%H%M')
|
timeslot_time = make_aware(datetime.datetime.strptime(date + time,'%Y%m%d%H%M'), meeting.tz())
|
||||||
try:
|
try:
|
||||||
assignment = SchedTimeSessAssignment.objects.get(
|
assignment = SchedTimeSessAssignment.objects.get(
|
||||||
schedule__in = [meeting.schedule, meeting.schedule.base],
|
schedule__in = [meeting.schedule, meeting.schedule.base],
|
||||||
|
@ -103,7 +104,7 @@ def get_timeslot_for_filename(filename):
|
||||||
try:
|
try:
|
||||||
meeting = Meeting.objects.get(number=match.groupdict()['number'])
|
meeting = Meeting.objects.get(number=match.groupdict()['number'])
|
||||||
room_mapping = {normalize_room_name(room.name): room.name for room in meeting.room_set.all()}
|
room_mapping = {normalize_room_name(room.name): room.name for room in meeting.room_set.all()}
|
||||||
time = datetime.datetime.strptime(match.groupdict()['time'],'%Y%m%d-%H%M')
|
time = make_aware(datetime.datetime.strptime(match.groupdict()['time'],'%Y%m%d-%H%M'), meeting.tz())
|
||||||
slots = TimeSlot.objects.filter(
|
slots = TimeSlot.objects.filter(
|
||||||
meeting=meeting,
|
meeting=meeting,
|
||||||
location__name=room_mapping[match.groupdict()['room']],
|
location__name=room_mapping[match.groupdict()['room']],
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.utils import timezone
|
||||||
from ietf.meeting.models import Meeting
|
from ietf.meeting.models import Meeting
|
||||||
from ietf.doc.models import DocEvent, Document
|
from ietf.doc.models import DocEvent, Document
|
||||||
from ietf.secr.proceedings.proc_utils import get_progress_stats
|
from ietf.secr.proceedings.proc_utils import get_progress_stats
|
||||||
|
from ietf.utils.timezone import datetime_from_date
|
||||||
|
|
||||||
|
|
||||||
def report_id_activity(start,end):
|
def report_id_activity(start,end):
|
||||||
|
|
||||||
|
@ -13,8 +15,8 @@ def report_id_activity(start,end):
|
||||||
meeting = Meeting.objects.filter(date__lt=timezone.now(),type='ietf').order_by('-date')[0]
|
meeting = Meeting.objects.filter(date__lt=timezone.now(),type='ietf').order_by('-date')[0]
|
||||||
syear,smonth,sday = start.split('-')
|
syear,smonth,sday = start.split('-')
|
||||||
eyear,emonth,eday = end.split('-')
|
eyear,emonth,eday = end.split('-')
|
||||||
sdate = datetime.datetime(int(syear),int(smonth),int(sday))
|
sdate = datetime_from_date(datetime.date(int(syear),int(smonth),int(sday)), meeting.tz())
|
||||||
edate = datetime.datetime(int(eyear),int(emonth),int(eday))
|
edate = datetime_from_date(datetime.date(int(eyear),int(emonth),int(eday)), meeting.tz())
|
||||||
|
|
||||||
#queryset = Document.objects.filter(type='draft').annotate(start_date=Min('docevent__time'))
|
#queryset = Document.objects.filter(type='draft').annotate(start_date=Min('docevent__time'))
|
||||||
new_docs = Document.objects.filter(type='draft').filter(docevent__type='new_revision',
|
new_docs = Document.objects.filter(type='draft').filter(docevent__type='new_revision',
|
||||||
|
@ -45,7 +47,7 @@ def report_id_activity(start,end):
|
||||||
approved = events.filter(type='iesg_approved').count()
|
approved = events.filter(type='iesg_approved').count()
|
||||||
|
|
||||||
# get 4 weeks
|
# get 4 weeks
|
||||||
monday = Meeting.get_current_meeting().get_ietf_monday()
|
monday = datetime_from_date(Meeting.get_current_meeting().get_ietf_monday(), meeting.tz())
|
||||||
cutoff = monday + datetime.timedelta(days=3)
|
cutoff = monday + datetime.timedelta(days=3)
|
||||||
ff1_date = cutoff - datetime.timedelta(days=28)
|
ff1_date = cutoff - datetime.timedelta(days=28)
|
||||||
#ff2_date = cutoff - datetime.timedelta(days=21)
|
#ff2_date = cutoff - datetime.timedelta(days=21)
|
||||||
|
|
|
@ -50,8 +50,9 @@ class VideoRecordingTestCase(TestCase):
|
||||||
meeting = session.meeting
|
meeting = session.meeting
|
||||||
number = meeting.number
|
number = meeting.number
|
||||||
name = session.group.acronym
|
name = session.group.acronym
|
||||||
date = session.official_timeslotassignment().timeslot.time.strftime('%Y%m%d')
|
ts_time = session.official_timeslotassignment().timeslot.local_start_time()
|
||||||
time = session.official_timeslotassignment().timeslot.time.strftime('%H%M')
|
date = ts_time.strftime('%Y%m%d')
|
||||||
|
time = ts_time.strftime('%H%M')
|
||||||
self.assertEqual(_get_session(number,name,date,time),session)
|
self.assertEqual(_get_session(number,name,date,time),session)
|
||||||
|
|
||||||
def test_get_urls_from_json(self):
|
def test_get_urls_from_json(self):
|
||||||
|
@ -113,7 +114,7 @@ class RecordingTestCase(TestCase):
|
||||||
return "{prefix}-{room}-{date}.mp3".format(
|
return "{prefix}-{room}-{date}.mp3".format(
|
||||||
prefix=timeslot.meeting.type.slug + timeslot.meeting.number,
|
prefix=timeslot.meeting.type.slug + timeslot.meeting.number,
|
||||||
room=normalize_room_name(timeslot.location.name),
|
room=normalize_room_name(timeslot.location.name),
|
||||||
date=timeslot.time.strftime('%Y%m%d-%H%M'))
|
date=timeslot.local_start_time().strftime('%Y%m%d-%H%M'))
|
||||||
|
|
||||||
def test_import_audio_files_shared_timeslot(self):
|
def test_import_audio_files_shared_timeslot(self):
|
||||||
meeting = MeetingFactory(type_id='ietf',number='72')
|
meeting = MeetingFactory(type_id='ietf',number='72')
|
||||||
|
|
|
@ -15,6 +15,7 @@ from ietf.doc.factories import (WgDraftFactory, IndividualRfcFactory, CharterFac
|
||||||
from ietf.doc.models import BallotDocEvent, BallotType, BallotPositionDocEvent, State, Document
|
from ietf.doc.models import BallotDocEvent, BallotType, BallotPositionDocEvent, State, Document
|
||||||
from ietf.doc.utils import update_telechat, create_ballot_if_not_open
|
from ietf.doc.utils import update_telechat, create_ballot_if_not_open
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
|
from ietf.utils.timezone import datetime_today
|
||||||
from ietf.iesg.models import TelechatDate
|
from ietf.iesg.models import TelechatDate
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.person.factories import PersonFactory
|
from ietf.person.factories import PersonFactory
|
||||||
|
@ -120,7 +121,7 @@ class SecrTelechatTestCase(TestCase):
|
||||||
def test_doc_detail_charter(self):
|
def test_doc_detail_charter(self):
|
||||||
by=Person.objects.get(name="(System)")
|
by=Person.objects.get(name="(System)")
|
||||||
charter = CharterFactory(states=[('charter','intrev')])
|
charter = CharterFactory(states=[('charter','intrev')])
|
||||||
last_week = datetime.date.today()-datetime.timedelta(days=7)
|
last_week = datetime_today()-datetime.timedelta(days=7)
|
||||||
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
||||||
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
||||||
time=last_week)
|
time=last_week)
|
||||||
|
@ -149,7 +150,7 @@ class SecrTelechatTestCase(TestCase):
|
||||||
def test_doc_detail_post_update_ballot(self):
|
def test_doc_detail_post_update_ballot(self):
|
||||||
by=Person.objects.get(name="(System)")
|
by=Person.objects.get(name="(System)")
|
||||||
charter = CharterFactory(states=[('charter','intrev')])
|
charter = CharterFactory(states=[('charter','intrev')])
|
||||||
last_week = datetime.date.today()-datetime.timedelta(days=7)
|
last_week = datetime_today()-datetime.timedelta(days=7)
|
||||||
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
||||||
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
||||||
time=last_week)
|
time=last_week)
|
||||||
|
@ -187,7 +188,7 @@ class SecrTelechatTestCase(TestCase):
|
||||||
def test_doc_detail_post_update_state(self):
|
def test_doc_detail_post_update_state(self):
|
||||||
by=Person.objects.get(name="(System)")
|
by=Person.objects.get(name="(System)")
|
||||||
charter = CharterFactory(states=[('charter','intrev')])
|
charter = CharterFactory(states=[('charter','intrev')])
|
||||||
last_week = datetime.date.today()-datetime.timedelta(days=7)
|
last_week = datetime_today()-datetime.timedelta(days=7)
|
||||||
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=charter, rev=charter.rev,
|
||||||
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
|
||||||
time=last_week)
|
time=last_week)
|
||||||
|
@ -215,7 +216,7 @@ class SecrTelechatTestCase(TestCase):
|
||||||
ad=Person.objects.get(user__username='ad'),
|
ad=Person.objects.get(user__username='ad'),
|
||||||
authors=PersonFactory.create_batch(3),
|
authors=PersonFactory.create_batch(3),
|
||||||
)
|
)
|
||||||
last_week = datetime.date.today()-datetime.timedelta(days=7)
|
last_week = datetime_today()-datetime.timedelta(days=7)
|
||||||
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=draft, rev=draft.rev,
|
BallotDocEvent.objects.create(type='created_ballot',by=by,doc=draft, rev=draft.rev,
|
||||||
ballot_type=BallotType.objects.get(doc_type=draft.type,slug='approve'),
|
ballot_type=BallotType.objects.get(doc_type=draft.type,slug='approve'),
|
||||||
time=last_week)
|
time=last_week)
|
||||||
|
|
|
@ -1039,8 +1039,6 @@ CHAT_URL_PATTERN = 'https://zulip.ietf.org/#narrow/stream/{chat_room_name}'
|
||||||
# If we need to revert to xmpp
|
# If we need to revert to xmpp
|
||||||
# CHAT_ARCHIVE_URL_PATTERN = 'https://www.ietf.org/jabber/logs/{chat_room_name}?C=M;O=D'
|
# CHAT_ARCHIVE_URL_PATTERN = 'https://www.ietf.org/jabber/logs/{chat_room_name}?C=M;O=D'
|
||||||
|
|
||||||
PRODUCTION_TIMEZONE = "America/Los_Angeles"
|
|
||||||
|
|
||||||
PYFLAKES_DEFAULT_ARGS= ["ietf", ]
|
PYFLAKES_DEFAULT_ARGS= ["ietf", ]
|
||||||
VULTURE_DEFAULT_ARGS= ["ietf", ]
|
VULTURE_DEFAULT_ARGS= ["ietf", ]
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ from ietf.stats.models import MeetingRegistration, CountryAlias
|
||||||
from ietf.stats.utils import get_aliased_affiliations, get_aliased_countries, compute_hirsch_index
|
from ietf.stats.utils import get_aliased_affiliations, get_aliased_countries, compute_hirsch_index
|
||||||
from ietf.ietfauth.utils import has_role
|
from ietf.ietfauth.utils import has_role
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
|
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||||
|
|
||||||
|
|
||||||
def stats_index(request):
|
def stats_index(request):
|
||||||
return render(request, "stats/index.html")
|
return render(request, "stats/index.html")
|
||||||
|
@ -1066,12 +1068,12 @@ def review_stats(request, stats_type=None, acronym=None):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
today = datetime.date.today()
|
today = date_today(DEADLINE_TZINFO)
|
||||||
from_date = parse_date(request.GET.get("from")) or today - dateutil.relativedelta.relativedelta(years=1)
|
from_date = parse_date(request.GET.get("from")) or today - dateutil.relativedelta.relativedelta(years=1)
|
||||||
to_date = parse_date(request.GET.get("to")) or today
|
to_date = parse_date(request.GET.get("to")) or today
|
||||||
|
|
||||||
from_time = datetime.datetime.combine(from_date, datetime.time.min)
|
from_time = datetime.datetime.combine(from_date, datetime.time.min, tzinfo=DEADLINE_TZINFO)
|
||||||
to_time = datetime.datetime.combine(to_date, datetime.time.max)
|
to_time = datetime.datetime.combine(to_date, datetime.time.max, tzinfo=DEADLINE_TZINFO)
|
||||||
|
|
||||||
# teams/reviewers
|
# teams/reviewers
|
||||||
teams = list(Group.objects.exclude(reviewrequest=None).distinct().order_by("name"))
|
teams = list(Group.objects.exclude(reviewrequest=None).distinct().order_by("name"))
|
||||||
|
|
|
@ -9,6 +9,8 @@ import json
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from email.utils import parsedate_to_datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.encoding import smart_bytes, force_str
|
from django.utils.encoding import smart_bytes, force_str
|
||||||
|
@ -22,7 +24,7 @@ from ietf.doc.utils import add_state_change_event
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.utils.log import log
|
from ietf.utils.log import log
|
||||||
from ietf.utils.mail import parseaddr, get_payload_text
|
from ietf.utils.mail import parseaddr, get_payload_text
|
||||||
from ietf.utils.timezone import local_timezone_to_utc, email_time_to_local_timezone, utc_to_local_timezone
|
from ietf.utils.timezone import local_timezone_to_utc
|
||||||
|
|
||||||
|
|
||||||
#PROTOCOLS_URL = "https://www.iana.org/protocols/"
|
#PROTOCOLS_URL = "https://www.iana.org/protocols/"
|
||||||
|
@ -160,8 +162,7 @@ def update_history_with_changes(changes, send_email=True):
|
||||||
|
|
||||||
for c in changes:
|
for c in changes:
|
||||||
docname = c['doc']
|
docname = c['doc']
|
||||||
timestamp = datetime.datetime.strptime(c["time"], "%Y-%m-%d %H:%M:%S")
|
timestamp = datetime.datetime.strptime(c["time"], "%Y-%m-%d %H:%M:%S",).replace(tzinfo=datetime.timezone.utc)
|
||||||
timestamp = utc_to_local_timezone(timestamp) # timestamps are in UTC
|
|
||||||
|
|
||||||
if c['type'] in ("iana_state", "iana_review"):
|
if c['type'] in ("iana_state", "iana_review"):
|
||||||
if c['type'] == "iana_state":
|
if c['type'] == "iana_state":
|
||||||
|
@ -244,7 +245,10 @@ def parse_review_email(text):
|
||||||
# date
|
# date
|
||||||
review_time = timezone.now()
|
review_time = timezone.now()
|
||||||
if "Date" in msg:
|
if "Date" in msg:
|
||||||
review_time = email_time_to_local_timezone(msg["Date"])
|
review_time = parsedate_to_datetime(msg["Date"])
|
||||||
|
# parsedate_to_datetime() may return a naive timezone - treat as UTC
|
||||||
|
if review_time.tzinfo is None or review_time.tzinfo.utcoffset(review_time) is None:
|
||||||
|
review_time = review_time.replace(tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
# by
|
# by
|
||||||
by = None
|
by = None
|
||||||
|
|
|
@ -25,6 +25,7 @@ from ietf.name.models import StdLevelName, StreamName
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.utils.log import log
|
from ietf.utils.log import log
|
||||||
from ietf.utils.mail import send_mail_text
|
from ietf.utils.mail import send_mail_text
|
||||||
|
from ietf.utils.timezone import datetime_from_date
|
||||||
|
|
||||||
#QUEUE_URL = "https://www.rfc-editor.org/queue2.xml"
|
#QUEUE_URL = "https://www.rfc-editor.org/queue2.xml"
|
||||||
#INDEX_URL = "https://www.rfc-editor.org/rfc/rfc-index.xml"
|
#INDEX_URL = "https://www.rfc-editor.org/rfc/rfc-index.xml"
|
||||||
|
@ -372,7 +373,7 @@ def update_docs_from_rfc_index(index_data, errata_data, skip_older_than_date=Non
|
||||||
|
|
||||||
for rfc_number, title, authors, rfc_published_date, current_status, updates, updated_by, obsoletes, obsoleted_by, also, draft, has_errata, stream, wg, file_formats, pages, abstract in index_data:
|
for rfc_number, title, authors, rfc_published_date, current_status, updates, updated_by, obsoletes, obsoleted_by, also, draft, has_errata, stream, wg, file_formats, pages, abstract in index_data:
|
||||||
|
|
||||||
if skip_older_than_date and rfc_published_date < skip_older_than_date:
|
if skip_older_than_date and datetime_from_date(rfc_published_date) < datetime_from_date(skip_older_than_date):
|
||||||
# speed up the process by skipping old entries
|
# speed up the process by skipping old entries
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -443,7 +444,7 @@ def update_docs_from_rfc_index(index_data, errata_data, skip_older_than_date=Non
|
||||||
# unfortunately, rfc_published_date doesn't include the correct day
|
# unfortunately, rfc_published_date doesn't include the correct day
|
||||||
# at the moment because the data only has month/year, so
|
# at the moment because the data only has month/year, so
|
||||||
# try to deduce it
|
# try to deduce it
|
||||||
d = datetime.datetime.combine(rfc_published_date, datetime.time())
|
d = datetime_from_date(rfc_published_date)
|
||||||
synthesized = timezone.now()
|
synthesized = timezone.now()
|
||||||
if abs(d - synthesized) > datetime.timedelta(days=60):
|
if abs(d - synthesized) > datetime.timedelta(days=60):
|
||||||
synthesized = d
|
synthesized = d
|
||||||
|
|
|
@ -23,6 +23,7 @@ from ietf.sync import iana, rfceditor
|
||||||
from ietf.utils.mail import outbox, empty_outbox
|
from ietf.utils.mail import outbox, empty_outbox
|
||||||
from ietf.utils.test_utils import login_testing_unauthorized
|
from ietf.utils.test_utils import login_testing_unauthorized
|
||||||
from ietf.utils.test_utils import TestCase
|
from ietf.utils.test_utils import TestCase
|
||||||
|
from ietf.utils.timezone import date_today
|
||||||
|
|
||||||
|
|
||||||
class IANASyncTests(TestCase):
|
class IANASyncTests(TestCase):
|
||||||
|
@ -191,7 +192,7 @@ ICANN
|
||||||
doc_name, review_time, by, comment = iana.parse_review_email(msg.encode('utf-8'))
|
doc_name, review_time, by, comment = iana.parse_review_email(msg.encode('utf-8'))
|
||||||
|
|
||||||
self.assertEqual(doc_name, draft.name)
|
self.assertEqual(doc_name, draft.name)
|
||||||
self.assertEqual(review_time, datetime.datetime(2012, 5, 10, 5, 0, rtime))
|
self.assertEqual(review_time, datetime.datetime(2012, 5, 10, 12, 0, rtime, tzinfo=datetime.timezone.utc))
|
||||||
self.assertEqual(by, Person.objects.get(user__username="iana"))
|
self.assertEqual(by, Person.objects.get(user__username="iana"))
|
||||||
self.assertIn("there are no IANA Actions", comment.replace("\n", ""))
|
self.assertIn("there are no IANA Actions", comment.replace("\n", ""))
|
||||||
|
|
||||||
|
@ -238,7 +239,7 @@ class RFCSyncTests(TestCase):
|
||||||
DocAlias.objects.create(name=updated_doc.name).docs.add(updated_doc)
|
DocAlias.objects.create(name=updated_doc.name).docs.add(updated_doc)
|
||||||
DocAlias.objects.create(name="rfc123").docs.add(updated_doc)
|
DocAlias.objects.create(name="rfc123").docs.add(updated_doc)
|
||||||
|
|
||||||
today = datetime.date.today()
|
today = date_today()
|
||||||
|
|
||||||
t = '''<?xml version="1.0" encoding="UTF-8"?>
|
t = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<rfc-index xmlns="http://www.rfc-editor.org/rfc-index"
|
<rfc-index xmlns="http://www.rfc-editor.org/rfc-index"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
def object_as_shallow_dict(obj):
|
def object_as_shallow_dict(obj):
|
||||||
|
@ -14,7 +16,7 @@ def object_as_shallow_dict(obj):
|
||||||
if isinstance(f, models.ManyToManyField):
|
if isinstance(f, models.ManyToManyField):
|
||||||
v = list(v.values_list("pk", flat=True))
|
v = list(v.values_list("pk", flat=True))
|
||||||
elif isinstance(f, models.DateTimeField):
|
elif isinstance(f, models.DateTimeField):
|
||||||
v = v.strftime('%Y-%m-%d %H:%M:%S')
|
v = v.astimezone(datetime.timezone.utc).isoformat()
|
||||||
elif isinstance(f, models.DateField):
|
elif isinstance(f, models.DateField):
|
||||||
v = v.strftime('%Y-%m-%d')
|
v = v.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,27 @@
|
||||||
import pytz
|
import pytz
|
||||||
import email.utils
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
|
# Default time zone for deadlines / expiration dates.
|
||||||
|
DEADLINE_TZINFO = ZoneInfo('PST8PDT')
|
||||||
|
|
||||||
|
|
||||||
|
def make_aware(dt, tzinfo):
|
||||||
|
"""Assign timezone to a naive datetime
|
||||||
|
|
||||||
|
Helper to deal with both pytz and zoneinfo type time zones. Can go away when pytz is removed.
|
||||||
|
"""
|
||||||
|
if hasattr(tzinfo, 'localize'):
|
||||||
|
return tzinfo.localize(dt) # pytz-style
|
||||||
|
else:
|
||||||
|
return dt.replace(tzinfo=tzinfo) # zoneinfo- / datetime.timezone-style
|
||||||
|
|
||||||
|
|
||||||
def local_timezone_to_utc(d):
|
def local_timezone_to_utc(d):
|
||||||
"""Takes a naive datetime in the local timezone and returns a
|
"""Takes a naive datetime in the local timezone and returns a
|
||||||
naive datetime with the corresponding UTC time."""
|
naive datetime with the corresponding UTC time."""
|
||||||
|
@ -14,29 +31,11 @@ def local_timezone_to_utc(d):
|
||||||
|
|
||||||
return d.replace(tzinfo=None)
|
return d.replace(tzinfo=None)
|
||||||
|
|
||||||
def utc_to_local_timezone(d):
|
|
||||||
"""Takes a naive datetime UTC and returns a naive datetime in the
|
|
||||||
local time zone."""
|
|
||||||
local_timezone = pytz.timezone(settings.TIME_ZONE)
|
|
||||||
|
|
||||||
d = local_timezone.normalize(d.replace(tzinfo=pytz.utc).astimezone(local_timezone))
|
def datetime_from_date(date, tz=pytz.utc):
|
||||||
|
"""Get datetime at midnight on a given date"""
|
||||||
return d.replace(tzinfo=None)
|
# accept either pytz or zoneinfo tzinfos until we get rid of pytz
|
||||||
|
return make_aware(datetime.datetime(date.year, date.month, date.day), tz)
|
||||||
def email_time_to_local_timezone(date_string):
|
|
||||||
"""Takes a time string from an email and returns a naive datetime
|
|
||||||
in the local time zone."""
|
|
||||||
|
|
||||||
t = email.utils.parsedate_tz(date_string)
|
|
||||||
d = datetime.datetime(*t[:6])
|
|
||||||
|
|
||||||
if t[7] != None:
|
|
||||||
d += datetime.timedelta(seconds=t[9])
|
|
||||||
|
|
||||||
return utc_to_local_timezone(d)
|
|
||||||
|
|
||||||
def date2datetime(date, tz=pytz.utc):
|
|
||||||
return datetime.datetime(*(date.timetuple()[:6]), tzinfo=tz)
|
|
||||||
|
|
||||||
|
|
||||||
def datetime_today(tzinfo=None):
|
def datetime_today(tzinfo=None):
|
||||||
|
|
Loading…
Reference in a new issue