Merge branch 'feat/tzaware' into jennifer/main-to-tzaware-conflicts

This commit is contained in:
Jennifer Richards 2022-09-14 12:36:33 -03:00 committed by GitHub
commit 1366b6de38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
136 changed files with 1903 additions and 834 deletions

View file

@ -139,6 +139,8 @@ jobs:
run: |
echo "Running checks..."
./ietf/manage.py check
./ietf/manage.py migrate || true
echo "USE_TZ = True" >> ./ietf/settings_local.py
./ietf/manage.py migrate
echo "Validating migrations..."
if ! ( ietf/manage.py makemigrations --dry-run --check --verbosity 3 ) ; then

View file

@ -12,5 +12,24 @@ yarn legacy:build
echo "Creating data directories..."
chmod +x ./docker/scripts/app-create-dirs.sh
./docker/scripts/app-create-dirs.sh
./ietf/manage.py check
./ietf/manage.py migrate
if ./ietf/manage.py showmigrations | grep "\[ \] 0003_pause_to_change_use_tz"; then
if grep "USE_TZ" ./ietf/settings_local.py; then
cat ./ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = False/' > /tmp/settings_local.py && mv /tmp/settings_local.py ./ietf/settings_local.py
else
echo "USE_TZ = False" >> ./ietf/settings_local.py
fi
# This is expected to exit non-zero at the pause
/usr/local/bin/python ./ietf/manage.py migrate || true
cat ./ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = True/' > /tmp/settings_local.py && mv /tmp/settings_local.py ./ietf/settings_local.py
/usr/local/bin/python ./ietf/manage.py migrate
else
if grep "USE_TZ" ./ietf/settings_local.py; then
cat ./ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = True/' > /tmp/settings_local.py && mv /tmp/settings_local.py ./ietf/settings_local.py
else
echo "USE_TZ = True" >> ./ietf/settings_local.py
/usr/local/bin/python ./ietf/manage.py migrate
fi
fi

View file

@ -100,7 +100,28 @@ echo "Starting memcached..."
echo "Running initial checks..."
/usr/local/bin/python $WORKSPACEDIR/ietf/manage.py check --settings=settings_local
# /usr/local/bin/python $WORKSPACEDIR/ietf/manage.py migrate --settings=settings_local
# Migrate, adjusting to what the current state of the underlying database might be:
if ietf/manage.py showmigrations | grep "\[ \] 0003_pause_to_change_use_tz"; then
if grep "USE_TZ" $WORKSPACEDIR/ietf/settings_local.py; then
cat $WORKSPACEDIR/ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = False/' > /tmp/settings_local.py && mv /tmp/settings_local.py $WORKSPACEDIR/ietf/settings_local.py
else
echo "USE_TZ = False" >> $WORKSPACEDIR/ietf/settings_local.py
fi
# This is expected to exit non-zero at the pause
/usr/local/bin/python $WORKSPACEDIR/ietf/manage.py migrate --settings=settings_local || true
cat $WORKSPACEDIR/ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = True/' > /tmp/settings_local.py && mv /tmp/settings_local.py $WORKSPACEDIR/ietf/settings_local.py
/usr/local/bin/python $WORKSPACEDIR/ietf/manage.py migrate --settings=settings_local
else
if grep "USE_TZ" $WORKSPACEDIR/ietf/settings_local.py; then
cat $WORKSPACEDIR/ietf/settings_local.py | sed 's/USE_TZ.*$/USE_TZ = True/' > /tmp/settings_local.py && mv /tmp/settings_local.py $WORKSPACEDIR/ietf/settings_local.py
else
echo "USE_TZ = True" >> $WORKSPACEDIR/ietf/settings_local.py
/usr/local/bin/python $WORKSPACEDIR/ietf/manage.py migrate --settings=settings_local
fi
fi
echo "-----------------------------------------------------------------"
echo "Done!"

View file

@ -3,7 +3,6 @@
import os
import datetime
import collections
import io
@ -14,6 +13,7 @@ import debug # pyflakes:ignore
from django.core.management.base import AppCommand
from django.db import models
from django.template import Template, Context
from django.utils import timezone
from tastypie.resources import ModelResource
@ -89,7 +89,7 @@ class Command(AppCommand):
info = dict(
app=app.name,
app_label=app.label,
date=datetime.datetime.now()
date=timezone.now()
)
new_models = {}
for model, rclass_name in missing_resources:

View file

@ -30,6 +30,7 @@ from ietf.person.models import User
from ietf.person.models import PersonalApiKey
from ietf.stats.models import MeetingRegistration
from ietf.utils.mail import outbox, get_payload_text
from ietf.utils.models import DumpInfo
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
OMITTED_APPS = (
@ -419,10 +420,17 @@ class CustomApiTests(TestCase):
self.assertEqual(set(missing_fields), set(drop_fields))
def test_api_version(self):
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=timezone.utc), host='testapi.example.com',tz='UTC')
url = urlreverse('ietf.api.views.version')
r = self.client.get(url)
data = r.json()
self.assertEqual(data['version'], ietf.__version__+ietf.__patch__)
self.assertEqual(data['dumptime'], "2022-08-31 07:10:01 +0000")
DumpInfo.objects.update(tz='PST8PDT')
r = self.client.get(url)
data = r.json()
self.assertEqual(data['dumptime'], "2022-08-31 07:10:01 -0700")
def test_api_appauth(self):
url = urlreverse('ietf.api.views.app_auth')

View file

@ -203,8 +203,13 @@ def api_new_meeting_registration(request):
def version(request):
dumpdate = None
dumpinfo = DumpInfo.objects.order_by('-date').first()
dumptime = pytz.timezone(dumpinfo.tz).localize(dumpinfo.date).strftime('%Y-%m-%d %H:%M:%S %z') if dumpinfo else None
if dumpinfo:
dumpdate = dumpinfo.date
if dumpinfo.tz != "UTC":
dumpdate = pytz.timezone(dumpinfo.tz).localize(dumpinfo.date.replace(tzinfo=None))
dumptime = dumpdate.strftime('%Y-%m-%d %H:%M:%S %z') if dumpinfo else None
return HttpResponse(
json.dumps({
'version': ietf.__version__+ietf.__patch__,

View file

@ -18,6 +18,7 @@ django.setup()
from django.conf import settings
from optparse import OptionParser
from zoneinfo import ZoneInfo
parser = OptionParser()
parser.add_option("-f", "--from", dest="start",
@ -38,13 +39,16 @@ CLOCK_SKEW_COMPENSATION = 5 # seconds
MAX_INTERVAL_ACCEPTED_BY_IANA = datetime.timedelta(hours=23)
local_tzinfo = ZoneInfo(settings.TIME_ZONE)
start = datetime.datetime.now() - datetime.timedelta(hours=23) + datetime.timedelta(seconds=CLOCK_SKEW_COMPENSATION)
if options.start:
start = datetime.datetime.strptime(options.start, "%Y-%m-%d %H:%M:%S")
start = start.replace(tzinfo=local_tzinfo).astimezone(datetime.timezone.utc)
end = start + datetime.timedelta(hours=23)
if options.end:
end = datetime.datetime.strptime(options.end, "%Y-%m-%d %H:%M:%S")
end = datetime.datetime.strptime(options.end, "%Y-%m-%d %H:%M:%S").replace(tzinfo=local_tzinfo)
end = end.astimezone(datetime.timezone.utc)
syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER)
@ -52,7 +56,13 @@ syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER)
from ietf.sync.iana import fetch_changes_json, parse_changes_json, update_history_with_changes
syslog.syslog("Updating history log with new changes from IANA from %s, period %s - %s" % (settings.IANA_SYNC_CHANGES_URL, start, end))
syslog.syslog(
"Updating history log with new changes from IANA from %s, period %s - %s" % (
settings.IANA_SYNC_CHANGES_URL,
start.astimezone(local_tzinfo),
end.astimezone(local_tzinfo),
)
)
t = start
while t < end:

View file

@ -3,7 +3,7 @@
# This script requires that the proper virtual python environment has been
# invoked before start
import datetime, os, sys
import os, sys
import syslog
# boilerplate
@ -16,6 +16,7 @@ syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER)
import django
django.setup()
from django.utils import timezone
from ietf.utils.mail import log_smtp_exception, send_error_email
from smtplib import SMTPException
@ -32,7 +33,7 @@ from ietf.message.models import SendQueue
mode = sys.argv[1]
now = datetime.datetime.now()
now = timezone.now()
needs_sending = SendQueue.objects.filter(sent_at=None).select_related("message")
if mode == "specific":

View file

@ -10,6 +10,7 @@ import uuid
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404, render
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from django.utils.html import strip_tags
import debug # pyflakes:ignore
@ -218,7 +219,7 @@ def feed(request, username=None, acronym=None, group_type=None):
significant = request.GET.get('significant', '') == '1'
documents = docs_tracked_by_community_list(clist).values_list('pk', flat=True)
since = datetime.datetime.now() - datetime.timedelta(days=14)
since = timezone.now() - datetime.timedelta(days=14)
events = DocEvent.objects.filter(
doc__id__in=documents,
@ -243,7 +244,7 @@ def feed(request, username=None, acronym=None, group_type=None):
'title': title,
'subtitle': subtitle,
'id': feed_id.urn,
'updated': datetime.datetime.now(),
'updated': timezone.now(),
}, content_type='text/xml')

View file

@ -4,6 +4,7 @@
from django.conf import settings
from django.utils import timezone
import datetime, os, shutil, glob, re
from pathlib import Path
@ -17,6 +18,7 @@ from ietf.person.models import Person
from ietf.meeting.models import Meeting
from ietf.doc.utils import add_state_change_event, update_action_holders
from ietf.mailtrigger.utils import gather_address_lists
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
nonexpirable_states: Optional[List[State]] = None
@ -52,17 +54,17 @@ def expirable_drafts(queryset=None):
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)
return expirable_drafts().filter(expires__gte=start_date, expires__lt=end_date)
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):
if when == None:
when = datetime.datetime.now()
when = timezone.now()
meeting = Meeting.objects.filter(type='ietf', date__gte=when-datetime.timedelta(days=7)).order_by('date').first()

View file

@ -10,6 +10,7 @@ import datetime
from typing import Optional # pyflakes:ignore
from django.conf import settings
from django.utils import timezone
from ietf.doc.models import ( Document, DocEvent, NewRevisionDocEvent, DocAlias, State, DocumentAuthor,
StateDocEvent, BallotPositionDocEvent, BallotDocEvent, BallotType, IRSGBallotDocEvent, TelechatDocEvent,
@ -38,7 +39,7 @@ class BaseDocumentFactory(factory.django.DjangoModelFactory):
rev = '00'
std_level_id = None # type: Optional[str]
intended_std_level_id = None
time = datetime.datetime.now()
time = timezone.now()
expires = factory.LazyAttribute(lambda o: o.time+datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE))
pages = factory.fuzzy.FuzzyInteger(2,400)
@ -357,7 +358,8 @@ class TelechatDocEventFactory(DocEventFactory):
class Meta:
model = TelechatDocEvent
telechat_date = datetime.datetime.today()+datetime.timedelta(days=14)
# note: this is evaluated at import time and not updated - all events will have the same telechat_date
telechat_date = timezone.now()+datetime.timedelta(days=14)
type = 'scheduled_for_telechat'
class NewRevisionDocEventFactory(DocEventFactory):
@ -410,7 +412,7 @@ class IRSGBallotDocEventFactory(BallotDocEventFactory):
class Meta:
model = IRSGBallotDocEvent
duedate = datetime.datetime.now() + datetime.timedelta(days=14)
duedate = timezone.now() + datetime.timedelta(days=14)
ballot_type = factory.SubFactory(BallotTypeFactory, slug='irsg-approve')
class BallotPositionDocEventFactory(DocEventFactory):

View file

@ -10,11 +10,13 @@ from django.utils.feedgenerator import Atom1Feed, Rss201rev2Feed
from django.urls import reverse as urlreverse
from django.template.defaultfilters import truncatewords, truncatewords_html, date as datefilter
from django.template.defaultfilters import linebreaks # type: ignore
from django.utils import timezone
from django.utils.html import strip_tags
from ietf.doc.models import Document, State, LastCallDocEvent, DocEvent
from ietf.doc.utils import augment_events_with_revision
from ietf.doc.templatetags.ietf_filters import format_textarea
from ietf.utils.timezone import RPC_TZINFO
def strip_control_characters(s):
@ -133,9 +135,16 @@ class RfcFeed(Feed):
def items(self):
if self.year:
rfc_events = DocEvent.objects.filter(type='published_rfc',time__year=self.year).order_by('-time')
# Find published RFCs based on their official publication year
start_of_year = datetime.datetime(int(self.year), 1, 1, tzinfo=RPC_TZINFO)
start_of_next_year = datetime.datetime(int(self.year) + 1, 1, 1, tzinfo=RPC_TZINFO)
rfc_events = DocEvent.objects.filter(
type='published_rfc',
time__gte=start_of_year,
time__lt=start_of_next_year,
).order_by('-time')
else:
cutoff = datetime.datetime.now() - datetime.timedelta(days=8)
cutoff = timezone.now() - datetime.timedelta(days=8)
rfc_events = DocEvent.objects.filter(type='published_rfc',time__gte=cutoff).order_by('-time')
results = [(e.doc, e.time) for e in rfc_events]
for doc,time in results:

View file

@ -10,6 +10,7 @@ from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
from django.urls import reverse as urlreverse
from django.utils import timezone
from django.utils.encoding import force_text
import debug # pyflakes:ignore
@ -418,7 +419,7 @@ def generate_issue_ballot_mail(request, doc, ballot):
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
last_call_expires = e.expires if e else None
last_call_has_expired = last_call_expires and last_call_expires < datetime.datetime.now()
last_call_has_expired = last_call_expires and last_call_expires < timezone.now()
return render_to_string("doc/mail/issue_iesg_ballot_mail.txt",
dict(doc=doc,
@ -437,7 +438,7 @@ def _send_irsg_ballot_email(request, doc, ballot, subject, template):
(to, cc) = gather_address_lists('irsg_ballot_issued', doc=doc)
sender = 'IESG Secretary <iesg-secretary@ietf.org>'
ballot_expired = ballot.duedate < datetime.datetime.now()
ballot_expired = ballot.duedate < timezone.now()
active_ballot = doc.active_ballot()
if active_ballot is None:
needed_bps = ''

View file

@ -16,6 +16,7 @@ from tempfile import mkstemp
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
import debug # pyflakes:ignore
@ -101,7 +102,7 @@ class Command(BaseCommand):
'that have seen activity in the last %s years.' % (DEFAULT_YEARS))
def handle(self, *args, **options):
show_since = datetime.datetime.now() - datetime.timedelta(DEFAULT_YEARS*365)
show_since = timezone.now() - datetime.timedelta(DEFAULT_YEARS*365)
date = time.strftime("%Y-%m-%d_%H:%M:%S")
signature = '# Generated by %s at %s\n' % (os.path.abspath(__file__), date)

View file

@ -11,6 +11,7 @@ import sys
from django.conf import settings
from django.core.management.base import BaseCommand
from django.template.loader import render_to_string
from django.utils import timezone
import debug # pyflakes:ignore
@ -68,7 +69,7 @@ class Command(BaseCommand):
if process_all:
doc_events = NewRevisionDocEvent.objects.filter(type='new_revision', doc__type_id='draft')
else:
start = datetime.datetime.now() - datetime.timedelta(days=days)
start = timezone.now() - datetime.timedelta(days=days)
doc_events = NewRevisionDocEvent.objects.filter(type='new_revision', doc__type_id='draft', time__gte=start)
doc_events = doc_events.order_by('time')

View file

@ -0,0 +1,39 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('doc', '0044_procmaterials_states'),
]
operations = [
migrations.AlterField(
model_name='deletedevent',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='docevent',
name='time',
field=models.DateTimeField(db_index=True, default=django.utils.timezone.now, help_text='When the event happened'),
),
migrations.AlterField(
model_name='dochistory',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='document',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='documentactionholder',
name='time_added',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

View 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),
]

View file

@ -18,6 +18,7 @@ from django.core.validators import URLValidator, RegexValidator
from django.urls import reverse as urlreverse
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.html import mark_safe # type:ignore
@ -35,6 +36,7 @@ from ietf.utils.decorators import memoize
from ietf.utils.validators import validate_no_control_chars
from ietf.utils.mail import formataddr
from ietf.utils.models import ForeignKey
from ietf.utils.timezone import RPC_TZINFO
if TYPE_CHECKING:
# importing other than for type checking causes errors due to cyclic imports
from ietf.meeting.models import ProceedingsMaterial, Session
@ -85,7 +87,7 @@ IESG_SUBSTATE_TAGS = ('ad-f-up', 'need-rev', 'extpty')
class DocumentInfo(models.Model):
"""Any kind of document. Draft, RFC, Charter, IPR Statement, Liaison Statement"""
time = models.DateTimeField(default=datetime.datetime.now) # should probably have auto_now=True
time = models.DateTimeField(default=timezone.now) # should probably have auto_now=True
type = ForeignKey(DocTypeName, blank=True, null=True) # Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email, Review, Issue, Wiki, External ...
title = models.CharField(max_length=255, validators=[validate_no_control_chars, ])
@ -682,7 +684,7 @@ class DocumentActionHolder(models.Model):
"""Action holder for a document"""
document = ForeignKey('Document')
person = ForeignKey(Person)
time_added = models.DateTimeField(default=datetime.datetime.now)
time_added = models.DateTimeField(default=timezone.now)
CLEAR_ACTION_HOLDERS_STATES = ['approved', 'ann', 'rfcqueue', 'pub', 'dead'] # draft-iesg state slugs
GROUP_ROLES_OF_INTEREST = ['chair', 'techadv', 'editor', 'secr']
@ -838,7 +840,7 @@ class Document(DocumentInfo):
def previous_telechat_date(self):
"Return the most recent telechat date in the past, if any (even if there's another in the future)"
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat", telechat_date__lt=datetime.datetime.now())
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat", telechat_date__lt=timezone.now())
return e.telechat_date if e else None
def request_closed_time(self, review_req):
@ -924,13 +926,18 @@ class Document(DocumentInfo):
return s
def pub_date(self):
"""This is the rfc publication date (datetime) for RFCs,
and the new-revision datetime for other documents."""
"""Get the publication date for this document
This is the rfc publication date for RFCs, and the new-revision date for other documents.
"""
if self.get_state_slug() == "rfc":
# As of Sept 2022, in ietf.sync.rfceditor.update_docs_from_rfc_index() `published_rfc` events are
# created with a timestamp whose date *in the PST8PDT timezone* is the official publication date
# assigned by the RFC editor.
event = self.latest_event(type='published_rfc')
else:
event = self.latest_event(type='new_revision')
return event.time
return event.time.astimezone(RPC_TZINFO).date() if event else None
def is_dochistory(self):
return False
@ -1208,7 +1215,7 @@ EVENT_TYPES = [
class DocEvent(models.Model):
"""An occurrence for a document, used for tracking who, when and what."""
time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened", db_index=True)
time = models.DateTimeField(default=timezone.now, help_text="When the event happened", db_index=True)
type = models.CharField(max_length=50, choices=EVENT_TYPES)
by = ForeignKey(Person)
doc = ForeignKey(Document)
@ -1388,7 +1395,7 @@ class DeletedEvent(models.Model):
content_type = ForeignKey(ContentType)
json = models.TextField(help_text="Deleted object in JSON format, with attribute names chosen to be suitable for passing into the relevant create method.")
by = ForeignKey(Person)
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
def __str__(self):
return u"%s by %s %s" % (self.content_type, self.by, self.time)

View file

@ -18,6 +18,7 @@ from django.urls import reverse as urlreverse
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.urls import NoReverseMatch
from django.utils import timezone
import debug # pyflakes:ignore
@ -318,7 +319,7 @@ def timesince_days(date):
"""Returns the number of days since 'date' (relative to now)"""
if date.__class__ is not datetime.datetime:
date = datetime.datetime(date.year, date.month, date.day)
delta = datetime.datetime.now() - date
delta = timezone.now() - date
return delta.days
@register.filter
@ -637,19 +638,19 @@ def action_holder_badge(action_holder):
>>> action_holder_badge(DocumentActionHolderFactory())
''
>>> action_holder_badge(DocumentActionHolderFactory(time_added=datetime.datetime.now() - datetime.timedelta(days=15)))
>>> action_holder_badge(DocumentActionHolderFactory(time_added=timezone.now() - datetime.timedelta(days=15)))
''
>>> action_holder_badge(DocumentActionHolderFactory(time_added=datetime.datetime.now() - datetime.timedelta(days=16)))
>>> action_holder_badge(DocumentActionHolderFactory(time_added=timezone.now() - datetime.timedelta(days=16)))
'<span class="badge bg-danger" title="In state for 16 days; goal is &lt;15 days."><i class="bi bi-clock-fill"></i> 16</span>'
>>> action_holder_badge(DocumentActionHolderFactory(time_added=datetime.datetime.now() - datetime.timedelta(days=30)))
>>> action_holder_badge(DocumentActionHolderFactory(time_added=timezone.now() - datetime.timedelta(days=30)))
'<span class="badge bg-danger" title="In state for 30 days; goal is &lt;15 days."><i class="bi bi-clock-fill"></i> 30</span>'
>>> settings.DOC_ACTION_HOLDER_AGE_LIMIT_DAYS = old_limit
"""
age_limit = settings.DOC_ACTION_HOLDER_AGE_LIMIT_DAYS
age = (datetime.datetime.now() - action_holder.time_added).days
age = (timezone.now() - action_holder.time_added).days
if age > age_limit:
return mark_safe(
'<span class="badge bg-danger" title="In state for %d day%s; goal is &lt;%d days."><i class="bi bi-clock-fill"></i> %d</span>'

View file

@ -18,6 +18,7 @@ from pyquery import PyQuery
from urllib.parse import urlparse, parse_qs
from tempfile import NamedTemporaryFile
from collections import defaultdict
from zoneinfo import ZoneInfo
from django.core.management import call_command
from django.urls import reverse as urlreverse
@ -25,6 +26,7 @@ from django.conf import settings
from django.forms import Form
from django.utils.html import escape
from django.test import override_settings
from django.utils import timezone
from django.utils.text import slugify
from tastypie.test import ResourceTestCaseMixin
@ -56,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 TestCase
from ietf.utils.text import normalize_text
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO, RPC_TZINFO
class SearchTests(TestCase):
def test_search(self):
@ -385,13 +389,13 @@ class SearchTests(TestCase):
# Three drafts to show with various warnings
drafts = WgDraftFactory.create_batch(3,states=[('draft','active'),('draft-iesg','ad-eval')])
for index, draft in enumerate(drafts):
StateDocEventFactory(doc=draft, state=('draft-iesg','ad-eval'), time=datetime.datetime.now()-datetime.timedelta(days=[1,15,29][index]))
StateDocEventFactory(doc=draft, state=('draft-iesg','ad-eval'), time=timezone.now()-datetime.timedelta(days=[1,15,29][index]))
draft.action_holders.set([PersonFactory()])
# And one draft that should not show (with the default of 7 days to view)
old = WgDraftFactory()
old.docevent_set.filter(newrevisiondocevent__isnull=False).update(time=datetime.datetime.now()-datetime.timedelta(days=8))
StateDocEventFactory(doc=old, time=datetime.datetime.now()-datetime.timedelta(days=8))
old.docevent_set.filter(newrevisiondocevent__isnull=False).update(time=timezone.now()-datetime.timedelta(days=8))
StateDocEventFactory(doc=old, time=timezone.now()-datetime.timedelta(days=8))
url = urlreverse('ietf.doc.views_search.recent_drafts')
r = self.client.get(url)
@ -764,7 +768,7 @@ Man Expires September 22, 2015 [Page 3]
replacement = WgDraftFactory(
name="draft-ietf-replacement",
time=datetime.datetime.now(),
time=timezone.now(),
title="Replacement Draft",
stream_id=draft.stream_id, group_id=draft.group_id, abstract=draft.abstract,stream=draft.stream, rev=draft.rev,
pages=draft.pages, intended_std_level_id=draft.intended_std_level_id,
@ -1427,6 +1431,8 @@ Man Expires September 22, 2015 [Page 3]
def test_draft_group_link(self):
"""Link to group 'about' page should have correct format"""
event_datetime = datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO)
for group_type_id in ['wg', 'rg', 'ag']:
group = GroupFactory(type_id=group_type_id)
draft = WgDraftFactory(name='draft-document-%s' % group_type_id, group=group)
@ -1435,7 +1441,7 @@ Man Expires September 22, 2015 [Page 3]
self.assert_correct_wg_group_link(r, 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
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)))
@ -1450,7 +1456,7 @@ Man Expires September 22, 2015 [Page 3]
self.assert_correct_non_wg_group_link(r, 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
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)))
@ -1580,7 +1586,7 @@ class DocTestCase(TestCase):
name = "session-72-mars-1",
meeting = Meeting.objects.get(number='72'),
group = Group.objects.get(acronym='mars'),
modified = datetime.datetime.now(),
modified = timezone.now(),
add_to_schedule=False,
)
SchedulingEvent.objects.create(
@ -1610,7 +1616,7 @@ class DocTestCase(TestCase):
type="changed_ballot_position",
pos_id="yes",
comment="Looks fine to me",
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
balloter=Person.objects.get(user__username="ad"),
by=Person.objects.get(name="(System)"))
@ -1644,7 +1650,7 @@ class DocTestCase(TestCase):
type="changed_ballot_position",
pos_id="noobj",
comment="Still looks okay to me",
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
balloter=Person.objects.get(user__username="ad"),
by=Person.objects.get(name="(System)"))
@ -1666,7 +1672,7 @@ class DocTestCase(TestCase):
type="changed_ballot_position",
pos_id="yes",
comment="Looks fine to me",
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
balloter=Person.objects.get(user__username="ad"),
by=Person.objects.get(name="(System)"))
@ -1836,7 +1842,7 @@ class DocTestCase(TestCase):
desc="Last call\x0b", # include a control character to be sure it does not break anything
type="sent_last_call",
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/")
self.assertEqual(r.status_code, 200)
@ -1884,10 +1890,14 @@ class DocTestCase(TestCase):
#other_aliases = ['rfc6020',],
states = [('draft','rfc'),('draft-iesg','pub')],
std_level_id = 'ps',
time = datetime.datetime(2010,10,10),
time = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo(settings.TIME_ZONE)),
)
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=RPC_TZINFO),
)
#
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=rfc.name))
r = self.client.get(url)
@ -1905,10 +1915,14 @@ class DocTestCase(TestCase):
stream_id = 'ise',
states = [('draft','rfc'),('draft-iesg','pub')],
std_level_id = 'inf',
time = datetime.datetime(1990,0o4,0o1),
time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo(settings.TIME_ZONE)),
)
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=RPC_TZINFO),
)
#
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=april1.name))
r = self.client.get(url)
@ -2043,7 +2057,8 @@ class GenerateDraftAliasesTests(TestCase):
super().tearDown()
def testManagementCommand(self):
a_month_ago = datetime.datetime.now() - datetime.timedelta(30)
a_month_ago = (timezone.now() - datetime.timedelta(30)).astimezone(RPC_TZINFO)
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
shepherd = PersonFactory()
author1 = PersonFactory()
@ -2058,9 +2073,9 @@ class GenerateDraftAliasesTests(TestCase):
doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), 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)
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago.strftime("%Y-%m-%d"))
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10))
DocEventFactory.create(doc=doc4, type='published_rfc', time = '2010-10-10')
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, tzinfo=ZoneInfo(settings.TIME_ZONE)))
DocEventFactory.create(doc=doc4, type='published_rfc', time=datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO))
doc5 = IndividualDraftFactory(authors=[author6])
args = [ ]

View file

@ -12,6 +12,7 @@ import debug # pyflakes:ignore
from django.test import RequestFactory
from django.utils.text import slugify
from django.urls import reverse as urlreverse
from django.utils import timezone
from ietf.doc.models import (Document, State, DocEvent,
BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, TelechatDocEvent)
@ -105,7 +106,7 @@ class EditPositionTests(TestCase):
draft = WgDraftFactory(ad=ad)
url = urlreverse('ietf.doc.views_ballot.api_set_position')
create_ballot_if_not_open(None, draft, ad, 'approve')
ad.user.last_login = datetime.datetime.now()
ad.user.last_login = timezone.now()
ad.user.save()
apikey = PersonalApiKey.objects.create(endpoint=url, person=ad)
@ -238,9 +239,9 @@ class EditPositionTests(TestCase):
doc=draft, rev=draft.rev, type="changed_ballot_position",
by=ad, balloter=ad, ballot=ballot, pos=BallotPositionName.objects.get(slug="discuss"),
discuss="This draft seems to be lacking a clearer title?",
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
comment="Test!",
comment_time=datetime.datetime.now())
comment_time=timezone.now())
url = urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=draft.name,
ballot_id=ballot.pk))
@ -466,7 +467,7 @@ class BallotWriteupsTests(TestCase):
doc=draft,
rev=draft.rev,
desc='issued last call',
expires = datetime.datetime.now()+datetime.timedelta(days = 1 if case=='future' else -1)
expires = timezone.now()+datetime.timedelta(days = 1 if case=='future' else -1)
)
url = urlreverse('ietf.doc.views_ballot.ballot_writeupnotes', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "ad", url)
@ -791,7 +792,7 @@ class ApproveBallotTests(TestCase):
doc=draft,
rev=draft.rev,
desc='issued last call',
expires = datetime.datetime.now()-datetime.timedelta(days=14) )
expires = timezone.now()-datetime.timedelta(days=14) )
WriteupDocEvent.objects.create(
by=Person.objects.get(name='(System)'),
doc=draft,
@ -1117,7 +1118,7 @@ class RegenerateLastCallTestCase(TestCase):
class BallotContentTests(TestCase):
def test_ballotpositiondocevent_any_email_sent(self):
now = datetime.datetime.now() # be sure event timestamps are at distinct times
now = timezone.now() # be sure event timestamps are at distinct times
bpde_with_null_send_email = BallotPositionDocEventFactory(
time=now - datetime.timedelta(minutes=30),
send_email=None,
@ -1219,7 +1220,7 @@ class BallotContentTests(TestCase):
balloter=balloters[0],
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=True,
)
BallotPositionDocEventFactory(
@ -1227,7 +1228,7 @@ class BallotContentTests(TestCase):
balloter=balloters[1],
pos_id='noobj',
comment='Commentary',
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
send_email=True,
)
@ -1237,7 +1238,7 @@ class BallotContentTests(TestCase):
balloter=balloters[2],
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=False,
)
BallotPositionDocEventFactory(
@ -1245,7 +1246,7 @@ class BallotContentTests(TestCase):
balloter=balloters[3],
pos_id='noobj',
comment='Commentary',
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
send_email=False,
)
@ -1255,7 +1256,7 @@ class BallotContentTests(TestCase):
balloter=balloters[4],
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now() - datetime.timedelta(days=1),
discuss_time=timezone.now() - datetime.timedelta(days=1),
send_email=True,
)
BallotPositionDocEventFactory(
@ -1263,7 +1264,7 @@ class BallotContentTests(TestCase):
balloter=balloters[4],
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=False,
)
BallotPositionDocEventFactory(
@ -1271,7 +1272,7 @@ class BallotContentTests(TestCase):
balloter=balloters[5],
pos_id='noobj',
comment='Commentary',
comment_time=datetime.datetime.now() - datetime.timedelta(days=1),
comment_time=timezone.now() - datetime.timedelta(days=1),
send_email=True,
)
BallotPositionDocEventFactory(
@ -1279,7 +1280,7 @@ class BallotContentTests(TestCase):
balloter=balloters[5],
pos_id='noobj',
comment='Commentary',
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
send_email=False,
)
@ -1296,7 +1297,7 @@ class BallotContentTests(TestCase):
balloter__plain='plain name1',
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=False,
).balloter
send_email_balloter = BallotPositionDocEventFactory(
@ -1304,7 +1305,7 @@ class BallotContentTests(TestCase):
balloter__plain='plain name2',
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=True,
).balloter
prev_send_email_balloter = BallotPositionDocEventFactory(
@ -1312,7 +1313,7 @@ class BallotContentTests(TestCase):
balloter__plain='plain name3',
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now() - datetime.timedelta(days=1),
discuss_time=timezone.now() - datetime.timedelta(days=1),
send_email=True,
).balloter
BallotPositionDocEventFactory(
@ -1320,7 +1321,7 @@ class BallotContentTests(TestCase):
balloter=prev_send_email_balloter,
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=False,
)
@ -1351,7 +1352,7 @@ class BallotContentTests(TestCase):
balloter=balloters[0],
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=None,
)
BallotPositionDocEventFactory(
@ -1359,7 +1360,7 @@ class BallotContentTests(TestCase):
balloter=balloters[1],
pos_id='noobj',
comment='Commentary',
comment_time=datetime.datetime.now(),
comment_time=timezone.now(),
send_email=None,
)
old_balloter = BallotPositionDocEventFactory(
@ -1367,7 +1368,7 @@ class BallotContentTests(TestCase):
balloter__plain='plain name', # ensure plain name is slugifiable
pos_id='discuss',
discuss='Discussion text',
discuss_time=datetime.datetime.now(),
discuss_time=timezone.now(),
send_email=None,
).balloter

View file

@ -14,6 +14,7 @@ from html import unescape
from django.conf import settings
from django.urls import reverse as urlreverse
from django.template.loader import render_to_string
from django.utils import timezone
from ietf.group.factories import RoleFactory
from ietf.doc.factories import BofreqFactory, NewRevisionDocEventFactory
@ -48,7 +49,7 @@ This test section has some text.
states = State.objects.filter(type_id='bofreq')
self.assertTrue(states.count()>0)
for i in range(3*len(states)):
BofreqFactory(states=[('bofreq',states[i%len(states)].slug)],newrevisiondocevent__time=datetime.datetime.today()-datetime.timedelta(days=randint(0,20)))
BofreqFactory(states=[('bofreq',states[i%len(states)].slug)],newrevisiondocevent__time=timezone.now()-datetime.timedelta(days=randint(0,20)))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)

View file

@ -26,6 +26,8 @@ from ietf.person.models import Person
from ietf.utils.test_utils import TestCase
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import login_testing_unauthorized
from ietf.utils.timezone import datetime_today, date_today, DEADLINE_TZINFO
class ViewCharterTests(TestCase):
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
# 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,
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
time=last_week)
@ -746,7 +748,7 @@ class EditCharterTests(TestCase):
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,
state_id="active",
desc="Has been copied",
@ -826,7 +828,7 @@ class EditCharterTests(TestCase):
m = GroupMilestone.objects.create(group=charter.group,
state_id="active",
desc="Test milestone",
due=datetime.date.today(),
due=date_today(DEADLINE_TZINFO),
resolved="")
url = urlreverse('ietf.doc.views_charter.charter_with_milestones_txt', kwargs=dict(name=charter.name, rev=charter.rev))

View file

@ -13,6 +13,7 @@ from pyquery import PyQuery
from django.urls import reverse as urlreverse
from django.conf import settings
from django.utils import timezone
from django.utils.html import escape
import debug # pyflakes:ignore
@ -33,6 +34,7 @@ from ietf.iesg.models import TelechatDate
from ietf.utils.test_utils import login_testing_unauthorized
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import TestCase
from ietf.utils.timezone import date_today, datetime_from_date
class ChangeStateTests(TestCase):
@ -401,11 +403,11 @@ class EditInfoTests(TestCase):
# change to a telechat that should cause returning item to be auto-detected
# 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()
ad = Person.objects.get(user__username="ad")
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()
r = self.client.post(url, data)
@ -428,7 +430,7 @@ class EditInfoTests(TestCase):
self.assertTrue("Telechat update" in outbox[-1]['Subject'])
# 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.date = next_week
td.save()
@ -618,7 +620,7 @@ class ResurrectTests(DraftFileMixin, TestCase):
self.assertEqual(draft.docevent_set.count(), events_before + 1)
self.assertEqual(draft.latest_event().type, "completed_resurrect")
self.assertEqual(draft.get_state_slug(), "active")
self.assertTrue(draft.expires >= datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE - 1))
self.assertTrue(draft.expires >= timezone.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE - 1))
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue('Resurrection Completed' in outbox[-1]['Subject'])
self.assertTrue('iesg-secretary' in outbox[-1]['To'])
@ -659,7 +661,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
# hack into expirable state
draft.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
draft.expires = datetime.datetime.now() + datetime.timedelta(days=10)
draft.expires = timezone.now() + datetime.timedelta(days=10)
draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
self.assertEqual(len(list(get_soon_to_expire_drafts(14))), 1)
@ -698,7 +700,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
# hack into expirable state
draft.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
draft.expires = datetime.datetime.now()
draft.expires = timezone.now()
draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
self.assertEqual(len(list(get_expired_drafts())), 1)
@ -741,7 +743,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
draft.delete()
rgdraft = RgDraftFactory(expires=datetime.datetime.now())
rgdraft = RgDraftFactory(expires=timezone.now())
self.assertEqual(len(list(get_expired_drafts())), 1)
for slug in ('iesg-rev','irsgpoll'):
rgdraft.set_state(State.objects.get(type_id='draft-stream-irtf',slug=slug))
@ -791,7 +793,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
# expire draft
draft.set_state(State.objects.get(used=True, type="draft", slug="expired"))
draft.expires = datetime.datetime.now() - datetime.timedelta(days=1)
draft.expires = timezone.now() - datetime.timedelta(days=1)
draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
e = DocEvent(doc=draft, rev=draft.rev, type= "expired_document", time=draft.expires,
@ -824,7 +826,7 @@ class ExpireLastCallTests(TestCase):
e = LastCallDocEvent(doc=draft, rev=draft.rev, type="sent_last_call", by=secretary)
e.text = "Last call sent"
e.expires = datetime.datetime.now() + datetime.timedelta(days=14)
e.expires = timezone.now() + datetime.timedelta(days=14)
e.save()
self.assertEqual(len(list(get_expired_last_calls())), 0)
@ -832,7 +834,7 @@ class ExpireLastCallTests(TestCase):
# test expired
e = LastCallDocEvent(doc=draft, rev=draft.rev, type="sent_last_call", by=secretary)
e.text = "Last call sent"
e.expires = datetime.datetime.now()
e.expires = timezone.now()
e.save()
drafts = list(get_expired_last_calls())
@ -866,7 +868,7 @@ class ExpireLastCallTests(TestCase):
e = LastCallDocEvent(doc=draft, rev=draft.rev, type="sent_last_call", by=secretary)
e.text = "Last call sent"
e.desc = "Blah, blah, blah.\n\nThis document makes the following downward references (downrefs):\n ** Downref: Normative reference to an Experimental RFC: RFC 4764"
e.expires = datetime.datetime.now()
e.expires = timezone.now()
e.save()
drafts = list(get_expired_last_calls())
@ -1730,7 +1732,7 @@ class ChangeStreamStateTests(TestCase):
self.assertEqual(draft.docevent_set.count() - events_before, 2)
reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s")
self.assertEqual(len(reminder), 1)
due = datetime.datetime.now() + datetime.timedelta(weeks=10)
due = timezone.now() + datetime.timedelta(weeks=10)
self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1))
self.assertEqual(len(outbox), 1)
self.assertTrue("state changed" in outbox[0]["Subject"].lower())
@ -1775,7 +1777,7 @@ class ChangeStreamStateTests(TestCase):
self.assertEqual(draft.docevent_set.count() - events_before, 2)
reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s")
self.assertEqual(len(reminder), 1)
due = datetime.datetime.now() + datetime.timedelta(weeks=10)
due = timezone.now() + datetime.timedelta(weeks=10)
self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1))
self.assertEqual(len(outbox), 1)
self.assertTrue("state changed" in outbox[0]["Subject"].lower())
@ -1826,7 +1828,7 @@ class ChangeReplacesTests(TestCase):
name="draft-test-base-b",
title="Base B",
group=mars_wg,
expires = datetime.datetime.now() - datetime.timedelta(days = 365 - settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
expires = timezone.now() - datetime.timedelta(days = 365 - settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
)
p = PersonFactory(name="baseb_author")
e = Email.objects.create(address="baseb_author@example.com", person=p, origin=p.user.username)

View file

@ -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.group.factories import RoleFactory
from ietf.person.models import Person
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
class IssueIRSGBallotTests(TestCase):
@ -254,7 +255,7 @@ class IssueIRSGBallotTests(TestCase):
irsgmember = get_active_irsg()[0]
secr = RoleFactory(group__acronym='secretariat',name_id='secr')
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)
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=wg_draft.name, ballot_id=wg_ballot.pk))

View file

@ -4,7 +4,6 @@
import os
import shutil
import datetime
import io
from pathlib import Path
@ -14,6 +13,7 @@ import debug # pyflakes:ignore
from django.conf import settings
from django.urls import reverse as urlreverse
from django.utils import timezone
from ietf.doc.models import Document, State, DocAlias, NewRevisionDocEvent
from ietf.group.factories import RoleFactory
@ -155,7 +155,7 @@ class GroupMaterialTests(TestCase):
name = "session-42-mars-1",
meeting = Meeting.objects.get(number='42'),
group = Group.objects.get(acronym='mars'),
modified = datetime.datetime.now(),
modified = timezone.now(),
)
SchedulingEvent.objects.create(
session=session,

View file

@ -10,10 +10,10 @@ import email.mime.multipart, email.mime.text, email.utils
from mock import patch
from requests import Response
from django.apps import apps
from django.urls import reverse as urlreverse
from django.conf import settings
from django.utils import timezone
from pyquery import PyQuery
@ -38,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 TestCase
from ietf.utils.text import strip_prefix, xslugify
from ietf.utils.timezone import DEADLINE_TZINFO
from django.utils.html import escape
class ReviewTests(TestCase):
@ -67,7 +68,7 @@ class ReviewTests(TestCase):
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
RoleFactory(group=review_team3,person__user__username='reviewsecretary3',person__user__email='reviewsecretary3@example.com',name_id='secr')
req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request = req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name })
@ -145,7 +146,7 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request=review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
# move the review request to a doubly-replaced document to
@ -166,7 +167,7 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01', authors=[author])
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -195,7 +196,7 @@ class ReviewTests(TestCase):
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
RoleFactory(group=review_team,person__user__username='reviewsecretary2',person__user__email='reviewsecretary2@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request=review_req, state_id='accepted', reviewer=rev_role.person.email_set.first())
close_url = urlreverse('ietf.doc.views_review.close_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -260,7 +261,7 @@ class ReviewTests(TestCase):
# previous review
req = ReviewRequestFactory(
time=datetime.datetime.now() - datetime.timedelta(days=100),
time=timezone.now() - datetime.timedelta(days=100),
requested_by=Person.objects.get(name="(System)"),
doc=doc,
type_id='early',
@ -372,7 +373,7 @@ class ReviewTests(TestCase):
doc = WgDraftFactory(group__acronym='mars',rev='01')
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, state_id='assigned', reviewer=rev_role.person.email_set.first())
url = urlreverse('ietf.doc.views_review.review_request', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -395,7 +396,7 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request = review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
reject_url = urlreverse('ietf.doc.views_review.reject_reviewer_assignment', kwargs={ "name": doc.name, "assignment_id": assignment.pk })
@ -495,7 +496,7 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, reviewer=rev_role.person.email_set.first(), state_id='accepted')
# test URL construction
@ -587,7 +588,7 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
assignment = ReviewAssignmentFactory(review_request=review_req, state_id='accepted', reviewer=rev_role.person.email_set.first())
for r in ReviewResultName.objects.filter(slug__in=("issues", "ready")):
review_req.team.reviewteamsettings.review_results.add(r)
@ -699,7 +700,7 @@ class ReviewTests(TestCase):
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.state_id, "completed")
# Completed time should be close to now, but will not be exactly, so check within 10s margin
completed_time_diff = datetime.datetime.now() - assignment.completed_on
completed_time_diff = timezone.now() - assignment.completed_on
self.assertLess(completed_time_diff, datetime.timedelta(seconds=10))
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
@ -733,15 +734,15 @@ class ReviewTests(TestCase):
# The secretary is allowed to set a custom completion date (#2590)
assignment = reload_db_objects(assignment)
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:
# - the event logging when the change when it was entered, i.e. very close to now.
# - the completion of the review, set to the provided date/time
events = ReviewAssignmentDocEvent.objects.filter(doc=assignment.review_request.doc).order_by('-time')
event0_time_diff = datetime.datetime.now() - events[0].time
event0_time_diff = timezone.now() - events[0].time
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:
self.assertEqual(f.read(), "This is a review\nwith two lines")
@ -985,7 +986,7 @@ class ReviewTests(TestCase):
self.assertEqual(assignment.state_id, "completed")
# The revision event time should be the date the revision was submitted, i.e. not backdated
event1 = assignment.review_request.doc.latest_event(ReviewAssignmentDocEvent)
event_time_diff = datetime.datetime.now() - event1.time
event_time_diff = timezone.now() - event1.time
self.assertLess(event_time_diff, datetime.timedelta(seconds=10))
self.assertTrue('revised' in event1.desc.lower())
@ -1012,7 +1013,7 @@ class ReviewTests(TestCase):
assignment = reload_db_objects(assignment)
self.assertEqual(assignment.review.rev, "01")
event2 = assignment.review_request.doc.latest_event(ReviewAssignmentDocEvent)
event_time_diff = datetime.datetime.now() - event2.time
event_time_diff = timezone.now() - event2.time
self.assertLess(event_time_diff, datetime.timedelta(seconds=10))
# Ensure that a new event was created for the new revision (#2590)
self.assertNotEqual(event1.id, event2.id)
@ -1024,7 +1025,7 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='assigned',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.edit_comment', kwargs={ "name": doc.name, "request_id": review_req.pk })
@ -1046,7 +1047,7 @@ class ReviewTests(TestCase):
review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", list_email="reviewteam@ietf.org", parent=Group.objects.get(acronym="farfut"))
rev_role = RoleFactory(group=review_team,person__user__username='reviewer',person__user__email='reviewer@example.com',name_id='reviewer')
RoleFactory(group=review_team,person__user__username='reviewsecretary',person__user__email='reviewsecretary@example.com',name_id='secr')
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,deadline=datetime.datetime.now()+datetime.timedelta(days=20))
review_req = ReviewRequestFactory(doc=doc,team=review_team,type_id='early',state_id='accepted',requested_by=rev_role.person,deadline=timezone.now()+datetime.timedelta(days=20))
ReviewAssignmentFactory(review_request = review_req, reviewer = rev_role.person.email_set.first(), state_id='accepted')
url = urlreverse('ietf.doc.views_review.edit_deadline', kwargs={ "name": doc.name, "request_id": review_req.pk })

View file

@ -143,7 +143,7 @@ class ActionHoldersTests(TestCase):
doc = self.doc_in_iesg_state('pub-req')
doc.action_holders.set([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()
self.assertNotEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), datetime.date.today())

View file

@ -18,6 +18,7 @@ from django.conf import settings
from django.contrib import messages
from django.forms import ValidationError
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.html import escape
from django.urls import reverse as urlreverse
@ -38,6 +39,7 @@ from ietf.review.models import ReviewWish
from ietf.utils import draft, log
from ietf.utils.mail import send_mail
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
@ -636,11 +638,22 @@ def has_same_ballot(doc, date1, date2=None):
""" Test if the most recent ballot created before the end of date1
is the same as the most recent ballot created before the
end of date 2. """
datetime1 = datetime_from_date(date1, DEADLINE_TZINFO)
if date2 is None:
date2 = datetime.date.today()
ballot1 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date1+datetime.timedelta(days=1))
ballot2 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date2+datetime.timedelta(days=1))
return ballot1==ballot2
datetime2 = datetime_today(DEADLINE_TZINFO)
else:
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
def make_notify_changed_event(request, doc, by, new_notify, time=None):
@ -686,7 +699,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
and on_agenda
and prev_agenda
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)
):
returning = True
@ -807,7 +820,7 @@ def set_replaces_for_document(request, doc, new_replaces, by, email_subject, com
cc.update(other_addrs.cc)
RelatedDocument.objects.filter(source=doc, target=d, relationship=relationship).delete()
if not RelatedDocument.objects.filter(target=d, relationship=relationship):
s = 'active' if d.document.expires > datetime.datetime.now() else 'expired'
s = 'active' if d.document.expires > timezone.now() else 'expired'
d.document.set_state(State.objects.get(type='draft', slug=s))
for d in new_replaces:
@ -955,6 +968,7 @@ def make_rev_history(doc):
history[url]['pages'] = d.history_set.filter(rev=e.newrevisiondocevent.rev).first().pages
if doc.type_id == "draft":
# e.time.date() agrees with RPC publication date when shown in the RPC_TZINFO time zone
e = doc.latest_event(type='published_rfc')
else:
e = doc.latest_event(type='iesg_approved')
@ -1118,7 +1132,7 @@ def build_doc_meta_block(doc, path):
lines[i] = line
return lines
#
now = datetime.datetime.now()
now = timezone.now()
draft_state = doc.get_state('draft')
block = ''
meta = {}

View file

@ -11,6 +11,7 @@ import shutil
from django.conf import settings
from django.urls import reverse as urlreverse
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import smart_text, force_text
import debug # pyflakes:ignore
@ -73,7 +74,7 @@ def change_group_state_after_charter_approval(group, by):
save_group_in_history(group)
group.state = new_state
group.time = datetime.datetime.now()
group.time = timezone.now()
group.save()
# create an event for the group state change, too
@ -132,7 +133,7 @@ def historic_milestones_for_charter(charter, rev):
# revision (when approving a charter)
just_before_next_rev = e[0].time - datetime.timedelta(seconds=5)
else:
just_before_next_rev = datetime.datetime.now()
just_before_next_rev = timezone.now()
res = []
if hasattr(charter, 'chartered_group'):
@ -197,7 +198,7 @@ def derive_new_work_text(review_text,group):
return smart_text(m.as_string())
def default_review_text(group, charter, by):
now = datetime.datetime.now()
now = timezone.now()
addrs = gather_address_lists('charter_external_review',group=group).as_strings(compact=False)
e1 = WriteupDocEvent(doc=charter, rev=charter.rev, by=by)

View file

@ -5,6 +5,10 @@ import re
import datetime
import debug # pyflakes:ignore
from zoneinfo import ZoneInfo
from django.conf import settings
from ietf.doc.models import Document, DocAlias, RelatedDocument, DocEvent, TelechatDocEvent, BallotDocEvent
from ietf.doc.expire import expirable_drafts
from ietf.doc.utils import augment_docs_and_user_with_user_info
@ -204,7 +208,7 @@ def prepare_document_table(request, docs, query=None, max_results=200):
if sort_key == "title":
res.append(d.title)
elif sort_key == "date":
res.append(str(d.latest_revision_date))
res.append(str(d.latest_revision_date.astimezone(ZoneInfo(settings.TIME_ZONE))))
elif sort_key == "status":
if rfc_num != None:
res.append(num(rfc_num))

View file

@ -40,6 +40,8 @@ from ietf.person.models import Person
from ietf.utils.mail import send_mail_text, send_mail_preformatted
from ietf.utils.decorators import require_api_key
from ietf.utils.response import permission_denied
from ietf.utils.timezone import date_today, datetime_from_date, DEADLINE_TZINFO
BALLOT_CHOICES = (("yes", "Yes"),
("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 += announcement
if form.cleaned_data['last_call_sent_date'] != e.time.date():
e.time = datetime.datetime.combine(form.cleaned_data['last_call_sent_date'], e.time.time())
e.expires = expiration_date
e_production_time = e.time.astimezone(DEADLINE_TZINFO)
if form.cleaned_data['last_call_sent_date'] != e_production_time.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()
events.append(e)
@ -1108,7 +1112,7 @@ def issue_irsg_ballot(request, name):
raise Http404
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':
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)
if (duedate == None or duedate==""):
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.desc = "Created IRSG Ballot"
ballot_type = BallotType.objects.get(doc_type=doc.type, slug="irsg-approve")

View file

@ -16,6 +16,7 @@ from django.utils.safestring import mark_safe
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.html import escape
@ -77,7 +78,7 @@ def change_state(request, name, option=None):
chartering_type = get_chartering_type(charter)
initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review")
if charter.get_state_slug() != "infrev" or (initial_review and initial_review.expires < datetime.datetime.now()) or chartering_type == "rechartering":
if charter.get_state_slug() != "infrev" or (initial_review and initial_review.expires < timezone.now()) or chartering_type == "rechartering":
initial_review = None
by = request.user.person
@ -183,7 +184,7 @@ def change_state(request, name, option=None):
if charter_state.slug == "infrev" and clean["initial_time"] and clean["initial_time"] != 0:
e = InitialReviewDocEvent(type="initial_review", by=by, doc=charter, rev=charter.rev)
e.expires = datetime.datetime.now() + datetime.timedelta(weeks=clean["initial_time"])
e.expires = timezone.now() + datetime.timedelta(weeks=clean["initial_time"])
e.desc = "Initial review time expires %s" % e.expires.strftime("%Y-%m-%d")
e.save()
@ -506,7 +507,7 @@ def review_announcement_text(request, name):
existing_new_work.type = "changed_new_work_text"
existing_new_work.desc = "%s review text was changed" % group.type.name
existing_new_work.text = derive_new_work_text(existing.text,group)
existing_new_work.time = datetime.datetime.now()
existing_new_work.time = timezone.now()
form = ReviewAnnouncementTextForm(initial=dict(announcement_text=escape(existing.text),new_work_text=escape(existing_new_work.text)))
@ -514,7 +515,7 @@ def review_announcement_text(request, name):
form = ReviewAnnouncementTextForm(request.POST)
if "save_text" in request.POST and form.is_valid():
now = datetime.datetime.now()
now = timezone.now()
events = []
t = form.cleaned_data['announcement_text']

View file

@ -967,7 +967,7 @@ def document_bibtex(request, name, rev=None):
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
replaced_by = [d.name for d in doc.related_that("replaces")]
published = doc.latest_event(type="published_rfc")
published = doc.latest_event(type="published_rfc") is not None
rfc = latest_revision.doc if latest_revision and latest_revision.doc.get_state_slug() == "rfc" else None
if rev != None and rev != doc.rev:

View file

@ -19,6 +19,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.forms.utils import ErrorList
from django.template.defaultfilters import pluralize
from django.utils import timezone
import debug # pyflakes:ignore
@ -52,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 import log
from ietf.utils.response import permission_denied
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
class ChangeStateForm(forms.Form):
state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg"), empty_label=None, required=True)
@ -857,7 +860,7 @@ def resurrect(request, name):
events.append(e)
doc.set_state(State.objects.get(used=True, type="draft", slug="active"))
doc.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE)
doc.expires = timezone.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE)
doc.save_with_history(events)
restore_draft_file(request, doc)
@ -1476,7 +1479,7 @@ def adopt_draft(request, name):
due_date = 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)
@ -1667,7 +1670,7 @@ def change_stream_state(request, name, state_type):
due_date = 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)

View file

@ -10,7 +10,9 @@ import datetime
import requests
import email.utils
from django.utils import timezone
from django.utils.http import is_safe_url
from simple_history.utils import update_change_reason
import debug # pyflakes:ignore
@ -52,6 +54,8 @@ from ietf.utils.mail import send_mail_message
from ietf.mailtrigger.utils import gather_address_lists
from ietf.utils.fields import MultiEmailField
from ietf.utils.response import permission_denied
from ietf.utils.timezone import DEADLINE_TZINFO
def clean_doc_revision(doc, rev):
if rev:
@ -117,7 +121,7 @@ def request_review(request, name):
if not can_request_review_of_doc(request.user, doc):
permission_denied(request, "You do not have permission to perform this action")
now = datetime.datetime.now()
now = timezone.now()
lc_ends = None
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
@ -364,7 +368,7 @@ def reject_reviewer_assignment(request, name, assignment_id):
if form.is_valid():
# reject the assignment
review_assignment.state = ReviewAssignmentStateName.objects.get(slug="rejected")
review_assignment.completed_on = datetime.datetime.now()
review_assignment.completed_on = timezone.now()
review_assignment.save()
descr = "Assignment of request for {} review by {} to {} was rejected".format(
@ -731,13 +735,13 @@ def complete_review(request, name, assignment_id=None, acronym=None):
review_request=review_request,
state_id='assigned',
reviewer=form.cleaned_data['reviewer'].role_email('reviewer', group=team),
assigned_on=datetime.datetime.now(),
assigned_on=timezone.now(),
review = review,
)
review.rev = "00" if not review.rev else "{:02}".format(int(review.rev) + 1)
review.title = "{} Review of {}-{}".format(assignment.review_request.type.name, assignment.review_request.doc.name, form.cleaned_data["reviewed_rev"])
review.time = datetime.datetime.now()
review.time = timezone.now()
if review_submission == "link":
review.external_url = form.cleaned_data['review_url']
@ -764,9 +768,13 @@ def complete_review(request, name, assignment_id=None, acronym=None):
with io.open(filename, 'w', encoding='utf-8') as destination:
destination.write(content)
completion_datetime = datetime.datetime.now()
completion_datetime = timezone.now()
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
assignment.state = form.cleaned_data["state"]
@ -799,7 +807,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
close_event.by = request.user.person
close_event.desc = desc
close_event.state = assignment.state
close_event.time = datetime.datetime.now()
close_event.time = timezone.now()
close_event.save()
# If the completion date is different, record when the initial review was made too.

View file

@ -453,7 +453,7 @@ def ad_dashboard_sort_key(doc):
ageseconds = 0
changetime= doc.latest_event(type='changed_document')
if changetime:
ad = (datetime.datetime.now()-doc.latest_event(type='changed_document').time)
ad = (timezone.now()-doc.latest_event(type='changed_document').time)
ageseconds = (ad.microseconds + (ad.seconds + ad.days * 24 * 3600) * 10**6) / 10**6
return "1%d%s%s%010d" % (state[0].order,seed,doc.type.slug,ageseconds)
@ -761,7 +761,7 @@ def recent_drafts(request, days=7):
cache_key = f'recentdraftsview{days}'
cached_val = slowcache.get(cache_key)
if not cached_val:
since = datetime.datetime.now()-datetime.timedelta(days=days)
since = timezone.now()-datetime.timedelta(days=days)
state = State.objects.get(type='draft', slug='active')
events = NewRevisionDocEvent.objects.filter(time__gt=since)
names = [ e.doc.name for e in events ]

View file

@ -5,6 +5,8 @@ import factory
from typing import List # pyflakes:ignore
from django.utils import timezone
from ietf.group.models import Group, Role, GroupEvent, GroupMilestone, \
GroupHistory, RoleHistory
from ietf.review.factories import ReviewTeamSettingsFactory
@ -66,7 +68,7 @@ class BaseGroupMilestoneFactory(factory.django.DjangoModelFactory):
class DatedGroupMilestoneFactory(BaseGroupMilestoneFactory):
group = factory.SubFactory(GroupFactory, uses_milestone_dates=True)
due = datetime.datetime.today()+datetime.timedelta(days=180)
due = timezone.now()+datetime.timedelta(days=180)
class DatelessGroupMilestoneFactory(BaseGroupMilestoneFactory):
group = factory.SubFactory(GroupFactory, uses_milestone_dates=False)

View file

@ -15,6 +15,7 @@ from tempfile import mkstemp
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
import debug # pyflakes:ignore
@ -39,7 +40,7 @@ class Command(BaseCommand):
'have seen activity in the last %s years.' % (DEFAULT_YEARS))
def handle(self, *args, **options):
show_since = datetime.datetime.now() - datetime.timedelta(DEFAULT_YEARS*365)
show_since = timezone.now() - datetime.timedelta(DEFAULT_YEARS*365)
date = time.strftime("%Y-%m-%d_%H:%M:%S")
signature = '# Generated by %s at %s\n' % (os.path.abspath(__file__), date)

View file

@ -0,0 +1,29 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('group', '0058_alter_has_default_chat'),
]
operations = [
migrations.AlterField(
model_name='group',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='groupevent',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='When the event happened'),
),
migrations.AlterField(
model_name='grouphistory',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

View file

@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
import datetime
import email.utils
import jsonfield
import os
@ -13,6 +12,7 @@ from django.core.validators import RegexValidator
from django.db import models
from django.db.models.deletion import CASCADE, PROTECT
from django.dispatch import receiver
from django.utils import timezone
import debug # pyflakes:ignore
@ -27,7 +27,7 @@ from ietf.utils.validators import JSONForeignKeyListValidator
class GroupInfo(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
name = models.CharField(max_length=80)
state = ForeignKey(GroupStateName, null=True)
type = ForeignKey(GroupTypeName, null=True)
@ -180,11 +180,15 @@ class Group(GroupInfo):
return self.role_set.none()
def status_for_meeting(self,meeting):
end_date = meeting.end_date()+datetime.timedelta(days=1)
previous_meeting = meeting.previous_meeting()
status_events = self.groupevent_set.filter(type='status_update',time__lte=end_date).order_by('-time')
status_events = self.groupevent_set.filter(
type='status_update',
time__lt=meeting.end_datetime(),
).order_by('-time')
if previous_meeting:
status_events = status_events.filter(time__gte=previous_meeting.end_date()+datetime.timedelta(days=1))
status_events = status_events.filter(
time__gte=previous_meeting.end_datetime()
)
return status_events.first()
def get_description(self):
@ -353,7 +357,7 @@ GROUP_EVENT_CHOICES = [
class GroupEvent(models.Model):
"""An occurrence for a group, used for tracking who, when and what."""
group = ForeignKey(Group)
time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened")
time = models.DateTimeField(default=timezone.now, help_text="When the event happened")
type = models.CharField(max_length=50, choices=GROUP_EVENT_CHOICES)
by = ForeignKey(Person)
desc = models.TextField()

View file

@ -13,6 +13,7 @@ from django.conf import settings
from django.urls import reverse as urlreverse
from django.db.models import Q
from django.test import Client
from django.utils import timezone
import debug # pyflakes:ignore
@ -115,8 +116,8 @@ class GenerateGroupAliasesTests(TestCase):
super().tearDown()
def testManagementCommand(self):
a_month_ago = datetime.datetime.now() - datetime.timedelta(30)
a_decade_ago = datetime.datetime.now() - datetime.timedelta(3650)
a_month_ago = timezone.now() - datetime.timedelta(30)
a_decade_ago = timezone.now() - datetime.timedelta(3650)
role1 = RoleFactory(name_id='ad', group__type_id='area', group__acronym='myth', group__state_id='active')
area = role1.group
ad = role1.person

View file

@ -1900,7 +1900,7 @@ class StatusUpdateTests(TestCase):
def test_view_status_update_for_meeting(self):
chair = RoleFactory(name_id='chair',group__type_id='wg')
GroupEventFactory(type='status_update',group=chair.group)
sess = SessionFactory.create(meeting__type_id='ietf',group=chair.group,meeting__date=datetime.datetime.today()-datetime.timedelta(days=1))
sess = SessionFactory.create(meeting__type_id='ietf',group=chair.group,meeting__date=timezone.now()-datetime.timedelta(days=1))
url = urlreverse('ietf.group.views.group_about_status_meeting',kwargs={'acronym':chair.group.acronym,'num':sess.meeting.number})
response = self.client.get(url)
self.assertEqual(response.status_code,200)

View file

@ -8,6 +8,7 @@ import debug # pyflakes:ignore
from pyquery import PyQuery
from django.urls import reverse as urlreverse
from django.utils import timezone
from ietf.review.policies import get_reviewer_queue_policy
from ietf.utils.test_utils import login_testing_unauthorized, TestCase, reload_db_objects
@ -25,6 +26,7 @@ from ietf.person.factories import PersonFactory, EmailFactory
from ietf.doc.factories import DocumentFactory
from ietf.group.factories import RoleFactory, ReviewTeamFactory, GroupFactory
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
class ReviewTests(TestCase):
@ -131,7 +133,7 @@ class ReviewTests(TestCase):
doc.states.add(State.objects.get(type="draft-iesg", slug="lc", used=True))
LastCallDocEvent.objects.create(
doc=doc,
expires=datetime.datetime.now() + datetime.timedelta(days=365),
expires=timezone.now() + datetime.timedelta(days=365),
by=Person.objects.get(name="(System)"),
rev=doc.rev
)
@ -155,7 +157,7 @@ class ReviewTests(TestCase):
review_request__doc=review_req1.doc,
review_request__team=review_req1.team,
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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "accepted",
@ -165,7 +167,7 @@ class ReviewTests(TestCase):
UnavailablePeriod.objects.create(
team=review_req1.team,
person=reviewer,
start_date=datetime.date.today() - datetime.timedelta(days=10),
start_date=date_today() - datetime.timedelta(days=10),
availability="unavailable",
)
@ -210,7 +212,7 @@ class ReviewTests(TestCase):
review_request__doc=review_req2.doc,
review_request__team=review_req2.team,
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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "no-response",
@ -231,15 +233,15 @@ class ReviewTests(TestCase):
review_req3 = ReviewRequestFactory(state_id='completed', team=team)
ReviewAssignmentFactory(
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__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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "completed",
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)
self.assertEqual(r.status_code, 200)
@ -252,15 +254,15 @@ class ReviewTests(TestCase):
for i in range(10):
ReviewAssignmentFactory(
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__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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "completed",
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)
self.assertEqual(r.status_code, 200)
@ -304,28 +306,28 @@ class ReviewTests(TestCase):
review_req4 = ReviewRequestFactory(state_id='completed', team=team)
ReviewAssignmentFactory(
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__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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "accepted",
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)
ReviewAssignmentFactory(
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__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__requested_by=Person.objects.get(user__username="reviewer"),
state_id = "accepted",
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)
self.assertEqual(r.status_code, 200)
@ -429,7 +431,7 @@ class ReviewTests(TestCase):
doc.states.add(State.objects.get(type="draft-iesg", slug="lc", used=True))
LastCallDocEvent.objects.create(
doc=doc,
expires=datetime.datetime.now() + datetime.timedelta(days=365),
expires=timezone.now() + datetime.timedelta(days=365),
by=Person.objects.get(name="(System)"),
rev=doc.rev
)

View file

@ -53,6 +53,7 @@ from django.http import HttpResponse, HttpResponseRedirect, Http404, JsonRespons
from django.shortcuts import render, redirect, get_object_or_404
from django.template.loader import render_to_string
from django.urls import reverse as urlreverse
from django.utils import timezone
from django.utils.html import escape
from django.views.decorators.cache import cache_page, cache_control
@ -118,6 +119,7 @@ from ietf.settings import MAILING_LIST_INFO_URL
from ietf.utils.response import permission_denied
from ietf.utils.text import strip_suffix
from ietf.utils import markdown
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
# --- Helpers ----------------------------------------------------------
@ -566,7 +568,7 @@ def all_status(request):
if e:
wg_reports.append(e)
wg_reports.sort(key=lambda x: (x.group.parent.acronym,datetime.datetime.now()-x.time))
wg_reports.sort(key=lambda x: (x.group.parent.acronym,timezone.now()-x.time))
rg_reports = []
for rg in rgs:
@ -808,7 +810,7 @@ def email_aliases(request, acronym=None, group_type=None):
def meetings(request, acronym=None, group_type=None):
group = get_group_or_404(acronym,group_type) if acronym else None
four_years_ago = datetime.datetime.now()-datetime.timedelta(days=4*365)
four_years_ago = timezone.now()-datetime.timedelta(days=4*365)
sessions = add_event_info_to_session_qs(
group.session_set.filter(
@ -972,7 +974,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
try:
group = Group.objects.get(acronym=clean["acronym"])
save_group_in_history(group)
group.time = datetime.datetime.now()
group.time = timezone.now()
group.save()
except Group.DoesNotExist:
group = Group.objects.create(name=clean["name"],
@ -1071,7 +1073,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
)
))
group.time = datetime.datetime.now()
group.time = timezone.now()
if changes and not new_group:
for attr, new, desc in changes:
@ -1420,11 +1422,14 @@ def review_requests(request, acronym, group_type=None):
}[since]
closed_review_requests = closed_review_requests.filter(
Q(reviewrequestdocevent__type='closed_review_request', reviewrequestdocevent__time__gte=datetime.date.today() - date_limit)
| Q(reviewrequestdocevent__isnull=True, time__gte=datetime.date.today() - date_limit)
Q(reviewrequestdocevent__type='closed_review_request',
reviewrequestdocevent__time__gte=datetime_today(DEADLINE_TZINFO) - date_limit)
| Q(reviewrequestdocevent__isnull=True, time__gte=datetime_today(DEADLINE_TZINFO) - date_limit)
).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',
construct_group_menu_context(request, group, "review requests", group_type, {
@ -1509,7 +1514,7 @@ def reviewer_overview(request, acronym, group_type=None):
int(math.ceil(d.assignment_to_closure_days)) if d.assignment_to_closure_days is not None else None))
if d.state in ["completed", "completed_in_time", "completed_late"]:
if d.assigned_time is not None:
delta = datetime.datetime.now() - d.assigned_time
delta = timezone.now() - d.assigned_time
if d.assignment_to_closure_days is not None:
days = int(delta.days - d.assignment_to_closure_days)
if days_since > days: days_since = days

View file

@ -11,6 +11,7 @@ import pytz
from django.conf import settings
from django.template.loader import render_to_string
from django.utils import timezone
import debug # pyflakes:ignore
@ -296,6 +297,6 @@ def id_index_txt(with_abstracts=False):
return render_to_string("idindex/id_index.txt", {
'groups': groups,
'time': datetime.datetime.now(pytz.UTC).strftime("%Y-%m-%d %H:%M:%S %Z"),
'time': timezone.now().astimezone(pytz.utc).strftime("%Y-%m-%d %H:%M:%S %Z"),
'with_abstracts': with_abstracts,
})

View file

@ -7,6 +7,7 @@ import datetime
from pathlib import Path
from django.conf import settings
from django.utils import timezone
import debug # pyflakes:ignore
@ -120,7 +121,7 @@ class IndexTests(TestCase):
draft.set_state(State.objects.get(type="draft", slug="active"))
draft.set_state(State.objects.get(type="draft-iesg", slug="lc"))
e = LastCallDocEvent.objects.create(doc=draft, rev=draft.rev, type="sent_last_call", expires=datetime.datetime.now() + datetime.timedelta(days=14), by=draft.ad)
e = LastCallDocEvent.objects.create(doc=draft, rev=draft.rev, type="sent_last_call", expires=timezone.now() + datetime.timedelta(days=14), by=draft.ad)
t = get_fields(all_id2_txt())
self.assertEqual(t[11], e.expires.strftime("%Y-%m-%d"))

View file

@ -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.person.models import Person
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):
events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved"))
@ -73,9 +74,9 @@ def review_decisions(request, year=None):
year = int(year)
events = events.filter(time__year=year)
else:
d = datetime.date.today() - datetime.timedelta(days=185)
d = date_today() - datetime.timedelta(days=185)
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")

View file

@ -8,6 +8,7 @@ from textwrap import dedent
from django.conf import settings
from django.core.management.base import BaseCommand
from django.utils import timezone
import debug # pyflakes:ignore
@ -37,7 +38,7 @@ class Command(BaseCommand):
keys = PersonalApiKey.objects.filter(valid=True)
for key in keys:
earliest = datetime.datetime.now() - datetime.timedelta(days=days)
earliest = timezone.now() - datetime.timedelta(days=days)
events = PersonApiKeyEvent.objects.filter(key=key, time__gt=earliest)
count = events.count()
events = events[:32]

View file

@ -28,6 +28,7 @@ from django.urls import reverse as urlreverse
from django.contrib.auth.models import User
from django.conf import settings
from django.template.loader import render_to_string
from django.utils import timezone
import debug # pyflakes:ignore
@ -750,11 +751,11 @@ class IetfAuthTests(TestCase):
self.assertContains(r, 'Invalid apikey', status_code=403)
# too long since regular login
person.user.last_login = datetime.datetime.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS+1)
person.user.last_login = timezone.now() - datetime.timedelta(days=settings.UTILS_APIKEY_GUI_LOGIN_LIMIT_DAYS+1)
person.user.save()
r = self.client.post(key.endpoint, {'apikey':key.hash(), 'dummy':'dummy',})
self.assertContains(r, 'Too long since last regular login', status_code=400)
person.user.last_login = datetime.datetime.now()
person.user.last_login = timezone.now()
person.user.save()
# endpoint mismatch
@ -783,7 +784,7 @@ class IetfAuthTests(TestCase):
# apikey usage will be registered)
count = 2
# avoid usage across dates
if datetime.datetime.now().time() > datetime.time(hour=23, minute=59, second=58):
if timezone.now().time() > datetime.time(hour=23, minute=59, second=58):
time.sleep(2)
for i in range(count):
for key in person.apikeys.all():

View file

@ -34,9 +34,9 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import datetime
import importlib
from datetime import date as Date, datetime as DateTime
# needed if we revert to higher barrier for account creation
#from datetime import datetime as DateTime, timedelta as TimeDelta, date as Date
from collections import defaultdict
@ -79,6 +79,7 @@ from ietf.doc.fields import SearchableDocumentField
from ietf.utils.decorators import person_required
from ietf.utils.mail import send_mail
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
@ -224,7 +225,7 @@ def profile(request):
emails = Email.objects.filter(person=person).exclude(address__startswith='unknown-email-').order_by('-active','-time')
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():
volunteer_status = 'volunteered'
elif nc and nc.is_accepting_volunteers:
@ -456,7 +457,7 @@ def confirm_password_reset(request, auth):
password = data['password']
last_login = None
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:
raise Http404("Invalid or expired auth")
@ -558,7 +559,7 @@ def review_overview(request):
reviewer__person__user=request.user,
state__in=["assigned", "accepted"],
)
today = Date.today()
today = date_today(DEADLINE_TZINFO)
for r in open_review_assignments:
r.due = max(0, (today - r.review_request.deadline).days)

View file

@ -5,6 +5,7 @@
import datetime
import factory
from django.utils import timezone
from ietf.ipr.models import (
IprDisclosureBase, HolderIprDisclosure, ThirdPartyIprDisclosure, NonDocSpecificIprDisclosure,
@ -13,7 +14,7 @@ from ietf.ipr.models import (
def _fake_patent_info():
return "Date: %s\nNotes: %s\nTitle: %s\nNumber: %s\nInventor: %s\n" % (
(datetime.datetime.today()-datetime.timedelta(days=365)).strftime("%Y-%m-%d"),
(timezone.now()-datetime.timedelta(days=365)).strftime("%Y-%m-%d"),
factory.Faker('paragraph'),
factory.Faker('sentence', nb_words=8),
'US9999999',

View file

@ -3,13 +3,14 @@
import base64
import email
import datetime
from dateutil.tz import tzoffset
import os
import pytz
import re
from email import message_from_bytes
from email.utils import parsedate_tz
from django.template.loader import render_to_string
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
"""
try:
tuple = email.utils.parsedate_tz(date)
tuple = parsedate_tz(date)
if not tuple:
return None
tz = tuple[-1]
@ -62,10 +63,12 @@ def parsedate_to_datetime(date):
def utc_from_string(s):
date = parsedate_to_datetime(s)
if is_aware(date):
return date.astimezone(pytz.utc).replace(tzinfo=None)
if date is None:
return None
elif is_aware(date):
return date.astimezone(datetime.timezone.utc)
else:
return date
return date.replace(tzinfo=datetime.timezone.utc)
# ----------------------------------------------------------------
# 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
IprEvent. Create a Message object for the incoming message and associate it to
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', '')
# exit if this isn't a response we're interested in (with plus addressing)

View file

@ -2,11 +2,10 @@
# -*- coding: utf-8 -*-
import datetime
from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils import timezone
from ietf.doc.models import DocAlias, DocEvent
from ietf.name.models import DocRelationshipName,IprDisclosureStateName,IprLicenseTypeName,IprEventTypeName
@ -220,7 +219,7 @@ class IprEvent(models.Model):
"""Returns true if it's beyond the response_due date and no response has been
received"""
qs = IprEvent.objects.filter(disclosure=self.disclosure,in_reply_to=self.message)
if not qs and datetime.datetime.now().date() > self.response_due.date():
if not qs and timezone.now().date() > self.response_due.date():
return True
else:
return False

View file

@ -8,8 +8,9 @@ import datetime
from pyquery import PyQuery
from urllib.parse import quote, urlparse
from django.urls import reverse as urlreverse
from django.conf import settings
from django.urls import reverse as urlreverse
from django.utils import timezone
import debug # pyflakes:ignore
@ -640,7 +641,7 @@ I would like to revoke this declaration.
message_string.format(
to=addrs.to,
cc=addrs.cc,
date=datetime.datetime.now().ctime()
date=timezone.now().ctime()
)
)
self.assertIsNone(result)
@ -650,7 +651,7 @@ I would like to revoke this declaration.
From: joe@test.com
Date: {}
Subject: test
""".format(reply_to, datetime.datetime.now().ctime())
""".format(reply_to, timezone.now().ctime())
result = process_response_email(message_string)
self.assertIsInstance(result, Message)
@ -664,7 +665,7 @@ Subject: test
From: joe@test.com
Date: {}
Subject: test
""".format(reply_to, datetime.datetime.now().ctime())
""".format(reply_to, timezone.now().ctime())
message_bytes = message_string.encode('utf8') + b'\nInvalid stuff: \xfe\xff\n'
result = process_response_email(message_bytes)
self.assertIsInstance(result, Message)
@ -680,7 +681,7 @@ Subject: test
message_bytes = message_string.format(
to=addrs.to,
cc=addrs.cc,
date=datetime.datetime.now().ctime(),
date=timezone.now().ctime(),
).encode('utf8') + b'\nInvalid stuff: \xfe\xff\n'
result = process_response_email(message_bytes)
self.assertIsNone(result)

View file

@ -43,6 +43,7 @@ from ietf.utils.draft_search import normalize_draftname
from ietf.utils.mail import send_mail, send_mail_message
from ietf.utils.response import permission_denied
from ietf.utils.text import text_to_dict
from ietf.utils.timezone import datetime_from_date, datetime_today, DEADLINE_TZINFO
# ----------------------------------------------------------------
# Globals
@ -147,15 +148,14 @@ def ipr_rfc_number(disclosureDate, thirdPartyDisclosureFlag):
# made on 1993-07-23, which is more than a year after RFC 1310.
# 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 = {
1310 : datetime.datetime(1992, 3, 13, 0, 0),
1802 : datetime.datetime(1994, 3, 23, 0, 0),
2026 : datetime.datetime(1996, 10, 29, 0, 0),
3668 : datetime.datetime(2004, 2, 18, 0, 0),
3979 : datetime.datetime(2005, 3, 2, 2, 23),
4879 : datetime.datetime(2007, 4, 10, 18, 21),
8179 : datetime.datetime(2017, 5, 31, 23, 1),
1310 : datetime.datetime(1992, 3, 13, 0, 0, tzinfo=datetime.timezone.utc),
1802 : datetime.datetime(1994, 3, 23, 0, 0, tzinfo=datetime.timezone.utc),
2026 : datetime.datetime(1996, 10, 29, 0, 0, tzinfo=datetime.timezone.utc),
3668 : datetime.datetime(2004, 2, 18, 0, 0, tzinfo=datetime.timezone.utc),
3979 : datetime.datetime(2005, 3, 2, 2, 23, tzinfo=datetime.timezone.utc),
4879 : datetime.datetime(2007, 4, 10, 18, 21, tzinfo=datetime.timezone.utc),
8179 : datetime.datetime(2017, 5, 31, 23, 1, tzinfo=datetime.timezone.utc),
}
if disclosureDate < ipr_rfc_pub_datetime[1310]:
@ -396,7 +396,7 @@ def email(request, id):
type_id = 'msgout',
by = request.user.person,
disclosure = ipr,
response_due = form.cleaned_data['response_due'],
response_due = datetime_from_date(form.cleaned_data['response_due'], DEADLINE_TZINFO),
message = msg,
)
@ -590,7 +590,7 @@ def notify(request, id, type):
type_id = form.cleaned_data['type'],
by = request.user.person,
disclosure = ipr,
response_due = datetime.datetime.now().date() + datetime.timedelta(days=30),
response_due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=30),
message = message,
)
messages.success(request,'Notifications sent')

View file

@ -3,7 +3,7 @@
import io
import datetime, os
import os
import operator
from typing import Union # pyflakes:ignore
@ -34,6 +34,7 @@ from ietf.person.models import Email
from ietf.person.fields import SearchableEmailField
from ietf.doc.models import Document, DocAlias
from ietf.utils.fields import DatepickerDateField
from ietf.utils.timezone import date_today, datetime_from_date, DEADLINE_TZINFO
from functools import reduce
'''
@ -185,9 +186,12 @@ class SearchLiaisonForm(forms.Form):
end_date = self.cleaned_data.get('end_date')
events = None
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:
events = events.filter(time__lte=end_date)
events = events.filter(time__lte=datetime_from_date(end_date, DEADLINE_TZINFO))
elif end_date:
events = LiaisonStatementEvent.objects.filter(type='posted', time__lte=end_date)
if events:
@ -222,7 +226,7 @@ class LiaisonModelForm(BetterModelForm):
to_groups.widget.attrs['data-minimum-input-length'] = 0
deadline = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label='Deadline', required=True)
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)
attach_title = forms.CharField(label='Title', required=False)
attach_file = forms.FileField(label='File', required=False)
@ -538,7 +542,7 @@ class EditLiaisonForm(LiaisonModelForm):
super(EditLiaisonForm, self).save(*args,**kwargs)
if self.has_changed() and 'submitted_date' in self.changed_data:
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()
return self.instance

View file

@ -14,6 +14,8 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.urls import reverse as urlreverse
from django.db.models import Q
from django.utils import timezone
from io import StringIO
from pyquery import PyQuery
@ -50,7 +52,7 @@ def get_liaison_post_data(type='incoming'):
to_contacts='to_contacts@example.com',
purpose="info",
title="title",
submitted_date=datetime.datetime.today().strftime("%Y-%m-%d"),
submitted_date=timezone.now().strftime("%Y-%m-%d"),
body="body",
send="1" )
@ -385,7 +387,7 @@ class LiaisonManagementTests(TestCase):
def test_edit_liaison(self):
liaison = LiaisonStatementFactory(deadline=datetime.date.today()+datetime.timedelta(days=1))
LiaisonStatementEventFactory(statement=liaison,type_id='submitted', time=datetime.datetime.now()-datetime.timedelta(days=1))
LiaisonStatementEventFactory(statement=liaison,type_id='submitted', time=timezone.now()-datetime.timedelta(days=1))
LiaisonStatementEventFactory(statement=liaison,type_id='posted')
from_group = liaison.from_groups.first()
to_group = liaison.to_groups.first()
@ -1021,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'),])
# Statement 2
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()
# test list only, no search filters

View file

@ -187,7 +187,9 @@ class TimeSlotFactory(factory.django.DjangoModelFactory):
@factory.lazy_attribute
def time(self):
return datetime.datetime.combine(self.meeting.date,datetime.time(11,0))
return self.meeting.tz().localize(
datetime.datetime.combine(self.meeting.date, datetime.time(11, 0))
)
@factory.lazy_attribute
def duration(self):

View file

@ -32,7 +32,7 @@
"comments": "",
"list_subscribe": "",
"state": "active",
"time": "2012-02-26T00:21:36",
"time": "2012-02-26T00:21:36Z",
"unused_tags": [],
"list_archive": "",
"type": "ietf",

View file

@ -17,6 +17,7 @@ from django.contrib.auth.models import AnonymousUser
from django.urls import reverse
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string
from django.utils import timezone
import debug # pyflakes:ignore
@ -42,7 +43,7 @@ def get_meeting(num=None,type_in=['ietf',],days=28):
if type_in:
meetings = meetings.filter(type__in=type_in)
if num == None:
meetings = meetings.filter(date__gte=datetime.datetime.today()-datetime.timedelta(days=days)).order_by('date')
meetings = meetings.filter(date__gte=timezone.now()-datetime.timedelta(days=days)).order_by('date')
else:
meetings = meetings.filter(number=num)
if meetings.exists():
@ -51,7 +52,7 @@ def get_meeting(num=None,type_in=['ietf',],days=28):
raise Http404("No such meeting found: %s" % num)
def get_current_ietf_meeting():
meetings = Meeting.objects.filter(type='ietf',date__gte=datetime.datetime.today()-datetime.timedelta(days=31)).order_by('date')
meetings = Meeting.objects.filter(type='ietf',date__gte=timezone.now()-datetime.timedelta(days=31)).order_by('date')
return meetings.first()
def get_current_ietf_meeting_num():
@ -117,7 +118,10 @@ def preprocess_assignments_for_agenda(assignments_queryset, meeting, extra_prefe
# assignments = list(assignments_queryset) # make sure we're set in stone
assignments = assignments_queryset
meeting_time = datetime.datetime.combine(meeting.date, datetime.time())
# meeting_time is meeting-local midnight at the start of the meeting date
meeting_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time())
)
# replace groups with historic counterparts
groups = [ ]
@ -1148,11 +1152,15 @@ def sessions_post_cancel(request, sessions):
def update_interim_session_assignment(form):
"""Helper function to create / update timeslot assigned to interim session"""
time = datetime.datetime.combine(
form.cleaned_data['date'],
form.cleaned_data['time'])
"""Helper function to create / update timeslot assigned to interim session
form is an InterimSessionModelForm
"""
session = form.instance
meeting = session.meeting
time = meeting.tz().localize(
datetime.datetime.combine(form.cleaned_data['date'], form.cleaned_data['time'])
)
if session.official_timeslotassignment():
slot = session.official_timeslotassignment().timeslot
slot.time = time
@ -1160,14 +1168,14 @@ def update_interim_session_assignment(form):
slot.save()
else:
slot = TimeSlot.objects.create(
meeting=session.meeting,
meeting=meeting,
type_id='regular',
duration=session.requested_duration,
time=time)
SchedTimeSessAssignment.objects.create(
timeslot=slot,
session=session,
schedule=session.meeting.schedule)
schedule=meeting.schedule)
def populate_important_dates(meeting):
assert ImportantDate.objects.filter(meeting=meeting).exists() is False

View file

@ -48,7 +48,7 @@ import socket
import datetime
import pytz
from django.core.management.base import BaseCommand
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.db.models import Q
@ -75,10 +75,12 @@ class Command(BaseCommand):
def _meeting_datetime(self, day, *time_args):
"""Generate a datetime on a meeting day"""
return datetime.datetime.combine(
self.start_date,
datetime.time(*time_args)
) + datetime.timedelta(days=day)
return self.meeting_tz.localize(
datetime.datetime.combine(
self.start_date,
datetime.time(*time_args)
) + datetime.timedelta(days=day)
)
def handle(self, *args, **options):
if socket.gethostname().split('.')[0] in ['core3', 'ietfa', 'ietfb', 'ietfc', ]:
@ -87,10 +89,7 @@ class Command(BaseCommand):
opt_delete = options.get('delete', False)
opt_use_old_conflicts = options.get('old_conflicts', False)
self.start_date = options['start_date']
meeting_tz = options['tz']
if not opt_delete and (meeting_tz not in pytz.common_timezones):
self.stderr.write("Warning: {} is not a recognized time zone.".format(meeting_tz))
meeting_tzname = options['tz']
if opt_delete:
if Meeting.objects.filter(number='999').exists():
Meeting.objects.filter(number='999').delete()
@ -98,6 +97,11 @@ class Command(BaseCommand):
else:
self.stderr.write("Dummy meeting IETF 999 does not exist; nothing to do.\n")
else:
try:
self.meeting_tz = pytz.timezone(meeting_tzname)
except pytz.UnknownTimeZoneError:
raise CommandError("{} is not a recognized time zone.".format(meeting_tzname))
if Meeting.objects.filter(number='999').exists():
self.stderr.write("Dummy meeting IETF 999 already exists; nothing to do.\n")
else:
@ -111,7 +115,7 @@ class Command(BaseCommand):
type_id='IETF',
date=self._meeting_datetime(0).date(),
days=7,
time_zone=meeting_tz,
time_zone=meeting_tzname,
)
# Set enabled constraints

View file

@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('meeting', '0055_pytz_2022_2_1'),
]
operations = [
migrations.AlterField(
model_name='schedulingevent',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='When the event happened'),
),
]

View file

@ -0,0 +1,47 @@
# Generated by Django 2.2.28 on 2022-08-08 11:37
import datetime
from django.db import migrations
# date of last meeting with an empty time_zone before this migration
LAST_EMPTY_TZ = datetime.date(2022, 7, 1)
def forward(apps, schema_editor):
Meeting = apps.get_model('meeting', 'Meeting')
# Check that we will be able to identify the migrated meetings later
old_meetings_in_pst8pdt = Meeting.objects.filter(type_id='interim', time_zone='PST8PDT', date__lte=LAST_EMPTY_TZ)
assert old_meetings_in_pst8pdt.count() == 0, 'not expecting interim meetings in PST8PDT time_zone'
meetings_with_empty_tz = Meeting.objects.filter(time_zone='')
# check our expected conditions
for mtg in meetings_with_empty_tz:
assert mtg.type_id == 'interim', 'was not expecting non-interim meetings to be affected'
assert mtg.date <= LAST_EMPTY_TZ, 'affected meeting outside expected date range'
mtg.time_zone = 'PST8PDT'
# commit the changes
Meeting.objects.bulk_update(meetings_with_empty_tz, ['time_zone'])
def reverse(apps, schema_editor):
Meeting = apps.get_model('meeting', 'Meeting')
meetings_to_restore = Meeting.objects.filter(time_zone='PST8PDT', date__lte=LAST_EMPTY_TZ)
for mtg in meetings_to_restore:
mtg.time_zone = ''
# commit the changes
Meeting.objects.bulk_update(meetings_to_restore, ['time_zone'])
class Migration(migrations.Migration):
dependencies = [
('meeting', '0056_use_timezone_now_for_meeting_models'),
]
operations = [
migrations.RunPython(forward, reverse),
]

File diff suppressed because one or more lines are too long

View file

@ -24,6 +24,7 @@ from django.db.models import Max, Subquery, OuterRef, TextField, Value, Q
from django.db.models.functions import Coalesce
from django.conf import settings
from django.urls import reverse as urlreverse
from django.utils import timezone
from django.utils.text import slugify
from django.utils.safestring import mark_safe
@ -40,7 +41,7 @@ from ietf.person.models import Person
from ietf.utils.decorators import memoize
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
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.validators import (
MaxImageSizeValidator, WrappedValidator, validate_file_size, validate_mime_type,
@ -85,7 +86,7 @@ class Meeting(models.Model):
# We can't derive time-zone from country, as there are some that have
# more than one timezone, and the pytz module doesn't provide timezone
# lookup information for all relevant city/country combinations.
time_zone = models.CharField(blank=True, max_length=255, choices=timezones)
time_zone = models.CharField(max_length=255, choices=timezones, default='UTC')
idsubmit_cutoff_day_offset_00 = models.IntegerField(blank=True,
default=settings.IDSUBMIT_DEFAULT_CUTOFF_DAY_OFFSET_00,
help_text = "The number of days before the meeting start date when the submission of -00 drafts will be closed.")
@ -137,6 +138,14 @@ class Meeting(models.Model):
def end_date(self):
return self.get_meeting_date(self.days-1)
def start_datetime(self):
"""Start-of-day on meeting.date in meeting time zone"""
return datetime_from_date(self.date, self.tz())
def end_datetime(self):
"""Datetime of the first instant _after_ the meeting's last day in meeting time zone"""
return datetime_from_date(self.get_meeting_date(self.days), self.tz())
def get_00_cutoff(self):
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day, tzinfo=pytz.utc)
importantdate = self.importantdate_set.filter(name_id='idcutoff').first()
@ -146,7 +155,7 @@ class Meeting(models.Model):
cutoff_date = importantdate.date
else:
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
def get_01_cutoff(self):
@ -158,7 +167,7 @@ class Meeting(models.Model):
cutoff_date = importantdate.date
else:
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
def get_reopen_time(self):
@ -176,7 +185,7 @@ class Meeting(models.Model):
@classmethod
def get_current_meeting(cls, type="ietf"):
return cls.objects.filter(type=type, date__gte=datetime.datetime.today()-datetime.timedelta(days=7) ).order_by('date').first()
return cls.objects.filter(type=type, date__gte=timezone.now()-datetime.timedelta(days=7) ).order_by('date').first()
def get_first_cut_off(self):
return self.get_00_cutoff()
@ -321,7 +330,7 @@ class Meeting(models.Model):
for ts in self.timeslot_set.all():
if ts.location_id is None:
continue
ymd = ts.time.date()
ymd = ts.local_start_time().date()
if ymd not in time_slices:
time_slices[ymd] = []
slots[ymd] = []
@ -329,15 +338,15 @@ class Meeting(models.Model):
if ymd in time_slices:
# only keep unique entries
if [ts.time, ts.time + ts.duration, ts.duration.seconds] not in time_slices[ymd]:
time_slices[ymd].append([ts.time, ts.time + ts.duration, ts.duration.seconds])
if [ts.local_start_time(), ts.local_end_time(), ts.duration.seconds] not in time_slices[ymd]:
time_slices[ymd].append([ts.local_start_time(), ts.local_end_time(), ts.duration.seconds])
slots[ymd].append(ts)
days.sort()
for ymd in time_slices:
# Make sure these sort the same way
time_slices[ymd].sort()
slots[ymd].sort(key=lambda x: (x.time, x.duration))
slots[ymd].sort(key=lambda x: (x.local_start_time(), x.duration))
return days,time_slices,slots
# this functions makes a list of timeslices and rooms, and
@ -353,20 +362,24 @@ class Meeting(models.Model):
# SchedTimeSessAssignment.objects.create(schedule = sched,
# timeslot = ts)
def tz(self):
if not hasattr(self, '_cached_tz'):
self._cached_tz = pytz.timezone(self.time_zone)
return self._cached_tz
def vtimezone(self):
if self.time_zone:
try:
tzfn = os.path.join(settings.TZDATA_ICS_PATH, self.time_zone + ".ics")
if os.path.exists(tzfn):
with io.open(tzfn) as tzf:
icstext = tzf.read()
vtimezone = re.search("(?sm)(\nBEGIN:VTIMEZONE.*\nEND:VTIMEZONE\n)", icstext).group(1).strip()
if vtimezone:
vtimezone += "\n"
return vtimezone
except IOError:
pass
return ''
try:
tzfn = os.path.join(settings.TZDATA_ICS_PATH, self.time_zone + ".ics")
if os.path.exists(tzfn):
with io.open(tzfn) as tzf:
icstext = tzf.read()
vtimezone = re.search("(?sm)(\nBEGIN:VTIMEZONE.*\nEND:VTIMEZONE\n)", icstext).group(1).strip()
if vtimezone:
vtimezone += "\n"
return vtimezone
except IOError:
pass
return None
def set_official_schedule(self, schedule):
if self.schedule != schedule:
@ -374,16 +387,14 @@ class Meeting(models.Model):
self.save()
def updated(self):
min_time = datetime.datetime(1970, 1, 1, 0, 0, 0) # should be Meeting.modified, but we don't have that
# should be Meeting.modified, but we don't have that
min_time = pytz.utc.localize(datetime.datetime(1970, 1, 1, 0, 0, 0))
timeslots_updated = self.timeslot_set.aggregate(Max('modified'))["modified__max"] or min_time
sessions_updated = self.session_set.aggregate(Max('modified'))["modified__max"] or min_time
assignments_updated = min_time
if self.schedule:
assignments_updated = SchedTimeSessAssignment.objects.filter(schedule__in=[self.schedule, self.schedule.base if self.schedule else None]).aggregate(Max('modified'))["modified__max"] or min_time
ts = max(timeslots_updated, sessions_updated, assignments_updated)
tz = pytz.timezone(settings.PRODUCTION_TIMEZONE)
ts = tz.localize(ts)
return ts
return max(timeslots_updated, sessions_updated, assignments_updated)
@memoize
def previous_meeting(self):
@ -604,41 +615,22 @@ class TimeSlot(models.Model):
return self._cached_html_location
def tz(self):
if not hasattr(self, '_cached_tz'):
if self.meeting.time_zone:
self._cached_tz = pytz.timezone(self.meeting.time_zone)
else:
self._cached_tz = None
return self._cached_tz
return self.meeting.tz()
def tzname(self):
if self.tz():
return self.tz().tzname(self.time)
else:
return ""
return self.tz().tzname(self.time)
def utc_start_time(self):
if self.tz():
local_start_time = self.tz().localize(self.time)
return local_start_time.astimezone(pytz.utc)
else:
return None
return self.time.astimezone(pytz.utc) # USE_TZ is True, so time is aware
def utc_end_time(self):
utc_start = self.utc_start_time()
# Add duration after converting start time, otherwise errors creep in around DST change
return None if utc_start is None else utc_start + self.duration
return self.time.astimezone(pytz.utc) + self.duration # USE_TZ is True, so time is aware
def local_start_time(self):
if self.tz():
return self.tz().localize(self.time)
else:
return None
return self.time.astimezone(self.tz())
def local_end_time(self):
local_start = self.local_start_time()
# Add duration after converting start time, otherwise errors creep in around DST change
return None if local_start is None else local_start + self.duration
return (self.time.astimezone(pytz.utc) + self.duration).astimezone(self.tz())
@property
def js_identifier(self):
@ -1280,7 +1272,7 @@ class Session(models.Model):
class SchedulingEvent(models.Model):
session = ForeignKey(Session)
time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened")
time = models.DateTimeField(default=timezone.now, help_text="When the event happened")
status = ForeignKey(SessionStatusName)
by = ForeignKey(Person)

View file

@ -21,10 +21,12 @@ from ietf.person.factories import PersonFactory
from ietf.person.models import Person
from ietf.utils.test_data import make_test_data
def make_interim_meeting(group,date,status='sched'):
def make_interim_meeting(group,date,status='sched',tz='UTC'):
system_person = Person.objects.get(name="(System)")
time = datetime.datetime.combine(date, datetime.time(9))
meeting = create_interim_meeting(group=group,date=date)
meeting = create_interim_meeting(group=group,date=date,timezone=tz)
time = meeting.tz().localize(
datetime.datetime.combine(date, datetime.time(9))
)
session = SessionFactory(meeting=meeting, group=group,
attendees=10,
requested_duration=datetime.timedelta(minutes=20),
@ -102,24 +104,37 @@ def make_meeting_test_data(meeting=None, create_interims=False):
# slots
session_date = meeting.date + datetime.timedelta(days=1)
tz = meeting.tz()
slot1 = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
duration=datetime.timedelta(minutes=60),
time=datetime.datetime.combine(session_date, datetime.time(9, 30)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(9, 30))
))
slot2 = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
duration=datetime.timedelta(minutes=60),
time=datetime.datetime.combine(session_date, datetime.time(10, 50)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(10, 50))
))
breakfast_slot = TimeSlot.objects.create(meeting=meeting, type_id="lead", location=breakfast_room,
duration=datetime.timedelta(minutes=90),
time=datetime.datetime.combine(session_date, datetime.time(7,0)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(7,0))
))
reg_slot = TimeSlot.objects.create(meeting=meeting, type_id="reg", location=reg_room,
duration=datetime.timedelta(minutes=480),
time=datetime.datetime.combine(session_date, datetime.time(9,0)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(9,0))
))
break_slot = TimeSlot.objects.create(meeting=meeting, type_id="break", location=break_room,
duration=datetime.timedelta(minutes=90),
time=datetime.datetime.combine(session_date, datetime.time(7,0)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(7,0))
))
plenary_slot = TimeSlot.objects.create(meeting=meeting, type_id="plenary", location=room,
duration=datetime.timedelta(minutes=60),
time=datetime.datetime.combine(session_date, datetime.time(11,0)))
time=tz.localize(
datetime.datetime.combine(session_date, datetime.time(11,0))
))
# mars WG
mars = Group.objects.get(acronym='mars')
mars_session = SessionFactory(meeting=meeting, group=mars,
@ -213,7 +228,7 @@ def make_meeting_test_data(meeting=None, create_interims=False):
return meeting
def make_interim_test_data():
def make_interim_test_data(meeting_tz='UTC'):
date = datetime.date.today() + datetime.timedelta(days=365)
date2 = datetime.date.today() + datetime.timedelta(days=1000)
PersonFactory(user__username='plain')
@ -225,10 +240,10 @@ def make_interim_test_data():
RoleFactory(group=mars,person__user__username='marschairman',name_id='chair')
RoleFactory(group=ames,person__user__username='ameschairman',name_id='chair')
make_interim_meeting(group=mars,date=date,status='sched')
make_interim_meeting(group=mars,date=date2,status='apprw')
make_interim_meeting(group=ames,date=date,status='canceled')
make_interim_meeting(group=ames,date=date2,status='apprw')
make_interim_meeting(group=mars,date=date,status='sched',tz=meeting_tz)
make_interim_meeting(group=mars,date=date2,status='apprw',tz=meeting_tz)
make_interim_meeting(group=ames,date=date,status='canceled',tz=meeting_tz)
make_interim_meeting(group=ames,date=date2,status='apprw',tz=meeting_tz)
return

View file

@ -11,8 +11,8 @@ from unittest import skipIf
import urllib.parse
import django
from django.utils import timezone
from django.utils.text import slugify
from django.utils.timezone import now
from django.db.models import F
import pytz
@ -36,6 +36,7 @@ from ietf.meeting.utils import add_event_info_to_session_qs
from ietf.utils.test_utils import assert_ical_response_is_valid
from ietf.utils.jstest import ( IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled,
presence_of_element_child_by_css_selector )
from ietf.utils.timezone import datetime_today, datetime_from_date, date_today
if selenium_enabled():
from selenium.webdriver.common.action_chains import ActionChains
@ -307,7 +308,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
room = RoomFactory(meeting=meeting)
# get current time in meeting time zone
right_now = now().astimezone(
right_now = timezone.now().astimezone(
pytz.timezone(meeting.time_zone)
)
if not settings.USE_TZ:
@ -394,11 +395,11 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
def test_past_swap_days_buttons(self):
"""Swap days buttons should be hidden for past items"""
wait = WebDriverWait(self.driver, 2)
meeting = MeetingFactory(type_id='ietf', date=datetime.datetime.today() - datetime.timedelta(days=3), days=7)
meeting = MeetingFactory(type_id='ietf', date=timezone.now() - datetime.timedelta(days=3), days=7)
room = RoomFactory(meeting=meeting)
# get current time in meeting time zone
right_now = now().astimezone(
right_now = timezone.now().astimezone(
pytz.timezone(meeting.time_zone)
)
if not settings.USE_TZ:
@ -518,11 +519,11 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
def test_past_swap_timeslot_col_buttons(self):
"""Swap timeslot column buttons should be hidden for past items"""
wait = WebDriverWait(self.driver, 2)
meeting = MeetingFactory(type_id='ietf', date=datetime.datetime.today() - datetime.timedelta(days=3), days=7)
meeting = MeetingFactory(type_id='ietf', date=timezone.now() - datetime.timedelta(days=3), days=7)
room = RoomFactory(meeting=meeting)
# get current time in meeting time zone
right_now = now().astimezone(
right_now = timezone.now().astimezone(
pytz.timezone(meeting.time_zone)
)
if not settings.USE_TZ:
@ -1434,13 +1435,16 @@ class AgendaTests(IetfSeleniumTestCase):
# for others (break, reg, other):
# row-<meeting#>-<year>-<month>-<day>-<DoW>-<HHMM>-<group acro>-<session name slug>
meeting_number = components[1]
start_time = datetime.datetime(
year=int(components[2]),
month=int(components[3]),
day=int(components[4]),
hour=int(components[6][0:2]),
minute=int(components[6][2:4]),
start_time = pytz.utc.localize(
datetime.datetime(
year=int(components[2]),
month=int(components[3]),
day=int(components[4]),
hour=int(components[6][0:2]),
minute=int(components[6][2:4]),
)
)
# If labeled as plenary, it's plenary...
if components[7] == '1plenary':
session_type = 'plenary'
@ -1904,10 +1908,9 @@ class WeekviewTests(IetfSeleniumTestCase):
# Session during a single day in meeting local time but multi-day UTC
# Compute a time that overlaps midnight, UTC, but won't when shifted to a local time zone
start_time_utc = pytz.timezone('UTC').localize(
start_time_utc = pytz.utc.localize(
datetime.datetime.combine(self.meeting.date, datetime.time(23,0))
)
start_time_local = start_time_utc.astimezone(pytz.timezone(self.meeting.time_zone))
daytime_session = SessionFactory(
meeting=self.meeting,
@ -1916,7 +1919,7 @@ class WeekviewTests(IetfSeleniumTestCase):
)
daytime_timeslot = TimeSlotFactory(
meeting=self.meeting,
time=start_time_local.replace(tzinfo=None), # drop timezone for Django
time=start_time_utc,
duration=duration,
)
daytime_session.timeslotassignments.create(timeslot=daytime_timeslot, schedule=self.meeting.schedule)
@ -1929,11 +1932,12 @@ class WeekviewTests(IetfSeleniumTestCase):
)
overnight_timeslot = TimeSlotFactory(
meeting=self.meeting,
time=datetime.datetime.combine(self.meeting.date, datetime.time(23,0)),
time=self.meeting.tz().localize(
datetime.datetime.combine(self.meeting.date, datetime.time(23,0))
),
duration=duration,
)
overnight_session.timeslotassignments.create(timeslot=overnight_timeslot, schedule=self.meeting.schedule)
# Check assumptions about events overlapping midnight
self.assertEqual(daytime_timeslot.local_start_time().day,
daytime_timeslot.local_end_time().day,
@ -2048,7 +2052,7 @@ class InterimTests(IetfSeleniumTestCase):
Session.objects.filter(
meeting__type_id='interim',
timeslotassignments__schedule=F('meeting__schedule'),
timeslotassignments__timeslot__time__gte=datetime.datetime.today()
timeslotassignments__timeslot__time__gte=timezone.now()
)
).filter(current_status__in=('sched','canceled'))
meetings = []
@ -2061,7 +2065,7 @@ class InterimTests(IetfSeleniumTestCase):
def all_ietf_meetings(self):
meetings = Meeting.objects.filter(
type_id='ietf',
date__gte=datetime.datetime.today()-datetime.timedelta(days=7)
date__gte=timezone.now()-datetime.timedelta(days=7)
)
for m in meetings:
m.calendar_label = 'IETF %s' % m.number
@ -2191,7 +2195,7 @@ class InterimTests(IetfSeleniumTestCase):
expected_assignments = list(SchedTimeSessAssignment.objects.filter(
schedule__in=expected_schedules,
session__in=expected_interim_sessions,
timeslot__time__gte=datetime.date.today(),
timeslot__time__gte=datetime_today(),
))
# The UID formats should match those in the upcoming.ics template
expected_uids = [
@ -2609,7 +2613,7 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
self.meeting: Meeting = MeetingFactory(
type_id='ietf',
number=120,
date=datetime.datetime.today() + datetime.timedelta(days=10),
date=date_today() + datetime.timedelta(days=10),
populate_schedule=False,
)
self.edit_timeslot_url = self.absreverse(
@ -2679,22 +2683,23 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
self.do_delete_timeslot_test(cancel=True)
def do_delete_time_interval_test(self, cancel=False):
delete_day = self.meeting.date.date()
delete_day = self.meeting.date
delete_time = datetime.time(hour=10)
other_day = self.meeting.get_meeting_date(1).date()
other_day = self.meeting.get_meeting_date(1)
other_time = datetime.time(hour=12)
duration = datetime.timedelta(minutes=60)
delete: [TimeSlot] = TimeSlotFactory.create_batch(
2,
meeting=self.meeting,
time=datetime.datetime.combine(delete_day, delete_time),
duration=duration)
time=datetime_from_date(delete_day, self.meeting.tz()).replace(hour=delete_time.hour),
duration=duration,
)
keep: [TimeSlot] = [
TimeSlotFactory(
meeting=self.meeting,
time=datetime.datetime.combine(day, time),
time=datetime_from_date(day, self.meeting.tz()).replace(hour=time.hour),
duration=duration
)
for (day, time) in (
@ -2711,7 +2716,9 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
'[data-col-id="{}T{}-{}"]'.format(
delete_day.isoformat(),
delete_time.strftime('%H:%M'),
(datetime.datetime.combine(delete_day, delete_time) + duration).strftime(
self.meeting.tz().localize(
datetime.datetime.combine(delete_day, delete_time) + duration
).strftime(
'%H:%M'
))
)
@ -2726,22 +2733,22 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
self.do_delete_time_interval_test(cancel=True)
def do_delete_day_test(self, cancel=False):
delete_day = self.meeting.date.date()
times = [datetime.time(hour=10), datetime.time(hour=12)]
other_days = [self.meeting.get_meeting_date(d).date() for d in range(1, 3)]
delete_day = self.meeting.date
hours = [10, 12]
other_days = [self.meeting.get_meeting_date(d) for d in range(1, 3)]
delete: [TimeSlot] = [
TimeSlotFactory(
meeting=self.meeting,
time=datetime.datetime.combine(delete_day, time),
) for time in times
time=datetime_from_date(delete_day, self.meeting.tz()).replace(hour=hour),
) for hour in hours
]
keep: [TimeSlot] = [
TimeSlotFactory(
meeting=self.meeting,
time=datetime.datetime.combine(day, time),
) for day in other_days for time in times
time=datetime_from_date(day, self.meeting.tz()).replace(hour=hour),
) for day in other_days for hour in hours
]
selector = (

View file

@ -3,6 +3,8 @@
"""Tests of models in the Meeting application"""
import datetime
from mock import patch
from ietf.meeting.factories import MeetingFactory, SessionFactory
from ietf.stats.factories import MeetingRegistrationFactory
from ietf.utils.test_utils import TestCase
@ -52,6 +54,22 @@ class MeetingTests(TestCase):
self.assertEqual(attendance.online, 0)
self.assertEqual(attendance.onsite, 5)
def test_vtimezone(self):
# normal time zone that should have a zoneinfo file
meeting = MeetingFactory(type_id='ietf', time_zone='America/Los_Angeles', populate_schedule=False)
vtz = meeting.vtimezone()
self.assertIsNotNone(vtz)
self.assertGreater(len(vtz), 0)
# time zone that does not have a zoneinfo file should return None
meeting = MeetingFactory(type_id='ietf', time_zone='Fake/Time_Zone', populate_schedule=False)
vtz = meeting.vtimezone()
self.assertIsNone(vtz)
# ioerror trying to read zoneinfo should return None
meeting = MeetingFactory(type_id='ietf', time_zone='America/Los_Angeles', populate_schedule=False)
with patch('ietf.meeting.models.io.open', side_effect=IOError):
vtz = meeting.vtimezone()
self.assertIsNone(vtz)
class SessionTests(TestCase):
def test_chat_archive_url_with_jabber(self):

View file

@ -1,6 +1,7 @@
# Copyright The IETF Trust 2020, All Rights Reserved
import calendar
import datetime
import pytz
from io import StringIO
from django.core.management.base import CommandError
@ -36,9 +37,11 @@ class ScheduleGeneratorTest(TestCase):
t = TimeSlotFactory(
meeting=self.meeting,
location=room,
time=datetime.datetime.combine(
self.meeting.date + datetime.timedelta(days=day),
datetime.time(hour, 0),
time=self.meeting.tz().localize(
datetime.datetime.combine(
self.meeting.date + datetime.timedelta(days=day),
datetime.time(hour, 0),
)
),
duration=datetime.timedelta(minutes=60),
)
@ -306,8 +309,11 @@ class ScheduleGeneratorTest(TestCase):
add_to_schedule=False
)
# use a timeslot not on Sunday
meeting_date = pytz.utc.localize(
datetime.datetime.combine(self.meeting.get_meeting_date(1), datetime.time())
)
ts = self.meeting.timeslot_set.filter(
time__gt=self.meeting.date + datetime.timedelta(days=1),
time__gt=meeting_date,
location__capacity__lt=base_reg_session.attendees,
).order_by(
'time'

View file

@ -29,8 +29,8 @@ from django.test import Client, override_settings
from django.db.models import F, Max
from django.http import QueryDict, FileResponse
from django.template import Context, Template
from django.utils import timezone
from django.utils.text import slugify
from django.utils.timezone import now
import debug # pyflakes:ignore
@ -52,6 +52,7 @@ from ietf.utils.decorators import skip_coverage
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
from ietf.utils.text import xslugify
from ietf.utils.timezone import date_today, time_now
from ietf.person.factories import PersonFactory
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
@ -155,7 +156,7 @@ class MeetingTests(BaseMeetingTestCase):
#
self.write_materials_files(meeting, session)
#
future_year = datetime.date.today().year+1
future_year = date_today().year+1
future_num = (future_year-1984)*3 # valid for the mid-year meeting
future_meeting = Meeting.objects.create(date=datetime.date(future_year, 7, 22), number=future_num, type_id='ietf',
city="Panama City", country="PA", time_zone='America/Panama')
@ -228,7 +229,10 @@ class MeetingTests(BaseMeetingTestCase):
'Time zone indicator should be in nav sidebar')
# plain
time_interval = r"%s<span.*/span>-%s" % (slot.time.strftime("%H:%M").lstrip("0"), (slot.time + slot.duration).strftime("%H:%M").lstrip("0"))
time_interval = r"{}<span.*/span>-{}".format(
slot.time.astimezone(meeting.tz()).strftime("%H:%M").lstrip("0"),
slot.end_time().astimezone(meeting.tz()).strftime("%H:%M").lstrip("0"),
)
r = self.client.get(urlreverse("ietf.meeting.views.agenda", kwargs=dict(num=meeting.number)))
self.assertEqual(r.status_code, 200)
@ -430,7 +434,9 @@ class MeetingTests(BaseMeetingTestCase):
session_date = meeting.date + datetime.timedelta(days=1)
slot3 = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
duration=datetime.timedelta(minutes=60),
time=datetime.datetime.combine(session_date, datetime.time(13, 30)))
time=meeting.tz().localize(
datetime.datetime.combine(session_date, datetime.time(13, 30))
))
SchedTimeSessAssignment.objects.create(timeslot=slot3, session=venus_session, schedule=meeting.schedule)
url = urlreverse('ietf.meeting.views.agenda', kwargs=dict(num=meeting.number))
r = self.client.get(url)
@ -604,7 +610,7 @@ class MeetingTests(BaseMeetingTestCase):
def test_interim_materials(self):
make_meeting_test_data()
group = Group.objects.get(acronym='mars')
date = datetime.datetime.today() - datetime.timedelta(days=10)
date = timezone.now() - datetime.timedelta(days=10)
meeting = make_interim_meeting(group=group, date=date, status='sched')
session = meeting.session_set.first()
@ -801,7 +807,12 @@ class MeetingTests(BaseMeetingTestCase):
a1 = s1.official_timeslotassignment()
t1 = a1.timeslot
# Create an extra session
t2 = TimeSlotFactory.create(meeting=meeting, time=datetime.datetime.combine(meeting.date, datetime.time(11, 30)))
t2 = TimeSlotFactory.create(
meeting=meeting,
time=meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(11, 30))
)
)
s2 = SessionFactory.create(meeting=meeting, group=s1.group, add_to_schedule=False)
SchedTimeSessAssignment.objects.create(timeslot=t2, session=s2, schedule=meeting.schedule)
#
@ -811,16 +822,16 @@ class MeetingTests(BaseMeetingTestCase):
r,
expected_event_summaries=['mars - Martian Special Interest Group'],
expected_event_count=2)
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t1.local_start_time().strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t2.local_start_time().strftime('%Y%m%dT%H%M%S'))
#
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'session_id':s1.id, })
r = self.client.get(url)
assert_ical_response_is_valid(self, r,
expected_event_summaries=['mars - Martian Special Interest Group'],
expected_event_count=1)
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
self.assertNotContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t1.local_start_time().strftime('%Y%m%dT%H%M%S'))
self.assertNotContains(r, t2.local_start_time().strftime('%Y%m%dT%H%M%S'))
def test_meeting_agenda_has_static_ical_links(self):
"""Links to the agenda_ical view must appear on the agenda page
@ -960,7 +971,7 @@ class MeetingTests(BaseMeetingTestCase):
url = urlreverse('ietf.meeting.views.current_materials')
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
MeetingFactory(type_id='ietf', date=datetime.date.today())
MeetingFactory(type_id='ietf', date=date_today())
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
@ -1105,7 +1116,9 @@ class EditMeetingScheduleTests(TestCase):
TimeSlotFactory(
meeting=meeting,
location=room,
time=datetime.datetime.combine(meeting.date, time),
time=meeting.tz().localize(
datetime.datetime.combine(meeting.date, time)
),
duration=datetime.timedelta(minutes=duration),
)
@ -1189,7 +1202,11 @@ class EditMeetingScheduleTests(TestCase):
]
# Set up different sets of timeslots
t0 = datetime.datetime.combine(meeting.date, datetime.time(11, 0))
# Work with t0 in UTC for arithmetic. This does not change the results but is cleaner if someone looks
# at intermediate results which may be misleading until passed through tz.normalize().
t0 = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(11, 0))
).astimezone(pytz.utc)
dur = datetime.timedelta(hours=2)
for room in room_groups[0]:
TimeSlotFactory(meeting=meeting, location=room, duration=dur, time=t0)
@ -1284,7 +1301,7 @@ class EditMeetingScheduleTests(TestCase):
self.client.login(username=username, password=username + '+password')
# Swap group 0's first and last sessions, first in the past
right_now = self._right_now_in(meeting.time_zone)
right_now = self._right_now_in(meeting.tz())
for room in room_groups[0]:
ts = room.timeslot_set.last()
ts.time = right_now - datetime.timedelta(minutes=5)
@ -1466,12 +1483,18 @@ class EditMeetingScheduleTests(TestCase):
self.client.login(username=username, password=username + '+password')
# Swap group 0's first and last sessions, first in the past
right_now = self._right_now_in(meeting.time_zone)
yesterday = (right_now - datetime.timedelta(days=1)).date()
day_before = (right_now - datetime.timedelta(days=2)).date()
right_now = self._right_now_in(meeting.tz())
yesterday = right_now.date() - datetime.timedelta(days=1)
day_before = right_now.date() - datetime.timedelta(days=2)
for room in room_groups[0]:
ts = room.timeslot_set.last()
ts.time = datetime.datetime.combine(yesterday, ts.time.time())
# Calculation keeps local clock time, shifted to a different day.
ts.time = meeting.tz().localize(
datetime.datetime.combine(
yesterday,
ts.time.astimezone(meeting.tz()).time()
),
)
ts.save()
# timeslot_set is ordered by -time, so check that we know which is past/future
self.assertTrue(room_groups[0][0].timeslot_set.last().time < right_now)
@ -1505,7 +1528,12 @@ class EditMeetingScheduleTests(TestCase):
# now with both in the past
for room in room_groups[0]:
ts = room.timeslot_set.first()
ts.time = datetime.datetime.combine(day_before, ts.time.time())
ts.time = meeting.tz().localize(
datetime.datetime.combine(
day_before,
ts.time.astimezone(meeting.tz()).time(),
)
)
ts.save()
past_slots = room_groups[0][0].timeslot_set.filter(time__lt=right_now)
self.assertEqual(len(past_slots), 2, 'Need two timeslots in the past!')
@ -1528,8 +1556,8 @@ class EditMeetingScheduleTests(TestCase):
self.fail('Response was not valid JSON: {}'.format(err))
@staticmethod
def _right_now_in(tzname):
right_now = now().astimezone(pytz.timezone(tzname))
def _right_now_in(tzinfo):
right_now = timezone.now().astimezone(tzinfo)
if not settings.USE_TZ:
right_now = right_now.replace(tzinfo=None)
return right_now
@ -1538,10 +1566,10 @@ class EditMeetingScheduleTests(TestCase):
"""Allow assignment to future timeslots only for official schedule"""
meeting = MeetingFactory(
type_id='ietf',
date=(datetime.datetime.today() - datetime.timedelta(days=1)).date(),
date=(timezone.now() - datetime.timedelta(days=1)).date(),
days=3,
)
right_now = self._right_now_in(meeting.time_zone)
right_now = self._right_now_in(meeting.tz())
schedules = dict(
official=meeting.schedule,
@ -1598,10 +1626,10 @@ class EditMeetingScheduleTests(TestCase):
"""Do not allow assignment of past sessions for official schedule"""
meeting = MeetingFactory(
type_id='ietf',
date=(datetime.datetime.today() - datetime.timedelta(days=1)).date(),
date=(timezone.now() - datetime.timedelta(days=1)).date(),
days=3,
)
right_now = self._right_now_in(meeting.time_zone)
right_now = self._right_now_in(meeting.tz())
schedules = dict(
official=meeting.schedule,
@ -1733,10 +1761,10 @@ class EditMeetingScheduleTests(TestCase):
"""Allow unassignment only of future timeslots for official schedule"""
meeting = MeetingFactory(
type_id='ietf',
date=(datetime.datetime.today() - datetime.timedelta(days=1)).date(),
date=(timezone.now() - datetime.timedelta(days=1)).date(),
days=3,
)
right_now = self._right_now_in(meeting.time_zone)
right_now = self._right_now_in(meeting.tz())
schedules = dict(
official=meeting.schedule,
@ -1857,7 +1885,7 @@ class EditTimeslotsTests(TestCase):
return MeetingFactory(
type_id='ietf',
number=number,
date=datetime.datetime.today() + datetime.timedelta(days=10),
date=date_today() + datetime.timedelta(days=10),
populate_schedule=False,
)
@ -1889,7 +1917,8 @@ class EditTimeslotsTests(TestCase):
meeting = self.create_bare_meeting(number=number)
RoomFactory.create_batch(8, meeting=meeting)
self.create_initial_schedule(meeting)
return meeting
# retrieve meeting from DB so it goes through Django's processing
return Meeting.objects.get(pk=meeting.pk)
def test_view_permissions(self):
"""Only the secretary should be able to edit timeslots"""
@ -2058,7 +2087,7 @@ class EditTimeslotsTests(TestCase):
meeting = self.create_meeting()
# add some timeslots
times = [datetime.time(hour=h) for h in (11, 14)]
days = [meeting.get_meeting_date(ii).date() for ii in range(meeting.days)]
days = [meeting.get_meeting_date(ii) for ii in range(meeting.days)]
timeslots = []
duration = datetime.timedelta(minutes=90)
@ -2068,7 +2097,7 @@ class EditTimeslotsTests(TestCase):
TimeSlotFactory(
meeting=meeting,
location=room,
time=datetime.datetime.combine(day, t),
time=meeting.tz().localize(datetime.datetime.combine(day, t)),
duration=duration,
)
for t in times
@ -2149,17 +2178,21 @@ class EditTimeslotsTests(TestCase):
TimeSlotFactory(
meeting=meeting,
location=meeting.room_set.first(),
time=datetime.datetime.combine(
meeting.get_meeting_date(day).date(),
datetime.time(hour=11)
time=meeting.tz().localize(
datetime.datetime.combine(
meeting.get_meeting_date(day),
datetime.time(hour=11),
)
),
)
TimeSlotFactory(
meeting=meeting,
location=meeting.room_set.first(),
time=datetime.datetime.combine(
meeting.get_meeting_date(day).date(),
datetime.time(hour=14)
time=meeting.tz().localize(
datetime.datetime.combine(
meeting.get_meeting_date(day),
datetime.time(hour=14),
)
),
)
@ -2258,10 +2291,8 @@ class EditTimeslotsTests(TestCase):
name_before = 'Name Classic (tm)'
type_before = 'regular'
time_before = datetime.datetime.combine(
meeting.date,
datetime.time(hour=10),
)
time_utc = pytz.utc.localize(datetime.datetime.combine(meeting.date, datetime.time(hour=10)))
time_before = time_utc.astimezone(meeting.tz())
duration_before = datetime.timedelta(minutes=60)
show_location_before = True
location_before = meeting.room_set.first()
@ -2278,7 +2309,7 @@ class EditTimeslotsTests(TestCase):
self.login()
name_after = 'New Name (tm)'
type_after = 'plenary'
time_after = time_before + datetime.timedelta(days=1, hours=2)
time_after = (time_utc + datetime.timedelta(days=1, hours=2)).astimezone(meeting.tz())
duration_after = duration_before * 2
show_location_after = False
location_after = meeting.room_set.last()
@ -2458,8 +2489,8 @@ class EditTimeslotsTests(TestCase):
ts = meeting.timeslot_set.exclude(pk__in=timeslots_before).first() # only 1
self.assertEqual(ts.name, post_data['name'])
self.assertEqual(ts.type_id, post_data['type'])
self.assertEqual(str(ts.time.date().toordinal()), post_data['days'])
self.assertEqual(ts.time.strftime('%H:%M'), post_data['time'])
self.assertEqual(str(ts.local_start_time().date().toordinal()), post_data['days'])
self.assertEqual(ts.local_start_time().strftime('%H:%M'), post_data['time'])
self.assertEqual(str(ts.duration), '{}:00'.format(post_data['duration'])) # add seconds
self.assertEqual(ts.show_location, post_data['show_location'])
self.assertEqual(str(ts.location.pk), post_data['locations'])
@ -2468,7 +2499,7 @@ class EditTimeslotsTests(TestCase):
"""Creating a single timeslot outside the official meeting days should work"""
meeting = self.create_meeting()
timeslots_before = set(ts.pk for ts in meeting.timeslot_set.all())
other_date = meeting.get_meeting_date(-7).date()
other_date = meeting.get_meeting_date(-7)
post_data = dict(
name='some name',
type='regular',
@ -2491,8 +2522,8 @@ class EditTimeslotsTests(TestCase):
ts = meeting.timeslot_set.exclude(pk__in=timeslots_before).first() # only 1
self.assertEqual(ts.name, post_data['name'])
self.assertEqual(ts.type_id, post_data['type'])
self.assertEqual(ts.time.date(), other_date)
self.assertEqual(ts.time.strftime('%H:%M'), post_data['time'])
self.assertEqual(ts.local_start_time().date(), other_date)
self.assertEqual(ts.local_start_time().strftime('%H:%M'), post_data['time'])
self.assertEqual(str(ts.duration), '{}:00'.format(post_data['duration'])) # add seconds
self.assertEqual(ts.show_location, post_data['show_location'])
self.assertEqual(str(ts.location.pk), post_data['locations'])
@ -2706,8 +2737,8 @@ class EditTimeslotsTests(TestCase):
"""Creating multiple timeslots should work"""
meeting = self.create_meeting()
timeslots_before = set(ts.pk for ts in meeting.timeslot_set.all())
days = [meeting.get_meeting_date(n).date() for n in range(meeting.days)]
other_date = meeting.get_meeting_date(-1).date() # date before start of meeting
days = [meeting.get_meeting_date(n) for n in range(meeting.days)]
other_date = meeting.get_meeting_date(-1) # date before start of meeting
self.assertNotIn(other_date, days)
locations = meeting.room_set.all()
post_data = dict(
@ -2737,10 +2768,10 @@ class EditTimeslotsTests(TestCase):
for ts in meeting.timeslot_set.exclude(pk__in=timeslots_before):
self.assertEqual(ts.name, post_data['name'])
self.assertEqual(ts.type_id, post_data['type'])
self.assertEqual(ts.time.strftime('%H:%M'), post_data['time'])
self.assertEqual(ts.local_start_time().strftime('%H:%M'), post_data['time'])
self.assertEqual(str(ts.duration), '{}:00'.format(post_data['duration'])) # add seconds
self.assertEqual(ts.show_location, post_data['show_location'])
self.assertIn(ts.time.date(), days)
self.assertIn(ts.local_start_time().date(), days)
self.assertIn(ts.location, locations)
self.assertIn((ts.time.date(), ts.location), day_locs,
'Duplicated day / location found')
@ -3288,7 +3319,9 @@ class EditTests(TestCase):
room = Room.objects.get(meeting=meeting, session_types='regular')
base_timeslot = TimeSlot.objects.create(meeting=meeting, type_id='regular', location=room,
duration=datetime.timedelta(minutes=50),
time=datetime.datetime.combine(meeting.date + datetime.timedelta(days=2), datetime.time(9, 30)))
time=meeting.tz().localize(
datetime.datetime.combine(meeting.date + datetime.timedelta(days=2), datetime.time(9, 30))
))
timeslots = list(TimeSlot.objects.filter(meeting=meeting, type='regular').order_by('time'))
@ -3510,7 +3543,12 @@ class EditTests(TestCase):
self.assertIn("#scroll=1234", r['Location'])
test_timeslot = TimeSlot.objects.get(meeting=meeting, name="IETF Testing")
self.assertEqual(test_timeslot.time, datetime.datetime.combine(meeting.date, datetime.time(8, 30)))
self.assertEqual(
test_timeslot.time,
meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(8, 30))
),
)
self.assertEqual(test_timeslot.duration, datetime.timedelta(hours=1, minutes=30))
self.assertEqual(test_timeslot.location_id, break_room.pk)
self.assertEqual(test_timeslot.show_location, True)
@ -3552,7 +3590,12 @@ class EditTests(TestCase):
})
self.assertNoFormPostErrors(r)
test_timeslot.refresh_from_db()
self.assertEqual(test_timeslot.time, datetime.datetime.combine(meeting.date, datetime.time(9, 30)))
self.assertEqual(
test_timeslot.time,
meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(9, 30))
),
)
self.assertEqual(test_timeslot.duration, datetime.timedelta(hours=1))
self.assertEqual(test_timeslot.location_id, breakfast_room.pk)
self.assertEqual(test_timeslot.show_location, False)
@ -4329,12 +4372,10 @@ class InterimTests(TestCase):
self.do_interim_skip_announcement_test(extra_session=True, canceled_session=True, base_session=True)
def do_interim_send_announcement_test(self, base_session=False, extra_session=False, canceled_session=False):
make_interim_test_data()
make_interim_test_data(meeting_tz='America/Los_Angeles')
session = Session.objects.with_current_status().filter(
meeting__type='interim', group__acronym='mars', current_status='apprw').first()
meeting = session.meeting
meeting.time_zone = 'America/Los_Angeles'
meeting.save()
if base_session:
base_session = SessionFactory(meeting=meeting, status_id='apprw', add_to_schedule=False)
@ -4679,9 +4720,9 @@ class InterimTests(TestCase):
def do_interim_request_single_virtual(self, emails_expected):
make_meeting_test_data()
group = Group.objects.get(acronym='mars')
date = datetime.date.today() + datetime.timedelta(days=30)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
dt = datetime.datetime.combine(date, time)
date = date_today() + datetime.timedelta(days=30)
time = time_now().replace(microsecond=0,second=0)
dt = pytz.utc.localize(datetime.datetime.combine(date, time))
duration = datetime.timedelta(hours=3)
remote_instructions = 'Use webex'
agenda = 'Intro. Slides. Discuss.'
@ -4750,13 +4791,14 @@ class InterimTests(TestCase):
def test_interim_request_single_in_person(self):
make_meeting_test_data()
group = Group.objects.get(acronym='mars')
date = datetime.date.today() + datetime.timedelta(days=30)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
dt = datetime.datetime.combine(date, time)
date = date_today() + datetime.timedelta(days=30)
time = time_now().replace(microsecond=0,second=0)
time_zone = 'America/Los_Angeles'
tz = pytz.timezone(time_zone)
dt = tz.localize(datetime.datetime.combine(date, time))
duration = datetime.timedelta(hours=3)
city = 'San Francisco'
country = 'US'
time_zone = 'America/Los_Angeles'
remote_instructions = 'Use webex'
agenda = 'Intro. Slides. Discuss.'
agenda_note = 'On second level'
@ -4797,16 +4839,17 @@ class InterimTests(TestCase):
def test_interim_request_multi_day(self):
make_meeting_test_data()
date = datetime.date.today() + datetime.timedelta(days=30)
date = date_today() + datetime.timedelta(days=30)
date2 = date + datetime.timedelta(days=1)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
dt = datetime.datetime.combine(date, time)
dt2 = datetime.datetime.combine(date2, time)
time = time_now().replace(microsecond=0,second=0)
time_zone = 'America/Los_Angeles'
tz = pytz.timezone(time_zone)
dt = tz.localize(datetime.datetime.combine(date, time))
dt2 = tz.localize(datetime.datetime.combine(date2, time))
duration = datetime.timedelta(hours=3)
group = Group.objects.get(acronym='mars')
city = 'San Francisco'
country = 'US'
time_zone = 'America/Los_Angeles'
remote_instructions = 'Use webex'
agenda = 'Intro. Slides. Discuss.'
agenda_note = 'On second level'
@ -4865,7 +4908,7 @@ class InterimTests(TestCase):
make_meeting_test_data()
date = datetime.date.today() + datetime.timedelta(days=30)
date2 = date + datetime.timedelta(days=2)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
time = timezone.now().time().replace(microsecond=0,second=0)
group = Group.objects.get(acronym='mars')
city = 'San Francisco'
country = 'US'
@ -4923,7 +4966,7 @@ class InterimTests(TestCase):
def test_interim_request_series(self):
make_meeting_test_data()
meeting_count_before = Meeting.objects.filter(type='interim').count()
date = datetime.date.today() + datetime.timedelta(days=30)
date = date_today() + datetime.timedelta(days=30)
if (date.month, date.day) == (12, 31):
# Avoid date and date2 in separate years
# (otherwise the test will fail if run on December 1st)
@ -4933,14 +4976,15 @@ class InterimTests(TestCase):
if date.year != date2.year:
date += datetime.timedelta(days=1)
date2 += datetime.timedelta(days=1)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
dt = datetime.datetime.combine(date, time)
dt2 = datetime.datetime.combine(date2, time)
time = time_now().replace(microsecond=0,second=0)
time_zone = 'America/Los_Angeles'
tz = pytz.timezone(time_zone)
dt = tz.localize(datetime.datetime.combine(date, time))
dt2 = tz.localize(datetime.datetime.combine(date2, time))
duration = datetime.timedelta(hours=3)
group = Group.objects.get(acronym='mars')
city = ''
country = ''
time_zone = 'America/Los_Angeles'
remote_instructions = 'Use webex'
agenda = 'Intro. Slides. Discuss.'
agenda_note = 'On second level'
@ -5082,14 +5126,14 @@ class InterimTests(TestCase):
self.assertFalse(can_manage_group(user=user,group=group))
def test_interim_request_details(self):
make_interim_test_data()
make_interim_test_data(meeting_tz='America/Chicago')
meeting = Session.objects.with_current_status().filter(
meeting__type='interim', group__acronym='mars', current_status='apprw').first().meeting
url = urlreverse('ietf.meeting.views.interim_request_details',kwargs={'number':meeting.number})
login_testing_unauthorized(self,"secretary",url)
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
start_time = meeting.session_set.first().official_timeslotassignment().timeslot.time.strftime('%H:%M')
start_time = meeting.session_set.first().official_timeslotassignment().timeslot.local_start_time().strftime('%H:%M')
utc_start_time = meeting.session_set.first().official_timeslotassignment().timeslot.utc_start_time().strftime('%H:%M')
self.assertIn(start_time, unicontent(r))
self.assertIn(utc_start_time, unicontent(r))
@ -5615,7 +5659,7 @@ class InterimTests(TestCase):
def test_send_interim_minutes_reminder(self):
make_meeting_test_data()
group = Group.objects.get(acronym='mars')
date = datetime.datetime.today() - datetime.timedelta(days=10)
date = timezone.now() - datetime.timedelta(days=10)
meeting = make_interim_meeting(group=group, date=date, status='sched')
length_before = len(outbox)
send_interim_minutes_reminder(meeting=meeting)
@ -5630,7 +5674,11 @@ class InterimTests(TestCase):
a1 = s1.official_timeslotassignment()
t1 = a1.timeslot
# Create an extra session
t2 = TimeSlotFactory.create(meeting=meeting, time=datetime.datetime.combine(meeting.date, datetime.time(11, 30)))
t2 = TimeSlotFactory.create(
meeting=meeting,
time=meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(11, 30))
))
s2 = SessionFactory.create(meeting=meeting, group=s1.group, add_to_schedule=False)
SchedTimeSessAssignment.objects.create(timeslot=t2, session=s2, schedule=meeting.schedule)
#
@ -5640,8 +5688,8 @@ class InterimTests(TestCase):
self.assertContains(r, 'BEGIN:VEVENT')
self.assertEqual(r.content.count(b'UID'), 2)
self.assertContains(r, 'SUMMARY:mars - Martian Special Interest Group')
self.assertContains(r, t1.time.strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t2.time.strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t1.local_start_time().strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, t2.local_start_time().strftime('%Y%m%dT%H%M%S'))
self.assertContains(r, 'END:VEVENT')
#
url = urlreverse('ietf.meeting.views.agenda_ical', kwargs={'num':meeting.number, 'session_id':s1.id, })
@ -6563,7 +6611,7 @@ class HasMeetingsTests(TestCase):
q = PyQuery(r.content)
self.assertTrue(q('#id_group option[value="%d"]'%group.pk))
date = datetime.date.today() + datetime.timedelta(days=30+meeting_count)
time = datetime.datetime.now().time().replace(microsecond=0,second=0)
time = timezone.now().time().replace(microsecond=0,second=0)
remote_instructions = 'Use webex'
agenda = 'Intro. Slides. Discuss.'
agenda_note = 'On second level'
@ -6659,7 +6707,7 @@ class HasMeetingsTests(TestCase):
session = SessionFactory(
group__type_id = gf.type_id,
meeting__type_id='interim',
meeting__date = datetime.datetime.today()+datetime.timedelta(days=30),
meeting__date = timezone.now()+datetime.timedelta(days=30),
status_id='sched',
)
sessions.append(session)
@ -6675,7 +6723,7 @@ class HasMeetingsTests(TestCase):
sessions=[]
for gf in GroupFeatures.objects.filter(has_meetings=True):
group = GroupFactory(type_id=gf.type_id)
meeting_date = datetime.datetime.today() + datetime.timedelta(days=30)
meeting_date = timezone.now() + datetime.timedelta(days=30)
session = SessionFactory(
group=group,
meeting__type_id='interim',
@ -6696,7 +6744,7 @@ class HasMeetingsTests(TestCase):
sessions=[]
for gf in GroupFeatures.objects.filter(has_meetings=True):
group = GroupFactory(type_id=gf.type_id)
meeting_date = datetime.datetime.today() + datetime.timedelta(days=30)
meeting_date = timezone.now() + datetime.timedelta(days=30)
session = SessionFactory(
group=group,
meeting__type_id='interim',
@ -7422,7 +7470,7 @@ class ProceedingsTests(BaseMeetingTestCase):
def test_proceedings_no_agenda(self):
# Meeting number must be larger than the last special-cased proceedings (currently 96)
meeting = MeetingFactory(type_id='ietf',populate_schedule=False,date=datetime.date.today(), number='100')
meeting = MeetingFactory(type_id='ietf',populate_schedule=False,date=date_today(), number='100')
url = urlreverse('ietf.meeting.views.proceedings')
r = self.client.get(url)
self.assertRedirects(r, urlreverse('ietf.meeting.views.materials'))
@ -7531,7 +7579,7 @@ class ProceedingsTests(BaseMeetingTestCase):
"""Generate a meeting for proceedings material test"""
# meeting number 123 avoids various legacy cases that affect these tests
# (as of Aug 2021, anything above 96 is probably ok)
return MeetingFactory(type_id='ietf', number='123', date=datetime.date.today())
return MeetingFactory(type_id='ietf', number='123', date=date_today())
def _secretary_only_permission_test(self, url, include_post=True):
self.client.logout()

View file

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import datetime
import itertools
import pytz
import requests
import subprocess
@ -12,6 +13,7 @@ from urllib.error import HTTPError
from django.conf import settings
from django.contrib import messages
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import smart_text
import debug # pyflakes:ignore
@ -33,13 +35,17 @@ def session_time_for_sorting(session, use_meeting_date):
if official_timeslot:
return official_timeslot.time
elif use_meeting_date and session.meeting.date:
return datetime.datetime.combine(session.meeting.date, datetime.time.min)
return session.meeting.tz().localize(
datetime.datetime.combine(session.meeting.date, datetime.time.min)
)
else:
first_event = SchedulingEvent.objects.filter(session=session).order_by('time', 'id').first()
if first_event:
return first_event.time
else:
return datetime.datetime.min
# n.b. cannot interpret this in timezones west of UTC. That is not expected to be necessary,
# but could probably safely add a day to the minimum datetime to make that possible.
return pytz.utc.localize(datetime.datetime.min)
def session_requested_by(session):
first_event = SchedulingEvent.objects.filter(session=session).order_by('time', 'id').first()
@ -158,7 +164,12 @@ def create_proceedings_templates(meeting):
def finalize(meeting):
end_date = meeting.end_date()
end_time = datetime.datetime.combine(end_date, datetime.datetime.min.time())+datetime.timedelta(days=1)
end_time = meeting.tz().localize(
datetime.datetime.combine(
end_date,
datetime.time.min,
)
).astimezone(pytz.utc) + datetime.timedelta(days=1)
for session in meeting.session_set.all():
for sp in session.sessionpresentation_set.filter(document__type='draft',rev=None):
rev_before_end = [e for e in sp.document.docevent_set.filter(newrevisiondocevent__isnull=False).order_by('-time') if e.time <= end_time ]
@ -322,7 +333,9 @@ def preprocess_constraints_for_meeting_schedule_editor(meeting, sessions):
# synthesize AD constraints - we can treat them as a special kind of 'bethere'
responsible_ad_for_group = {}
session_groups = set(s.group for s in sessions if s.group and s.group.parent and s.group.parent.type_id == 'area')
meeting_time = datetime.datetime.combine(meeting.date, datetime.time(0, 0, 0))
meeting_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time(0, 0, 0))
)
# dig up historic AD names
for group_id, history_time, pk in Person.objects.filter(rolehistory__name='ad', rolehistory__group__group__in=session_groups, rolehistory__group__time__lte=meeting_time).values_list('rolehistory__group__group', 'rolehistory__group__time', 'pk').order_by('rolehistory__group__time'):
@ -511,7 +524,7 @@ def swap_meeting_schedule_timeslot_assignments(schedule, source_timeslots, targe
if max_overlap > datetime.timedelta(minutes=5):
for a in lts_assignments:
a.timeslot = most_overlapping_rts
a.modified = datetime.datetime.now()
a.modified = timezone.now()
a.save()
swapped = True

View file

@ -36,10 +36,10 @@ from django.db.models import F, Max, Q
from django.forms.models import modelform_factory, inlineformset_factory
from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import force_str
from django.utils.functional import curry
from django.utils.text import slugify
from django.utils.timezone import now
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt
from django.views.generic import RedirectView
@ -96,6 +96,7 @@ from ietf.utils.pipe import pipe
from ietf.utils.pdf import pdf_pages
from ietf.utils.response import permission_denied
from ietf.utils.text import xslugify
from ietf.utils.timezone import datetime_today, date_today
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm,
@ -127,8 +128,8 @@ def materials(request, num=None):
begin_date = meeting.get_submission_start_date()
cut_off_date = meeting.get_submission_cut_off_date()
cor_cut_off_date = meeting.get_submission_correction_date()
now = datetime.date.today()
old = datetime.datetime.now() - datetime.timedelta(days=1)
now = date_today()
old = timezone.now() - datetime.timedelta(days=1)
if settings.SERVER_MODE != 'production' and '_testoverride' in request.GET:
pass
elif now > cor_cut_off_date:
@ -142,7 +143,7 @@ def materials(request, num=None):
'cor_cut_off_date': cor_cut_off_date
})
past_cutoff_date = datetime.date.today() > meeting.get_submission_correction_date()
past_cutoff_date = date_today() > meeting.get_submission_correction_date()
schedule = get_schedule(meeting, None)
@ -192,7 +193,7 @@ def materials(request, num=None):
})
def current_materials(request):
today = datetime.date.today()
today = date_today()
meetings = Meeting.objects.exclude(number__startswith='interim-').filter(date__lte=today).order_by('-date')
if meetings:
return redirect(materials, meetings[0].number)
@ -281,60 +282,63 @@ def materials_editable_groups(request, num=None):
def edit_timeslots(request, num=None):
meeting = get_meeting(num)
timezone.activate(meeting.tz())
try:
if request.method == 'POST':
# handle AJAX requests
action = request.POST.get('action')
if action == 'delete':
# delete a timeslot
# Parameters:
# slot_id: comma-separated list of TimeSlot PKs to delete
slot_id = request.POST.get('slot_id')
if slot_id is None:
return HttpResponseBadRequest('missing slot_id')
slot_ids = [id.strip() for id in slot_id.split(',')]
try:
timeslots = meeting.timeslot_set.filter(pk__in=slot_ids)
except ValueError:
return HttpResponseBadRequest('invalid slot_id specification')
missing_ids = set(slot_ids).difference(str(ts.pk) for ts in timeslots)
if len(missing_ids) != 0:
return HttpResponseNotFound('TimeSlot ids not found in meeting {}: {}'.format(
meeting.number,
', '.join(sorted(missing_ids))
))
timeslots.delete()
return HttpResponse(content='; '.join('Deleted TimeSlot {}'.format(id) for id in slot_ids))
else:
return HttpResponseBadRequest('unknown action')
if request.method == 'POST':
# handle AJAX requests
action = request.POST.get('action')
if action == 'delete':
# delete a timeslot
# Parameters:
# slot_id: comma-separated list of TimeSlot PKs to delete
slot_id = request.POST.get('slot_id')
if slot_id is None:
return HttpResponseBadRequest('missing slot_id')
slot_ids = [id.strip() for id in slot_id.split(',')]
try:
timeslots = meeting.timeslot_set.filter(pk__in=slot_ids)
except ValueError:
return HttpResponseBadRequest('invalid slot_id specification')
missing_ids = set(slot_ids).difference(str(ts.pk) for ts in timeslots)
if len(missing_ids) != 0:
return HttpResponseNotFound('TimeSlot ids not found in meeting {}: {}'.format(
meeting.number,
', '.join(sorted(missing_ids))
))
timeslots.delete()
return HttpResponse(content='; '.join('Deleted TimeSlot {}'.format(id) for id in slot_ids))
else:
return HttpResponseBadRequest('unknown action')
# Labels here differ from those in the build_timeslices() method. The labels here are
# relative to the table: time_slices are the row headings (ie, days), date_slices are
# the column headings (i.e., time intervals), and slots are the per-day list of timeslots
# (with only one timeslot per unique time/duration)
time_slices, date_slices, slots = meeting.build_timeslices()
# Labels here differ from those in the build_timeslices() method. The labels here are
# relative to the table: time_slices are the row headings (ie, days), date_slices are
# the column headings (i.e., time intervals), and slots are the per-day list of timeslots
# (with only one timeslot per unique time/duration)
time_slices, date_slices, slots = meeting.build_timeslices()
ts_list = deque()
rooms = meeting.room_set.order_by("capacity","name","id")
for room in rooms:
for day in time_slices:
for slice in date_slices[day]:
ts_list.append(room.timeslot_set.filter(time=slice[0],duration=datetime.timedelta(seconds=slice[2])))
ts_list = deque()
rooms = meeting.room_set.order_by("capacity","name","id")
for room in rooms:
for day in time_slices:
for slice in date_slices[day]:
ts_list.append(room.timeslot_set.filter(time=slice[0],duration=datetime.timedelta(seconds=slice[2])))
# Grab these in one query each to identify sessions that are in use and should be handled with care
ts_with_official_assignments = meeting.timeslot_set.filter(sessionassignments__schedule=meeting.schedule)
ts_with_any_assignments = meeting.timeslot_set.filter(sessionassignments__isnull=False)
# Grab these in one query each to identify sessions that are in use and should be handled with care
ts_with_official_assignments = meeting.timeslot_set.filter(sessionassignments__schedule=meeting.schedule)
ts_with_any_assignments = meeting.timeslot_set.filter(sessionassignments__isnull=False)
return render(request, "meeting/timeslot_edit.html",
{"rooms":rooms,
"time_slices":time_slices,
"slot_slices": slots,
"date_slices":date_slices,
"meeting":meeting,
"ts_list":ts_list,
"ts_with_official_assignments": ts_with_official_assignments,
"ts_with_any_assignments": ts_with_any_assignments,
})
return render(request, "meeting/timeslot_edit.html",
{"rooms":rooms,
"time_slices":time_slices,
"slot_slices": slots,
"date_slices":date_slices,
"meeting":meeting,
"ts_list":ts_list,
"ts_with_official_assignments": ts_with_official_assignments,
"ts_with_any_assignments": ts_with_any_assignments,
})
finally:
timezone.deactivate()
class NewScheduleForm(forms.ModelForm):
class Meta:
@ -442,7 +446,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
lock_time = settings.MEETING_SESSION_LOCK_TIME
def timeslot_locked(ts):
meeting_now = now().astimezone(pytz.timezone(meeting.time_zone))
meeting_now = timezone.now().astimezone(pytz.timezone(meeting.time_zone))
if not settings.USE_TZ:
meeting_now = meeting_now.replace(tzinfo=None)
return schedule.is_official and (ts.time - meeting_now < lock_time)
@ -785,7 +789,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
timeslot=old_timeslot,
)
existing_assignments.update(timeslot=timeslot, modified=datetime.datetime.now())
existing_assignments.update(timeslot=timeslot, modified=timezone.now())
else:
SchedTimeSessAssignment.objects.create(
session=session,
@ -1151,7 +1155,7 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
meeting=meeting,
type=c['type'],
name=c['name'],
time=datetime.datetime.combine(c['day'], c['time']),
time=meeting.tz().localize(datetime.datetime.combine(c['day'], c['time'])),
duration=c['duration'],
location=c['location'],
show_location=c['show_location'],
@ -1200,7 +1204,7 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
timeslot.type = c['type']
timeslot.name = c['name']
timeslot.time = datetime.datetime.combine(c['day'], c['time'])
timeslot.time = meeting.tz().localize(datetime.datetime.combine(c['day'], c['time']))
timeslot.duration = c['duration']
timeslot.location = c['location']
timeslot.show_location = c['show_location']
@ -1287,8 +1291,9 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
for t in timeslot_qs:
timeslots_by_day_and_room[(t.time.date(), t.location_id)].append(t)
min_time = min([t.time.time() for t in timeslot_qs] + [datetime.time(8)])
max_time = max([t.end_time().time() for t in timeslot_qs] + [datetime.time(22)])
# Calculate full time range for display in meeting-local time, always showing at least 8am to 10pm
min_time = min([t.local_start_time().time() for t in timeslot_qs] + [datetime.time(8)])
max_time = max([t.local_end_time().time() for t in timeslot_qs] + [datetime.time(22)])
min_max_delta = datetime.datetime.combine(meeting.date, max_time) - datetime.datetime.combine(meeting.date, min_time)
day_grid = []
@ -1310,7 +1315,14 @@ def edit_meeting_timeslots_and_misc_sessions(request, num=None, owner=None, name
if s:
t.assigned_sessions.append(s)
t.left_offset = 100.0 * (t.time - datetime.datetime.combine(t.time.date(), min_time)) / min_max_delta
local_start_dt = t.local_start_time()
local_min_dt = local_start_dt.replace(
hour=min_time.hour,
minute=min_time.minute,
second=min_time.second,
microsecond=min_time.microsecond,
)
t.left_offset = 100.0 * (local_start_dt - local_min_dt) / min_max_delta
t.layout_width = min(100.0 * t.duration / min_max_delta, 100 - t.left_offset)
ts.append(t)
@ -1553,19 +1565,29 @@ def agenda(request, num=None, name=None, base=None, ext=None, owner=None, utc=""
is_current_meeting = (num is None) or (num == get_current_ietf_meeting_num())
rendered_page = render(request, "meeting/"+base+ext, {
"personalize": False,
"schedule": schedule,
"filtered_assignments": filtered_assignments,
"updated": updated,
"filter_categories": filter_organizer.get_filter_categories(),
"non_area_keywords": filter_organizer.get_non_area_keywords(),
"now": datetime.datetime.now().astimezone(pytz.UTC),
"timezone": meeting.time_zone,
"is_current_meeting": is_current_meeting,
"use_codimd": True if meeting.date>=settings.MEETING_USES_CODIMD_DATE else False,
"cache_time": 150 if is_current_meeting else 3600,
}, content_type=mimetype[ext])
display_timezone = 'UTC' if utc else meeting.time_zone
timezone.activate(display_timezone)
try:
rendered_page = render(
request,
"meeting/" + base + ext,
{
"personalize": False,
"schedule": schedule,
"filtered_assignments": filtered_assignments,
"updated": updated,
"filter_categories": filter_organizer.get_filter_categories(),
"non_area_keywords": filter_organizer.get_non_area_keywords(),
"now": timezone.now().astimezone(pytz.utc),
"display_timezone": display_timezone,
"is_current_meeting": is_current_meeting,
"use_codimd": True if meeting.date>=settings.MEETING_USES_CODIMD_DATE else False,
"cache_time": 150 if is_current_meeting else 3600,
},
content_type=mimetype[ext],
)
finally:
timezone.deactivate()
return rendered_page
@ -1917,7 +1939,7 @@ def agenda_personalize(request, num):
'filtered_assignments': filtered_assignments,
'filter_categories': filter_organizer.get_filter_categories(),
'non_area_labels': filter_organizer.get_non_area_keywords(),
'timezone': meeting.time_zone,
'display_timezone': meeting.time_zone,
'is_current_meeting': is_current_meeting,
'cache_time': 150 if is_current_meeting else 3600,
}
@ -2305,16 +2327,14 @@ def agenda_json(request, num=None):
meetinfo.sort(key=lambda x: x['modified'],reverse=True)
last_modified = meetinfo and meetinfo[0]['modified']
tz = pytz.timezone(settings.PRODUCTION_TIMEZONE)
for obj in meetinfo:
obj['modified'] = tz.localize(obj['modified']).astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
obj['modified'] = obj['modified'].astimezone(pytz.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
data = {"%s"%num: meetinfo}
response = HttpResponse(json.dumps(data, indent=2, sort_keys=True), content_type='application/json;charset=%s'%settings.DEFAULT_CHARSET)
if last_modified:
last_modified = tz.localize(last_modified).astimezone(pytz.utc)
last_modified = last_modified.astimezone(pytz.utc)
response['Last-Modified'] = format_date_time(timegm(last_modified.timetuple()))
return response
@ -2386,7 +2406,9 @@ def session_details(request, num, acronym):
# Find the time of the meeting, so that we can look back historically
# for what the group was called at the time.
meeting_time = datetime.datetime.combine(meeting.date, datetime.time())
meeting_time = meeting.tz().localize(
datetime.datetime.combine(meeting.date, datetime.time())
)
groups = list(set([ s.group for s in sessions ]))
group_replacements = find_history_replacements_active_at(groups, meeting_time)
@ -2453,8 +2475,8 @@ def session_details(request, num, acronym):
'is_materials_manager' : session.group.has_role(request.user, session.group.features.matman_roles),
'can_manage_materials' : can_manage,
'can_view_request': can_view_request,
'thisweek': datetime.date.today()-datetime.timedelta(days=7),
'now': datetime.datetime.now(),
'thisweek': datetime_today()-datetime.timedelta(days=7),
'now': timezone.now(),
'use_codimd': True if meeting.date>=settings.MEETING_USES_CODIMD_DATE else False,
})
@ -3542,7 +3564,7 @@ def interim_request_edit(request, number):
@cache_page(60*60)
def past(request):
'''List of past meetings'''
today = datetime.datetime.today()
today = timezone.now()
meetings = data_for_meetings_overview(Meeting.objects.filter(date__lte=today).order_by('-date'))
@ -3552,7 +3574,7 @@ def past(request):
def upcoming(request):
'''List of upcoming meetings'''
today = datetime.date.today()
today = datetime_today()
# Get ietf meetings starting 7 days ago, and interim meetings starting today
ietf_meetings = Meeting.objects.filter(type_id='ietf', date__gte=today-datetime.timedelta(days=7))
@ -3615,8 +3637,8 @@ def upcoming(request):
'menu_actions': actions,
'menu_entries': menu_entries,
'selected_menu_entry': selected_menu_entry,
'now': datetime.datetime.now(),
'use_codimd': True if datetime.date.today()>=settings.MEETING_USES_CODIMD_DATE else False,
'now': timezone.now(),
'use_codimd': (date_today() >= settings.MEETING_USES_CODIMD_DATE),
})
@ -3630,7 +3652,7 @@ def upcoming_ical(request):
except ValueError as e:
return HttpResponseBadRequest(str(e))
today = datetime.date.today()
today = datetime_today()
# get meetings starting 7 days ago -- we'll filter out sessions in the past further down
meetings = data_for_meetings_overview(Meeting.objects.filter(date__gte=today-datetime.timedelta(days=7)).prefetch_related('schedule').order_by('date'))
@ -3662,9 +3684,12 @@ def upcoming_ical(request):
ietfs = [m for m in meetings if m.type_id == 'ietf']
preprocess_meeting_important_dates(ietfs)
meeting_vtz = {meeting.vtimezone() for meeting in meetings}
meeting_vtz.discard(None)
# icalendar response file should have '\r\n' line endings per RFC5545
response = render_to_string('meeting/upcoming.ics', {
'vtimezones': ''.join(sorted(list({meeting.vtimezone() for meeting in meetings if meeting.vtimezone()}))),
'vtimezones': ''.join(sorted(meeting_vtz)),
'assignments': assignments,
'ietfs': ietfs,
}, request=request)
@ -3677,7 +3702,7 @@ def upcoming_ical(request):
def upcoming_json(request):
'''Return Upcoming meetings in json format'''
today = datetime.date.today()
today = datetime_today()
# get meetings starting 7 days ago -- we'll filter out sessions in the past further down
meetings = data_for_meetings_overview(Meeting.objects.filter(date__gte=today-datetime.timedelta(days=7)).order_by('date'))
@ -3726,7 +3751,7 @@ def proceedings(request, num=None):
begin_date = meeting.get_submission_start_date()
cut_off_date = meeting.get_submission_cut_off_date()
cor_cut_off_date = meeting.get_submission_correction_date()
now = datetime.date.today()
now = date_today()
schedule = get_schedule(meeting, None)
sessions = add_event_info_to_session_qs(
@ -4023,7 +4048,7 @@ def important_dates(request, num=None, output_format=None):
base_num = int(meeting.number)
user = request.user
today = datetime.date.today()
today = datetime_today()
meetings = []
if meeting.show_important_dates or meeting.date < today:
meetings.append(meeting)
@ -4075,23 +4100,27 @@ def edit_timeslot(request, num, slot_id):
meeting = get_object_or_404(Meeting, number=num)
if timeslot.meeting != meeting:
raise Http404()
if request.method == 'POST':
form = TimeSlotEditForm(instance=timeslot, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('ietf.meeting.views.edit_timeslots', kwargs={'num': num}))
else:
form = TimeSlotEditForm(instance=timeslot)
timezone.activate(meeting.tz()) # specifies current_timezone used for rendering and form handling
try:
if request.method == 'POST':
form = TimeSlotEditForm(instance=timeslot, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('ietf.meeting.views.edit_timeslots', kwargs={'num': num}))
else:
form = TimeSlotEditForm(instance=timeslot)
sessions = timeslot.sessions.filter(
timeslotassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None])
sessions = timeslot.sessions.filter(
timeslotassignments__schedule__in=[meeting.schedule, meeting.schedule.base if meeting.schedule else None])
return render(
request,
'meeting/edit_timeslot.html',
{'timeslot': timeslot, 'form': form, 'sessions': sessions},
status=400 if form.errors else 200,
)
return render(
request,
'meeting/edit_timeslot.html',
{'timeslot': timeslot, 'form': form, 'sessions': sessions},
status=400 if form.errors else 200,
)
finally:
timezone.deactivate()
@role_required('Secretariat')
@ -4102,7 +4131,7 @@ def create_timeslot(request, num):
if form.is_valid():
bulk_create_timeslots(
meeting,
[datetime.datetime.combine(day, form.cleaned_data['time'])
[meeting.tz().localize(datetime.datetime.combine(day, form.cleaned_data['time']))
for day in form.cleaned_data.get('days', [])],
form.cleaned_data['locations'],
dict(

View file

@ -6,6 +6,7 @@ import email
import datetime
from django.core.management.base import BaseCommand
from django.utils import timezone
import debug # pyflakes:ignore
@ -25,7 +26,7 @@ class Command(BaseCommand):
"""
def add_arguments(self, parser):
default_start = datetime.datetime.now() - datetime.timedelta(days=14)
default_start = timezone.now() - datetime.timedelta(days=14)
parser.add_argument(
'-t', '--start', '--from', type=str, default=default_start.strftime('%Y-%m-%d %H:%M'),
help='Limit the list to messages saved after the given time (default %(default)s).',

View file

@ -0,0 +1,24 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('message', '0011_auto_20201109_0439'),
]
operations = [
migrations.AlterField(
model_name='message',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='sendqueue',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

View file

@ -2,10 +2,10 @@
# -*- coding: utf-8 -*-
import datetime
import email.utils
from django.db import models
from django.utils import timezone
import debug # pyflakes:ignore
@ -17,7 +17,7 @@ from ietf.utils.models import ForeignKey
from ietf.utils.mail import get_email_addresses_from_text
class Message(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
by = ForeignKey(Person)
subject = models.CharField(max_length=255)
@ -62,7 +62,7 @@ class MessageAttachment(models.Model):
class SendQueue(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
by = ForeignKey(Person)
message = ForeignKey(Message)

View file

@ -5,6 +5,7 @@
import datetime
from django.urls import reverse as urlreverse
from django.utils import timezone
import debug # pyflakes:ignore
@ -87,7 +88,7 @@ class SendScheduledAnnouncementsTests(TestCase):
q = SendQueue.objects.create(
by=Person.objects.get(name="(System)"),
message=msg,
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
send_at=timezone.now() + datetime.timedelta(hours=12)
)
mailbox_before = len(outbox)
@ -113,7 +114,7 @@ class SendScheduledAnnouncementsTests(TestCase):
q = SendQueue.objects.create(
by=Person.objects.get(name="(System)"),
message=msg,
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
send_at=timezone.now() + datetime.timedelta(hours=12)
)
mailbox_before = len(outbox)

View file

@ -2,8 +2,9 @@
# -*- coding: utf-8 -*-
import re, datetime, email
import re, email
from django.utils import timezone
from django.utils.encoding import force_str
from ietf.utils.mail import send_mail_text, send_mail_mime
@ -52,7 +53,7 @@ def send_scheduled_message_from_send_queue(queue_item):
send_mail_mime(None, message.to, message.frm, message.subject,
msg, cc=message.cc, bcc=message.bcc)
queue_item.sent_at = datetime.datetime.now()
queue_item.sent_at = timezone.now()
queue_item.save()
queue_item.message.sent = queue_item.sent_at

View file

@ -15998,7 +15998,7 @@
"fields": {
"command": "xym",
"switch": "--version",
"time": "2022-07-13T00:09:29.108",
"time": "2022-07-13T00:09:29.108Z",
"used": true,
"version": "xym 0.5"
},
@ -16009,7 +16009,7 @@
"fields": {
"command": "pyang",
"switch": "--version",
"time": "2022-07-13T00:09:29.475",
"time": "2022-07-13T00:09:29.475Z",
"used": true,
"version": "pyang 2.5.3"
},
@ -16020,7 +16020,7 @@
"fields": {
"command": "yanglint",
"switch": "--version",
"time": "2022-07-13T00:09:29.497",
"time": "2022-07-13T00:09:29.497Z",
"used": true,
"version": "yanglint SO 1.9.2"
},
@ -16031,7 +16031,7 @@
"fields": {
"command": "xml2rfc",
"switch": "--version",
"time": "2022-07-13T00:09:30.513",
"time": "2022-07-13T00:09:30.513Z",
"used": true,
"version": "xml2rfc 3.13.0"
},

View file

@ -17,6 +17,7 @@ from django.conf import settings
from django.core.files import File
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import force_str
import debug # pyflakes:ignore
@ -49,6 +50,8 @@ from ietf.stats.models import MeetingRegistration
from ietf.stats.factories import MeetingRegistrationFactory
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.timezone import datetime_today, datetime_from_date, DEADLINE_TZINFO
client_test_cert_files = None
@ -1091,7 +1094,7 @@ class ReminderTest(TestCase):
rai = Position.objects.get(nomcom=self.nomcom,name='RAI')
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_4 = today - datetime.timedelta(days=4)
e1 = EmailFactory(address="nominee1@example.org", person=PersonFactory(name="Nominee 1"), origin='test')
@ -1389,7 +1392,7 @@ class FeedbackLastSeenTests(TestCase):
f.nominees.add(self.nominee)
f = FeedbackFactory.create(author=self.author,nomcom=self.nc,type_id='comment')
f.topics.add(self.topic)
now = datetime.datetime.now()
now = timezone.now()
self.hour_ago = now - datetime.timedelta(hours=1)
self.half_hour_ago = now - datetime.timedelta(minutes=30)
self.second_from_now = now + datetime.timedelta(seconds=1)
@ -2417,7 +2420,8 @@ class rfc8989EligibilityTests(TestCase):
nobody=PersonFactory()
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
@ -2435,7 +2439,7 @@ class rfc8989EligibilityTests(TestCase):
def test_elig_by_office_edge(self):
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)
two_days_after = elig_date + datetime.timedelta(days=2)
@ -2450,15 +2454,15 @@ class rfc8989EligibilityTests(TestCase):
def test_elig_by_office_closed_groups(self):
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)
# special case for Feb 29
if elig_date.month == 2 and elig_date.day == 29:
year_before = datetime.date(elig_date.year - 1, 2, 28)
three_years_before = datetime.date(elig_date.year - 3, 2, 28)
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 = datetime.date(elig_date.year - 1, elig_date.month, elig_date.day)
three_years_before = datetime.date(elig_date.year - 3, elig_date.month, elig_date.day)
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_before_three_years_before = three_years_before - datetime.timedelta(days=1)
@ -2517,14 +2521,14 @@ class rfc8989EligibilityTests(TestCase):
for nomcom in self.nomcoms:
elig_date = get_eligibility_date(nomcom)
last_date = elig_date
last_date = datetime_from_date(elig_date, DEADLINE_TZINFO)
# special case for Feb 29
if last_date.month == 2 and last_date.day == 29:
first_date = datetime.date(last_date.year - 5, 2, 28)
middle_date = datetime.date(last_date.year - 3, 2, 28)
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 = datetime.date(last_date.year - 5, last_date.month, last_date.day)
middle_date = datetime.date(last_date.year - 3, last_date.month, last_date.day)
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_before_first_date = first_date-datetime.timedelta(days=1)
@ -2664,7 +2668,7 @@ class VolunteerDecoratorUnitTests(TestCase):
office_person = PersonFactory()
RoleHistoryFactory(
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',
person=office_person,
)
@ -2676,11 +2680,13 @@ class VolunteerDecoratorUnitTests(TestCase):
DocEventFactory(
type='published_rfc',
doc=da.document,
time=datetime.date(
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)
volunteers = nomcom.volunteer_set.all()

View file

@ -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.log import log
from ietf.person.name import unidecode_name
from ietf.utils.timezone import datetime_from_date, datetime_today, DEADLINE_TZINFO
import debug # pyflakes:ignore
@ -536,34 +537,35 @@ def get_8989_eligibility_querysets(date, base_qs):
base_qs = Person.objects.all()
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)
# 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.date(date.year - 3, 2, 28)
five_years_ago = datetime.date(date.year - 5, 2, 28)
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.date(date.year - 3, date.month, date.day)
five_years_ago = datetime.date(date.year - 5, date.month, date.day)
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(
# is currently an officer
Q(role__name_id__in=('chair','secr'),
role__group__state_id='active',
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)
| 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__group__state_id='active',
rolehistory__group__type_id='wg',
)
).distinct()
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).values_list('doc__pk',flat=True))
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))
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))
qualifying_pks = rfc_pks.union(iesgappr_pks.difference(rfc_pks))
author_qs = base_qs.filter(
documentauthor__document__pk__in=qualifying_pks
@ -604,7 +606,7 @@ def get_eligibility_date(nomcom=None, date=None):
else:
return datetime.date(next_nomcom_year,5,1)
else:
return datetime.date(datetime.date.today().year,5,1)
return datetime.date(datetime_today().year,5,1)
def previous_five_meetings(date = None):
if date is None:

View file

@ -951,7 +951,7 @@ def view_feedback_topic(request, year, topic_id):
feedback_types = FeedbackTypeName.objects.filter(slug__in=['comment',])
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:
last_seen.save()
else:
@ -973,7 +973,7 @@ def view_feedback_nominee(request, year, nominee_id):
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_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:
last_seen.save()
else:

View file

@ -1,9 +1,10 @@
# Copyright The IETF Trust 2021, All Rights Reserved
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from datetime import timedelta
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Max, Min
from django.utils import timezone
from ietf.person.models import PersonApiKeyEvent
@ -33,7 +34,7 @@ class Command(BaseCommand):
self.stdout.write('Finding events older than {}\n'.format(_format_count(keep_days)))
self.stdout.flush()
now = datetime.now()
now = timezone.now()
old_events = PersonApiKeyEvent.objects.filter(
time__lt=now - timedelta(days=keep_days)
)

View file

@ -5,6 +5,7 @@ import datetime
from io import StringIO
from django.core.management import call_command, CommandError
from django.utils import timezone
from ietf.person.factories import PersonApiKeyEventFactory
from ietf.person.models import PersonApiKeyEvent, PersonEvent
@ -51,7 +52,7 @@ class CommandTests(TestCase):
# Remember how many PersonEvents were present so we can verify they're cleaned up properly.
personevents_before = PersonEvent.objects.count()
now = datetime.datetime.now()
now = timezone.now()
# The first of these events will be timestamped a fraction of a second more than keep_days
# days ago by the time we call the management command, so will just barely chosen for purge.
old_events = [
@ -101,7 +102,7 @@ class CommandTests(TestCase):
def test_purge_old_personal_api_key_events_rejects_invalid_arguments(self):
"""The purge_old_personal_api_key_events command should reject invalid arguments"""
event = PersonApiKeyEventFactory(time=datetime.datetime.now() - datetime.timedelta(days=30))
event = PersonApiKeyEventFactory(time=timezone.now() - datetime.timedelta(days=30))
with self.assertRaises(CommandError):
self._call_command('purge_old_personal_api_key_events')

View file

@ -0,0 +1,34 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('person', '0024_pronouns'),
]
operations = [
migrations.AlterField(
model_name='historicalperson',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='person',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='personalapikey',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='personevent',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now, help_text='When the event happened'),
),
]

View file

@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
import datetime
import email.utils
import email.header
import jsonfield
@ -18,6 +17,7 @@ from django.core.validators import validate_email
from django.db import models
from django.template.loader import render_to_string
from django.urls import reverse as urlreverse
from django.utils import timezone
from django.utils.encoding import smart_bytes
from django.utils.text import slugify
@ -38,7 +38,7 @@ from ietf.utils.models import ForeignKey, OneToOneField
class Person(models.Model):
history = HistoricalRecords()
user = OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
time = models.DateTimeField(default=timezone.now) # When this Person record entered the system
# The normal unicode form of the name. This must be
# set to the same value as the ascii-form if equal.
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True, help_text="Preferred long form of name.")
@ -377,7 +377,7 @@ PERSON_API_KEY_ENDPOINTS = sorted(list(set([ (v, n) for (v, n, r) in PERSON_API_
class PersonalApiKey(models.Model):
person = ForeignKey(Person, related_name='apikeys')
endpoint = models.CharField(max_length=128, null=False, blank=False, choices=PERSON_API_KEY_ENDPOINTS)
created = models.DateTimeField(default=datetime.datetime.now, null=False)
created = models.DateTimeField(default=timezone.now, null=False)
valid = models.BooleanField(default=True)
salt = models.BinaryField(default=salt, max_length=12, null=False, blank=False)
count = models.IntegerField(default=0, null=False, blank=False)
@ -427,7 +427,7 @@ PERSON_EVENT_CHOICES = [
class PersonEvent(models.Model):
person = ForeignKey(Person)
time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened")
time = models.DateTimeField(default=timezone.now, help_text="When the event happened")
type = models.CharField(max_length=50, choices=PERSON_EVENT_CHOICES)
desc = models.TextField()

View file

@ -13,6 +13,7 @@ from pyquery import PyQuery
from django.http import HttpRequest
from django.test import override_settings
from django.urls import reverse as urlreverse
from django.utils import timezone
from django.utils.encoding import iri_to_uri
import debug # pyflakes:ignore
@ -211,7 +212,7 @@ class PersonUtilsTests(TestCase):
self.assertEqual(results,(p1,p3))
# both have User
today = datetime.datetime.today()
today = timezone.now()
p2.user.last_login = today
p2.user.save()
p4.user.last_login = today - datetime.timedelta(days=30)

View file

@ -2,6 +2,8 @@
import factory
import datetime
from django.utils import timezone
from ietf.review.models import ReviewTeamSettings, ReviewRequest, ReviewAssignment, ReviewerSettings
from ietf.name.models import ReviewTypeName, ReviewResultName
@ -39,7 +41,7 @@ class ReviewRequestFactory(factory.django.DjangoModelFactory):
type_id = 'lc'
doc = factory.SubFactory('ietf.doc.factories.DocumentFactory',type_id='draft')
team = factory.SubFactory('ietf.group.factories.ReviewTeamFactory',type_id='review')
deadline = datetime.datetime.today()+datetime.timedelta(days=14)
deadline = timezone.now()+datetime.timedelta(days=14)
requested_by = factory.SubFactory('ietf.person.factories.PersonFactory')
class ReviewAssignmentFactory(factory.django.DjangoModelFactory):
@ -49,7 +51,7 @@ class ReviewAssignmentFactory(factory.django.DjangoModelFactory):
review_request = factory.SubFactory('ietf.review.factories.ReviewRequestFactory')
state_id = 'assigned'
reviewer = factory.SubFactory('ietf.person.factories.EmailFactory')
assigned_on = datetime.datetime.now()
assigned_on = timezone.now()
class ReviewerSettingsFactory(factory.django.DjangoModelFactory):
class Meta:

View file

@ -0,0 +1,29 @@
# Generated by Django 2.2.28 on 2022-07-12 11:24
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('review', '0028_auto_20220513_1456'),
]
operations = [
migrations.AlterField(
model_name='historicalreviewrequest',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='reviewrequest',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='reviewwish',
name='time',
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

View file

@ -7,6 +7,7 @@ import datetime
from simple_history.models import HistoricalRecords
from django.db import models
from django.utils import timezone
import debug # pyflakes:ignore
@ -95,7 +96,7 @@ class UnavailablePeriod(models.Model):
class ReviewWish(models.Model):
"""Reviewer wishes to review a document when it becomes available for review."""
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
team = ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))
person = ForeignKey(Person)
doc = ForeignKey(Document)
@ -125,7 +126,7 @@ class ReviewRequest(models.Model):
# Fields filled in on the initial record creation - these
# constitute the request part.
time = models.DateTimeField(default=datetime.datetime.now)
time = models.DateTimeField(default=timezone.now)
type = ForeignKey(ReviewTypeName)
doc = ForeignKey(Document, related_name='reviewrequest_set')
team = ForeignKey(Group, limit_choices_to=~models.Q(reviewteamsettings=None))

View file

@ -5,6 +5,7 @@ import datetime
from ietf.group.factories import RoleFactory
from ietf.utils.mail import empty_outbox, get_payload_text, outbox
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 .mailarch import hash_list_message_id
from .models import ReviewerSettings, ReviewSecretarySettings, ReviewTeamSettings, UnavailablePeriod
@ -408,7 +409,7 @@ class ReviewAssignmentReminderTests(TestCase):
review_request__state_id='assigned',
review_request__deadline=self.deadline,
state_id='assigned',
assigned_on=self.deadline,
assigned_on=datetime_from_date(self.deadline),
reviewer=self.reviewer.email_set.first(),
).review_request.team
second_team.reviewteamsettings.delete() # prevent it from being sent reminders
@ -420,7 +421,7 @@ class ReviewAssignmentReminderTests(TestCase):
review_request__state_id='assigned',
review_request__deadline=not_overdue,
state_id='assigned',
assigned_on=not_overdue,
assigned_on=datetime_from_date(not_overdue),
reviewer=self.reviewer.email_set.first(),
)
ReviewAssignmentFactory(
@ -428,7 +429,7 @@ class ReviewAssignmentReminderTests(TestCase):
review_request__state_id='assigned',
review_request__deadline=not_overdue,
state_id='assigned',
assigned_on=not_overdue,
assigned_on=datetime_from_date(not_overdue),
reviewer=self.reviewer.email_set.first(),
)
@ -439,7 +440,7 @@ class ReviewAssignmentReminderTests(TestCase):
review_request__state_id='assigned',
review_request__deadline=in_grace_period,
state_id='assigned',
assigned_on=in_grace_period,
assigned_on=datetime_from_date(in_grace_period),
reviewer=self.reviewer.email_set.first(),
)
ReviewAssignmentFactory(
@ -447,7 +448,7 @@ class ReviewAssignmentReminderTests(TestCase):
review_request__state_id='assigned',
review_request__deadline=in_grace_period,
state_id='assigned',
assigned_on=in_grace_period,
assigned_on=datetime_from_date(in_grace_period),
reviewer=self.reviewer.email_set.first(),
)

View file

@ -12,6 +12,8 @@ from django.template.defaultfilters import pluralize
from django.template.loader import render_to_string
from django.urls import reverse as urlreverse
from django.contrib.sites.models import Site
from django.utils import timezone
from simple_history.utils import update_change_reason
import debug # pyflakes:ignore
@ -30,6 +32,8 @@ from ietf.review.models import (ReviewRequest, ReviewAssignment, ReviewRequestSt
from ietf.utils.mail import send_mail
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
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".
# This date is arbitrarily chosen and has no special meaning, but should be consistent.
@ -119,7 +123,7 @@ def days_needed_to_fulfill_min_interval_for_reviewers(team):
min_intervals = dict(ReviewerSettings.objects.filter(team=team).values_list("person_id", "min_interval"))
now = datetime.datetime.now()
now = timezone.now()
res = {}
for person_id, latest_assignment_time in latest_assignments.items():
@ -192,7 +196,10 @@ def extract_review_assignment_data(teams=None, reviewers=None, time_from=None, t
assigned_time = assigned_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)
assignment_to_closure_days = positive_days(assigned_time, closed_time)
request_to_closure_days = positive_days(requested_time, closed_time)
@ -283,7 +290,7 @@ def latest_review_assignments_for_reviewers(team, days_back=365):
extracted_data = extract_review_assignment_data(
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"],
)
@ -495,7 +502,7 @@ def suggested_review_requests_for_team(team):
requests = {}
now = datetime.datetime.now()
now = timezone.now()
reviewable_docs_qs = Document.objects.filter(type="draft").exclude(stream="ise")

View file

@ -14,6 +14,7 @@ import debug # pyflakes:ignore
from django.conf import settings
from django.urls import reverse
from django.utils import timezone
from ietf.group.models import Group, GroupEvent
from ietf.meeting.factories import MeetingFactory
@ -137,7 +138,10 @@ class SecrMeetingTestCase(TestCase):
"Edit Meeting"
meeting = make_meeting_test_data()
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,
idsubmit_cutoff_day_offset_00=13,
idsubmit_cutoff_day_offset_01=20,
@ -212,8 +216,8 @@ class SecrMeetingTestCase(TestCase):
self.assertEqual(q('#id_notification_list').html(),'ames, mars')
# test that only changes since last notification show up
now = datetime.datetime.now()
then = datetime.datetime.now()+datetime.timedelta(hours=1)
now = timezone.now()
then = timezone.now()+datetime.timedelta(hours=1)
person = Person.objects.get(name="(System)")
GroupEvent.objects.create(group=mars_group,time=now,type='sent_notification',
by=person,desc='sent scheduled notification for %s' % meeting)
@ -285,7 +289,7 @@ class SecrMeetingTestCase(TestCase):
url = reverse('ietf.secr.meetings.views.times_delete',kwargs={
'meeting_id':meeting.number,
'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={
'meeting_id':meeting.number,
@ -305,7 +309,7 @@ class SecrMeetingTestCase(TestCase):
url = reverse('ietf.secr.meetings.views.times_edit',kwargs={
'meeting_id':72,
'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")
response = self.client.post(url, {
@ -371,7 +375,7 @@ class SecrMeetingTestCase(TestCase):
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})
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")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@ -389,7 +393,7 @@ class SecrMeetingTestCase(TestCase):
})
self.assertRedirects(response, redirect_url)
timeslot = session.official_timeslotassignment().timeslot
self.assertEqual(timeslot.time,new_time)
self.assertEqual(timeslot.time, new_time)
def test_meetings_misc_session_delete(self):
meeting = make_meeting_test_data()

View file

@ -11,6 +11,7 @@ from django.db.models import IntegerField
from django.db.models.functions import Cast
from django.forms.models import inlineformset_factory
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from django.utils.text import slugify
import debug # pyflakes:ignore
@ -30,6 +31,7 @@ from ietf.secr.meetings.forms import ( BaseMeetingRoomFormSet, MeetingModelForm,
from ietf.secr.sreq.views import get_initial_session
from ietf.secr.utils.meeting import get_session, get_timeslot
from ietf.mailtrigger.utils import gather_address_lists
from ietf.utils.timezone import make_aware
# prep for agenda changes
@ -121,7 +123,7 @@ def send_notifications(meeting, groups, person):
Send session scheduled email notifications for each group in groups. Person is the
user who initiated this action, request.uesr.get_profile().
'''
now = datetime.datetime.now()
now = timezone.now()
for group in groups:
sessions = group.session_set.filter(meeting=meeting)
addrs = gather_address_lists('session_scheduled',group=group,session=sessions[0])
@ -341,7 +343,7 @@ def edit_meeting(request, meeting_id):
else:
form = MeetingModelForm(instance=meeting)
debug.show('form.errors')
return render(request, 'meetings/edit_meeting.html', {
'meeting': meeting,
'form' : form, },
@ -798,7 +800,8 @@ def get_timeslot_time(form, meeting):
day = form.cleaned_data['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')
def times_edit(request, meeting_id, schedule_name, time):
@ -809,7 +812,7 @@ def times_edit(request, meeting_id, schedule_name, time):
schedule = get_object_or_404(Schedule, meeting=meeting, name=schedule_name)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
dtime = make_aware(datetime.datetime(*parts), meeting.tz())
timeslots = TimeSlot.objects.filter(meeting=meeting,time=dtime)
if request.method == 'POST':
@ -860,7 +863,7 @@ def times_delete(request, meeting_id, schedule_name, time):
meeting = get_object_or_404(Meeting, number=meeting_id)
parts = [ int(x) for x in time.split(':') ]
dtime = datetime.datetime(*parts)
dtime = make_aware(datetime.datetime(*parts), meeting.tz())
status = SessionStatusName.objects.get(slug='schedw')
if request.method == 'POST' and request.POST['post'] == 'yes':

View file

@ -9,6 +9,7 @@ This module contains all the functions for generating static proceedings pages
'''
import datetime
import os
import pytz
import re
import subprocess
from urllib.parse import urlencode
@ -24,6 +25,7 @@ from ietf.meeting.models import Meeting, SessionPresentation, TimeSlot, SchedTim
from ietf.person.models import Person
from ietf.utils.log import log
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})')
VIDEO_TITLE_RE = re.compile(r'IETF(?P<number>[\d]+)-(?P<name>.*)-(?P<date>\d{8})-(?P<time>\d{4})')
@ -32,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):
'''Lookup session using data from video title'''
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:
assignment = SchedTimeSessAssignment.objects.get(
schedule__in = [meeting.schedule, meeting.schedule.base],
@ -102,7 +104,7 @@ def get_timeslot_for_filename(filename):
try:
meeting = Meeting.objects.get(number=match.groupdict()['number'])
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(
meeting=meeting,
location__name=room_mapping[match.groupdict()['room']],
@ -201,17 +203,22 @@ def send_audio_import_warning(unmatched_files):
# End Recording Functions
# -------------------------------------------------
def get_progress_stats(sdate,edate):
def get_progress_stats(sdate, edate):
'''
This function takes a date range and produces a dictionary of statistics / objects for
use in a progress report. Generally the end date will be the date of the last meeting
and the start date will be the date of the meeting before that.
Data between midnight UTC on the specified dates are included in the stats.
'''
sdatetime = pytz.utc.localize(datetime.datetime.combine(sdate, datetime.time()))
edatetime = pytz.utc.localize(datetime.datetime.combine(edate, datetime.time()))
data = {}
data['sdate'] = sdate
data['edate'] = edate
events = DocEvent.objects.filter(doc__type='draft',time__gte=sdate,time__lt=edate)
events = DocEvent.objects.filter(doc__type='draft', time__gte=sdatetime, time__lt=edatetime)
data['actions_count'] = events.filter(type='iesg_approved').count()
data['last_calls_count'] = events.filter(type='sent_last_call').count()
@ -226,7 +233,7 @@ def get_progress_stats(sdate,edate):
data['updated_drafts_count'] = len(set([ e.doc_id for e in update_events ]))
# Calculate Final Four Weeks stats (ffw)
ffwdate = edate - datetime.timedelta(days=28)
ffwdate = edatetime - datetime.timedelta(days=28)
ffw_new_count = events.filter(time__gte=ffwdate,newrevisiondocevent__rev='00').count()
try:
ffw_new_percent = format(ffw_new_count / float(data['new_drafts_count']),'.0%')
@ -257,14 +264,14 @@ def get_progress_stats(sdate,edate):
data['new_groups'] = Group.objects.filter(
type='wg',
groupevent__changestategroupevent__state='active',
groupevent__time__gte=sdate,
groupevent__time__lt=edate)
groupevent__time__gte=sdatetime,
groupevent__time__lt=edatetime)
data['concluded_groups'] = Group.objects.filter(
type='wg',
groupevent__changestategroupevent__state='conclude',
groupevent__time__gte=sdate,
groupevent__time__lt=edate)
groupevent__time__gte=sdatetime,
groupevent__time__lt=edatetime)
return data

View file

@ -1,19 +1,22 @@
import datetime
from django.template.loader import render_to_string
from django.utils import timezone
from ietf.meeting.models import Meeting
from ietf.doc.models import DocEvent, Document
from ietf.secr.proceedings.proc_utils import get_progress_stats
from ietf.utils.timezone import datetime_from_date
def report_id_activity(start,end):
# get previous meeting
meeting = Meeting.objects.filter(date__lt=datetime.datetime.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('-')
eyear,emonth,eday = end.split('-')
sdate = datetime.datetime(int(syear),int(smonth),int(sday))
edate = datetime.datetime(int(eyear),int(emonth),int(eday))
sdate = datetime_from_date(datetime.date(int(syear),int(smonth),int(sday)), meeting.tz())
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'))
new_docs = Document.objects.filter(type='draft').filter(docevent__type='new_revision',
@ -44,7 +47,7 @@ def report_id_activity(start,end):
approved = events.filter(type='iesg_approved').count()
# 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)
ff1_date = cutoff - datetime.timedelta(days=28)
#ff2_date = cutoff - datetime.timedelta(days=21)

View file

@ -38,20 +38,6 @@ def display_duration(value):
x=int(value)
return "%d Hours %d Minutes %d Seconds"%(x//3600,(x%3600)//60,x%60)
@register.filter
def get_published_date(doc):
'''
Returns the published date for a RFC Document
'''
event = doc.latest_event(type='published_rfc')
if event:
return event.time
event = doc.latest_event(type='new_revision')
if event:
return event.time
else:
return None
@register.filter
def is_ppt(value):
'''

View file

@ -50,8 +50,9 @@ class VideoRecordingTestCase(TestCase):
meeting = session.meeting
number = meeting.number
name = session.group.acronym
date = session.official_timeslotassignment().timeslot.time.strftime('%Y%m%d')
time = session.official_timeslotassignment().timeslot.time.strftime('%H%M')
ts_time = session.official_timeslotassignment().timeslot.local_start_time()
date = ts_time.strftime('%Y%m%d')
time = ts_time.strftime('%H%M')
self.assertEqual(_get_session(number,name,date,time),session)
def test_get_urls_from_json(self):
@ -113,7 +114,7 @@ class RecordingTestCase(TestCase):
return "{prefix}-{room}-{date}.mp3".format(
prefix=timeslot.meeting.type.slug + timeslot.meeting.number,
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):
meeting = MeetingFactory(type_id='ietf',number='72')

View file

@ -1,6 +1,8 @@
import datetime
import debug # pyflakes:ignore
from django.utils import timezone
from ietf.doc.factories import DocumentFactory,NewRevisionDocEventFactory
from ietf.secr.proceedings.reports import report_id_activity, report_progress_report
from ietf.utils.test_utils import TestCase
@ -10,7 +12,7 @@ class ReportsTestCase(TestCase):
def test_report_id_activity(self):
today = datetime.datetime.today()
today = timezone.now()
yesterday = today - datetime.timedelta(days=1)
last_quarter = today - datetime.timedelta(days=3*30)
next_week = today+datetime.timedelta(days=7)
@ -24,7 +26,7 @@ class ReportsTestCase(TestCase):
self.assertTrue('IETF Activity since last IETF Meeting' in result)
def test_report_progress_report(self):
today = datetime.datetime.today()
today = timezone.now()
last_quarter = today - datetime.timedelta(days=3*30)
next_week = today+datetime.timedelta(days=7)

View file

@ -42,20 +42,6 @@ def display_duration(value):
else:
return "%d Hours %d Minutes %d Seconds"%(value//3600,(value%3600)//60,value%60)
@register.filter
def get_published_date(doc):
'''
Returns the published date for a RFC Document
'''
event = doc.latest_event(type='published_rfc')
if event:
return event.time
event = doc.latest_event(type='new_revision')
if event:
return event.time
else:
return None
@register.filter
def is_ppt(value):
'''

Some files were not shown because too many files have changed in this diff Show more