Merge branch 'feat/tzaware' into dev/main-merge-to-tzaware
This commit is contained in:
commit
f42016d8fb
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -144,6 +144,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
|
||||
|
|
|
@ -16,6 +16,7 @@ from django.conf import settings
|
|||
from django.core.validators import validate_email, ValidationError
|
||||
from ietf.utils.draft import PlaintextDraft
|
||||
from ietf.submit.utils import update_authors
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -66,9 +67,9 @@ for name in sorted(names):
|
|||
print name, rev, "Can't parse", p,":",e
|
||||
continue
|
||||
if draft.errors and draft.errors.keys()!=['draftname',]:
|
||||
print "Errors - could not process", name, rev, datetime.datetime.fromtimestamp(p.stat().st_mtime), draft.errors, draft.get_title().encode('utf8')
|
||||
print "Errors - could not process", name, rev, datetime.datetime.fromtimestamp(p.stat().st_mtime, datetime.timezone.utc), draft.errors, draft.get_title().encode('utf8')
|
||||
else:
|
||||
time = datetime.datetime.fromtimestamp(p.stat().st_mtime)
|
||||
time = datetime.datetime.fromtimestamp(p.stat().st_mtime, datetime.timezone.utc)
|
||||
if not doc:
|
||||
doc = Document.objects.create(name=name,
|
||||
time=time,
|
||||
|
@ -140,7 +141,7 @@ for name in sorted(names):
|
|||
doc = doc,
|
||||
rev = rev,
|
||||
by = system,
|
||||
desc = "Revision added from id-archive on %s by %s"%(datetime.date.today(),sys.argv[0]),
|
||||
desc = "Revision added from id-archive on %s by %s"%(date_today(),sys.argv[0]),
|
||||
time=time,
|
||||
)
|
||||
events.append(e)
|
||||
|
|
|
@ -162,7 +162,7 @@ def get_first_commit(path):
|
|||
else:
|
||||
pass
|
||||
except OSError:
|
||||
rev, who, when = None, None, datetime.datetime.now()
|
||||
rev, who, when = None, None, datetime.datetime.now(datetime.timezone.utc)
|
||||
return { path: { 'rev': rev, 'who': who, 'date': when.strftime('%Y-%m-%d %H:%M:%S'), }, }
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -32,6 +32,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 = (
|
||||
|
@ -508,10 +509,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')
|
||||
|
|
|
@ -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__,
|
||||
|
|
|
@ -28,7 +28,7 @@ def warn(string):
|
|||
# ------------------------------------------------------------------------------
|
||||
|
||||
import re
|
||||
from datetime import datetime as Datetime
|
||||
import datetime
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
@ -44,7 +44,7 @@ system_entity = Person.objects.get(name="(System)")
|
|||
charterdir = Path(settings.CHARTER_PATH)
|
||||
for file in charterdir.files("charter-ietf-*.txt"):
|
||||
fname = file.name
|
||||
ftime = Datetime.fromtimestamp(file.mtime)
|
||||
ftime = datetime.datetime.fromtimestamp(file.mtime, datetime.timezone.utc)
|
||||
match = re.search("^(?P<name>[a-z0-9-]+)-(?P<rev>\d\d-\d\d)\.txt$", fname)
|
||||
if match:
|
||||
name = match.group("name")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -29,6 +29,7 @@ from django.core.mail import mail_admins
|
|||
from ietf.doc.utils import rebuild_reference_relations
|
||||
from ietf.utils.log import log
|
||||
from ietf.utils.pipe import pipe
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
import ietf.sync.rfceditor
|
||||
|
||||
|
@ -39,7 +40,7 @@ parser.add_option("-d", dest="skip_date",
|
|||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
skip_date = datetime.date.today() - datetime.timedelta(days=365)
|
||||
skip_date = date_today() - datetime.timedelta(days=365)
|
||||
if options.skip_date:
|
||||
skip_date = datetime.datetime.strptime(options.skip_date, "%Y-%m-%d").date()
|
||||
|
||||
|
|
|
@ -26,8 +26,9 @@ from ietf.review.utils import (
|
|||
send_unavailability_period_ending_reminder, send_reminder_all_open_reviews,
|
||||
send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments)
|
||||
from ietf.utils.log import log
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today(DEADLINE_TZINFO)
|
||||
|
||||
for assignment in review_assignments_needing_reviewer_reminder(today):
|
||||
email_reviewer_reminder(assignment)
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
||||
|
|
|
@ -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 date_today, 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()
|
||||
|
||||
|
@ -84,7 +86,7 @@ def send_expire_warning_for_draft(doc):
|
|||
(doc.get_state_slug("draft") != "active")):
|
||||
return # don't warn about dead or inactive documents
|
||||
|
||||
expiration = doc.expires.date()
|
||||
expiration = doc.expires.astimezone(DEADLINE_TZINFO).date()
|
||||
|
||||
(to,cc) = gather_address_lists('doc_expires_soon',doc=doc)
|
||||
|
||||
|
@ -171,7 +173,7 @@ def expire_draft(doc):
|
|||
|
||||
def clean_up_draft_files():
|
||||
"""Move unidentified and old files out of the Internet Draft directory."""
|
||||
cut_off = datetime.date.today()
|
||||
cut_off = date_today(DEADLINE_TZINFO)
|
||||
|
||||
pattern = os.path.join(settings.INTERNET_DRAFT_PATH, "draft-*.*")
|
||||
filename_re = re.compile(r'^(.*)-(\d\d)$')
|
||||
|
@ -214,7 +216,9 @@ def clean_up_draft_files():
|
|||
|
||||
if state in ("rfc","repl"):
|
||||
move_file_to("")
|
||||
elif state in ("expired", "auth-rm", "ietf-rm") and doc.expires and doc.expires.date() < cut_off:
|
||||
elif (state in ("expired", "auth-rm", "ietf-rm")
|
||||
and doc.expires
|
||||
and doc.expires.astimezone(DEADLINE_TZINFO).date() < cut_off):
|
||||
move_file_to("")
|
||||
|
||||
except Document.DoesNotExist:
|
||||
|
|
|
@ -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,
|
||||
|
@ -18,6 +19,7 @@ from ietf.group.models import Group
|
|||
from ietf.person.factories import PersonFactory
|
||||
from ietf.group.factories import RoleFactory
|
||||
from ietf.utils.text import xslugify
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def draft_name_generator(type_id,group,n):
|
||||
|
@ -38,7 +40,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)
|
||||
|
||||
|
@ -320,7 +322,7 @@ class ConflictReviewFactory(BaseDocumentFactory):
|
|||
# This is very skeletal. It is enough for the tests that use it now, but when it's needed, it will need to be improved with, at least, a group generator that backs the object with a review team.
|
||||
class ReviewFactory(BaseDocumentFactory):
|
||||
type_id = 'review'
|
||||
name = factory.LazyAttribute(lambda o: 'review-doesnotexist-00-%s-%s'%(o.group.acronym,datetime.date.today().isoformat()))
|
||||
name = factory.LazyAttribute(lambda o: 'review-doesnotexist-00-%s-%s'%(o.group.acronym,date_today().isoformat()))
|
||||
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='review')
|
||||
|
||||
class DocAliasFactory(factory.django.DjangoModelFactory):
|
||||
|
@ -357,7 +359,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 +413,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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -15,6 +15,7 @@ from ietf.person.fields import SearchablePersonField, SearchablePersonsField
|
|||
from ietf.person.models import Email, Person
|
||||
|
||||
from ietf.name.models import ExtResourceName
|
||||
from ietf.utils.timezone import date_today
|
||||
from ietf.utils.validators import validate_external_resource_value
|
||||
|
||||
class TelechatForm(forms.Form):
|
||||
|
@ -34,7 +35,7 @@ class TelechatForm(forms.Form):
|
|||
for d in dates:
|
||||
self.page_count[d] = telechat_page_count(date=d).for_approval
|
||||
choice_display[d] = '%s (%s pages)' % (d.strftime("%Y-%m-%d"),self.page_count[d])
|
||||
if d-datetime.date.today() < datetime.timedelta(days=13):
|
||||
if d - date_today() < datetime.timedelta(days=13):
|
||||
choice_display[d] += ' : WARNING - this may not leave enough time for directorate reviews!'
|
||||
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, choice_display[d]) for d in dates]
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# helpers for handling last calls on Internet Drafts
|
||||
|
||||
import datetime
|
||||
|
||||
from django.db.models import Q
|
||||
|
||||
from ietf.doc.models import Document, State, DocEvent, LastCallDocEvent, WriteupDocEvent
|
||||
|
@ -10,6 +8,8 @@ from ietf.person.models import Person
|
|||
from ietf.doc.utils import add_state_change_event, update_action_holders
|
||||
from ietf.doc.mails import generate_ballot_writeup, generate_approval_mail, generate_last_call_announcement
|
||||
from ietf.doc.mails import send_last_call_request, email_last_call_expired, email_last_call_expired_with_downref
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
def request_last_call(request, doc):
|
||||
if not doc.latest_event(type="changed_ballot_writeup_text"):
|
||||
|
@ -33,11 +33,10 @@ def request_last_call(request, doc):
|
|||
e.save()
|
||||
|
||||
def get_expired_last_calls():
|
||||
today = datetime.date.today()
|
||||
for d in Document.objects.filter(Q(states__type="draft-iesg", states__slug="lc")
|
||||
| Q(states__type="statchg", states__slug="in-lc")):
|
||||
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
if e and e.expires.date() <= today:
|
||||
if e and e.expires.astimezone(DEADLINE_TZINFO).date() <= date_today(DEADLINE_TZINFO):
|
||||
yield d
|
||||
|
||||
def expire_last_call(doc):
|
||||
|
|
|
@ -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
|
||||
|
@ -24,6 +25,7 @@ from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible
|
|||
from ietf.group.models import Role
|
||||
from ietf.doc.models import Document
|
||||
from ietf.mailtrigger.utils import gather_address_lists
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
def email_state_changed(request, doc, text, mailtrigger_id=None):
|
||||
|
@ -191,7 +193,7 @@ def generate_ballot_rfceditornote(request, doc):
|
|||
return e
|
||||
|
||||
def generate_last_call_announcement(request, doc):
|
||||
expiration_date = datetime.date.today() + datetime.timedelta(days=14)
|
||||
expiration_date = date_today(DEADLINE_TZINFO) + datetime.timedelta(days=14)
|
||||
if doc.group.type_id in ("individ", "area"):
|
||||
group = "an individual submitter"
|
||||
expiration_date += datetime.timedelta(days=14)
|
||||
|
@ -418,7 +420,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 +439,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 = ''
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -10,6 +10,7 @@ import sys
|
|||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
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')
|
||||
|
||||
|
|
39
ietf/doc/migrations/0046_use_timezone_now_for_doc_models.py
Normal file
39
ietf/doc/migrations/0046_use_timezone_now_for_doc_models.py
Normal 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', '0045_docstates_chatlogs_polls'),
|
||||
]
|
||||
|
||||
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),
|
||||
),
|
||||
]
|
61
ietf/doc/migrations/0047_tzaware_deletedevents.py
Normal file
61
ietf/doc/migrations/0047_tzaware_deletedevents.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Generated by Django 2.2.28 on 2022-08-31 20:26
|
||||
|
||||
import datetime
|
||||
import json
|
||||
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
TZ_BEFORE = ZoneInfo('PST8PDT')
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
|
||||
for deleted_event in DeletedEvent.objects.all():
|
||||
fields = json.loads(deleted_event.json)
|
||||
replacements = {}
|
||||
for k, v in fields.items():
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
dt = datetime.datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
replacements[k] = dt.replace(tzinfo=TZ_BEFORE).astimezone(datetime.timezone.utc).isoformat()
|
||||
if len(replacements) > 0:
|
||||
fields.update(replacements)
|
||||
deleted_event.json = json.dumps(fields)
|
||||
deleted_event.save()
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
|
||||
for deleted_event in DeletedEvent.objects.all():
|
||||
fields = json.loads(deleted_event.json)
|
||||
replacements = {}
|
||||
for k, v in fields.items():
|
||||
if isinstance(v, str) and 'T' in v:
|
||||
try:
|
||||
dt = datetime.datetime.fromisoformat(v)
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
replacements[k] = dt.astimezone(TZ_BEFORE).replace(tzinfo=None).strftime('%Y-%m-%d %H:%M:%S')
|
||||
if len(replacements) > 0:
|
||||
fields.update(replacements)
|
||||
deleted_event.json = json.dumps(fields)
|
||||
deleted_event.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('doc', '0046_use_timezone_now_for_doc_models'),
|
||||
('utils', '0003_pause_to_change_use_tz'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, reverse),
|
||||
]
|
|
@ -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 date_today, RPC_TZINFO, DEADLINE_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, ])
|
||||
|
@ -349,7 +351,7 @@ class DocumentInfo(models.Model):
|
|||
elif iesg_state.slug == "lc":
|
||||
e = self.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
if e:
|
||||
return iesg_state_summary + " (ends %s)" % e.expires.date().isoformat()
|
||||
return iesg_state_summary + " (ends %s)" % e.expires.astimezone(DEADLINE_TZINFO).date().isoformat()
|
||||
|
||||
return iesg_state_summary
|
||||
else:
|
||||
|
@ -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']
|
||||
|
@ -829,16 +831,20 @@ class Document(DocumentInfo):
|
|||
def telechat_date(self, e=None):
|
||||
if not e:
|
||||
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
return e.telechat_date if e and e.telechat_date and e.telechat_date >= datetime.date.today() else None
|
||||
return e.telechat_date if e and e.telechat_date and e.telechat_date >= date_today(settings.TIME_ZONE) else None
|
||||
|
||||
def past_telechat_date(self):
|
||||
"Return the latest telechat date if it isn't in the future; else None"
|
||||
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
return e.telechat_date if e and e.telechat_date and e.telechat_date < datetime.date.today() else None
|
||||
return e.telechat_date if e and e.telechat_date and e.telechat_date < date_today(settings.TIME_ZONE) else None
|
||||
|
||||
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=date_today(settings.TIME_ZONE),
|
||||
)
|
||||
return e.telechat_date if e else None
|
||||
|
||||
def request_closed_time(self, review_req):
|
||||
|
@ -904,14 +910,21 @@ class Document(DocumentInfo):
|
|||
def future_presentations(self):
|
||||
""" returns related SessionPresentation objects for meetings that
|
||||
have not yet ended. This implementation allows for 2 week meetings """
|
||||
candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__gte=datetime.date.today()-datetime.timedelta(days=15))
|
||||
return sorted([pres for pres in candidate_presentations if pres.session.meeting.end_date()>=datetime.date.today()], key=lambda x:x.session.meeting.date)
|
||||
candidate_presentations = self.sessionpresentation_set.filter(
|
||||
session__meeting__date__gte=date_today() - datetime.timedelta(days=15)
|
||||
)
|
||||
return sorted(
|
||||
[pres for pres in candidate_presentations
|
||||
if pres.session.meeting.end_date() >= date_today()],
|
||||
key=lambda x:x.session.meeting.date,
|
||||
)
|
||||
|
||||
def last_presented(self):
|
||||
""" returns related SessionPresentation objects for the most recent meeting in the past"""
|
||||
# Assumes no two meetings have the same start date - if the assumption is violated, one will be chosen arbitrariy
|
||||
candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__lte=datetime.date.today())
|
||||
candidate_meetings = set([p.session.meeting for p in candidate_presentations if p.session.meeting.end_date()<datetime.date.today()])
|
||||
today = date_today()
|
||||
candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__lte=today)
|
||||
candidate_meetings = set([p.session.meeting for p in candidate_presentations if p.session.meeting.end_date()<today])
|
||||
if candidate_meetings:
|
||||
mtg = sorted(list(candidate_meetings),key=lambda x:x.date,reverse=True)[0]
|
||||
return self.sessionpresentation_set.filter(session__meeting=mtg)
|
||||
|
@ -924,13 +937,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
|
||||
|
@ -955,7 +973,7 @@ class Document(DocumentInfo):
|
|||
elif rev_events.exists():
|
||||
time = rev_events.first().time
|
||||
else:
|
||||
time = datetime.datetime.fromtimestamp(0)
|
||||
time = datetime.datetime.fromtimestamp(0, datetime.timezone.utc)
|
||||
dh = DocHistory(name=self.name, rev=rev, doc=self, time=time, type=self.type, title=self.title,
|
||||
stream=self.stream, group=self.group)
|
||||
|
||||
|
@ -1208,7 +1226,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 +1406,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)
|
||||
|
|
|
@ -38,6 +38,7 @@ import debug # pyflakes:ignore
|
|||
from django import template
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from ietf.ietfauth.utils import user_is_person, has_role
|
||||
|
@ -173,17 +174,17 @@ def state_age_colored(doc):
|
|||
if iesg_state in ["dead", "watching", "pub", "idexists"]:
|
||||
return ""
|
||||
try:
|
||||
state_date = (
|
||||
state_datetime = (
|
||||
doc.docevent_set.filter(
|
||||
Q(type="started_iesg_process")
|
||||
| Q(type="changed_state", statedocevent__state_type="draft-iesg")
|
||||
)
|
||||
.order_by("-time")[0]
|
||||
.time.date()
|
||||
.time
|
||||
)
|
||||
except IndexError:
|
||||
state_date = datetime.date(1990, 1, 1)
|
||||
days = (datetime.date.today() - state_date).days
|
||||
state_datetime = datetime.datetime(1990, 1, 1, tzinfo=datetime.timezone.utc)
|
||||
days = (timezone.now() - state_datetime).days
|
||||
# loosely based on
|
||||
# https://trac.ietf.org/trac/iesg/wiki/PublishPath
|
||||
if iesg_state == "lc":
|
||||
|
|
|
@ -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
|
||||
|
@ -501,6 +502,36 @@ def ics_esc(text):
|
|||
text = re.sub(r"([\n,;\\])", r"\\\1", text)
|
||||
return text
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def ics_date_time(dt, tzname):
|
||||
"""Render a datetime as an iCalendar date-time
|
||||
|
||||
dt a datetime, localized to the timezone to be displayed
|
||||
tzname is the name for this timezone
|
||||
|
||||
Caller must arrange for a VTIMEZONE for the tzname to be included in the iCalendar file.
|
||||
Output includes a ':'. Use like:
|
||||
DTSTART{% ics_date_time timestamp 'America/Los_Angeles' %}
|
||||
to get
|
||||
DTSTART;TZID=America/Los_Angeles:20221021T111200
|
||||
|
||||
>>> ics_date_time(datetime.datetime(2022,1,2,3,4,5), 'utc')
|
||||
':20220102T030405Z'
|
||||
|
||||
>>> ics_date_time(datetime.datetime(2022,1,2,3,4,5), 'UTC')
|
||||
':20220102T030405Z'
|
||||
|
||||
>>> ics_date_time(datetime.datetime(2022,1,2,3,4,5), 'America/Los_Angeles')
|
||||
';TZID=America/Los_Angeles:20220102T030405'
|
||||
"""
|
||||
timestamp = dt.strftime('%Y%m%dT%H%M%S')
|
||||
if tzname.lower() == 'utc':
|
||||
return f':{timestamp}Z'
|
||||
else:
|
||||
return f';TZID={ics_esc(tzname)}:{timestamp}'
|
||||
|
||||
|
||||
@register.filter
|
||||
def consensus(doc):
|
||||
"""Returns document consensus Yes/No/Unknown."""
|
||||
|
@ -637,19 +668,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 rounded-pill bg-danger" title="In state for 16 days; goal is <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 rounded-pill bg-danger" title="In state for 30 days; goal is <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 rounded-pill bg-danger" title="In state for %d day%s; goal is <%d days."><i class="bi bi-clock-fill"></i> %d</span>'
|
||||
|
|
|
@ -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 date_today, 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)))
|
||||
|
@ -1586,7 +1592,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(
|
||||
|
@ -1616,7 +1622,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)"))
|
||||
|
||||
|
@ -1650,7 +1656,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)"))
|
||||
|
||||
|
@ -1672,7 +1678,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)"))
|
||||
|
||||
|
@ -1842,7 +1848,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)
|
||||
|
@ -1890,10 +1896,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)
|
||||
|
@ -1911,10 +1921,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)
|
||||
|
@ -2049,7 +2063,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()
|
||||
|
@ -2064,9 +2079,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 = [ ]
|
||||
|
@ -2217,7 +2232,7 @@ class DocumentMeetingTests(TestCase):
|
|||
self.other_chair = PersonFactory()
|
||||
self.other_group.role_set.create(name_id='chair',person=self.other_chair,email=self.other_chair.email())
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
cut_days = settings.MEETING_MATERIALS_DEFAULT_SUBMISSION_CORRECTION_DAYS
|
||||
self.past_cutoff = SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=1+cut_days))
|
||||
self.past = SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=cut_days/2))
|
||||
|
|
|
@ -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)
|
||||
|
@ -30,6 +31,7 @@ from ietf.person.utils import get_active_ads
|
|||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
|
||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||
from ietf.utils.text import unwrap
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
class EditPositionTests(TestCase):
|
||||
|
@ -105,7 +107,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 +240,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 +468,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 +793,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,
|
||||
|
@ -898,7 +900,7 @@ class MakeLastCallTests(TestCase):
|
|||
|
||||
mailbox_before = len(outbox)
|
||||
|
||||
last_call_sent_date = datetime.date.today()
|
||||
last_call_sent_date = date_today()
|
||||
expire_date = last_call_sent_date+datetime.timedelta(days=14)
|
||||
|
||||
r = self.client.post(url,
|
||||
|
@ -1117,7 +1119,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 +1221,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 +1229,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 +1239,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 +1247,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 +1257,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 +1265,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 +1273,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 +1281,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 +1298,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 +1306,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 +1314,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 +1322,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 +1353,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 +1361,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 +1369,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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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'])
|
||||
|
@ -638,7 +640,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
|
|||
|
||||
meeting = Meeting.objects.create(number="123",
|
||||
type=MeetingTypeName.objects.get(slug="ietf"),
|
||||
date=datetime.date.today())
|
||||
date=date_today())
|
||||
second_cut_off = meeting.get_second_cut_off()
|
||||
ietf_monday = meeting.get_ietf_monday()
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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 date_today, 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))
|
||||
|
@ -323,7 +324,7 @@ class BaseManipulationTests():
|
|||
def test_issue_ballot(self):
|
||||
draft = RgDraftFactory()
|
||||
url = urlreverse('ietf.doc.views_ballot.issue_irsg_ballot',kwargs=dict(name=draft.name))
|
||||
due = datetime.date.today()+datetime.timedelta(days=14)
|
||||
due = date_today(DEADLINE_TZINFO)+datetime.timedelta(days=14)
|
||||
empty_outbox()
|
||||
|
||||
login_testing_unauthorized(self, self.username , url)
|
||||
|
@ -444,7 +445,7 @@ class IRSGMemberTests(TestCase):
|
|||
|
||||
def test_cant_issue_irsg_ballot(self):
|
||||
draft = RgDraftFactory()
|
||||
due = datetime.date.today()+datetime.timedelta(days=14)
|
||||
due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=14)
|
||||
url = urlreverse('ietf.doc.views_ballot.close_irsg_ballot', kwargs=dict(name=draft.name))
|
||||
|
||||
self.client.login(username = self.username, password = self.username+'+password')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 date_today, 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 })
|
||||
|
@ -77,7 +78,7 @@ class ReviewTests(TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
deadline = datetime.date.today() + datetime.timedelta(days=10)
|
||||
deadline = date_today() + datetime.timedelta(days=10)
|
||||
|
||||
empty_outbox()
|
||||
|
||||
|
@ -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,14 +261,14 @@ 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',
|
||||
team=review_req.team,
|
||||
state_id='assigned',
|
||||
requested_rev="01",
|
||||
deadline=datetime.date.today() - datetime.timedelta(days=80),
|
||||
deadline=date_today() - datetime.timedelta(days=80),
|
||||
)
|
||||
ReviewAssignmentFactory(
|
||||
review_request = req,
|
||||
|
@ -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
|
||||
|
@ -525,7 +526,7 @@ class ReviewTests(TestCase):
|
|||
messages = r.json()["messages"]
|
||||
self.assertEqual(len(messages), 2)
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
|
||||
self.assertEqual(messages[0]["url"], "https://www.example.com/testmessage")
|
||||
self.assertTrue("John Doe" in messages[0]["content"])
|
||||
|
@ -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")
|
||||
|
@ -898,7 +899,7 @@ class ReviewTests(TestCase):
|
|||
assignment.review_request.team.acronym,
|
||||
assignment.review_request.type.slug,
|
||||
xslugify(assignment.reviewer.person.ascii_parts()[3]),
|
||||
datetime.date.today().isoformat(),
|
||||
date_today().isoformat(),
|
||||
]
|
||||
review_name = "-".join(c for c in name_components if c).lower()
|
||||
Document.objects.create(name=review_name,type_id='review',group=assignment.review_request.team)
|
||||
|
@ -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 })
|
||||
|
|
|
@ -16,6 +16,7 @@ from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor, Documen
|
|||
from ietf.doc.utils import (update_action_holders, add_state_change_event, update_documentauthors,
|
||||
fuzzy_find_documents, rebuild_reference_relations)
|
||||
from ietf.utils.draft import Draft, PlaintextDraft
|
||||
from ietf.utils.timezone import date_today
|
||||
from ietf.utils.xmldraft import XMLDraft
|
||||
|
||||
|
||||
|
@ -143,12 +144,13 @@ 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())
|
||||
today = date_today()
|
||||
self.assertNotEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), today)
|
||||
self.update_doc_state(doc, State.objects.get(slug='ad-eval'))
|
||||
self.assertEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), datetime.date.today())
|
||||
self.assertEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), today)
|
||||
|
||||
def test_update_action_holders_add_tag_need_rev(self):
|
||||
"""Adding need-rev tag adds authors as action holders"""
|
||||
|
|
|
@ -13,12 +13,14 @@ import textwrap
|
|||
|
||||
from collections import defaultdict, namedtuple
|
||||
from urllib.parse import quote
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.forms import ValidationError
|
||||
from django.http import Http404
|
||||
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
|
||||
|
||||
|
@ -39,6 +41,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
|
||||
|
||||
|
||||
|
@ -637,11 +640,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):
|
||||
|
||||
|
@ -687,7 +701,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
|
||||
|
@ -718,7 +732,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
|
|||
|
||||
e.save()
|
||||
|
||||
has_short_fuse = doc.type_id=='draft' and new_telechat_date and (( new_telechat_date - datetime.date.today() ) < datetime.timedelta(days=13))
|
||||
has_short_fuse = doc.type_id=='draft' and new_telechat_date and (( new_telechat_date - date_today() ) < datetime.timedelta(days=13))
|
||||
|
||||
from ietf.doc.mails import email_update_telechat
|
||||
|
||||
|
@ -808,7 +822,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:
|
||||
|
@ -956,6 +970,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')
|
||||
|
@ -1119,7 +1134,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 = {}
|
||||
|
@ -1337,14 +1352,15 @@ def bibxml_for_draft(doc, rev=None):
|
|||
latest_revision_event = doc.latest_event(NewRevisionDocEvent, type="new_revision")
|
||||
latest_revision_rev = latest_revision_event.rev if latest_revision_event else None
|
||||
best_events = NewRevisionDocEvent.objects.filter(doc__name=doc.name, rev=(rev or latest_revision_rev))
|
||||
tzinfo = ZoneInfo(settings.TIME_ZONE)
|
||||
if best_events.exists():
|
||||
# There was a period where it was possible to get more than one NewRevisionDocEvent for a revision.
|
||||
# A future data cleanup would allow this to be simplified
|
||||
best_event = best_events.order_by('time').first()
|
||||
log.assertion('doc.rev == best_event.rev')
|
||||
doc.date = best_event.time.date()
|
||||
doc.date = best_event.time.astimezone(tzinfo).date()
|
||||
else:
|
||||
doc.date = doc.time.date() # Even if this may be incoreect, what would be better?
|
||||
doc.date = doc.time.astimezone(tzinfo).date() # Even if this may be incorrect, what would be better?
|
||||
|
||||
return render_to_string('doc/bibxml.xml', {'name':doc.name, 'doc': doc, 'doc_bibtype':'I-D'})
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -24,6 +25,8 @@ from ietf.utils.mail import parse_preformatted
|
|||
from ietf.mailtrigger.utils import gather_address_lists
|
||||
from ietf.utils.log import log
|
||||
from ietf.group.utils import save_group_in_history
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def charter_name_for_group(group):
|
||||
if group.type_id == "rg":
|
||||
|
@ -73,7 +76,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 +135,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 +200,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)
|
||||
|
@ -215,7 +218,7 @@ def default_review_text(group, charter, by):
|
|||
parent_ads=group.parent.role_set.filter(name='ad'),
|
||||
techadv=group.role_set.filter(name="techadv"),
|
||||
milestones=group.groupmilestone_set.filter(state="charter"),
|
||||
review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(),
|
||||
review_date=(date_today() + datetime.timedelta(weeks=1)).isoformat(),
|
||||
review_type="new" if group.state_id in ["proposed","bof"] else "recharter",
|
||||
to=addrs.to,
|
||||
cc=addrs.cc,
|
||||
|
|
|
@ -5,11 +5,17 @@ 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
|
||||
from ietf.meeting.models import SessionPresentation, Meeting, Session
|
||||
from ietf.review.utils import review_assignments_to_list_for_docs
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def wrap_value(v):
|
||||
return lambda: v
|
||||
|
@ -30,8 +36,9 @@ def fill_in_telechat_date(docs, doc_dict=None, doc_ids=None):
|
|||
seen.add(e.doc_id)
|
||||
|
||||
def fill_in_document_sessions(docs, doc_dict, doc_ids):
|
||||
beg_date = datetime.date.today()-datetime.timedelta(days=7)
|
||||
end_date = datetime.date.today()+datetime.timedelta(days=30)
|
||||
today = date_today()
|
||||
beg_date = today-datetime.timedelta(days=7)
|
||||
end_date = today+datetime.timedelta(days=30)
|
||||
meetings = Meeting.objects.filter(date__gte=beg_date, date__lte=end_date).prefetch_related('session_set')
|
||||
# get sessions
|
||||
sessions = Session.objects.filter(meeting_id__in=[ m.id for m in meetings ])
|
||||
|
@ -204,7 +211,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))
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
@ -1080,7 +1084,7 @@ def make_last_call(request, name):
|
|||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
else:
|
||||
initial = {}
|
||||
initial["last_call_sent_date"] = datetime.date.today()
|
||||
initial["last_call_sent_date"] = date_today()
|
||||
if doc.type.slug == 'draft':
|
||||
# This logic is repeated in the code that edits last call text - why?
|
||||
expire_days = 14
|
||||
|
@ -1091,7 +1095,7 @@ def make_last_call(request, name):
|
|||
expire_days=28
|
||||
templ = 'doc/status_change/make_last_call.html'
|
||||
|
||||
initial["last_call_expiration_date"] = datetime.date.today() + datetime.timedelta(days=expire_days)
|
||||
initial["last_call_expiration_date"] = date_today() + datetime.timedelta(days=expire_days)
|
||||
|
||||
form = MakeLastCallForm(initial=initial)
|
||||
|
||||
|
@ -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")
|
||||
|
@ -1188,7 +1192,10 @@ def irsg_ballot_status(request):
|
|||
ballot = doc.active_ballot()
|
||||
if ballot:
|
||||
doc.ballot = ballot
|
||||
doc.duedate=datetime.datetime.strftime(ballot.irsgballotdocevent.duedate, '%Y-%m-%d')
|
||||
doc.duedate=datetime.datetime.strftime(
|
||||
ballot.irsgballotdocevent.duedate.astimezone(DEADLINE_TZINFO),
|
||||
'%Y-%m-%d',
|
||||
)
|
||||
|
||||
docs.append(doc)
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import io
|
||||
import json
|
||||
|
@ -85,6 +84,7 @@ from ietf.utils import markup_txt, log, markdown
|
|||
from ietf.utils.draft import PlaintextDraft
|
||||
from ietf.utils.response import permission_denied
|
||||
from ietf.utils.text import maybe_split
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def render_document_top(request, doc, tab, name):
|
||||
|
@ -186,7 +186,7 @@ def document_main(request, name, rev=None):
|
|||
|
||||
|
||||
telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
if telechat and (not telechat.telechat_date or telechat.telechat_date < datetime.date.today()):
|
||||
if telechat and (not telechat.telechat_date or telechat.telechat_date < date_today(settings.TIME_ZONE)):
|
||||
telechat = None
|
||||
|
||||
|
||||
|
@ -990,7 +990,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:
|
||||
|
@ -1408,11 +1408,12 @@ def telechat_date(request, name):
|
|||
|
||||
warnings = []
|
||||
if e and e.telechat_date and doc.type.slug != 'charter':
|
||||
if e.telechat_date==datetime.date.today():
|
||||
today = date_today(settings.TIME_ZONE)
|
||||
if e.telechat_date == today:
|
||||
warnings.append( "This document is currently scheduled for today's telechat. "
|
||||
+"Please set the returning item bit carefully.")
|
||||
|
||||
elif e.telechat_date<datetime.date.today() and has_same_ballot(doc,e.telechat_date):
|
||||
elif e.telechat_date < today and has_same_ballot(doc,e.telechat_date):
|
||||
initial_returning_item = True
|
||||
warnings.append( "This document appears to have been on a previous telechat with the same ballot, "
|
||||
+"so the returning item bit has been set. Clear it if that is not appropriate.")
|
||||
|
|
|
@ -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)
|
||||
|
@ -1480,7 +1483,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)
|
||||
|
||||
|
@ -1671,7 +1674,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)
|
||||
|
||||
|
|
|
@ -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 date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
def clean_doc_revision(doc, rev):
|
||||
if rev:
|
||||
|
@ -92,7 +96,7 @@ class RequestReviewForm(forms.ModelForm):
|
|||
|
||||
def clean_deadline(self):
|
||||
v = self.cleaned_data.get('deadline')
|
||||
if v < datetime.date.today():
|
||||
if v < date_today(DEADLINE_TZINFO):
|
||||
raise forms.ValidationError("Select today or a date in the future.")
|
||||
return v
|
||||
|
||||
|
@ -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")
|
||||
|
@ -348,7 +352,7 @@ class RejectReviewerAssignmentForm(forms.Form):
|
|||
def reject_reviewer_assignment(request, name, assignment_id):
|
||||
doc = get_object_or_404(Document, name=name)
|
||||
review_assignment = get_object_or_404(ReviewAssignment, pk=assignment_id, state__in=["assigned", "accepted"])
|
||||
review_request_past_deadline = review_assignment.review_request.deadline < datetime.date.today()
|
||||
review_request_past_deadline = review_assignment.review_request.deadline < date_today(DEADLINE_TZINFO)
|
||||
|
||||
if not review_assignment.reviewer:
|
||||
return redirect(review_request, name=review_assignment.review_request.doc.name, request_id=review_assignment.review_request.pk)
|
||||
|
@ -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(
|
||||
|
@ -531,7 +535,7 @@ class CompleteReviewForm(forms.Form):
|
|||
review_url = forms.URLField(label="Link to message", required=False)
|
||||
review_file = forms.FileField(label="Text file to upload", required=False)
|
||||
review_content = forms.CharField(widget=forms.Textarea, required=False, strip=False)
|
||||
completion_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={ "autoclose": "1" }, initial=datetime.date.today, help_text="Date of announcement of the results of this review")
|
||||
completion_date = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={ "autoclose": "1" }, initial=date_today, help_text="Date of announcement of the results of this review")
|
||||
completion_time = forms.TimeField(widget=forms.HiddenInput, initial=datetime.time.min)
|
||||
cc = MultiEmailField(required=False, help_text="Email addresses to send to in addition to the review team list")
|
||||
email_ad = forms.BooleanField(label="Send extra email to the responsible AD suggesting early attention", required=False)
|
||||
|
@ -704,7 +708,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
team.acronym,
|
||||
request_type.slug,
|
||||
xslugify(reviewer.person.ascii_parts()[3]),
|
||||
datetime.date.today().isoformat(),
|
||||
date_today().isoformat(),
|
||||
]
|
||||
review_name = "-".join(c for c in name_components if c).lower()
|
||||
if not Document.objects.filter(name=review_name).exists():
|
||||
|
@ -723,7 +727,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
type=form.cleaned_data['review_type'],
|
||||
doc=doc,
|
||||
team=team,
|
||||
deadline=datetime.date.today(),
|
||||
deadline=date_today(DEADLINE_TZINFO),
|
||||
requested_by=Person.objects.get(user=request.user),
|
||||
requested_rev=form.cleaned_data['reviewed_rev'],
|
||||
)
|
||||
|
@ -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"]
|
||||
|
@ -778,7 +786,7 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
|
||||
need_to_email_review = review_submission != "link" and assignment.review_request.team.list_email and not revising_review
|
||||
|
||||
submitted_on_different_date = completion_datetime.date() != datetime.date.today()
|
||||
submitted_on_different_date = completion_datetime.date() != date_today(DEADLINE_TZINFO)
|
||||
desc = "Request for {} review by {} {}: {}. Reviewer: {}.".format(
|
||||
assignment.review_request.type.name,
|
||||
assignment.review_request.team.acronym.upper(),
|
||||
|
@ -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.
|
||||
|
@ -894,8 +902,13 @@ def complete_review(request, name, assignment_id=None, acronym=None):
|
|||
}
|
||||
|
||||
try:
|
||||
initial['review_content'] = render_to_string('/group/%s/review/content_templates/%s.txt' % (assignment.review_request.team.acronym,
|
||||
request_type.slug), {'assignment':assignment, 'today':datetime.date.today()})
|
||||
initial['review_content'] = render_to_string(
|
||||
f'/group/{assignment.review_request.team.acronym}/review/content_templates/{request_type.slug}.txt',
|
||||
{
|
||||
'assignment': assignment,
|
||||
'today': date_today(settings.TIME_ZONE),
|
||||
},
|
||||
)
|
||||
except (TemplateDoesNotExist, AttributeError):
|
||||
pass
|
||||
|
||||
|
@ -984,7 +997,7 @@ class EditReviewRequestDeadlineForm(forms.ModelForm):
|
|||
|
||||
def clean_deadline(self):
|
||||
v = self.cleaned_data.get('deadline')
|
||||
if v < datetime.date.today():
|
||||
if v < date_today(DEADLINE_TZINFO):
|
||||
raise forms.ValidationError("Select today or a date in the future.")
|
||||
return v
|
||||
|
||||
|
|
|
@ -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 ]
|
||||
|
|
|
@ -19,6 +19,8 @@ from ietf.doc.utils import get_search_cache_key
|
|||
from ietf.doc.views_search import SearchForm, retrieve_search_results
|
||||
from ietf.name.models import DocTypeName
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
epochday = datetime.datetime.utcfromtimestamp(0).date().toordinal()
|
||||
|
||||
|
@ -47,7 +49,7 @@ def model_to_timeline_data(model, field='time', **kwargs):
|
|||
# This is needed for sqlite, when we're running tests:
|
||||
if type(obj_list[0]['date']) != datetime.date:
|
||||
obj_list = [ {'date': dt(e['date']), 'count': e['count']} for e in obj_list ]
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
if not obj_list[-1]['date'] == today:
|
||||
obj_list += [ {'date': today, 'count': 0} ]
|
||||
data = [ ((e['date'].toordinal()-epochday)*1000*60*60*24, e['count']) for e in obj_list ]
|
||||
|
|
|
@ -35,6 +35,7 @@ from ietf.name.models import DocRelationshipName, StdLevelName
|
|||
from ietf.person.models import Person
|
||||
from ietf.utils.mail import send_mail_preformatted
|
||||
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
class ChangeStateForm(forms.Form):
|
||||
|
@ -638,7 +639,7 @@ def generate_last_call_text(request, doc):
|
|||
# and when groups are set, vary the expiration time accordingly
|
||||
|
||||
requester = "an individual participant"
|
||||
expiration_date = datetime.date.today() + datetime.timedelta(days=28)
|
||||
expiration_date = date_today(DEADLINE_TZINFO) + datetime.timedelta(days=28)
|
||||
cc = []
|
||||
|
||||
new_text = render_to_string("doc/status_change/last_call_announcement.txt",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
|
||||
# Stdlib imports
|
||||
import datetime
|
||||
import re
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -27,6 +26,7 @@ from ietf.utils import log
|
|||
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||
#from ietf.utils.ordereddict import insert_after_in_ordered_dict
|
||||
from ietf.utils.fields import DatepickerDateField, MultiEmailField
|
||||
from ietf.utils.timezone import date_today
|
||||
from ietf.utils.validators import validate_external_resource_value
|
||||
|
||||
# --- Constants --------------------------------------------------------
|
||||
|
@ -364,7 +364,7 @@ class AddUnavailablePeriodForm(forms.ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(AddUnavailablePeriodForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields["start_date"] = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label=self.fields["start_date"].label, help_text=self.fields["start_date"].help_text, required=self.fields["start_date"].required, initial=datetime.date.today())
|
||||
self.fields["start_date"] = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label=self.fields["start_date"].label, help_text=self.fields["start_date"].help_text, required=self.fields["start_date"].required, initial=date_today())
|
||||
self.fields["end_date"] = DatepickerDateField(date_format="yyyy-mm-dd", picker_settings={"autoclose": "1" }, label=self.fields["end_date"].label, help_text=self.fields["end_date"].help_text, required=self.fields["end_date"].required)
|
||||
|
||||
self.fields['availability'].widget = forms.RadioSelect(choices=UnavailablePeriod.LONG_AVAILABILITY_CHOICES)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -42,6 +42,8 @@ from ietf.person.factories import PersonFactory, EmailFactory
|
|||
from ietf.review.factories import ReviewRequestFactory, ReviewAssignmentFactory
|
||||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||
from ietf.utils.test_utils import login_testing_unauthorized, TestCase, unicontent, reload_db_objects
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
def group_urlreverse_list(group, viewname):
|
||||
return [
|
||||
|
@ -269,7 +271,7 @@ class GroupPagesTests(TestCase):
|
|||
group=group,
|
||||
state_id="active",
|
||||
desc="Get Work Done",
|
||||
due=datetime.date.today() + datetime.timedelta(days=100))
|
||||
due=date_today(DEADLINE_TZINFO) + datetime.timedelta(days=100))
|
||||
milestone.docs.add(draft)
|
||||
|
||||
for url in [group.about_url(),] + group_urlreverse_list(group, 'ietf.group.views.group_about'):
|
||||
|
@ -876,7 +878,7 @@ class GroupEditTests(TestCase):
|
|||
self.assertEqual(r.status_code, 302)
|
||||
review_assignment.state_id = 'accepted'
|
||||
review_assignment.save()
|
||||
review_req.deadline = datetime.date.today() - datetime.timedelta(days=1)
|
||||
review_req.deadline = date_today(DEADLINE_TZINFO) - datetime.timedelta(days=1)
|
||||
review_req.save()
|
||||
|
||||
r = self.client.post(url, post_data)
|
||||
|
@ -1194,7 +1196,7 @@ class MilestoneTests(TestCase):
|
|||
m1 = GroupMilestone.objects.create(id=1,
|
||||
group=group,
|
||||
desc="Test 1",
|
||||
due=datetime.date.today(),
|
||||
due=date_today(DEADLINE_TZINFO),
|
||||
resolved="",
|
||||
state_id="active")
|
||||
m1.docs.set([draft])
|
||||
|
@ -1202,7 +1204,7 @@ class MilestoneTests(TestCase):
|
|||
m2 = GroupMilestone.objects.create(id=2,
|
||||
group=group,
|
||||
desc="Test 2",
|
||||
due=datetime.date.today(),
|
||||
due=date_today(DEADLINE_TZINFO),
|
||||
resolved="",
|
||||
state_id="charter")
|
||||
m2.docs.set([draft])
|
||||
|
@ -1246,7 +1248,7 @@ class MilestoneTests(TestCase):
|
|||
events_before = group.groupevent_set.count()
|
||||
doc_pks = pklist(Document.objects.filter(type="draft"))
|
||||
|
||||
due = self.last_day_of_month(datetime.date.today() + datetime.timedelta(days=365))
|
||||
due = self.last_day_of_month(date_today(DEADLINE_TZINFO) + datetime.timedelta(days=365))
|
||||
|
||||
# faulty post
|
||||
r = self.client.post(url, { 'prefix': "m-1",
|
||||
|
@ -1302,7 +1304,7 @@ class MilestoneTests(TestCase):
|
|||
|
||||
milestones_before = GroupMilestone.objects.filter(group=group).count()
|
||||
events_before = group.groupevent_set.count()
|
||||
due = self.last_day_of_month(datetime.date.today() + datetime.timedelta(days=365))
|
||||
due = self.last_day_of_month(date_today(DEADLINE_TZINFO) + datetime.timedelta(days=365))
|
||||
|
||||
# add
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -1393,7 +1395,7 @@ class MilestoneTests(TestCase):
|
|||
events_before = group.groupevent_set.count()
|
||||
doc_pks = pklist(Document.objects.filter(type="draft"))
|
||||
|
||||
due = self.last_day_of_month(datetime.date.today() + datetime.timedelta(days=365))
|
||||
due = self.last_day_of_month(date_today(DEADLINE_TZINFO) + datetime.timedelta(days=365))
|
||||
|
||||
# faulty post
|
||||
r = self.client.post(url, { 'prefix': "m1",
|
||||
|
@ -1776,7 +1778,7 @@ class MeetingInfoTests(TestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.group = GroupFactory.create(type_id='wg')
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=14))
|
||||
self.inprog = SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=1))
|
||||
SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today+datetime.timedelta(days=90))
|
||||
|
@ -1900,7 +1902,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=date_today()-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)
|
||||
|
|
|
@ -7,6 +7,7 @@ import debug # pyflakes:ignore
|
|||
from ietf.doc.factories import WgDraftFactory
|
||||
from ietf.group.factories import GroupFactory, RoleFactory, DatedGroupMilestoneFactory
|
||||
from ietf.utils.jstest import IetfSeleniumTestCase, ifSeleniumEnabled, selenium_enabled
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
if selenium_enabled():
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
|
@ -68,7 +69,7 @@ class MilestoneTests(IetfSeleniumTestCase):
|
|||
draft = WgDraftFactory()
|
||||
WgDraftFactory.create_batch(3) # some drafts to ignore
|
||||
description = 'some description'
|
||||
due_date = datetime.date.today() + datetime.timedelta(days=60)
|
||||
due_date = date_today() + datetime.timedelta(days=60)
|
||||
|
||||
assert(len(draft.name) > 5)
|
||||
draft_search_string = draft.name[-5:]
|
||||
|
|
|
@ -7,7 +7,9 @@ import debug # pyflakes:ignore
|
|||
|
||||
from pyquery import PyQuery
|
||||
|
||||
from django.conf import settings
|
||||
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 +27,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 +134,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 +158,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 +168,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 +213,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 +234,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 +255,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 +307,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 +432,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
|
||||
)
|
||||
|
@ -475,7 +478,7 @@ class ReviewTests(TestCase):
|
|||
review_req1 = ReviewRequestFactory()
|
||||
review_assignment_completed = ReviewAssignmentFactory(review_request=review_req1,reviewer=EmailFactory(person__user__username='marschairman'), state_id='completed', reviewed_rev=0)
|
||||
ReviewAssignmentFactory(review_request=review_req1,reviewer=review_assignment_completed.reviewer)
|
||||
TelechatDocEvent.objects.create(telechat_date=datetime.date.today(), type='scheduled_for_telechat', by=review_assignment_completed.reviewer.person, doc=review_req1.doc, rev=0)
|
||||
TelechatDocEvent.objects.create(telechat_date=date_today(settings.TIME_ZONE), type='scheduled_for_telechat', by=review_assignment_completed.reviewer.person, doc=review_req1.doc, rev=0)
|
||||
|
||||
DBTemplateFactory.create(path='/group/defaults/email/open_assignments.txt',
|
||||
type_id='django',
|
||||
|
@ -555,7 +558,7 @@ class ReviewTests(TestCase):
|
|||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.context['period_form']['start_date'].initial, datetime.date.today())
|
||||
self.assertEqual(r.context['period_form']['start_date'].initial, date_today())
|
||||
|
||||
# set settings
|
||||
empty_outbox()
|
||||
|
@ -596,7 +599,7 @@ class ReviewTests(TestCase):
|
|||
self.assertEqual(settings.skip_next, 0)
|
||||
|
||||
# add unavailable period
|
||||
start_date = datetime.date.today() + datetime.timedelta(days=10)
|
||||
start_date = date_today() + datetime.timedelta(days=10)
|
||||
empty_outbox()
|
||||
r = self.client.post(url, {
|
||||
"action": "add_period",
|
||||
|
|
|
@ -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 date_today, 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"],
|
||||
|
@ -1026,7 +1028,7 @@ def edit(request, group_type=None, acronym=None, action="edit", field=None):
|
|||
change_text=title + ' deleted: ' + ", ".join(x.name_and_email() for x in deleted)
|
||||
personnel_change_text+=change_text+"\n"
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
for deleted_email in deleted:
|
||||
# Verify the person doesn't have a separate reviewer role for the group with a different address
|
||||
if not group.role_set.filter(name_id='reviewer',person=deleted_email.person).exists():
|
||||
|
@ -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:
|
||||
|
@ -1387,7 +1389,7 @@ def review_requests(request, acronym, group_type=None):
|
|||
unassigned_review_requests.sort(key=lambda r: r.doc.name)
|
||||
|
||||
open_review_assignments = list(ReviewAssignment.objects.filter(review_request__team=group, state_id__in=('assigned','accepted')).order_by('-assigned_on'))
|
||||
today = datetime.date.today()
|
||||
today = date_today(DEADLINE_TZINFO)
|
||||
unavailable_periods = current_unavailable_periods_for_reviewers(group)
|
||||
for a in open_review_assignments:
|
||||
a.reviewer_unavailable = any(p.availability == "unavailable"
|
||||
|
@ -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, {
|
||||
|
@ -1455,7 +1460,7 @@ def reviewer_overview(request, acronym, group_type=None):
|
|||
unavailable_periods[p.person_id].append(p)
|
||||
reviewer_roles = { r.person_id: r for r in Role.objects.filter(group=group, name="reviewer").select_related("email") }
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
|
||||
max_closed_reqs = settings.GROUP_REVIEW_MAX_ITEMS_TO_SHOW_IN_REVIEWER_LIST
|
||||
days_back = settings.GROUP_REVIEW_DAYS_TO_SHOW_IN_REVIEWER_LIST
|
||||
|
@ -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
|
||||
|
@ -1685,7 +1690,7 @@ def email_open_review_assignments(request, acronym, group_type=None):
|
|||
r.section = 'Last calls:'
|
||||
r.section_order='1'
|
||||
e = r.review_request.doc.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
r.lastcall_ends = e and e.expires.date().isoformat()
|
||||
r.lastcall_ends = e and e.expires.astimezone(DEADLINE_TZINFO).date().isoformat()
|
||||
r.earlier_review = ReviewAssignment.objects.filter(review_request__doc=r.review_request.doc,reviewer__in=r.reviewer.person.email_set.all(),state="completed")
|
||||
if r.earlier_review:
|
||||
earlier_reviews_formatted = ['-{} {} reviewed'.format(ra.reviewed_rev, ra.review_request.type.slug) for ra in r.earlier_review]
|
||||
|
@ -1833,7 +1838,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
period.save()
|
||||
update_change_reason(period, "Added unavailability period: {}".format(period))
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
|
||||
in_the_past = period.end_date and period.end_date < today
|
||||
|
||||
|
@ -1874,7 +1879,7 @@ def change_reviewer_settings(request, acronym, reviewer_email, group_type=None):
|
|||
period.delete()
|
||||
update_change_reason(period, "Removed unavailability period: {}".format(period))
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
|
||||
in_the_past = period.end_date and period.end_date < today
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
import datetime
|
||||
import os
|
||||
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 +296,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(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z"),
|
||||
'with_abstracts': with_abstracts,
|
||||
})
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -17,13 +17,14 @@ from ietf.doc.models import Document, LastCallDocEvent, ConsensusDocEvent
|
|||
from ietf.doc.utils_search import fill_in_telechat_date
|
||||
from ietf.iesg.models import TelechatDate, TelechatAgendaItem
|
||||
from ietf.review.utils import review_assignments_to_list_for_docs
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
def get_agenda_date(date=None):
|
||||
if not date:
|
||||
try:
|
||||
return TelechatDate.objects.active().order_by('date')[0].date
|
||||
except IndexError:
|
||||
return datetime.date.today()
|
||||
return date_today()
|
||||
else:
|
||||
try:
|
||||
return TelechatDate.objects.active().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.utils.feedgenerator import Atom1Feed
|
||||
|
||||
from ietf.doc.models import Document, TelechatDocEvent
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
class IESGAgendaFeed(Feed):
|
||||
title = "Documents on Future IESG Telechat Agendas"
|
||||
|
@ -16,7 +17,7 @@ class IESGAgendaFeed(Feed):
|
|||
description_template = "iesg/feed_item_description.html"
|
||||
|
||||
def items(self):
|
||||
docs = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.today()).distinct()
|
||||
docs = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=date_today(settings.TIME_ZONE)).distinct()
|
||||
for d in docs:
|
||||
d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
|
||||
docs = [d for d in docs if d.latest_telechat_event.telechat_date]
|
||||
|
|
|
@ -36,8 +36,12 @@
|
|||
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
class TelechatAgendaItem(models.Model):
|
||||
TYPE_CHOICES = (
|
||||
(1, "Any Other Business (WG News, New Proposals, etc.)"),
|
||||
|
@ -72,11 +76,11 @@ def next_telechat_date():
|
|||
dates = TelechatDate.objects.order_by("-date")
|
||||
if dates:
|
||||
return dates[0].date + datetime.timedelta(days=14)
|
||||
return datetime.date.today()
|
||||
return date_today(settings.TIME_ZONE)
|
||||
|
||||
class TelechatDateManager(models.Manager):
|
||||
def active(self):
|
||||
return self.get_queryset().filter(date__gte=datetime.date.today())
|
||||
return self.get_queryset().filter(date__gte=date_today(settings.TIME_ZONE))
|
||||
|
||||
class TelechatDate(models.Model):
|
||||
objects = TelechatDateManager()
|
||||
|
|
|
@ -28,6 +28,7 @@ from ietf.name.models import StreamName
|
|||
from ietf.person.models import Person
|
||||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
|
||||
from ietf.iesg.factories import IESGMgmtItemFactory
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
class IESGTests(TestCase):
|
||||
|
@ -58,7 +59,7 @@ class IESGTests(TestCase):
|
|||
m = GroupMilestone.objects.create(group=draft.group,
|
||||
state_id="review",
|
||||
desc="Test milestone",
|
||||
due=datetime.date.today())
|
||||
due=date_today(DEADLINE_TZINFO))
|
||||
|
||||
url = urlreverse("ietf.iesg.views.milestones_needing_review")
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
@ -142,7 +143,7 @@ class IESGAgendaTests(TestCase):
|
|||
mgmtitem = self.mgmt_items
|
||||
|
||||
# put on agenda
|
||||
date = datetime.date.today() + datetime.timedelta(days=50)
|
||||
date = date_today(settings.TIME_ZONE) + datetime.timedelta(days=50)
|
||||
TelechatDate.objects.create(date=date)
|
||||
telechat_event = TelechatDocEvent.objects.create(
|
||||
type="scheduled_for_telechat",
|
||||
|
@ -430,7 +431,7 @@ class IESGAgendaTests(TestCase):
|
|||
self.assertNotIn(d.title, unicontent(r))
|
||||
# Add the documents to a past telechat
|
||||
by = Person.objects.get(name="Areað Irector")
|
||||
date = datetime.date.today() - datetime.timedelta(days=14)
|
||||
date = date_today(settings.TIME_ZONE) - datetime.timedelta(days=14)
|
||||
approved = State.objects.get(type='draft-iesg', slug='approved')
|
||||
iesg_eval = State.objects.get(type='draft-iesg', slug='iesg-eva')
|
||||
for d in list(self.telechat_docs.values()):
|
||||
|
@ -485,7 +486,7 @@ class IESGAgendaTests(TestCase):
|
|||
|
||||
def test_admin_change(self):
|
||||
draft = Document.objects.get(name="draft-ietf-mars-test")
|
||||
today = datetime.date.today()
|
||||
today = date_today(settings.TIME_ZONE)
|
||||
telechat_date = TelechatDate.objects.get(date=draft.telechat_date())
|
||||
url = urlreverse('admin:iesg_telechatdate_change', args=(telechat_date.id,))
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -46,6 +47,8 @@ from ietf.stats.models import MeetingRegistration
|
|||
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
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
import ietf.ietfauth.views
|
||||
|
||||
|
@ -390,7 +393,7 @@ class IetfAuthTests(TestCase):
|
|||
self.assertFalse(q('#volunteer-button'))
|
||||
self.assertFalse(q('#volunteered'))
|
||||
|
||||
year = datetime.date.today().year
|
||||
year = date_today().year
|
||||
nomcom = NomComFactory(group__acronym=f'nomcom{year}',is_accepting_volunteers=True)
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code,200)
|
||||
|
@ -505,7 +508,7 @@ class IetfAuthTests(TestCase):
|
|||
UnavailablePeriod.objects.create(
|
||||
team=review_req.team,
|
||||
person=reviewer,
|
||||
start_date=datetime.date.today() - datetime.timedelta(days=10),
|
||||
start_date=date_today() - datetime.timedelta(days=10),
|
||||
availability="unavailable",
|
||||
)
|
||||
|
||||
|
@ -750,11 +753,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,12 +786,12 @@ 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():
|
||||
self.client.post(key.endpoint, {'apikey':key.hash(), 'dummy': 'dummy', })
|
||||
date = str(datetime.date.today())
|
||||
date = str(date_today())
|
||||
|
||||
empty_outbox()
|
||||
cmd = Command()
|
||||
|
@ -905,7 +908,7 @@ class OpenIDConnectTests(TestCase):
|
|||
# an additional email
|
||||
EmailFactory(person=person)
|
||||
email_list = person.email_set.all().values_list('address', flat=True)
|
||||
meeting = MeetingFactory(type_id='ietf', date=datetime.date.today())
|
||||
meeting = MeetingFactory(type_id='ietf', date=date_today())
|
||||
MeetingRegistration.objects.create(
|
||||
meeting=meeting, person=None, first_name=person.first_name(), last_name=person.last_name(),
|
||||
email=email_list[0], ticket_type='full_week', reg_type='remote', affiliation='Some Company',
|
||||
|
|
|
@ -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
|
||||
|
@ -78,6 +78,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
|
||||
|
||||
|
@ -223,7 +224,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:
|
||||
|
@ -455,7 +456,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")
|
||||
|
||||
|
@ -557,7 +558,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)
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -27,6 +28,7 @@ from ietf.message.models import Message
|
|||
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
|
||||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized
|
||||
from ietf.utils.text import text_to_dict
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def make_data_from_content(content):
|
||||
|
@ -572,7 +574,7 @@ I would like to revoke this declaration.
|
|||
self.assertEqual(r.status_code,302)
|
||||
self.assertEqual(len(outbox),len_before+2)
|
||||
self.assertTrue('george@acme.com' in outbox[len_before]['To'])
|
||||
self.assertIn('posted on '+datetime.date.today().strftime("%Y-%m-%d"), get_payload_text(outbox[len_before]).replace('\n',' '))
|
||||
self.assertIn('posted on '+date_today().strftime("%Y-%m-%d"), get_payload_text(outbox[len_before]).replace('\n',' '))
|
||||
self.assertTrue('draft-ietf-mars-test@ietf.org' in outbox[len_before+1]['To'])
|
||||
self.assertTrue('mars-wg@ietf.org' in outbox[len_before+1]['Cc'])
|
||||
self.assertIn('Secretariat on '+ipr.get_latest_event_submitted().time.strftime("%Y-%m-%d"), get_payload_text(outbox[len_before+1]).replace('\n',' '))
|
||||
|
@ -600,7 +602,7 @@ I would like to revoke this declaration.
|
|||
ipr = HolderIprDisclosureFactory()
|
||||
url = urlreverse('ietf.ipr.views.email',kwargs={ "id": ipr.id })
|
||||
self.client.login(username="secretary", password="secretary+password")
|
||||
yesterday = datetime.date.today() - datetime.timedelta(1)
|
||||
yesterday = date_today() - datetime.timedelta(1)
|
||||
data = dict(
|
||||
to='joe@test.com',
|
||||
frm='ietf-ipr@ietf.org',
|
||||
|
@ -640,7 +642,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 +652,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 +666,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 +682,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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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=lambda: 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
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from ietf.utils.mail import send_mail_text
|
||||
from ietf.group.models import Role
|
||||
from ietf.mailtrigger.utils import gather_address_lists
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
def send_liaison_by_email(request, liaison):
|
||||
subject = 'New Liaison Statement, "%s"' % (liaison.title)
|
||||
|
@ -61,7 +61,7 @@ def possibly_send_deadline_reminder(liaison):
|
|||
0: 'today'
|
||||
}
|
||||
|
||||
days_to_go = (liaison.deadline - datetime.date.today()).days
|
||||
days_to_go = (liaison.deadline - date_today(DEADLINE_TZINFO)).days
|
||||
if not (days_to_go < 0 or days_to_go in list(PREVIOUS_DAYS.keys())):
|
||||
return None # no reminder
|
||||
|
||||
|
|
|
@ -8,13 +8,14 @@ from django.core.management.base import BaseCommand
|
|||
|
||||
from ietf.liaisons.models import LiaisonStatement
|
||||
from ietf.liaisons.mails import possibly_send_deadline_reminder
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = ("Check liaison deadlines and send a reminder if we are close to a deadline")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
today = datetime.date.today()
|
||||
today = date_today(DEADLINE_TZINFO)
|
||||
cutoff = today - datetime.timedelta(14)
|
||||
|
||||
msgs = []
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -29,6 +31,8 @@ from ietf.person.models import Person
|
|||
from ietf.group.models import Group
|
||||
from ietf.liaisons.mails import send_sdo_reminder, possibly_send_deadline_reminder
|
||||
from ietf.liaisons.views import contacts_from_roles, contact_email_from_role
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
||||
# -------------------------------------------------
|
||||
# Helper Functions
|
||||
|
@ -50,7 +54,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" )
|
||||
|
||||
|
@ -242,7 +246,7 @@ class ManagementCommandTests(TestCase):
|
|||
def test_check_liaison_deadlines(self):
|
||||
from django.core.management import call_command
|
||||
|
||||
LiaisonStatementFactory(deadline=datetime.date.today()+datetime.timedelta(days=1))
|
||||
LiaisonStatementFactory(deadline=date_today(DEADLINE_TZINFO)+datetime.timedelta(days=1))
|
||||
|
||||
out = io.StringIO()
|
||||
mailbox_before = len(outbox)
|
||||
|
@ -310,7 +314,7 @@ class LiaisonManagementTests(TestCase):
|
|||
self.assertNotContains(r, 'Private comment')
|
||||
|
||||
def test_taken_care_of(self):
|
||||
liaison = LiaisonStatementFactory(deadline=datetime.date.today()+datetime.timedelta(days=1))
|
||||
liaison = LiaisonStatementFactory(deadline=date_today(DEADLINE_TZINFO)+datetime.timedelta(days=1))
|
||||
|
||||
url = urlreverse('ietf.liaisons.views.liaison_detail', kwargs=dict(object_id=liaison.pk))
|
||||
# normal get
|
||||
|
@ -384,8 +388,8 @@ class LiaisonManagementTests(TestCase):
|
|||
self.assertTrue(liaison.liaisonstatementevent_set.filter(type='posted'))
|
||||
|
||||
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))
|
||||
liaison = LiaisonStatementFactory(deadline=date_today(DEADLINE_TZINFO) + 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()
|
||||
|
@ -696,7 +700,7 @@ class LiaisonManagementTests(TestCase):
|
|||
from_groups = [ str(g.pk) for g in Group.objects.filter(type="sdo") ]
|
||||
to_group = Group.objects.get(acronym="mars")
|
||||
submitter = Person.objects.get(user__username="marschairman")
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
related_liaison = liaison
|
||||
r = self.client.post(url,
|
||||
dict(from_groups=from_groups,
|
||||
|
@ -775,7 +779,7 @@ class LiaisonManagementTests(TestCase):
|
|||
from_group = Group.objects.get(acronym="mars")
|
||||
to_group = Group.objects.filter(type="sdo")[0]
|
||||
submitter = Person.objects.get(user__username="marschairman")
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
related_liaison = liaison
|
||||
r = self.client.post(url,
|
||||
dict(from_groups=str(from_group.pk),
|
||||
|
@ -843,7 +847,7 @@ class LiaisonManagementTests(TestCase):
|
|||
from_group = Group.objects.get(acronym="mars")
|
||||
to_group = Group.objects.filter(type="sdo")[0]
|
||||
submitter = Person.objects.get(user__username="marschairman")
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
r = self.client.post(url,
|
||||
dict(from_groups=str(from_group.pk),
|
||||
from_contact=submitter.email_address(),
|
||||
|
@ -862,7 +866,7 @@ class LiaisonManagementTests(TestCase):
|
|||
self.assertEqual(len(outbox), mailbox_before + 1)
|
||||
|
||||
def test_liaison_add_attachment(self):
|
||||
liaison = LiaisonStatementFactory(deadline=datetime.date.today()+datetime.timedelta(days=1))
|
||||
liaison = LiaisonStatementFactory(deadline=date_today()+datetime.timedelta(days=1))
|
||||
LiaisonStatementEventFactory(statement=liaison,type_id='submitted')
|
||||
|
||||
self.assertEqual(liaison.attachments.count(),0)
|
||||
|
@ -1021,7 +1025,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
|
||||
|
@ -1148,7 +1152,7 @@ class LiaisonManagementTests(TestCase):
|
|||
self.assertTrue('ulm-liaiman@' in outbox[-1]['To'])
|
||||
|
||||
def test_send_liaison_deadline_reminder(self):
|
||||
liaison = LiaisonStatementFactory(deadline=datetime.date.today()+datetime.timedelta(days=1))
|
||||
liaison = LiaisonStatementFactory(deadline=date_today(DEADLINE_TZINFO) + datetime.timedelta(days=1))
|
||||
|
||||
mailbox_before = len(outbox)
|
||||
possibly_send_deadline_reminder(liaison)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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():
|
||||
|
@ -118,7 +119,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 = [ ]
|
||||
|
@ -1149,11 +1153,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
|
||||
|
@ -1161,14 +1169,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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Copyright The IETF Trust 2022, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -9,6 +7,7 @@ from django.core.management.base import BaseCommand, CommandError
|
|||
|
||||
from ietf.meeting.models import Session
|
||||
from ietf.utils.meetecho import ConferenceManager, MeetechoAPIError
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
@ -85,7 +84,7 @@ class Command(BaseCommand):
|
|||
for conf in confs:
|
||||
conf_sessions[conf.id] = Session.objects.filter(
|
||||
group__acronym=group,
|
||||
meeting__date__gte=datetime.date.today(),
|
||||
meeting__date__gte=date_today(),
|
||||
remote_instructions__contains=conf.url,
|
||||
)
|
||||
return confs, conf_sessions
|
||||
|
|
|
@ -11,6 +11,8 @@ import debug # pyflakes:ignore
|
|||
from ietf.name.models import ImportantDateName
|
||||
from ietf.meeting.helpers import update_important_dates
|
||||
from ietf.meeting.models import Meeting, ImportantDate
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
|
@ -29,7 +31,7 @@ class Command(BaseCommand):
|
|||
if not meeting:
|
||||
self.stderr.write("\nMeeting not found: %s\n" % (m, ))
|
||||
continue
|
||||
if meeting.date < datetime.date.today() + datetime.timedelta(days=max_offset):
|
||||
if meeting.date < date_today(meeting.tz()) + datetime.timedelta(days=max_offset):
|
||||
self.stderr.write("\nMeeting %s: Won't change dates for meetings in the past or close future\n" % (meeting, ))
|
||||
continue
|
||||
self.stdout.write('\n%s\n\n' % (meeting, ))
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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),
|
||||
]
|
18
ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py
Normal file
18
ietf/meeting/migrations/0058_meeting_time_zone_not_blank.py
Normal file
File diff suppressed because one or more lines are too long
|
@ -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, date_today
|
||||
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,46 +138,52 @@ 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)
|
||||
"""Get the I-D submission 00 cutoff in UTC"""
|
||||
importantdate = self.importantdate_set.filter(name_id='idcutoff').first()
|
||||
if not importantdate:
|
||||
importantdate = self.importantdate_set.filter(name_id='00cutoff').first()
|
||||
if importantdate:
|
||||
cutoff_date = importantdate.date
|
||||
else:
|
||||
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||
cutoff_time = date2datetime(cutoff_date) + self.idsubmit_cutoff_time_utc
|
||||
cutoff_date = self.date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||
cutoff_time = datetime_from_date(cutoff_date, datetime.timezone.utc) + self.idsubmit_cutoff_time_utc
|
||||
return cutoff_time
|
||||
|
||||
def get_01_cutoff(self):
|
||||
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day, tzinfo=pytz.utc)
|
||||
"""Get the I-D submission 01 cutoff in UTC"""
|
||||
importantdate = self.importantdate_set.filter(name_id='idcutoff').first()
|
||||
if not importantdate:
|
||||
importantdate = self.importantdate_set.filter(name_id='01cutoff').first()
|
||||
if importantdate:
|
||||
cutoff_date = importantdate.date
|
||||
else:
|
||||
cutoff_date = start_date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||
cutoff_time = date2datetime(cutoff_date) + self.idsubmit_cutoff_time_utc
|
||||
cutoff_date = self.date + datetime.timedelta(days=ImportantDateName.objects.get(slug='idcutoff').default_offset_days)
|
||||
cutoff_time = datetime_from_date(cutoff_date, datetime.timezone.utc) + self.idsubmit_cutoff_time_utc
|
||||
return cutoff_time
|
||||
|
||||
def get_reopen_time(self):
|
||||
start_date = datetime.datetime(year=self.date.year, month=self.date.month, day=self.date.day)
|
||||
local_tz = pytz.timezone(self.time_zone)
|
||||
local_date = local_tz.localize(start_date)
|
||||
"""Get the I-D submission reopening time in meeting-local time"""
|
||||
cutoff = self.get_00_cutoff()
|
||||
if cutoff.date() == start_date:
|
||||
if cutoff.date() == self.date:
|
||||
# no cutoff, so no local-time re-open
|
||||
reopen_time = cutoff
|
||||
else:
|
||||
# reopen time is in local timezone. May need policy change?? XXX
|
||||
reopen_time = local_date + self.idsubmit_cutoff_time_utc
|
||||
reopen_time = datetime_from_date(self.date, self.tz()) + self.idsubmit_cutoff_time_utc
|
||||
return reopen_time
|
||||
|
||||
@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()
|
||||
|
@ -338,7 +345,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] = []
|
||||
|
@ -346,15 +353,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
|
||||
|
@ -370,20 +377,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:
|
||||
|
@ -391,16 +402,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):
|
||||
|
@ -621,41 +630,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):
|
||||
|
@ -753,7 +743,7 @@ class Schedule(models.Model):
|
|||
@property
|
||||
def is_official_record(self):
|
||||
return (self.is_official and
|
||||
self.meeting.end_date() <= datetime.date.today() )
|
||||
self.meeting.end_date() <= date_today() )
|
||||
|
||||
# returns a dictionary {group -> [schedtimesessassignment+]}
|
||||
# and it has [] if the session is not placed.
|
||||
|
@ -1180,7 +1170,7 @@ class Session(models.Model):
|
|||
return can_manage_materials(user,self.group)
|
||||
|
||||
def is_material_submission_cutoff(self):
|
||||
return datetime.date.today() > self.meeting.get_submission_correction_date()
|
||||
return date_today(datetime.timezone.utc) > self.meeting.get_submission_correction_date()
|
||||
|
||||
def joint_with_groups_acronyms(self):
|
||||
return [group.acronym for group in self.joint_with_groups.all()]
|
||||
|
@ -1307,7 +1297,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)
|
||||
|
||||
|
|
|
@ -20,11 +20,15 @@ from ietf.name.models import RoomResourceName
|
|||
from ietf.person.factories import PersonFactory
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_data import make_test_data
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
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 +106,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,
|
||||
|
@ -201,8 +218,8 @@ def make_meeting_test_data(meeting=None, create_interims=False):
|
|||
mars_session.sessionpresentation_set.add(pres)
|
||||
|
||||
# Future Interim Meetings
|
||||
date = datetime.date.today() + datetime.timedelta(days=365)
|
||||
date2 = datetime.date.today() + datetime.timedelta(days=1000)
|
||||
date = date_today() + datetime.timedelta(days=365)
|
||||
date2 = date_today() + datetime.timedelta(days=1000)
|
||||
ames = Group.objects.get(acronym="ames")
|
||||
|
||||
if create_interims:
|
||||
|
@ -213,9 +230,9 @@ def make_meeting_test_data(meeting=None, create_interims=False):
|
|||
|
||||
return meeting
|
||||
|
||||
def make_interim_test_data():
|
||||
date = datetime.date.today() + datetime.timedelta(days=365)
|
||||
date2 = datetime.date.today() + datetime.timedelta(days=1000)
|
||||
def make_interim_test_data(meeting_tz='UTC'):
|
||||
date = date_today() + datetime.timedelta(days=365)
|
||||
date2 = date_today() + datetime.timedelta(days=1000)
|
||||
PersonFactory(user__username='plain')
|
||||
area = GroupFactory(type_id='area')
|
||||
ad = Person.objects.get(user__username='ad')
|
||||
|
@ -225,10 +242,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
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from ietf.meeting.models import SchedTimeSessAssignment, Session
|
|||
from ietf.meeting.test_data import make_meeting_test_data
|
||||
from ietf.utils.meetecho import Conference
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
# override the legacy office hours setting to guarantee consistency with the tests
|
||||
|
@ -623,12 +624,13 @@ class HelperTests(TestCase):
|
|||
def test_get_ietf_meeting(self):
|
||||
"""get_ietf_meeting() should only return IETF meetings"""
|
||||
# put the IETF far in the past so it's not "current"
|
||||
ietf = MeetingFactory(type_id='ietf', date=datetime.date.today() - datetime.timedelta(days=5 * 365))
|
||||
today = date_today()
|
||||
ietf = MeetingFactory(type_id='ietf', date=today- datetime.timedelta(days=5 * 365))
|
||||
# put the interim meeting now so it will be picked up as "current" if there's a bug
|
||||
interim = MeetingFactory(type_id='interim', date=datetime.date.today())
|
||||
interim = MeetingFactory(type_id='interim', date=today)
|
||||
self.assertEqual(get_ietf_meeting(ietf.number), ietf, 'Return IETF meeting by number')
|
||||
self.assertIsNone(get_ietf_meeting(interim.number), 'Ignore non-IETF meetings')
|
||||
self.assertIsNone(get_ietf_meeting(), 'Return None if there is no current IETF meeting')
|
||||
ietf.date = datetime.date.today()
|
||||
ietf.date = today
|
||||
ietf.save()
|
||||
self.assertEqual(get_ietf_meeting(), ietf, 'Return current meeting if there is one')
|
||||
|
|
|
@ -10,8 +10,8 @@ import re
|
|||
from unittest import skipIf
|
||||
|
||||
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
|
||||
|
||||
|
@ -34,7 +34,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 timezone_not_near_midnight
|
||||
from ietf.utils.timezone import datetime_today, datetime_from_date, date_today, timezone_not_near_midnight
|
||||
|
||||
if selenium_enabled():
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
@ -269,14 +269,32 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
# modal_open.click()
|
||||
|
||||
self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal").is_displayed())
|
||||
self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal [value=\"{}\"]".format("ts-group-{}-{}".format(slot2.time.strftime("%Y%m%d-%H%M"), int(slot2.duration.total_seconds() / 60)))).click()
|
||||
self.driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"#timeslot-group-toggles-modal [value=\"{}\"]".format(
|
||||
"ts-group-{}-{}".format(
|
||||
slot2.time.astimezone(slot2.tz()).strftime("%Y%m%d-%H%M"),
|
||||
int(slot2.duration.total_seconds() / 60),
|
||||
),
|
||||
),
|
||||
).click()
|
||||
self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal [data-bs-dismiss=\"modal\"]").click()
|
||||
self.assertTrue(not self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal").is_displayed())
|
||||
|
||||
# swap days
|
||||
self.driver.find_element(By.CSS_SELECTOR, ".day .swap-days[data-dayid=\"{}\"]".format(slot4.time.date().isoformat())).click()
|
||||
self.driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
".day .swap-days[data-dayid=\"{}\"]".format(
|
||||
slot4.time.astimezone(slot4.tz()).date().isoformat(),
|
||||
),
|
||||
).click()
|
||||
self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal").is_displayed())
|
||||
self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal input[name=\"target_day\"][value=\"{}\"]".format(slot1.time.date().isoformat())).click()
|
||||
self.driver.find_element(
|
||||
By.CSS_SELECTOR,
|
||||
"#swap-days-modal input[name=\"target_day\"][value=\"{}\"]".format(
|
||||
slot1.time.astimezone(slot1.tz()).date().isoformat(),
|
||||
),
|
||||
).click()
|
||||
self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal button[type=\"submit\"]").click()
|
||||
|
||||
self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{} #session{}'.format(slot4.pk, s1.pk)),
|
||||
|
@ -306,7 +324,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:
|
||||
|
@ -395,14 +413,14 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
wait = WebDriverWait(self.driver, 2)
|
||||
meeting = MeetingFactory(
|
||||
type_id='ietf',
|
||||
date=datetime.datetime.today() - datetime.timedelta(days=3),
|
||||
date=date=timezone.now() - datetime.timedelta(days=3),
|
||||
days=7,
|
||||
time_zone=timezone_not_near_midnight(),
|
||||
)
|
||||
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:
|
||||
|
@ -433,21 +451,24 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
|
||||
past_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||
','.join(
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in past_timeslots
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in past_timeslots
|
||||
)
|
||||
)
|
||||
self.assertEqual(len(past_swap_days_buttons), len(past_timeslots), 'Missing past swap days buttons')
|
||||
|
||||
future_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||
','.join(
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in future_timeslots
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in future_timeslots
|
||||
)
|
||||
)
|
||||
self.assertEqual(len(future_swap_days_buttons), len(future_timeslots), 'Missing future swap days buttons')
|
||||
|
||||
now_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR,
|
||||
','.join(
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in now_timeslots
|
||||
'.swap-days[data-start="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in now_timeslots
|
||||
)
|
||||
)
|
||||
# only one "now" button because both sessions are on the same day
|
||||
|
@ -498,7 +519,8 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
self.assertFalse(
|
||||
any(radio.is_enabled()
|
||||
for radio in modal.find_elements(By.CSS_SELECTOR, ','.join(
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in past_timeslots)
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in past_timeslots)
|
||||
)),
|
||||
'Past day is enabled in swap-days modal for official schedule',
|
||||
)
|
||||
|
@ -507,14 +529,16 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
self.assertTrue(
|
||||
all(radio.is_enabled()
|
||||
for radio in modal.find_elements(By.CSS_SELECTOR, ','.join(
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in enabled_timeslots)
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in enabled_timeslots)
|
||||
)),
|
||||
'Future day is not enabled in swap-days modal for official schedule',
|
||||
)
|
||||
self.assertFalse(
|
||||
any(radio.is_enabled()
|
||||
for radio in modal.find_elements(By.CSS_SELECTOR, ','.join(
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in now_timeslots)
|
||||
'input[name="target_day"][value="{}"]'.format(ts.time.astimezone(ts.tz()).date().isoformat())
|
||||
for ts in now_timeslots)
|
||||
)),
|
||||
'"Now" day is enabled in swap-days modal for official schedule',
|
||||
)
|
||||
|
@ -522,11 +546,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:
|
||||
|
@ -812,7 +836,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
To test for recurrence of https://trac.ietf.org/trac/ietfdb/ticket/3327 need to have some constraints that
|
||||
do not conflict. Testing with only violated constraints does not exercise the code adequately.
|
||||
"""
|
||||
meeting = MeetingFactory(type_id='ietf', date=datetime.date.today(), populate_schedule=False)
|
||||
meeting = MeetingFactory(type_id='ietf', date=date_today(), populate_schedule=False)
|
||||
TimeSlotFactory.create_batch(5, meeting=meeting)
|
||||
schedule = ScheduleFactory(meeting=meeting)
|
||||
sessions = SessionFactory.create_batch(5, meeting=meeting, add_to_schedule=False)
|
||||
|
@ -934,7 +958,7 @@ class InterimTests(IetfSeleniumTestCase):
|
|||
|
||||
# Create a group with a plenary interim session for testing type filters
|
||||
somegroup = GroupFactory(acronym='sg', name='Some Group')
|
||||
sg_interim = make_interim_meeting(somegroup, datetime.date.today() + datetime.timedelta(days=20))
|
||||
sg_interim = make_interim_meeting(somegroup, date_today() + datetime.timedelta(days=20))
|
||||
sg_sess = sg_interim.session_set.first()
|
||||
sg_slot = sg_sess.timeslotassignments.first().timeslot
|
||||
sg_sess.purpose_id = 'plenary'
|
||||
|
@ -966,7 +990,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 = []
|
||||
|
@ -979,7 +1003,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
|
||||
|
@ -1109,7 +1133,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 = [
|
||||
|
@ -1425,7 +1449,7 @@ class ProceedingsMaterialTests(IetfSeleniumTestCase):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.wait = WebDriverWait(self.driver, 2)
|
||||
self.meeting = MeetingFactory(type_id='ietf', number='123', date=datetime.date.today())
|
||||
self.meeting = MeetingFactory(type_id='ietf', number='123', date=date_today())
|
||||
|
||||
def test_add_proceedings_material(self):
|
||||
url = self.absreverse(
|
||||
|
@ -1527,7 +1551,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(
|
||||
|
@ -1597,22 +1621,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 (
|
||||
|
@ -1629,7 +1654,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'
|
||||
))
|
||||
)
|
||||
|
@ -1644,22 +1671,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 = (
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
"""Tests of models in the Meeting application"""
|
||||
import datetime
|
||||
|
||||
from mock import patch
|
||||
|
||||
from ietf.meeting.factories import MeetingFactory, SessionFactory, AttendedFactory
|
||||
from ietf.stats.factories import MeetingRegistrationFactory
|
||||
from ietf.utils.test_utils import TestCase
|
||||
|
@ -86,6 +88,22 @@ class MeetingTests(TestCase):
|
|||
self.assertEqual(att.onsite, 1)
|
||||
self.assertEqual(att.remote, 0)
|
||||
|
||||
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):
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -21,6 +21,7 @@ from urllib.parse import urlparse, urlsplit
|
|||
from PIL import Image
|
||||
from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
|
@ -29,8 +30,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
|
||||
|
||||
|
@ -51,6 +52,7 @@ from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, Pro
|
|||
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.timezone import date_today, time_now
|
||||
|
||||
from ietf.person.factories import PersonFactory
|
||||
from ietf.group.factories import GroupFactory, GroupEventFactory, RoleFactory
|
||||
|
@ -154,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')
|
||||
|
@ -213,7 +215,10 @@ class MeetingTests(BaseMeetingTestCase):
|
|||
)
|
||||
|
||||
# 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"),
|
||||
)
|
||||
|
||||
# text
|
||||
# the rest of the results don't have as nicely formatted times
|
||||
|
@ -351,7 +356,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()
|
||||
|
||||
|
@ -548,7 +553,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)
|
||||
#
|
||||
|
@ -558,16 +568,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_parse_agenda_filter_params(self):
|
||||
def _r(show=(), hide=(), showtypes=(), hidetypes=()):
|
||||
|
@ -673,7 +683,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)
|
||||
|
||||
|
@ -818,7 +828,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),
|
||||
)
|
||||
|
||||
|
@ -889,7 +901,7 @@ class EditMeetingScheduleTests(TestCase):
|
|||
# Meeting must be in the future so it can be edited
|
||||
meeting = MeetingFactory(
|
||||
type_id='ietf',
|
||||
date=datetime.date.today() + datetime.timedelta(days=7),
|
||||
date=date_today() + datetime.timedelta(days=7),
|
||||
populate_schedule=False,
|
||||
)
|
||||
meeting.schedule = ScheduleFactory(meeting=meeting)
|
||||
|
@ -902,7 +914,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)
|
||||
|
@ -997,7 +1013,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)
|
||||
|
@ -1179,12 +1195,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)
|
||||
|
@ -1218,7 +1240,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!')
|
||||
|
@ -1241,20 +1268,18 @@ 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))
|
||||
if not settings.USE_TZ:
|
||||
right_now = right_now.replace(tzinfo=None)
|
||||
def _right_now_in(tzinfo):
|
||||
right_now = timezone.now().astimezone(tzinfo)
|
||||
return right_now
|
||||
|
||||
def test_assign_session(self):
|
||||
"""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,
|
||||
|
@ -1311,10 +1336,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,
|
||||
|
@ -1446,10 +1471,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,
|
||||
|
@ -1531,7 +1556,7 @@ class EditMeetingScheduleTests(TestCase):
|
|||
"""Schedule editor should not crash when there are no timeslots"""
|
||||
meeting = MeetingFactory(
|
||||
type_id='ietf',
|
||||
date=datetime.date.today() + datetime.timedelta(days=7),
|
||||
date=date_today() + datetime.timedelta(days=7),
|
||||
populate_schedule=False,
|
||||
)
|
||||
meeting.schedule = ScheduleFactory(meeting=meeting)
|
||||
|
@ -1545,6 +1570,58 @@ class EditMeetingScheduleTests(TestCase):
|
|||
self.assertContains(r, 'No timeslots exist')
|
||||
self.assertContains(r, urlreverse('ietf.meeting.views.edit_timeslots', kwargs={'num': meeting.number}))
|
||||
|
||||
def test_editor_time_zone(self):
|
||||
"""Agenda editor should show meeting time zone"""
|
||||
time_zone = 'Etc/GMT+8'
|
||||
meeting_tz = ZoneInfo(time_zone)
|
||||
meeting = MeetingFactory(
|
||||
type_id='ietf',
|
||||
date=date_today(meeting_tz) + datetime.timedelta(days=7),
|
||||
populate_schedule=False,
|
||||
time_zone=time_zone,
|
||||
)
|
||||
meeting.schedule = ScheduleFactory(meeting=meeting)
|
||||
meeting.save()
|
||||
timeslot = TimeSlotFactory(meeting=meeting)
|
||||
ts_start = timeslot.time.astimezone(meeting_tz)
|
||||
ts_end = timeslot.end_time().astimezone(meeting_tz)
|
||||
url = urlreverse('ietf.meeting.views.edit_meeting_schedule', kwargs={'num': meeting.number})
|
||||
self.assertTrue(self.client.login(username='secretary', password='secretary+password'))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
pq = PyQuery(r.content)
|
||||
|
||||
day_header = pq('.day-flow .day-label')
|
||||
self.assertIn(ts_start.strftime('%A'), day_header.text())
|
||||
|
||||
day_swap = day_header.find('.swap-days')
|
||||
self.assertEqual(day_swap.attr('data-dayid'), ts_start.date().isoformat())
|
||||
self.assertEqual(day_swap.attr('data-start'), ts_start.date().isoformat())
|
||||
|
||||
time_label = pq('.day-flow .time-header .time-label')
|
||||
self.assertEqual(len(time_label), 1)
|
||||
# strftime() does not seem to support hours without leading 0, so do this manually
|
||||
time_label_string = f'{ts_start.hour:d}:{ts_start.minute:02d} - {ts_end.hour:d}:{ts_end.minute:02d}'
|
||||
self.assertIn(time_label_string, time_label.text())
|
||||
self.assertEqual(time_label.attr('data-start'), ts_start.astimezone(datetime.timezone.utc).isoformat())
|
||||
self.assertEqual(time_label.attr('data-end'), ts_end.astimezone(datetime.timezone.utc).isoformat())
|
||||
|
||||
ts_swap = time_label.find('.swap-timeslot-col')
|
||||
origin_label = ts_swap.attr('data-origin-label')
|
||||
# testing the exact date in origin_label is hard because Django's date filter uses
|
||||
# different month formats than Python's strftime, so just check a couple parts.
|
||||
self.assertIn(ts_start.strftime('%A'), origin_label)
|
||||
self.assertIn(f'{ts_start.hour:d}:{ts_start.minute:02d}-{ts_end.hour:d}:{ts_end.minute:02d}', origin_label)
|
||||
|
||||
timeslot_elt = pq(f'#timeslot{timeslot.pk}')
|
||||
self.assertEqual(len(timeslot_elt), 1)
|
||||
self.assertEqual(timeslot_elt.attr('data-start'), ts_start.astimezone(datetime.timezone.utc).isoformat())
|
||||
self.assertEqual(timeslot_elt.attr('data-end'), ts_end.astimezone(datetime.timezone.utc).isoformat())
|
||||
|
||||
timeslot_label = pq(f'#timeslot{timeslot.pk} .time-label')
|
||||
self.assertEqual(len(timeslot_label), 1)
|
||||
self.assertIn(time_label_string, timeslot_label.text())
|
||||
|
||||
|
||||
class EditTimeslotsTests(TestCase):
|
||||
def login(self, username='secretary'):
|
||||
|
@ -1570,7 +1647,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,
|
||||
)
|
||||
|
||||
|
@ -1602,7 +1679,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"""
|
||||
|
@ -1771,7 +1849,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)
|
||||
|
@ -1781,7 +1859,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
|
||||
|
@ -1862,17 +1940,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),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1971,10 +2053,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()
|
||||
|
@ -1991,7 +2071,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()
|
||||
|
@ -2171,8 +2251,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'])
|
||||
|
@ -2181,7 +2261,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',
|
||||
|
@ -2204,8 +2284,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'])
|
||||
|
@ -2419,8 +2499,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(
|
||||
|
@ -2450,10 +2530,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')
|
||||
|
@ -2593,7 +2673,7 @@ class ReorderSlidesTests(TestCase):
|
|||
def test_add_slides_to_session(self):
|
||||
for type_id in ('ietf','interim'):
|
||||
chair_role = RoleFactory(name_id='chair')
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90), meeting__type_id=type_id)
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=date_today() - datetime.timedelta(days=90), meeting__type_id=type_id)
|
||||
slides = DocumentFactory(type_id='slides')
|
||||
url = urlreverse('ietf.meeting.views.ajax_add_slides_to_session', kwargs={'session_id':session.pk, 'num':session.meeting.number})
|
||||
|
||||
|
@ -2609,7 +2689,7 @@ class ReorderSlidesTests(TestCase):
|
|||
self.assertEqual(r.status_code, 403)
|
||||
self.assertIn('materials cutoff', unicontent(r))
|
||||
|
||||
session.meeting.date = datetime.date.today()
|
||||
session.meeting.date = date_today()
|
||||
session.meeting.save()
|
||||
|
||||
# Invalid order
|
||||
|
@ -2696,7 +2776,7 @@ class ReorderSlidesTests(TestCase):
|
|||
def test_remove_slides_from_session(self):
|
||||
for type_id in ['ietf','interim']:
|
||||
chair_role = RoleFactory(name_id='chair')
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90), meeting__type_id=type_id)
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=date_today()-datetime.timedelta(days=90), meeting__type_id=type_id)
|
||||
slides = DocumentFactory(type_id='slides')
|
||||
url = urlreverse('ietf.meeting.views.ajax_remove_slides_from_session', kwargs={'session_id':session.pk, 'num':session.meeting.number})
|
||||
|
||||
|
@ -2712,7 +2792,7 @@ class ReorderSlidesTests(TestCase):
|
|||
self.assertEqual(r.status_code, 403)
|
||||
self.assertIn('materials cutoff', unicontent(r))
|
||||
|
||||
session.meeting.date = datetime.date.today()
|
||||
session.meeting.date = date_today()
|
||||
session.meeting.save()
|
||||
|
||||
# Invalid order
|
||||
|
@ -2807,7 +2887,7 @@ class ReorderSlidesTests(TestCase):
|
|||
|
||||
def test_reorder_slides_in_session(self):
|
||||
chair_role = RoleFactory(name_id='chair')
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90))
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=date_today() - datetime.timedelta(days=90))
|
||||
sp_list = SessionPresentationFactory.create_batch(5, document__type_id='slides', session=session)
|
||||
for num, sp in enumerate(sp_list, start=1):
|
||||
sp.order = num
|
||||
|
@ -2817,7 +2897,7 @@ class ReorderSlidesTests(TestCase):
|
|||
for type_id in ['ietf','interim']:
|
||||
|
||||
session.meeting.type_id = type_id
|
||||
session.meeting.date = datetime.date.today()-datetime.timedelta(days=90)
|
||||
session.meeting.date = date_today()-datetime.timedelta(days=90)
|
||||
session.meeting.save()
|
||||
|
||||
# Not a valid user
|
||||
|
@ -2832,7 +2912,7 @@ class ReorderSlidesTests(TestCase):
|
|||
self.assertEqual(r.status_code, 403)
|
||||
self.assertIn('materials cutoff', unicontent(r))
|
||||
|
||||
session.meeting.date = datetime.date.today()
|
||||
session.meeting.date = date_today()
|
||||
session.meeting.save()
|
||||
|
||||
# Bad index values
|
||||
|
@ -2899,7 +2979,7 @@ class ReorderSlidesTests(TestCase):
|
|||
|
||||
def test_slide_order_reconditioning(self):
|
||||
chair_role = RoleFactory(name_id='chair')
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=datetime.date.today()-datetime.timedelta(days=90))
|
||||
session = SessionFactory(group=chair_role.group, meeting__date=date_today() - datetime.timedelta(days=90))
|
||||
sp_list = SessionPresentationFactory.create_batch(5, document__type_id='slides', session=session)
|
||||
for num, sp in enumerate(sp_list, start=1):
|
||||
sp.order = 2*num
|
||||
|
@ -2918,7 +2998,7 @@ class EditTests(TestCase):
|
|||
|
||||
def test_official_record_schedule_is_read_only(self):
|
||||
def _set_date_offset_and_retrieve_page(meeting, days_offset, client):
|
||||
meeting.date = datetime.date.today() + datetime.timedelta(days=days_offset)
|
||||
meeting.date = date_today() + datetime.timedelta(days=days_offset)
|
||||
meeting.save()
|
||||
client.login(username="secretary", password="secretary+password")
|
||||
url = urlreverse("ietf.meeting.views.edit_meeting_schedule", kwargs=dict(num=meeting.number))
|
||||
|
@ -3001,7 +3081,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'))
|
||||
|
||||
|
@ -3223,7 +3305,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)
|
||||
|
@ -3265,7 +3352,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)
|
||||
|
@ -3710,7 +3802,7 @@ class SessionDetailsTests(TestCase):
|
|||
def test_session_details(self):
|
||||
|
||||
group = GroupFactory.create(type_id='wg',state_id='active')
|
||||
session = SessionFactory.create(meeting__type_id='ietf',group=group, meeting__date=datetime.date.today()+datetime.timedelta(days=90))
|
||||
session = SessionFactory.create(meeting__type_id='ietf',group=group, meeting__date=date_today() + datetime.timedelta(days=90))
|
||||
SessionPresentationFactory.create(session=session,document__type_id='draft',rev=None)
|
||||
SessionPresentationFactory.create(session=session,document__type_id='minutes')
|
||||
SessionPresentationFactory.create(session=session,document__type_id='slides')
|
||||
|
@ -3729,7 +3821,7 @@ class SessionDetailsTests(TestCase):
|
|||
session = SessionFactory.create(
|
||||
meeting__type_id='ietf',
|
||||
group=group,
|
||||
meeting__date=datetime.date.today() + datetime.timedelta(days=90),
|
||||
meeting__date=date_today() + datetime.timedelta(days=90),
|
||||
)
|
||||
session_details_url = urlreverse(
|
||||
'ietf.meeting.views.session_details',
|
||||
|
@ -3781,7 +3873,7 @@ class SessionDetailsTests(TestCase):
|
|||
def test_session_details_past_interim(self):
|
||||
group = GroupFactory.create(type_id='wg',state_id='active')
|
||||
chair = RoleFactory(name_id='chair',group=group)
|
||||
session = SessionFactory.create(meeting__type_id='interim',group=group, meeting__date=datetime.date.today()-datetime.timedelta(days=90))
|
||||
session = SessionFactory.create(meeting__type_id='interim',group=group, meeting__date=date_today() - datetime.timedelta(days=90))
|
||||
SessionPresentationFactory.create(session=session,document__type_id='draft',rev=None)
|
||||
SessionPresentationFactory.create(session=session,document__type_id='minutes')
|
||||
SessionPresentationFactory.create(session=session,document__type_id='slides')
|
||||
|
@ -3800,7 +3892,7 @@ class SessionDetailsTests(TestCase):
|
|||
group = GroupFactory.create(type_id='wg',state_id='active')
|
||||
group_chair = PersonFactory.create()
|
||||
group.role_set.create(name_id='chair',person = group_chair, email = group_chair.email())
|
||||
session = SessionFactory.create(meeting__type_id='ietf',group=group, meeting__date=datetime.date.today()+datetime.timedelta(days=90))
|
||||
session = SessionFactory.create(meeting__type_id='ietf',group=group, meeting__date=date_today() + datetime.timedelta(days=90))
|
||||
SessionPresentationFactory.create(session=session,document__type_id='draft',rev=None)
|
||||
old_draft = session.sessionpresentation_set.filter(document__type='draft').first().document
|
||||
new_draft = DocumentFactory(type_id='draft')
|
||||
|
@ -3966,7 +4058,7 @@ class InterimTests(TestCase):
|
|||
def do_interim_skip_announcement_test(self, base_session=False, extra_session=False, canceled_session=False):
|
||||
make_meeting_test_data()
|
||||
group = Group.objects.get(acronym='irg')
|
||||
date = datetime.date.today() + datetime.timedelta(days=30)
|
||||
date = date_today() + datetime.timedelta(days=30)
|
||||
meeting = make_interim_meeting(group=group, date=date, status='scheda')
|
||||
session = meeting.session_set.first()
|
||||
if base_session:
|
||||
|
@ -4036,12 +4128,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)
|
||||
|
@ -4247,7 +4337,7 @@ class InterimTests(TestCase):
|
|||
self.do_interim_approve_by_secretariat_test(extra_session=True, canceled_session=True, base_session=True)
|
||||
|
||||
def test_past(self):
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
last_week = today - datetime.timedelta(days=7)
|
||||
ietf = SessionFactory(meeting__type_id='ietf',meeting__date=last_week,group__state_id='active',group__parent=GroupFactory(state_id='active'))
|
||||
SessionFactory(meeting__type_id='interim',meeting__date=last_week,status_id='canceled',group__state_id='active',group__parent=GroupFactory(state_id='active'))
|
||||
|
@ -4266,7 +4356,7 @@ class InterimTests(TestCase):
|
|||
if querystring is not None:
|
||||
url += '?' + querystring
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
interims = dict(
|
||||
mars=add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='mars')).filter(current_status='sched').first().meeting,
|
||||
ames=add_event_info_to_session_qs(Session.objects.filter(meeting__type='interim', meeting__date__gt=today, group__acronym='ames')).filter(current_status='canceled').first().meeting,
|
||||
|
@ -4386,9 +4476,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.'
|
||||
|
@ -4457,13 +4547,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'
|
||||
|
@ -4504,16 +4595,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'
|
||||
|
@ -4570,9 +4662,9 @@ class InterimTests(TestCase):
|
|||
|
||||
def test_interim_request_multi_day_non_consecutive(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=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'
|
||||
|
@ -4607,7 +4699,7 @@ class InterimTests(TestCase):
|
|||
def test_interim_request_multi_day_cancel(self):
|
||||
"""All sessions of a multi-day interim request should be canceled"""
|
||||
length_before = len(outbox)
|
||||
date = datetime.date.today()+datetime.timedelta(days=15)
|
||||
date = date_today() + datetime.timedelta(days=15)
|
||||
|
||||
# Set up an interim request with several sessions
|
||||
num_sessions = 3
|
||||
|
@ -4630,7 +4722,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)
|
||||
|
@ -4640,14 +4732,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'
|
||||
|
@ -4789,14 +4882,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))
|
||||
|
@ -4804,7 +4897,7 @@ class InterimTests(TestCase):
|
|||
def test_interim_request_details_announcement(self):
|
||||
'''Test access to Announce / Skip Announce features'''
|
||||
make_meeting_test_data()
|
||||
date = datetime.date.today() + datetime.timedelta(days=30)
|
||||
date = date_today() + datetime.timedelta(days=30)
|
||||
group = Group.objects.get(acronym='mars')
|
||||
meeting = make_interim_meeting(group=group, date=date, status='scheda')
|
||||
url = urlreverse('ietf.meeting.views.interim_request_details',kwargs={'number':meeting.number})
|
||||
|
@ -5322,7 +5415,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)
|
||||
|
@ -5337,7 +5430,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)
|
||||
#
|
||||
|
@ -5347,8 +5444,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, })
|
||||
|
@ -5539,7 +5636,7 @@ class MaterialsTests(TestCase):
|
|||
def test_upload_bluesheets_interim_chair_access(self):
|
||||
make_meeting_test_data()
|
||||
mars = Group.objects.get(acronym='mars')
|
||||
session=SessionFactory(meeting__type_id='interim',group=mars, meeting__date = datetime.date.today())
|
||||
session=SessionFactory(meeting__type_id='interim',group=mars, meeting__date = date_today())
|
||||
url = urlreverse('ietf.meeting.views.upload_session_bluesheets',kwargs={'num':session.meeting.number,'session_id':session.id})
|
||||
self.client.login(username="marschairman", password="marschairman+password")
|
||||
r = self.client.get(url)
|
||||
|
@ -5782,7 +5879,7 @@ class MaterialsTests(TestCase):
|
|||
for type_id in ['ietf','interim']:
|
||||
session = SessionFactory(meeting__type_id=type_id)
|
||||
chair = RoleFactory(group=session.group,name_id='chair').person
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
newperson = PersonFactory()
|
||||
|
||||
session_overview_url = urlreverse('ietf.meeting.views.session_details',kwargs={'num':session.meeting.number,'acronym':session.group.acronym})
|
||||
|
@ -5830,7 +5927,7 @@ class MaterialsTests(TestCase):
|
|||
|
||||
def test_disapprove_proposed_slides(self):
|
||||
submission = SlideSubmissionFactory()
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
self.assertEqual(SlideSubmission.objects.filter(status__slug = 'pending').count(), 1)
|
||||
chair = RoleFactory(group=submission.session.group,name_id='chair').person
|
||||
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
|
||||
|
@ -5848,7 +5945,7 @@ class MaterialsTests(TestCase):
|
|||
def test_approve_proposed_slides(self):
|
||||
submission = SlideSubmissionFactory()
|
||||
session = submission.session
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
chair = RoleFactory(group=submission.session.group,name_id='chair').person
|
||||
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
|
||||
login_testing_unauthorized(self, chair.user.username, url)
|
||||
|
@ -5873,7 +5970,7 @@ class MaterialsTests(TestCase):
|
|||
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
|
||||
session1 = submission.session
|
||||
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
chair = RoleFactory(group=submission.session.group,name_id='chair').person
|
||||
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
|
||||
login_testing_unauthorized(self, chair.user.username, url)
|
||||
|
@ -5890,7 +5987,7 @@ class MaterialsTests(TestCase):
|
|||
submission = SlideSubmissionFactory(session__meeting__type_id='ietf')
|
||||
session1 = submission.session
|
||||
session2 = SessionFactory(group=submission.session.group, meeting=submission.session.meeting)
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
submission.session.meeting.importantdate_set.create(name_id='revsub',date=date_today() + datetime.timedelta(days=20))
|
||||
chair = RoleFactory(group=submission.session.group,name_id='chair').person
|
||||
url = urlreverse('ietf.meeting.views.approve_proposed_slides', kwargs={'slidesubmission_id':submission.pk,'num':submission.session.meeting.number})
|
||||
login_testing_unauthorized(self, chair.user.username, url)
|
||||
|
@ -5904,7 +6001,7 @@ class MaterialsTests(TestCase):
|
|||
def test_submit_and_approve_multiple_versions(self):
|
||||
session = SessionFactory(meeting__type_id='ietf')
|
||||
chair = RoleFactory(group=session.group,name_id='chair').person
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=datetime.date.today()+datetime.timedelta(days=20))
|
||||
session.meeting.importantdate_set.create(name_id='revsub',date=date_today()+datetime.timedelta(days=20))
|
||||
newperson = PersonFactory()
|
||||
|
||||
propose_url = urlreverse('ietf.meeting.views.propose_session_slides', kwargs={'session_id':session.pk, 'num': session.meeting.number})
|
||||
|
@ -6285,8 +6382,8 @@ class HasMeetingsTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
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)
|
||||
date = date_today() + datetime.timedelta(days=30+meeting_count)
|
||||
time = time_now().replace(microsecond=0,second=0)
|
||||
remote_instructions = 'Use webex'
|
||||
agenda = 'Intro. Slides. Discuss.'
|
||||
agenda_note = 'On second level'
|
||||
|
@ -6382,7 +6479,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)
|
||||
|
@ -6398,7 +6495,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',
|
||||
|
@ -6419,7 +6516,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',
|
||||
|
@ -7145,7 +7242,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'))
|
||||
|
@ -7254,7 +7351,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()
|
||||
|
|
|
@ -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
|
||||
|
@ -26,6 +28,7 @@ from ietf.person.models import Person
|
|||
from ietf.secr.proceedings.proc_utils import import_audio_files
|
||||
from ietf.utils.html import sanitize_document
|
||||
from ietf.utils.log import log
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
def session_time_for_sorting(session, use_meeting_date):
|
||||
|
@ -33,13 +36,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()
|
||||
|
@ -64,12 +71,12 @@ def group_sessions(sessions):
|
|||
|
||||
sessions = sorted(sessions,key=lambda s:s.time)
|
||||
|
||||
today = datetime.date.today()
|
||||
future = []
|
||||
in_progress = []
|
||||
recent = []
|
||||
past = []
|
||||
for s in sessions:
|
||||
today = date_today(s.meeting.tz())
|
||||
if s.meeting.date > today:
|
||||
future.append(s)
|
||||
elif s.meeting.end_date() >= today:
|
||||
|
@ -101,7 +108,7 @@ def get_upcoming_manageable_sessions(user):
|
|||
# .filter(date__gte=today - F('days')), but unfortunately, it
|
||||
# doesn't work correctly with Django 1.11 and MySQL/SQLite
|
||||
|
||||
today = datetime.date.today()
|
||||
today = date_today()
|
||||
|
||||
candidate_sessions = add_event_info_to_session_qs(
|
||||
Session.objects.filter(meeting__date__gte=today - datetime.timedelta(days=15))
|
||||
|
@ -158,7 +165,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 +334,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 +525,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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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).',
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import datetime
|
||||
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.utils import timezone
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -14,10 +15,13 @@ from ietf.message.utils import send_scheduled_message_from_send_queue
|
|||
from ietf.person.models import Person
|
||||
from ietf.utils.mail import outbox, send_mail_text, send_mail_message, get_payload_text
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.timezone import date_today
|
||||
|
||||
|
||||
|
||||
class MessageTests(TestCase):
|
||||
def test_message_view(self):
|
||||
nomcom = GroupFactory(name="nomcom%s" % datetime.date.today().year, type_id="nomcom")
|
||||
nomcom = GroupFactory(name="nomcom%s" % date_today().year, type_id="nomcom")
|
||||
msg = Message.objects.create(
|
||||
by=Person.objects.get(name="(System)"),
|
||||
subject="This is a test",
|
||||
|
@ -87,7 +91,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 +117,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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -16116,7 +16116,7 @@
|
|||
"fields": {
|
||||
"command": "xym",
|
||||
"switch": "--version",
|
||||
"time": "2022-10-12T00:09:36.629",
|
||||
"time": "2022-10-12T00:09:36.629Z",
|
||||
"used": true,
|
||||
"version": "xym 0.5"
|
||||
},
|
||||
|
@ -16127,7 +16127,7 @@
|
|||
"fields": {
|
||||
"command": "pyang",
|
||||
"switch": "--version",
|
||||
"time": "2022-10-12T00:09:36.945",
|
||||
"time": "2022-10-12T00:09:36.945Z",
|
||||
"used": true,
|
||||
"version": "pyang 2.5.3"
|
||||
},
|
||||
|
@ -16138,7 +16138,7 @@
|
|||
"fields": {
|
||||
"command": "yanglint",
|
||||
"switch": "--version",
|
||||
"time": "2022-10-12T00:09:36.957",
|
||||
"time": "2022-10-12T00:09:36.957Z",
|
||||
"used": true,
|
||||
"version": "yanglint SO 1.9.2"
|
||||
},
|
||||
|
@ -16149,7 +16149,7 @@
|
|||
"fields": {
|
||||
"command": "xml2rfc",
|
||||
"switch": "--version",
|
||||
"time": "2022-10-12T00:09:37.890",
|
||||
"time": "2022-10-12T00:09:37.890Z",
|
||||
"used": true,
|
||||
"version": "xml2rfc 3.14.2"
|
||||
},
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue