feat: make the application tzaware
This commit is contained in:
commit
5b65f255c6
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
|
||||
|
|
5
.github/workflows/ci-run-tests.yml
vendored
5
.github/workflows/ci-run-tests.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'feat/tzaware'
|
||||
paths:
|
||||
- 'ietf/**'
|
||||
- 'requirements.txt'
|
||||
|
@ -42,6 +43,10 @@ jobs:
|
|||
run: |
|
||||
echo "Running checks..."
|
||||
./ietf/manage.py check
|
||||
echo "Running migrations with USE_TZ=False..."
|
||||
./ietf/manage.py migrate || true
|
||||
echo "USE_TZ = True" >> ./ietf/settings_local.py
|
||||
echo "Running migrations with USE_TZ=True..."
|
||||
./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'), }, }
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,31 @@ chmod +x ./app-create-dirs.sh
|
|||
./app-create-dirs.sh
|
||||
echo "Running Datatracker checks..."
|
||||
./ietf/manage.py check
|
||||
echo "Running Datatracker migrations..."
|
||||
./ietf/manage.py migrate
|
||||
|
||||
# Migrate, adjusting to what the current state of the underlying database might be:
|
||||
WORKSPACEDIR=.
|
||||
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
|
||||
echo "Running Datatracker migrations with USE_TZ = False..."
|
||||
# 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
|
||||
echo "Running Datatracker migrations with USE_TZ = True..."
|
||||
/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
|
||||
echo "Running Datatracker migrations..."
|
||||
/usr/local/bin/python $WORKSPACEDIR/ietf/manage.py migrate --settings=settings_local
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Starting Datatracker..."
|
||||
./ietf/manage.py runserver 0.0.0.0:8000 --settings=settings_local
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,9 +27,11 @@
|
|||
This project includes a devcontainer configuration which automates the setup of the development environment with all the required dependencies.
|
||||
|
||||
### Initial Setup
|
||||
|
||||
|
||||
1. Launch [VS Code](https://code.visualstudio.com/)
|
||||
2. Under the **Extensions** tab, ensure you have the **Remote - Containers** ([ms-vscode-remote.remote-containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)) extension installed. On Windows, you also need the **Remote - WSL** ([ms-vscode-remote.remote-wsl](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl)) extension to take advantage of the WSL 2 *(Windows Subsystem for Linux)* native integration.
|
||||
2. Under the **Extensions** tab, ensure you have the **Remote - Containers** ([ms-vscode-remote.remote-containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)) extension installed.
|
||||
* On Linux, note that the Snap installation of VS Code is [incompatible with this plugin](https://code.visualstudio.com/docs/devcontainers/containers#_system-requirements:~:text=snap%20package%20is%20not%20supported).
|
||||
* On Windows, you also need the **Remote - WSL** ([ms-vscode-remote.remote-wsl](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl)) extension to take advantage of the WSL 2 *(Windows Subsystem for Linux)* native integration.
|
||||
2. Open the top-level directory of the datatracker code you fetched above.
|
||||
3. A prompt inviting you to reopen the project in containers will appear in the bottom-right corner. Click the **Reopen in Container** button. If you missed the prompt, you can press `F1`, start typing `reopen in container` task and launch it.
|
||||
4. VS Code will relaunch in the dev environment and create the containers automatically.
|
||||
|
@ -44,8 +46,8 @@ You can also open the datatracker project folder and click the **Reopen in conta
|
|||
### Usage
|
||||
|
||||
- Under the **Run and Debug** tab, you can run the server with the debugger attached using **Run Server** (F5). Once the server is ready to accept connections, you'll be prompted to open in a browser. You can also open [http://localhost:8000](http://localhost:8000) in a browser.
|
||||
|
||||
> An alternate profile **Run Server with Debug Toolbar** is also available from the dropdown menu, which displays various tools
|
||||
|
||||
> An alternate profile **Run Server with Debug Toolbar** is also available from the dropdown menu, which displays various tools
|
||||
on top of the webpage. However, note that this configuration has a significant performance impact.
|
||||
|
||||
To add a **Breakpoint**, simply click to the left of the line gutter you wish to stop at. You can also add **Conditional Breakpoints** and **Logpoint** by right-clicking at the same location.
|
||||
|
@ -88,7 +90,7 @@ You can also open the datatracker project folder and click the **Reopen in conta
|
|||
cd docker
|
||||
./run
|
||||
```
|
||||
|
||||
|
||||
> Note that you can pass the `-r` flag to `./run` to force a rebuild of the containers. This is useful if you switched branches and that the existing containers still contain configurations from the old branch. You should also use this if you don't regularly keep up with main and your containers reflect a much older version of the branch.
|
||||
|
||||
On Windows *(using Powershell)*:
|
||||
|
@ -157,7 +159,7 @@ cd docker
|
|||
On Windows:
|
||||
```sh
|
||||
docker compose down -v --rmi all
|
||||
docker image prune
|
||||
docker image prune
|
||||
```
|
||||
|
||||
### Accessing MariaDB Port
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -248,7 +249,7 @@ def urlize_ietf_docs(string, autoescape=None):
|
|||
flags=re.IGNORECASE | re.ASCII,
|
||||
)
|
||||
string = re.sub(
|
||||
r"\b(?<![/\-:=#\"\'])((RFC|BCP|STD|FYI)\s*0*(\d+))\b",
|
||||
r"\b(?<![/\-:=#\"\'])((RFC|BCP|STD|FYI) *\n? *0*(\d+))\b",
|
||||
link_other_doc_match,
|
||||
string,
|
||||
flags=re.IGNORECASE | re.ASCII,
|
||||
|
@ -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 })
|
||||
|
@ -413,7 +414,7 @@ class ReviewTests(TestCase):
|
|||
login_testing_unauthorized(self, "reviewsecretary", reject_url)
|
||||
r = self.client.get(reject_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertContains(r, assignment.reviewer.person.name)
|
||||
self.assertContains(r, escape(assignment.reviewer.person.name))
|
||||
self.assertNotContains(r, 'can not be rejected')
|
||||
self.assertContains(r, '<button type="submit"')
|
||||
|
||||
|
@ -441,7 +442,7 @@ class ReviewTests(TestCase):
|
|||
|
||||
r = self.client.get(reject_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertContains(r, assignment.reviewer.person.name)
|
||||
self.assertContains(r, escape(assignment.reviewer.person.name))
|
||||
self.assertContains(r, 'can not be rejected')
|
||||
self.assertNotContains(r, '<button type="submit"')
|
||||
|
||||
|
@ -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(datetime.timezone.utc)
|
||||
|
||||
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 })
|
||||
|
@ -1153,4 +1154,4 @@ class ReviewTests(TestCase):
|
|||
ReviewWish.objects.create(person=reviewer, doc=doc, team=team)
|
||||
r = self.client.post(url + '?next=http://example.com')
|
||||
self.assertRedirects(r, doc.get_absolute_url(), fetch_redirect_response=False)
|
||||
self.assertFalse(ReviewWish.objects.all())
|
||||
self.assertFalse(ReviewWish.objects.all())
|
||||
|
|
|
@ -5,6 +5,7 @@ import debug # pyflakes:ignore
|
|||
from unittest.mock import patch
|
||||
|
||||
from django.db import IntegrityError
|
||||
from django.utils import timezone
|
||||
|
||||
from ietf.group.factories import GroupFactory, RoleFactory
|
||||
from ietf.name.models import DocTagName
|
||||
|
@ -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())
|
||||
right_now = timezone.now()
|
||||
self.assertLess(doc.documentactionholder_set.get(person=self.ad).time_added, right_now)
|
||||
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.assertGreaterEqual(doc.documentactionholder_set.get(person=self.ad).time_added, right_now)
|
||||
|
||||
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
|
||||
|
|
|
@ -7,9 +7,11 @@ import datetime
|
|||
|
||||
from pyquery import PyQuery
|
||||
from urllib.parse import quote, urlparse
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
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 +29,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 +575,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',' '))
|
||||
|
@ -593,14 +596,17 @@ I would like to revoke this declaration.
|
|||
r = self.client.post(url, data )
|
||||
self.assertEqual(r.status_code,302)
|
||||
self.assertEqual(len(outbox),2)
|
||||
self.assertIn('Secretariat on '+ipr.get_latest_event_submitted().time.strftime("%Y-%m-%d"), get_payload_text(outbox[1]).replace('\n',' '))
|
||||
self.assertIn(
|
||||
'Secretariat on ' + ipr.get_latest_event_submitted().time.astimezone(ZoneInfo(settings.TIME_ZONE)).strftime("%Y-%m-%d"),
|
||||
get_payload_text(outbox[1]).replace('\n',' '),
|
||||
)
|
||||
self.assertIn(f'{settings.IDTRACKER_BASE_URL}{urlreverse("ietf.ipr.views.showlist")}', get_payload_text(outbox[1]).replace('\n',' '))
|
||||
|
||||
def send_ipr_email_helper(self):
|
||||
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 +646,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 +656,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 +670,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 +686,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)
|
||||
|
@ -506,7 +510,7 @@ class OutgoingLiaisonForm(LiaisonModelForm):
|
|||
else:
|
||||
email = self.person.email_address()
|
||||
self.fields['from_contact'].initial = email
|
||||
self.fields['from_contact'].widget.attrs['disabled'] = True
|
||||
self.fields['from_contact'].widget.attrs['readonly'] = True
|
||||
|
||||
def set_to_fields(self):
|
||||
'''Set to_groups and to_contacts options and initial value based on user
|
||||
|
@ -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(datetime.timezone.utc)
|
||||
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(datetime.timezone.utc)
|
||||
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(datetime.timezone.utc)
|
||||
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(DEADLINE_TZINFO)+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",
|
||||
|
|
|
@ -773,6 +773,13 @@ class SessionEditForm(SessionDetailsForm):
|
|||
super().__init__(instance=instance, group=instance.group, *args, **kwargs)
|
||||
|
||||
|
||||
class SessionCancelForm(forms.Form):
|
||||
confirmed = forms.BooleanField(
|
||||
label='Cancel session?',
|
||||
help_text='Confirm that you want to cancel this session.',
|
||||
)
|
||||
|
||||
|
||||
class SessionDetailsInlineFormSet(forms.BaseInlineFormSet):
|
||||
def __init__(self, group, meeting, queryset=None, *args, **kwargs):
|
||||
self._meeting = meeting
|
||||
|
|
|
@ -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("Test 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("Test 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
|
||||
|
@ -177,6 +181,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1584, # grow
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -197,6 +202,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2166, # sidrops
|
||||
attendees=62,
|
||||
agenda_note="",
|
||||
|
@ -221,6 +227,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1452, # dnsop
|
||||
attendees=160,
|
||||
agenda_note="",
|
||||
|
@ -232,6 +239,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1452, # dnsop
|
||||
attendees=160,
|
||||
agenda_note="",
|
||||
|
@ -260,6 +268,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1895, # dnssd
|
||||
attendees=75,
|
||||
agenda_note="Joint with HOMENET",
|
||||
|
@ -271,6 +280,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1895, # dnssd
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -305,6 +315,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2216, # lsvr
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -331,6 +342,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2161, # quic
|
||||
attendees=200,
|
||||
agenda_note="",
|
||||
|
@ -343,6 +355,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2161, # quic
|
||||
attendees=200,
|
||||
agenda_note="",
|
||||
|
@ -399,6 +412,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1463, # tsvwg
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -410,6 +424,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1463, # tsvwg
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -449,6 +464,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1524, # ccamp
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -473,6 +489,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1763, # dispatch
|
||||
attendees=80,
|
||||
agenda_note="Joint with ARTAREA",
|
||||
|
@ -514,6 +531,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1578, # v6ops
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -539,6 +557,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1899, # stir
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -576,6 +595,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1397, # pim
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -609,6 +629,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2211, # suit
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -646,6 +667,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2234, # coinrg
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -669,6 +691,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1883, # nwcrg
|
||||
attendees=35,
|
||||
agenda_note="",
|
||||
|
@ -696,6 +719,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1620, # tcpm
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -721,6 +745,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1089, # ippm
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -757,6 +782,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2164, # lamps
|
||||
attendees=45,
|
||||
agenda_note="",
|
||||
|
@ -792,6 +818,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1718, # httpbis
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -803,6 +830,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1718, # httpbis
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -826,6 +854,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1924, # taps
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -873,6 +902,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1958, # dprive
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -897,6 +927,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2194, # teep
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -908,6 +939,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2194, # teep
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -934,6 +966,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=42, # iccrg
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -959,6 +992,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2231, # rats
|
||||
attendees=40,
|
||||
agenda_note="",
|
||||
|
@ -970,6 +1004,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2231, # rats
|
||||
attendees=40,
|
||||
agenda_note="",
|
||||
|
@ -998,6 +1033,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2219, # secdispatch
|
||||
attendees=200,
|
||||
agenda_note="",
|
||||
|
@ -1033,6 +1069,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1118, # mboned
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -1061,6 +1098,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1960, # bess
|
||||
attendees=90,
|
||||
agenda_note="",
|
||||
|
@ -1072,6 +1110,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1960, # bess
|
||||
attendees=90,
|
||||
agenda_note="",
|
||||
|
@ -1096,6 +1135,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1803, # homenet
|
||||
attendees=60,
|
||||
agenda_note="Joint with DNSSD",
|
||||
|
@ -1131,6 +1171,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1995, # acme
|
||||
attendees=70,
|
||||
agenda_note="",
|
||||
|
@ -1154,6 +1195,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2150, # babel
|
||||
attendees=21,
|
||||
agenda_note="",
|
||||
|
@ -1178,6 +1220,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2215, # lsr
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -1189,6 +1232,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2215, # lsr
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -1212,6 +1256,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1575, # netconf
|
||||
attendees=65,
|
||||
agenda_note="",
|
||||
|
@ -1238,6 +1283,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2145, # maprg
|
||||
attendees=200,
|
||||
agenda_note="",
|
||||
|
@ -1277,6 +1323,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1897, # 6lo
|
||||
attendees=55,
|
||||
agenda_note="",
|
||||
|
@ -1315,6 +1362,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1723, # 6man
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1326,6 +1374,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1723, # 6man
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1351,6 +1400,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1853, # irtfopen
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -1391,6 +1441,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2148, # lpwan
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -1421,6 +1472,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1903, # 6tisch
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -1451,6 +1503,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1751, # lisp
|
||||
attendees=45,
|
||||
agenda_note="",
|
||||
|
@ -1486,6 +1539,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1764, # mptcp
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -1510,6 +1564,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1730, # roll
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -1521,6 +1576,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1730, # roll
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -1551,6 +1607,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1187, # saag
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -1596,6 +1653,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1140, # mpls
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -1630,6 +1688,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1630, # pce
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -1664,6 +1723,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1962, # detnet
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -1696,6 +1756,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1744, # alto
|
||||
attendees=25,
|
||||
agenda_note="",
|
||||
|
@ -1716,6 +1777,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1968, # gaia
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -1732,6 +1794,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2233, # git
|
||||
attendees=65,
|
||||
agenda_note="",
|
||||
|
@ -1779,6 +1842,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2227, # pearg
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1809,6 +1873,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1326, # tls
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1820,6 +1885,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1326, # tls
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1854,6 +1920,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1679, # tsvarea
|
||||
attendees=120,
|
||||
agenda_note="",
|
||||
|
@ -1888,6 +1955,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2169, # secevent
|
||||
attendees=None,
|
||||
agenda_note="",
|
||||
|
@ -1900,6 +1968,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2209, # dinrg
|
||||
attendees=90,
|
||||
agenda_note="",
|
||||
|
@ -1921,6 +1990,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1714, # opsawg
|
||||
attendees=70,
|
||||
agenda_note="Combined OpsAWG / OpsAREA",
|
||||
|
@ -1946,6 +2016,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1812, # avtcore
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -1978,6 +2049,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1628, # bfd
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2007,6 +2079,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1674, # emu
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2045,6 +2118,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1988, # hrpc
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -2068,6 +2142,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2002, # cose
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -2095,6 +2170,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1996, # dots
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2119,6 +2195,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1041, # idr
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -2130,6 +2207,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1041, # idr
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -2158,6 +2236,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=945, # bmwg
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -2185,6 +2264,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2202, # panrg
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -2219,6 +2299,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=38, # nmrg
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -2230,6 +2311,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=38, # nmrg
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -2252,6 +2334,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1665, # intarea
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -2279,6 +2362,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1817, # lwig
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2312,6 +2396,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1619, # rtgwg
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -2323,6 +2408,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1619, # rtgwg
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -2344,6 +2430,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2214, # rift
|
||||
attendees=70,
|
||||
agenda_note="",
|
||||
|
@ -2370,6 +2457,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1905, # spring
|
||||
attendees=140,
|
||||
agenda_note="",
|
||||
|
@ -2381,6 +2469,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1905, # spring
|
||||
attendees=140,
|
||||
agenda_note="",
|
||||
|
@ -2401,6 +2490,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=988, # dhc
|
||||
attendees=25,
|
||||
agenda_note="",
|
||||
|
@ -2429,6 +2519,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1740, # ipsecme
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2456,6 +2547,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2146, # regext
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2480,6 +2572,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=31, # cfrg
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -2528,6 +2621,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1849, # icnrg
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -2555,6 +2649,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1847, # dmm
|
||||
attendees=45,
|
||||
agenda_note="",
|
||||
|
@ -2574,6 +2669,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2176, # jmap
|
||||
attendees=20,
|
||||
agenda_note="",
|
||||
|
@ -2608,6 +2704,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1953, # calext
|
||||
attendees=15,
|
||||
agenda_note="1330 - 1430",
|
||||
|
@ -2631,6 +2728,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2175, # cbor
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2675,6 +2773,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2242, # rum
|
||||
attendees=20,
|
||||
agenda_note="",
|
||||
|
@ -2699,6 +2798,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2220, # mls
|
||||
attendees=125,
|
||||
agenda_note="",
|
||||
|
@ -2728,6 +2828,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2205, # extra
|
||||
attendees=15,
|
||||
agenda_note="1430 - 1530",
|
||||
|
@ -2747,6 +2848,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1132, # manet
|
||||
attendees=20,
|
||||
agenda_note="",
|
||||
|
@ -2766,6 +2868,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1838, # rmcat
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -2794,6 +2897,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1956, # anima
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -2805,6 +2909,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1956, # anima
|
||||
attendees=80,
|
||||
agenda_note="",
|
||||
|
@ -2841,6 +2946,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1642, # ntp
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -2866,6 +2972,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1748, # oauth
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2877,6 +2984,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1748, # oauth
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -2904,6 +3012,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1869, # sacm
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -2935,6 +3044,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1964, # bier
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -2956,6 +3066,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1985, # teas
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -2983,6 +3094,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1638, # netmod
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -2994,6 +3106,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1638, # netmod
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3015,6 +3128,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1789, # core
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -3027,6 +3141,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1789, # core
|
||||
attendees=60,
|
||||
agenda_note="",
|
||||
|
@ -3075,6 +3190,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1991, # t2trg
|
||||
attendees=90,
|
||||
agenda_note="",
|
||||
|
@ -3122,6 +3238,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1921, # ace
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -3156,6 +3273,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2254, # wpack
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -3178,6 +3296,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2260, # webtrans
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3199,6 +3318,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2253, # abcd
|
||||
attendees=200,
|
||||
agenda_note="",
|
||||
|
@ -3215,6 +3335,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2255, # tmrid
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -3235,6 +3356,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2248, # mops
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3262,6 +3384,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2256, # raw
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3284,6 +3407,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2257, # txauth
|
||||
attendees=150,
|
||||
agenda_note="",
|
||||
|
@ -3322,6 +3446,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2252, # gendispatch
|
||||
attendees=75,
|
||||
agenda_note="",
|
||||
|
@ -3356,6 +3481,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1966, # dtn
|
||||
attendees=30,
|
||||
agenda_note="",
|
||||
|
@ -3376,6 +3502,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2249, # lake
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3397,6 +3524,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2258, # mathmesh
|
||||
attendees=100,
|
||||
agenda_note="",
|
||||
|
@ -3412,6 +3540,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=2228, # qirg
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
@ -3433,6 +3562,7 @@ class Command(BaseCommand):
|
|||
s = Session.objects.create(
|
||||
meeting=m,
|
||||
type_id="regular",
|
||||
purpose_id="regular",
|
||||
group_id=1840, # nvo3
|
||||
attendees=50,
|
||||
agenda_note="",
|
||||
|
|
|
@ -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,
|
||||
|
@ -59,16 +60,6 @@ for name in pytz.common_timezones:
|
|||
timezones.sort()
|
||||
|
||||
|
||||
# this is used in models to format dates, as the built-in json serializer
|
||||
# can not deal with them, and the django provided serializer is inaccessible.
|
||||
from django.utils import datetime_safe
|
||||
DATE_FORMAT = "%Y-%m-%d"
|
||||
TIME_FORMAT = "%H:%M:%S"
|
||||
|
||||
def fmt_date(o):
|
||||
d = datetime_safe.new_date(o)
|
||||
return d.strftime(DATE_FORMAT)
|
||||
|
||||
class Meeting(models.Model):
|
||||
# number is either the number for IETF meetings, or some other
|
||||
# identifier for interim meetings/IESG retreats/liaison summits/...
|
||||
|
@ -85,7 +76,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 +128,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 +335,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 +343,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 +367,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 +392,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 +620,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 +733,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 +1160,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 +1287,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=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'
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -129,6 +129,7 @@ urlpatterns = [
|
|||
url(r'^upcoming\.ics/?$', views.upcoming_ical),
|
||||
url(r'^upcoming\.json/?$', views.upcoming_json),
|
||||
url(r'^session/(?P<session_id>\d+)/agenda_materials$', views.session_materials),
|
||||
url(r'^session/(?P<session_id>\d+)/cancel/?', views.cancel_session),
|
||||
url(r'^session/(?P<session_id>\d+)/edit/?', views.edit_session),
|
||||
# Then patterns from more specific to less
|
||||
url(r'^(?P<num>interim-[a-z0-9-]+)/', include(type_interim_patterns)),
|
||||
|
|
|
@ -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).',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue