diff --git a/doc/development/api/customview.rst b/doc/development/api/customview.rst index 35c240036..8b11f0c5c 100644 --- a/doc/development/api/customview.rst +++ b/doc/development/api/customview.rst @@ -18,13 +18,13 @@ If you want to add a custom view to the control area of an event, just register .. code-block:: python - from django.conf.urls import url + from django.urls import re_path from . import views urlpatterns = [ - url(r'^control/event/(?P[^/]+)/(?P[^/]+)/mypluginname/', - views.admin_view, name='backend'), + re_path(r'^control/event/(?P[^/]+)/(?P[^/]+)/mypluginname/', + views.admin_view, name='backend'), ] It is required that your URL parameters are called ``organizer`` and ``event``. If you want to diff --git a/doc/development/implementation/urlconfig.rst b/doc/development/implementation/urlconfig.rst index 78dceb8cc..3d90e181b 100644 --- a/doc/development/implementation/urlconfig.rst +++ b/doc/development/implementation/urlconfig.rst @@ -35,13 +35,13 @@ automatically and should be provided by any plugin that provides any view. A very basic example that provides one view in the admin panel and one view in the frontend could look like this:: - from django.conf.urls import url + from django.urls import re_path from . import views urlpatterns = [ - url(r'^control/event/(?P[^/]+)/(?P[^/]+)/mypluginname/', - views.AdminView.as_view(), name='backend'), + re_path(r'^control/event/(?P[^/]+)/(?P[^/]+)/mypluginname/', + views.AdminView.as_view(), name='backend'), ] event_patterns = [ diff --git a/pyproject.toml b/pyproject.toml index bb1ce031f..2d22e2160 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Framework :: Django :: 3.2", + "Framework :: Django :: 4.1", ] dependencies = [ @@ -36,7 +36,7 @@ dependencies = [ "css-inline==0.8.*", "defusedcsv>=1.1.0", "dj-static", - "Django==3.2.*,>=3.2.18", + "Django==4.1.*", "django-bootstrap3==23.1.*", "django-compressor==4.3.*", "django-countries==7.5.*", @@ -87,6 +87,7 @@ dependencies = [ "python-dateutil==2.8.*", "python-u2flib-server==4.*", "pytz", + "pytz-deprecation-shim==0.1.*", "pyuca", "qrcode==7.4.*", "redis==4.5.*,>=4.5.4", diff --git a/src/pretix/_base_settings.py b/src/pretix/_base_settings.py index cebb8bd32..7e3645df9 100644 --- a/src/pretix/_base_settings.py +++ b/src/pretix/_base_settings.py @@ -30,7 +30,6 @@ from django.utils.translation import gettext_lazy as _ # NOQA BASE_DIR = os.path.dirname(os.path.dirname(__file__)) USE_I18N = True -USE_L10N = True USE_TZ = True INSTALLED_APPS = [ @@ -68,6 +67,7 @@ INSTALLED_APPS = [ 'oauth2_provider', 'phonenumber_field', 'statici18n', + 'django.forms', # after pretix.base for overrides ] FORMAT_MODULE_PATH = [ @@ -180,6 +180,8 @@ TEMPLATES = [ }, ] +FORM_RENDERER = "django.forms.renderers.TemplatesSetting" + STATIC_ROOT = os.path.join(os.path.dirname(__file__), 'static.dist') STATICFILES_FINDERS = ( diff --git a/src/pretix/api/serializers/order.py b/src/pretix/api/serializers/order.py index 5c73f9cb0..bf6b7deed 100644 --- a/src/pretix/api/serializers/order.py +++ b/src/pretix/api/serializers/order.py @@ -535,8 +535,9 @@ class OrderPaymentTypeField(serializers.Field): # TODO: Remove after pretix 2.2 def to_representation(self, instance: Order): t = None - for p in instance.payments.all(): - t = p.provider + if instance.pk: + for p in instance.payments.all(): + t = p.provider return t @@ -544,10 +545,10 @@ class OrderPaymentDateField(serializers.DateField): # TODO: Remove after pretix 2.2 def to_representation(self, instance: Order): t = None - for p in instance.payments.all(): - t = p.payment_date or t + if instance.pk: + for p in instance.payments.all(): + t = p.payment_date or t if t: - return super().to_representation(t.date()) @@ -1363,6 +1364,7 @@ class OrderCreateSerializer(I18nAwareModelSerializer): answers.append(answ) pos.answers = answers pos.pseudonymization_id = "PREVIEW" + pos.checkins = [] pos_map[pos.positionid] = pos else: if pos.voucher: @@ -1459,6 +1461,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer): if simulate: order.fees = fees order.positions = pos_map.values() + order.payments = [] + order.refunds = [] return order # ignore payments else: order.save(update_fields=['total']) diff --git a/src/pretix/api/urls.py b/src/pretix/api/urls.py index 4ac2f05a0..b86ded3a2 100644 --- a/src/pretix/api/urls.py +++ b/src/pretix/api/urls.py @@ -35,8 +35,7 @@ import importlib from django.apps import apps -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from rest_framework import routers from pretix.api.views import cart diff --git a/src/pretix/api/views/order.py b/src/pretix/api/views/order.py index bd723ecbc..c99df2598 100644 --- a/src/pretix/api/views/order.py +++ b/src/pretix/api/views/order.py @@ -23,9 +23,9 @@ import datetime import mimetypes import os from decimal import Decimal +from zoneinfo import ZoneInfo import django_filters -import pytz from django.db import transaction from django.db.models import ( Exists, F, OuterRef, Prefetch, Q, Subquery, prefetch_related_objects, @@ -612,7 +612,7 @@ class OrderViewSet(viewsets.ModelViewSet): status=status.HTTP_400_BAD_REQUEST ) - tz = pytz.timezone(self.request.event.settings.timezone) + tz = ZoneInfo(self.request.event.settings.timezone) new_date = make_aware(datetime.datetime.combine( new_date, datetime.time(hour=23, minute=59, second=59) diff --git a/src/pretix/base/exporter.py b/src/pretix/base/exporter.py index e10385779..2b911f05b 100644 --- a/src/pretix/base/exporter.py +++ b/src/pretix/base/exporter.py @@ -37,8 +37,8 @@ import tempfile from collections import OrderedDict, namedtuple from decimal import Decimal from typing import Optional, Tuple +from zoneinfo import ZoneInfo -import pytz from defusedcsv import csv from django import forms from django.conf import settings @@ -68,7 +68,7 @@ class BaseExporter: self.events = event self.event = None e = self.events.first() - self.timezone = e.timezone if e else pytz.timezone(settings.TIME_ZONE) + self.timezone = e.timezone if e else ZoneInfo(settings.TIME_ZONE) else: self.events = Event.objects.filter(pk=event.pk) self.timezone = event.timezone diff --git a/src/pretix/base/exporters/orderlist.py b/src/pretix/base/exporters/orderlist.py index 2f5a7e22f..235bf06a5 100644 --- a/src/pretix/base/exporters/orderlist.py +++ b/src/pretix/base/exporters/orderlist.py @@ -34,8 +34,8 @@ from collections import OrderedDict from decimal import Decimal +from zoneinfo import ZoneInfo -import pytz from django import forms from django.db.models import ( Case, CharField, Count, DateTimeField, F, IntegerField, Max, Min, OuterRef, @@ -326,7 +326,7 @@ class OrderListExporter(MultiSheetListExporter): yield self.ProgressSetTotal(total=qs.count()) for order in qs.order_by('datetime').iterator(): - tz = pytz.timezone(self.event_object_cache[order.event_id].settings.timezone) + tz = ZoneInfo(self.event_object_cache[order.event_id].settings.timezone) row = [ self.event_object_cache[order.event_id].slug, @@ -459,7 +459,7 @@ class OrderListExporter(MultiSheetListExporter): yield self.ProgressSetTotal(total=qs.count()) for op in qs.order_by('order__datetime').iterator(): order = op.order - tz = pytz.timezone(order.event.settings.timezone) + tz = ZoneInfo(order.event.settings.timezone) row = [ self.event_object_cache[order.event_id].slug, order.code, @@ -631,7 +631,7 @@ class OrderListExporter(MultiSheetListExporter): for op in ops: order = op.order - tz = pytz.timezone(self.event_object_cache[order.event_id].settings.timezone) + tz = ZoneInfo(self.event_object_cache[order.event_id].settings.timezone) row = [ self.event_object_cache[order.event_id].slug, order.code, @@ -1024,7 +1024,7 @@ class PaymentListExporter(ListExporter): yield self.ProgressSetTotal(total=len(objs)) for obj in objs: - tz = pytz.timezone(obj.order.event.settings.timezone) + tz = ZoneInfo(obj.order.event.settings.timezone) if isinstance(obj, OrderPayment) and obj.payment_date: d2 = obj.payment_date.astimezone(tz).date().strftime('%Y-%m-%d') elif isinstance(obj, OrderRefund) and obj.execution_date: @@ -1203,7 +1203,7 @@ class GiftcardRedemptionListExporter(ListExporter): yield headers for obj in objs: - tz = pytz.timezone(obj.order.event.settings.timezone) + tz = ZoneInfo(obj.order.event.settings.timezone) gc = GiftCard.objects.get(pk=obj.info_data.get('gift_card')) row = [ obj.order.event.slug, diff --git a/src/pretix/base/exporters/waitinglist.py b/src/pretix/base/exporters/waitinglist.py index bd71da88a..19d015e16 100644 --- a/src/pretix/base/exporters/waitinglist.py +++ b/src/pretix/base/exporters/waitinglist.py @@ -20,8 +20,8 @@ # . # from collections import OrderedDict +from zoneinfo import ZoneInfo -import pytz from django import forms from django.db.models import F, Q from django.dispatch import receiver @@ -137,7 +137,7 @@ class WaitingListExporter(ListExporter): # which event should be used to output dates in columns "Start date" and "End date" event_for_date_columns = entry.subevent if entry.subevent else entry.event - tz = pytz.timezone(entry.event.settings.timezone) + tz = ZoneInfo(entry.event.settings.timezone) datetime_format = '%Y-%m-%d %H:%M:%S' row = [ diff --git a/src/pretix/base/forms/__init__.py b/src/pretix/base/forms/__init__.py index 9e926aa8f..462b4986e 100644 --- a/src/pretix/base/forms/__init__.py +++ b/src/pretix/base/forms/__init__.py @@ -167,6 +167,7 @@ class SettingsForm(i18nfield.forms.I18nFormMixin, HierarkeyForm): class PrefixForm(forms.Form): prefix = forms.CharField(widget=forms.HiddenInput) + template_name = 'django/forms/formsets/table.html' class SafeSessionWizardView(SessionWizardView): diff --git a/src/pretix/base/forms/questions.py b/src/pretix/base/forms/questions.py index 82a125631..057ec1af6 100644 --- a/src/pretix/base/forms/questions.py +++ b/src/pretix/base/forms/questions.py @@ -38,10 +38,10 @@ import logging from datetime import timedelta from decimal import Decimal from io import BytesIO +from zoneinfo import ZoneInfo import dateutil.parser import pycountry -import pytz from django import forms from django.conf import settings from django.contrib import messages @@ -733,7 +733,7 @@ class BaseQuestionsForm(forms.Form): initial = answers[0] else: initial = None - tz = pytz.timezone(event.settings.timezone) + tz = ZoneInfo(event.settings.timezone) help_text = rich_text(q.help_text) label = escape(q.question) # django-bootstrap3 calls mark_safe required = q.required and not self.all_optional diff --git a/src/pretix/base/forms/renderers.py b/src/pretix/base/forms/renderers.py new file mode 100644 index 000000000..55f3def7b --- /dev/null +++ b/src/pretix/base/forms/renderers.py @@ -0,0 +1,63 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-2021 rami.io GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# +from bootstrap3.renderers import ( + FieldRenderer as BaseFieldRenderer, + InlineFieldRenderer as BaseInlineFieldRenderer, +) +from django.forms import ( + CheckboxInput, CheckboxSelectMultiple, ClearableFileInput, RadioSelect, + SelectDateWidget, +) + + +class FieldRenderer(BaseFieldRenderer): + # Local application of https://github.com/zostera/django-bootstrap3/pull/859 + + def post_widget_render(self, html): + if isinstance(self.widget, CheckboxSelectMultiple): + html = self.list_to_class(html, "checkbox") + elif isinstance(self.widget, RadioSelect): + html = self.list_to_class(html, "radio") + elif isinstance(self.widget, SelectDateWidget): + html = self.fix_date_select_input(html) + elif isinstance(self.widget, ClearableFileInput): + html = self.fix_clearable_file_input(html) + elif isinstance(self.widget, CheckboxInput): + html = self.put_inside_label(html) + return html + + +class InlineFieldRenderer(BaseInlineFieldRenderer): + # Local application of https://github.com/zostera/django-bootstrap3/pull/859 + + def post_widget_render(self, html): + if isinstance(self.widget, CheckboxSelectMultiple): + html = self.list_to_class(html, "checkbox") + elif isinstance(self.widget, RadioSelect): + html = self.list_to_class(html, "radio") + elif isinstance(self.widget, SelectDateWidget): + html = self.fix_date_select_input(html) + elif isinstance(self.widget, ClearableFileInput): + html = self.fix_clearable_file_input(html) + elif isinstance(self.widget, CheckboxInput): + html = self.put_inside_label(html) + return html diff --git a/src/pretix/base/management/commands/export.py b/src/pretix/base/management/commands/export.py index a27887a02..26e837628 100644 --- a/src/pretix/base/management/commands/export.py +++ b/src/pretix/base/management/commands/export.py @@ -22,7 +22,7 @@ import json import sys -import pytz +import pytz_deprecation_shim from django.core.management.base import BaseCommand from django.utils.timezone import override from django_scopes import scope @@ -60,7 +60,7 @@ class Command(BaseCommand): sys.exit(1) locale = options.get("locale", None) - timezone = pytz.timezone(options['timezone']) if options.get('timezone') else None + timezone = pytz_deprecation_shim.timezone(options['timezone']) if options.get('timezone') else None with scope(organizer=o): if options['event_slug']: diff --git a/src/pretix/base/middleware.py b/src/pretix/base/middleware.py index 8ad3ef16a..61af59c22 100644 --- a/src/pretix/base/middleware.py +++ b/src/pretix/base/middleware.py @@ -21,8 +21,8 @@ # from collections import OrderedDict from urllib.parse import urlsplit +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError -import pytz from django.conf import settings from django.http import Http404, HttpRequest, HttpResponse from django.middleware.common import CommonMiddleware @@ -98,9 +98,9 @@ class LocaleMiddleware(MiddlewareMixin): tzname = request.user.timezone if tzname: try: - timezone.activate(pytz.timezone(tzname)) + timezone.activate(ZoneInfo(tzname)) request.timezone = tzname - except pytz.UnknownTimeZoneError: + except ZoneInfoNotFoundError: pass else: timezone.deactivate() diff --git a/src/pretix/base/migrations/0031_auto_20160816_0648_squashed_0048_auto_20161129_1330.py b/src/pretix/base/migrations/0031_auto_20160816_0648_squashed_0048_auto_20161129_1330.py index 18b311835..1a305bb90 100644 --- a/src/pretix/base/migrations/0031_auto_20160816_0648_squashed_0048_auto_20161129_1330.py +++ b/src/pretix/base/migrations/0031_auto_20160816_0648_squashed_0048_auto_20161129_1330.py @@ -2,6 +2,8 @@ # Generated by Django 1.10.4 on 2017-02-03 14:21 from __future__ import unicode_literals +from zoneinfo import ZoneInfo + import django.core.validators import django.db.migrations.operations.special import django.db.models.deletion @@ -26,7 +28,7 @@ def forwards42(apps, schema_editor): for s in EventSetting.objects.filter(key='timezone').values('object_id', 'value') } for order in Order.objects.all(): - tz = pytz.timezone(etz.get(order.event_id, 'UTC')) + tz = ZoneInfo(etz.get(order.event_id, 'UTC')) order.expires = order.expires.astimezone(tz).replace(hour=23, minute=59, second=59) order.save() diff --git a/src/pretix/base/migrations/0042_order_expires.py b/src/pretix/base/migrations/0042_order_expires.py index 5d1ea51dc..233be5b60 100644 --- a/src/pretix/base/migrations/0042_order_expires.py +++ b/src/pretix/base/migrations/0042_order_expires.py @@ -2,9 +2,9 @@ # Generated by Django 1.10.2 on 2016-10-19 17:57 from __future__ import unicode_literals -import pytz +from zoneinfo import ZoneInfo + from django.db import migrations -from django.utils import timezone def forwards(apps, schema_editor): @@ -15,7 +15,7 @@ def forwards(apps, schema_editor): for s in EventSetting.objects.filter(key='timezone').values('object_id', 'value') } for order in Order.objects.all(): - tz = pytz.timezone(etz.get(order.event_id, 'UTC')) + tz = ZoneInfo(etz.get(order.event_id, 'UTC')) order.expires = order.expires.astimezone(tz).replace(hour=23, minute=59, second=59) order.save() diff --git a/src/pretix/base/migrations/0198_invoice_sent_to_customer.py b/src/pretix/base/migrations/0198_invoice_sent_to_customer.py index a82a362f5..0b6593de7 100644 --- a/src/pretix/base/migrations/0198_invoice_sent_to_customer.py +++ b/src/pretix/base/migrations/0198_invoice_sent_to_customer.py @@ -1,8 +1,7 @@ # Generated by Django 3.2.4 on 2021-09-30 10:25 -from datetime import datetime +from datetime import datetime, timezone from django.db import migrations, models -from pytz import UTC class Migration(migrations.Migration): @@ -15,7 +14,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='invoice', name='sent_to_customer', - field=models.DateTimeField(blank=True, null=True, default=UTC.localize(datetime(1970, 1, 1, 0, 0, 0, 0))), + field=models.DateTimeField(blank=True, null=True, default=datetime(1970, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc)), preserve_default=False, ), ] diff --git a/src/pretix/base/models/customers.py b/src/pretix/base/models/customers.py index 7ffebbdfb..6904fe569 100644 --- a/src/pretix/base/models/customers.py +++ b/src/pretix/base/models/customers.py @@ -121,14 +121,23 @@ class Customer(LoggedModel): if self.email: self.email = self.email.lower() if 'update_fields' in kwargs and 'last_modified' not in kwargs['update_fields']: - kwargs['update_fields'] = list(kwargs['update_fields']) + ['last_modified'] + kwargs['update_fields'] = {'last_modified'}.union(kwargs['update_fields']) if not self.identifier: self.assign_identifier() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'identifier'}.union(kwargs['update_fields']) if self.name_parts: - self.name_cached = self.name + name = self.name + if self.name_cached != name: + self.name_cached = name + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_cached'}.union(kwargs['update_fields']) else: - self.name_cached = "" - self.name_parts = {} + if self.name_cached != "" or self.name_parts != {}: + self.name_cached = "" + self.name_parts = {} + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_cached', 'name_parts'}.union(kwargs['update_fields']) super().save(**kwargs) def anonymize(self): diff --git a/src/pretix/base/models/devices.py b/src/pretix/base/models/devices.py index 158a78675..858ef5906 100644 --- a/src/pretix/base/models/devices.py +++ b/src/pretix/base/models/devices.py @@ -98,6 +98,8 @@ class Gate(LoggedModel): if not Gate.objects.filter(organizer=self.organizer, identifier=code).exists(): self.identifier = code break + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'identifier'}.union(kwargs['update_fields']) return super().save(*args, **kwargs) @@ -173,6 +175,8 @@ class Device(LoggedModel): def save(self, *args, **kwargs): if not self.device_id: self.device_id = (self.organizer.devices.aggregate(m=Max('device_id'))['m'] or 0) + 1 + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'device_id'}.union(kwargs['update_fields']) super().save(*args, **kwargs) def permission_set(self) -> set: diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index 97a91968a..3620050e2 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -40,8 +40,9 @@ from collections import Counter, OrderedDict, defaultdict from datetime import datetime, time, timedelta from operator import attrgetter from urllib.parse import urljoin +from zoneinfo import ZoneInfo -import pytz +import pytz_deprecation_shim from django.conf import settings from django.core.exceptions import ValidationError from django.core.files.storage import default_storage @@ -214,7 +215,7 @@ class EventMixin: @property def timezone(self): - return pytz.timezone(self.settings.timezone) + return pytz_deprecation_shim.timezone(self.settings.timezone) @property def effective_presale_end(self): @@ -773,7 +774,7 @@ class Event(EventMixin, LoggedModel): """ The last datetime of payments for this event. """ - tz = pytz.timezone(self.settings.timezone) + tz = ZoneInfo(self.settings.timezone) return make_aware(datetime.combine( self.settings.get('payment_term_last', as_type=RelativeDateWrapper).datetime(self).date(), time(hour=23, minute=59, second=59) diff --git a/src/pretix/base/models/exports.py b/src/pretix/base/models/exports.py index d0b113c7f..b01f26825 100644 --- a/src/pretix/base/models/exports.py +++ b/src/pretix/base/models/exports.py @@ -19,10 +19,11 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import zoneinfo from datetime import datetime, timedelta -import pytz from dateutil.rrule import rrulestr +from dateutil.tz import datetime_exists from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder from django.db import models @@ -108,12 +109,9 @@ class AbstractScheduledExport(LoggedModel): self.schedule_next_run = None return - try: - self.schedule_next_run = make_aware(datetime.combine(new_d.date(), self.schedule_rrule_time), tz) - except pytz.exceptions.AmbiguousTimeError: - self.schedule_next_run = make_aware(datetime.combine(new_d.date(), self.schedule_rrule_time), tz, is_dst=False) - except pytz.exceptions.NonExistentTimeError: - self.schedule_next_run = make_aware(datetime.combine(new_d.date(), self.schedule_rrule_time) + timedelta(hours=1), tz) + self.schedule_next_run = make_aware(datetime.combine(new_d.date(), self.schedule_rrule_time), tz) + if not datetime_exists(self.schedule_next_run): + self.schedule_next_run += timedelta(hours=1) class ScheduledEventExport(AbstractScheduledExport): @@ -136,4 +134,4 @@ class ScheduledOrganizerExport(AbstractScheduledExport): @property def tz(self): - return pytz.timezone(self.timezone) + return zoneinfo.ZoneInfo(self.timezone) diff --git a/src/pretix/base/models/invoices.py b/src/pretix/base/models/invoices.py index 5f65f91ca..9d2be8f6d 100644 --- a/src/pretix/base/models/invoices.py +++ b/src/pretix/base/models/invoices.py @@ -251,14 +251,20 @@ class Invoice(models.Model): raise ValueError('Every invoice needs to be connected to an order') if not self.event: self.event = self.order.event + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'event'}.union(kwargs['update_fields']) if not self.organizer: self.organizer = self.order.event.organizer + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'organizer'}.union(kwargs['update_fields']) if not self.prefix: self.prefix = self.event.settings.invoice_numbers_prefix or (self.event.slug.upper() + '-') if self.is_cancellation: self.prefix = self.event.settings.invoice_numbers_prefix_cancellations or self.prefix if '%' in self.prefix: self.prefix = self.date.strftime(self.prefix) + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'prefix'}.union(kwargs['update_fields']) if not self.invoice_no: if self.order.testmode: @@ -276,8 +282,13 @@ class Invoice(models.Model): # Suppress duplicate key errors and try again if i == 9: raise + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'invoice_no'}.union(kwargs['update_fields']) - self.full_invoice_no = self.prefix + self.invoice_no + if self.full_invoice_no != self.prefix + self.invoice_no: + self.full_invoice_no = self.prefix + self.invoice_no + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'full_invoice_no'}.union(kwargs['update_fields']) return super().save(*args, **kwargs) def delete(self, *args, **kwargs): diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py index 199b2a254..d8822c152 100644 --- a/src/pretix/base/models/items.py +++ b/src/pretix/base/models/items.py @@ -40,9 +40,10 @@ from collections import Counter, OrderedDict from datetime import date, datetime, time, timedelta from decimal import Decimal, DecimalException from typing import Optional, Tuple +from zoneinfo import ZoneInfo import dateutil.parser -import pytz +from dateutil.tz import datetime_exists from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import ( @@ -927,22 +928,22 @@ class Item(LoggedModel): ) if self.validity_dynamic_duration_days: replace_date += timedelta(days=self.validity_dynamic_duration_days) - valid_until = tz.localize(valid_until.replace( + valid_until = valid_until.replace( year=replace_date.year, month=replace_date.month, day=replace_date.day, hour=23, minute=59, second=59, microsecond=0, - tzinfo=None, - )) + tzinfo=tz, + ) elif self.validity_dynamic_duration_days: replace_date = valid_until.date() + timedelta(days=self.validity_dynamic_duration_days - 1) - valid_until = tz.localize(valid_until.replace( + valid_until = valid_until.replace( year=replace_date.year, month=replace_date.month, day=replace_date.day, hour=23, minute=59, second=59, microsecond=0, - tzinfo=None - )) + tzinfo=tz + ) if self.validity_dynamic_duration_hours: valid_until += timedelta(hours=self.validity_dynamic_duration_hours) @@ -950,6 +951,9 @@ class Item(LoggedModel): if self.validity_dynamic_duration_minutes: valid_until += timedelta(minutes=self.validity_dynamic_duration_minutes) + if not datetime_exists(valid_until): + valid_until += timedelta(hours=1) + return requested_start, valid_until else: @@ -1589,6 +1593,8 @@ class Question(LoggedModel): if not Question.objects.filter(event=self.event, identifier=code).exists(): self.identifier = code break + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'identifier'}.union(kwargs['update_fields']) super().save(*args, **kwargs) if self.event: self.event.cache.clear() @@ -1678,7 +1684,7 @@ class Question(LoggedModel): try: dt = dateutil.parser.parse(answer) if is_naive(dt): - dt = make_aware(dt, pytz.timezone(self.event.settings.timezone)) + dt = make_aware(dt, ZoneInfo(self.event.settings.timezone)) except: raise ValidationError(_('Invalid datetime input.')) else: @@ -1736,6 +1742,8 @@ class QuestionOption(models.Model): if not QuestionOption.objects.filter(question__event=self.question.event, identifier=code).exists(): self.identifier = code break + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'identifier'}.union(kwargs['update_fields']) super().save(*args, **kwargs) @staticmethod diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 9c2f7ea2f..5bb2aaa75 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -42,10 +42,10 @@ from collections import Counter from datetime import datetime, time, timedelta from decimal import Decimal from typing import Any, Dict, List, Union +from zoneinfo import ZoneInfo import dateutil import pycountry -import pytz from django.conf import settings from django.core.exceptions import ValidationError from django.db import models, transaction @@ -461,14 +461,20 @@ class Order(LockModel, LoggedModel): return '{event}-{code}'.format(event=self.event.slug.upper(), code=self.code) def save(self, **kwargs): - if 'update_fields' in kwargs and 'last_modified' not in kwargs['update_fields']: - kwargs['update_fields'] = list(kwargs['update_fields']) + ['last_modified'] + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'last_modified'}.union(kwargs['update_fields']) if not self.code: self.assign_code() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'code'}.union(kwargs['update_fields']) if not self.datetime: self.datetime = now() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'datetime'}.union(kwargs['update_fields']) if not self.expires: self.set_expires() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'expires'}.union(kwargs['update_fields']) is_new = not self.pk update_fields = kwargs.get('update_fields', []) @@ -496,7 +502,7 @@ class Order(LockModel, LoggedModel): def set_expires(self, now_dt=None, subevents=None): now_dt = now_dt or now() - tz = pytz.timezone(self.event.settings.timezone) + tz = ZoneInfo(self.event.settings.timezone) mode = self.event.settings.get('payment_term_mode') if mode == 'days': exp_by_date = now_dt.astimezone(tz) + timedelta(days=self.event.settings.get('payment_term_days', as_type=int)) @@ -870,7 +876,7 @@ class Order(LockModel, LoggedModel): @property def payment_term_last(self): - tz = pytz.timezone(self.event.settings.timezone) + tz = ZoneInfo(self.event.settings.timezone) term_last = self.event.settings.get('payment_term_last', as_type=RelativeDateWrapper) if term_last: if self.event.has_subevents: @@ -1230,7 +1236,7 @@ class QuestionAnswer(models.Model): try: d = dateutil.parser.parse(self.answer) if self.orderposition: - tz = pytz.timezone(self.orderposition.order.event.settings.timezone) + tz = ZoneInfo(self.orderposition.order.event.settings.timezone) d = d.astimezone(tz) return date_format(d, "SHORT_DATETIME_FORMAT") except ValueError: @@ -1442,12 +1448,20 @@ class AbstractPosition(models.Model): else self.variation.quotas.filter(subevent=self.subevent)) def save(self, *args, **kwargs): - update_fields = kwargs.get('update_fields', []) + update_fields = kwargs.get('update_fields', set()) if 'attendee_name_parts' in update_fields: - update_fields.append('attendee_name_cached') - self.attendee_name_cached = self.attendee_name + kwargs['update_fields'] = {'attendee_name_cached'}.union(kwargs['update_fields']) + + name = self.attendee_name + if name != self.attendee_name_cached: + self.attendee_name_cached = name + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'attendee_name_cached'}.union(kwargs['update_fields']) + if self.attendee_name_parts is None: self.attendee_name_parts = {} + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'attendee_name_parts'}.union(kwargs['update_fields']) super().save(*args, **kwargs) @property @@ -1827,6 +1841,8 @@ class OrderPayment(models.Model): def save(self, *args, **kwargs): if not self.local_id: self.local_id = (self.order.payments.aggregate(m=Max('local_id'))['m'] or 0) + 1 + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'local_id'}.union(kwargs['update_fields']) super().save(*args, **kwargs) def create_external_refund(self, amount=None, execution_date=None, info='{}'): @@ -2025,6 +2041,8 @@ class OrderRefund(models.Model): def save(self, *args, **kwargs): if not self.local_id: self.local_id = (self.order.refunds.aggregate(m=Max('local_id'))['m'] or 0) + 1 + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'local_id'}.union(kwargs['update_fields']) super().save(*args, **kwargs) @@ -2443,14 +2461,20 @@ class OrderPosition(AbstractPosition): assign_ticket_secret( event=self.order.event, position=self, force_invalidate=True, save=False ) + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'secret'}.union(kwargs['update_fields']) - if not self.blocked: + if not self.blocked and self.blocked is not None: self.blocked = None - elif not isinstance(self.blocked, list) or any(not isinstance(b, str) for b in self.blocked): + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'blocked'}.union(kwargs['update_fields']) + elif self.blocked and (not isinstance(self.blocked, list) or any(not isinstance(b, str) for b in self.blocked)): raise TypeError("blocked needs to be a list of strings") if not self.pseudonymization_id: self.assign_pseudonymization_id() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'pseudonymization_id'}.union(kwargs['update_fields']) if not self.get_deferred_fields(): if Transaction.key(self) != self.__initial_transaction_key or self.canceled != self.__initial_canceled or not self.pk: @@ -2936,10 +2960,17 @@ class InvoiceAddress(models.Model): self.order.touch() if self.name_parts: - self.name_cached = self.name + name = self.name + if self.name_cached != name: + self.name_cached = self.name + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_cached'}.union(kwargs['update_fields']) else: - self.name_cached = "" - self.name_parts = {} + if self.name_cached != "" or self.name_parts != {}: + self.name_cached = "" + self.name_parts = {} + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_cached', 'name_parts'}.union(kwargs['update_fields']) super().save(**kwargs) def describe(self): diff --git a/src/pretix/base/models/organizer.py b/src/pretix/base/models/organizer.py index 446ce0906..38b998043 100644 --- a/src/pretix/base/models/organizer.py +++ b/src/pretix/base/models/organizer.py @@ -35,7 +35,7 @@ import string from datetime import date, datetime, time -import pytz +import pytz_deprecation_shim from django.conf import settings from django.core.mail import get_connection from django.core.validators import MinLengthValidator, RegexValidator @@ -102,6 +102,7 @@ class Organizer(LoggedModel): is_new = not self.pk obj = super().save(*args, **kwargs) if is_new: + kwargs.pop('update_fields', None) # does not make sense here self.set_defaults() else: self.get_cache().clear() @@ -140,7 +141,7 @@ class Organizer(LoggedModel): @property def timezone(self): - return pytz.timezone(self.settings.timezone) + return pytz_deprecation_shim.timezone(self.settings.timezone) @cached_property def all_logentries_link(self): diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index bc7c3c2f0..33dc705cc 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -502,7 +502,10 @@ class Voucher(LoggedModel): return seat def save(self, *args, **kwargs): - self.code = self.code.upper() + if self.code != self.code.upper(): + self.code = self.code.upper() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'code'}.union(kwargs['update_fields']) super().save(*args, **kwargs) self.event.cache.set('vouchers_exist', True) diff --git a/src/pretix/base/models/waitinglist.py b/src/pretix/base/models/waitinglist.py index 1d4218215..667ad7be5 100644 --- a/src/pretix/base/models/waitinglist.py +++ b/src/pretix/base/models/waitinglist.py @@ -126,12 +126,19 @@ class WaitingListEntry(LoggedModel): raise ValidationError('Invalid input') def save(self, *args, **kwargs): - update_fields = kwargs.get('update_fields', []) + update_fields = kwargs.get('update_fields', set()) if 'name_parts' in update_fields: - update_fields.append('name_cached') - self.name_cached = self.name + kwargs['update_fields'] = {'name_cached'}.union(kwargs['update_fields']) + name = self.name + if name != self.name_cached: + self.name_cached = name + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_cached'}.union(kwargs['update_fields']) + if self.name_parts is None: self.name_parts = {} + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'name_parts'}.union(kwargs['update_fields']) super().save(*args, **kwargs) @property diff --git a/src/pretix/base/orderimport.py b/src/pretix/base/orderimport.py index e78f97ff4..e8bb6bb69 100644 --- a/src/pretix/base/orderimport.py +++ b/src/pretix/base/orderimport.py @@ -210,7 +210,7 @@ class SubeventColumn(ImportColumn): for format in input_formats: try: d = datetime.datetime.strptime(value, format) - d = self.event.timezone.localize(d) + d = d.replace(tzinfo=self.event.timezone) try: se = self.event.subevents.get( active=True, @@ -660,7 +660,7 @@ class ValidFrom(ImportColumn): for format in input_formats: try: d = datetime.datetime.strptime(value, format) - d = self.event.timezone.localize(d) + d = d.replace(tzinfo=self.event.timezone) return d except (ValueError, TypeError): pass @@ -683,7 +683,7 @@ class ValidUntil(ImportColumn): for format in input_formats: try: d = datetime.datetime.strptime(value, format) - d = self.event.timezone.localize(d) + d = d.replace(tzinfo=self.event.timezone) return d except (ValueError, TypeError): pass diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 6e82227f0..81fe7f337 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -39,8 +39,8 @@ import logging from collections import OrderedDict from decimal import ROUND_HALF_UP, Decimal from typing import Any, Dict, Union +from zoneinfo import ZoneInfo -import pytz from django import forms from django.conf import settings from django.contrib import messages @@ -518,7 +518,7 @@ class BasePaymentProvider: def _is_still_available(self, now_dt=None, cart_id=None, order=None): now_dt = now_dt or now() - tz = pytz.timezone(self.event.settings.timezone) + tz = ZoneInfo(self.event.settings.timezone) availability_date = self.settings.get('_availability_date', as_type=RelativeDateWrapper) if availability_date: diff --git a/src/pretix/base/pdf.py b/src/pretix/base/pdf.py index 6f38d2ab1..755e21365 100644 --- a/src/pretix/base/pdf.py +++ b/src/pretix/base/pdf.py @@ -61,7 +61,6 @@ from django.utils.timezone import now from django.utils.translation import gettext_lazy as _, pgettext from i18nfield.strings import LazyI18nString from pypdf import PdfReader -from pytz import timezone from reportlab.graphics import renderPDF from reportlab.graphics.barcode.qr import QrCodeWidget from reportlab.graphics.shapes import Drawing @@ -237,7 +236,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event begin date and time"), "editor_sample": _("2017-05-31 20:00"), "evaluate": lambda op, order, ev: date_format( - ev.date_from.astimezone(timezone(ev.settings.timezone)), + ev.date_from.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) if ev.date_from else "" }), @@ -245,7 +244,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event begin date"), "editor_sample": _("2017-05-31"), "evaluate": lambda op, order, ev: date_format( - ev.date_from.astimezone(timezone(ev.settings.timezone)), + ev.date_from.astimezone(ev.timezone), "SHORT_DATE_FORMAT" ) if ev.date_from else "" }), @@ -263,7 +262,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event end date and time"), "editor_sample": _("2017-05-31 22:00"), "evaluate": lambda op, order, ev: date_format( - ev.date_to.astimezone(timezone(ev.settings.timezone)), + ev.date_to.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) if ev.date_to else "" }), @@ -271,7 +270,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event end date"), "editor_sample": _("2017-05-31"), "evaluate": lambda op, order, ev: date_format( - ev.date_to.astimezone(timezone(ev.settings.timezone)), + ev.date_to.astimezone(ev.timezone), "SHORT_DATE_FORMAT" ) if ev.date_to else "" }), @@ -279,7 +278,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event end time"), "editor_sample": _("22:00"), "evaluate": lambda op, order, ev: date_format( - ev.date_to.astimezone(timezone(ev.settings.timezone)), + ev.date_to.astimezone(ev.timezone), "TIME_FORMAT" ) if ev.date_to else "" }), @@ -292,7 +291,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event admission date and time"), "editor_sample": _("2017-05-31 19:00"), "evaluate": lambda op, order, ev: date_format( - ev.date_admission.astimezone(timezone(ev.settings.timezone)), + ev.date_admission.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) if ev.date_admission else "" }), @@ -300,7 +299,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Event admission time"), "editor_sample": _("19:00"), "evaluate": lambda op, order, ev: date_format( - ev.date_admission.astimezone(timezone(ev.settings.timezone)), + ev.date_admission.astimezone(ev.timezone), "TIME_FORMAT" ) if ev.date_admission else "" }), @@ -385,7 +384,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Printing date"), "editor_sample": _("2017-05-31"), "evaluate": lambda op, order, ev: date_format( - now().astimezone(timezone(ev.settings.timezone)), + now().astimezone(ev.timezone), "SHORT_DATE_FORMAT" ) }), @@ -393,7 +392,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Printing date and time"), "editor_sample": _("2017-05-31 19:00"), "evaluate": lambda op, order, ev: date_format( - now().astimezone(timezone(ev.settings.timezone)), + now().astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) }), @@ -401,7 +400,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Printing time"), "editor_sample": _("19:00"), "evaluate": lambda op, order, ev: date_format( - now().astimezone(timezone(ev.settings.timezone)), + now().astimezone(ev.timezone), "TIME_FORMAT" ) }), @@ -409,7 +408,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity start date"), "editor_sample": _("2017-05-31"), "evaluate": lambda op, order, ev: date_format( - op.valid_from.astimezone(timezone(ev.settings.timezone)), + op.valid_from.astimezone(ev.timezone), "SHORT_DATE_FORMAT" ) if op.valid_from else "" }), @@ -417,7 +416,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity start date and time"), "editor_sample": _("2017-05-31 19:00"), "evaluate": lambda op, order, ev: date_format( - op.valid_from.astimezone(timezone(ev.settings.timezone)), + op.valid_from.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) if op.valid_from else "" }), @@ -425,7 +424,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity start time"), "editor_sample": _("19:00"), "evaluate": lambda op, order, ev: date_format( - op.valid_from.astimezone(timezone(ev.settings.timezone)), + op.valid_from.astimezone(ev.timezone), "TIME_FORMAT" ) if op.valid_from else "" }), @@ -433,7 +432,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity end date"), "editor_sample": _("2017-05-31"), "evaluate": lambda op, order, ev: date_format( - op.valid_until.astimezone(timezone(ev.settings.timezone)), + op.valid_until.astimezone(ev.timezone), "SHORT_DATE_FORMAT" ) if op.valid_until else "" }), @@ -441,7 +440,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity end date and time"), "editor_sample": _("2017-05-31 19:00"), "evaluate": lambda op, order, ev: date_format( - op.valid_until.astimezone(timezone(ev.settings.timezone)), + op.valid_until.astimezone(ev.timezone), "SHORT_DATETIME_FORMAT" ) if op.valid_until else "" }), @@ -449,7 +448,7 @@ DEFAULT_VARIABLES = OrderedDict(( "label": _("Validity end time"), "editor_sample": _("19:00"), "evaluate": lambda op, order, ev: date_format( - op.valid_until.astimezone(timezone(ev.settings.timezone)), + op.valid_until.astimezone(ev.timezone), "TIME_FORMAT" ) if op.valid_until else "" }), diff --git a/src/pretix/base/reldate.py b/src/pretix/base/reldate.py index 2122e2508..904a7d715 100644 --- a/src/pretix/base/reldate.py +++ b/src/pretix/base/reldate.py @@ -22,8 +22,8 @@ import datetime from collections import namedtuple from typing import Union +from zoneinfo import ZoneInfo -import pytz from dateutil import parser from django import forms from django.core.exceptions import ValidationError @@ -67,7 +67,7 @@ class RelativeDateWrapper: if self.data.minutes_before is not None: raise ValueError('A minute-based relative datetime can not be used as a date') - tz = pytz.timezone(event.settings.timezone) + tz = ZoneInfo(event.settings.timezone) if isinstance(event, SubEvent): base_date = ( getattr(event, self.data.base_date_name) @@ -86,7 +86,7 @@ class RelativeDateWrapper: if isinstance(self.data, (datetime.datetime, datetime.date)): return self.data else: - tz = pytz.timezone(event.settings.timezone) + tz = ZoneInfo(event.settings.timezone) if isinstance(event, SubEvent): base_date = ( getattr(event, self.data.base_date_name) @@ -99,8 +99,7 @@ class RelativeDateWrapper: if self.data.minutes_before is not None: return base_date.astimezone(tz) - datetime.timedelta(minutes=self.data.minutes_before) else: - oldoffset = base_date.astimezone(tz).utcoffset() - new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.data.days_before) + new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.data.days_before)).astimezone(tz) if self.data.time: new_date = new_date.replace( hour=self.data.time.hour, @@ -108,8 +107,6 @@ class RelativeDateWrapper: second=self.data.time.second ) new_date = new_date.astimezone(tz) - new_offset = new_date.utcoffset() - new_date += oldoffset - new_offset return new_date def to_string(self) -> str: diff --git a/src/pretix/base/services/checkin.py b/src/pretix/base/services/checkin.py index 6054f2951..9168f4544 100644 --- a/src/pretix/base/services/checkin.py +++ b/src/pretix/base/services/checkin.py @@ -32,12 +32,12 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. import os -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from functools import partial, reduce import dateutil import dateutil.parser -import pytz +from dateutil.tz import datetime_exists from django.core.files import File from django.db import IntegrityError, transaction from django.db.models import ( @@ -439,7 +439,7 @@ class SQLLogic: if operator == 'buildTime': if values[0] == "custom": - return Value(dateutil.parser.parse(values[1]).astimezone(pytz.UTC)) + return Value(dateutil.parser.parse(values[1]).astimezone(timezone.utc)) elif values[0] == "customtime": parsed = dateutil.parser.parse(values[1]) return Value(now().astimezone(self.list.event.timezone).replace( @@ -447,7 +447,7 @@ class SQLLogic: minute=parsed.minute, second=parsed.second, microsecond=parsed.microsecond, - ).astimezone(pytz.UTC)) + ).astimezone(timezone.utc)) elif values[0] == 'date_from': return Coalesce( F('subevent__date_from'), @@ -475,7 +475,7 @@ class SQLLogic: return int(values[1]) elif operator == 'var': if values[0] == 'now': - return Value(now().astimezone(pytz.UTC)) + return Value(now().astimezone(timezone.utc)) elif values[0] == 'now_isoweekday': return Value(now().astimezone(self.list.event.timezone).isoweekday()) elif values[0] == 'product': @@ -926,14 +926,11 @@ def process_exit_all(sender, **kwargs): if cl.event.settings.get(f'autocheckin_dst_hack_{cl.pk}'): # move time back if yesterday was DST switch d -= timedelta(hours=1) cl.event.settings.delete(f'autocheckin_dst_hack_{cl.pk}') - try: - cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone) - except pytz.exceptions.AmbiguousTimeError: - cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone, - is_dst=False) - except pytz.exceptions.NonExistentTimeError: + + cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time().replace(fold=1)), cl.event.timezone) + if not datetime_exists(cl.exit_all_at): cl.event.settings.set(f'autocheckin_dst_hack_{cl.pk}', True) d += timedelta(hours=1) - cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone) + cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time().replace(fold=1)), cl.event.timezone) # AmbiguousTimeError shouldn't be possible since d.time() includes fold=0 cl.save(update_fields=['exit_all_at']) diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py index 908b8b16d..4bdf729bc 100644 --- a/src/pretix/base/services/mail.py +++ b/src/pretix/base/services/mail.py @@ -45,8 +45,8 @@ from email.mime.image import MIMEImage from email.utils import formataddr from typing import Any, Dict, List, Sequence, Union from urllib.parse import urljoin, urlparse +from zoneinfo import ZoneInfo -import pytz import requests from bs4 import BeautifulSoup from celery import chain @@ -226,11 +226,11 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La if event: timezone = event.timezone elif user: - timezone = pytz.timezone(user.timezone) + timezone = ZoneInfo(user.timezone) elif organizer: timezone = organizer.timezone else: - timezone = pytz.timezone(settings.TIME_ZONE) + timezone = ZoneInfo(settings.TIME_ZONE) if settings_holder: if settings_holder.settings.mail_bcc: diff --git a/src/pretix/base/templates/django/forms/widgets/multiple_input.html b/src/pretix/base/templates/django/forms/widgets/multiple_input.html new file mode 100644 index 000000000..d7c963f26 --- /dev/null +++ b/src/pretix/base/templates/django/forms/widgets/multiple_input.html @@ -0,0 +1,6 @@ +{# this is the version from django 3.x, prior to https://github.com/django/django/commit/5942ab5eb165ee2e759174e297148a40dd855920 so that django-bootstrap3 can keep doing its magic #} +{% with id=widget.attrs.id %}{% for group, options, index in widget.optgroups %}{% if group %} +
  • {{ group }}{% endif %}{% for option in options %} +
  • {% include option.template_name with widget=option %}
  • {% endfor %}{% if group %} + {% endif %}{% endfor %} +{% endwith %} \ No newline at end of file diff --git a/src/pretix/base/timeframes.py b/src/pretix/base/timeframes.py index db1bccb48..8d3125d29 100644 --- a/src/pretix/base/timeframes.py +++ b/src/pretix/base/timeframes.py @@ -20,11 +20,10 @@ # . # import calendar -from datetime import date, datetime, time, timedelta +from datetime import date, datetime, time, timedelta, timezone from itertools import groupby from typing import Optional, Tuple -import pytz from django import forms from django.core.exceptions import ValidationError from django.utils.formats import date_format @@ -392,7 +391,7 @@ class SerializerDateFrameField(serializers.CharField): if data is None: return None try: - resolve_timeframe_to_dates_inclusive(now(), data, pytz.UTC) + resolve_timeframe_to_dates_inclusive(now(), data, timezone.utc) except: raise ValidationError("Invalid date frame") diff --git a/src/pretix/base/views/tasks.py b/src/pretix/base/views/tasks.py index 6302b3374..1cf216f10 100644 --- a/src/pretix/base/views/tasks.py +++ b/src/pretix/base/views/tasks.py @@ -21,9 +21,9 @@ # import logging from importlib import import_module +from zoneinfo import ZoneInfo import celery.exceptions -import pytz from celery import states from celery.result import AsyncResult from django.conf import settings @@ -252,7 +252,7 @@ class AsyncFormView(AsyncMixin, FormView): task_self = self view_instance._task_self = task_self - with translation.override(locale), timezone.override(pytz.timezone(tz)): + with translation.override(locale), timezone.override(ZoneInfo(tz)): form_class = view_instance.get_form_class() if form_kwargs.get('instance'): form_kwargs['instance'] = cls.model.objects.get(pk=form_kwargs['instance']) @@ -302,7 +302,7 @@ class AsyncFormView(AsyncMixin, FormView): 'url_args': self.args, 'url_kwargs': self.kwargs, 'locale': get_language(), - 'tz': get_current_timezone().zone, + 'tz': str(get_current_timezone()), } if hasattr(self.request, 'organizer'): kwargs['organizer'] = self.request.organizer.pk @@ -377,7 +377,7 @@ class AsyncPostView(AsyncMixin, View): task_self = self view_instance._task_self = task_self - with translation.override(locale), timezone.override(pytz.timezone(tz)): + with translation.override(locale), timezone.override(ZoneInfo(tz)): return view_instance.async_post(view_instance.request, *url_args, **url_kwargs) cls.async_execute = app.task( @@ -405,7 +405,7 @@ class AsyncPostView(AsyncMixin, View): 'locale': get_language(), 'url_args': args, 'url_kwargs': kwargs, - 'tz': get_current_timezone().zone, + 'tz': str(get_current_timezone()), } if hasattr(self.request, 'organizer'): kwargs['organizer'] = self.request.organizer.pk diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 7f9ab3c4f..5f11a83f2 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -36,6 +36,7 @@ from decimal import Decimal from urllib.parse import urlencode, urlparse +from zoneinfo import ZoneInfo from django import forms from django.conf import settings @@ -55,7 +56,7 @@ from django_countries.fields import LazyTypedChoiceField from i18nfield.forms import ( I18nForm, I18nFormField, I18nFormSetMixin, I18nTextarea, I18nTextInput, ) -from pytz import common_timezones, timezone +from pytz import common_timezones from pretix.base.channels import get_all_sales_channels from pretix.base.email import get_available_placeholders @@ -221,7 +222,7 @@ class EventWizardBasicsForm(I18nModelForm): }) # change timezone - zone = timezone(data.get('timezone')) + zone = ZoneInfo(data.get('timezone')) data['date_from'] = self.reset_timezone(zone, data.get('date_from')) data['date_to'] = self.reset_timezone(zone, data.get('date_to')) data['presale_start'] = self.reset_timezone(zone, data.get('presale_start')) @@ -230,7 +231,7 @@ class EventWizardBasicsForm(I18nModelForm): @staticmethod def reset_timezone(tz, dt): - return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None + return dt.replace(tzinfo=tz) if dt is not None else None def clean_slug(self): slug = self.cleaned_data['slug'] diff --git a/src/pretix/control/forms/item.py b/src/pretix/control/forms/item.py index cc5783506..748842ab2 100644 --- a/src/pretix/control/forms/item.py +++ b/src/pretix/control/forms/item.py @@ -747,7 +747,7 @@ class ItemVariationsFormSet(I18nFormSet): def _should_delete_form(self, form): should_delete = super()._should_delete_form(form) - if should_delete and (form.instance.orderposition_set.exists() or form.instance.cartposition_set.exists()): + if should_delete and form.instance.pk and (form.instance.orderposition_set.exists() or form.instance.cartposition_set.exists()): form._delete_fail = True return False return form.cleaned_data.get(DELETION_FIELD_NAME, False) diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index 8f96e4526..2f64789b5 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -602,7 +602,7 @@ class WebHookForm(forms.ModelForm): mark_safe('{} – {}'.format(a.verbose_name, a.action_type)) ) for a in get_all_webhook_events().values() ] - if self.instance: + if self.instance and self.instance.pk: self.fields['events'].initial = list(self.instance.listeners.values_list('action_type', flat=True)) class Meta: diff --git a/src/pretix/control/forms/renderers.py b/src/pretix/control/forms/renderers.py index a64aae2c4..94abd8bc0 100644 --- a/src/pretix/control/forms/renderers.py +++ b/src/pretix/control/forms/renderers.py @@ -19,7 +19,6 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from bootstrap3.renderers import FieldRenderer, InlineFieldRenderer from bootstrap3.text import text_value from django.forms import CheckboxInput from django.forms.utils import flatatt @@ -28,6 +27,8 @@ from django.utils.safestring import mark_safe from django.utils.translation import pgettext from i18nfield.forms import I18nFormField +from pretix.base.forms.renderers import FieldRenderer, InlineFieldRenderer + def render_label(content, label_for=None, label_class=None, label_title='', optional=False): """ diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py index d657c052c..cac79721d 100644 --- a/src/pretix/control/logdisplay.py +++ b/src/pretix/control/logdisplay.py @@ -39,7 +39,6 @@ from decimal import Decimal import bleach import dateutil.parser -import pytz from django.dispatch import receiver from django.urls import reverse from django.utils.formats import date_format @@ -209,7 +208,7 @@ def _display_checkin(event, logentry): if 'datetime' in data: dt = dateutil.parser.parse(data.get('datetime')) show_dt = abs((logentry.datetime - dt).total_seconds()) > 5 or 'forced' in data - tz = pytz.timezone(event.settings.timezone) + tz = event.timezone dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT") if 'list' in data: @@ -627,7 +626,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs): if logentry.action_type == 'pretix.control.views.checkin': # deprecated dt = dateutil.parser.parse(data.get('datetime')) - tz = pytz.timezone(sender.settings.timezone) + tz = sender.timezone dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT") if 'list' in data: try: diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 845e7b656..22da38b4a 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -33,8 +33,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from django.views.generic.base import RedirectView from pretix.control.views import ( diff --git a/src/pretix/control/views/checkin.py b/src/pretix/control/views/checkin.py index d5db89124..62274d30a 100644 --- a/src/pretix/control/views/checkin.py +++ b/src/pretix/control/views/checkin.py @@ -19,18 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # - -# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of -# the Apache License 2.0 can be obtained at . -# -# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A -# full history of changes and contributors is available at . -# -# This file contains Apache-licensed contributions copyrighted by: Jakob Schnell, jasonwaiting@live.hk, pajowu -# -# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under the License. +from datetime import timezone import dateutil.parser from django.contrib import messages @@ -44,7 +33,6 @@ from django.utils.functional import cached_property from django.utils.timezone import is_aware, make_aware, now from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView -from pytz import UTC from pretix.base.channels import get_all_sales_channels from pretix.base.models import Checkin, Order, OrderPosition @@ -60,6 +48,18 @@ from pretix.control.views import CreateView, PaginationMixin, UpdateView from pretix.helpers.compat import CompatDeleteView from pretix.helpers.models import modelcopy +# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of +# the Apache License 2.0 can be obtained at . +# +# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A +# full history of changes and contributors is available at . +# +# This file contains Apache-licensed contributions copyrighted by: Jakob Schnell, jasonwaiting@live.hk, pajowu +# +# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under the License. + class CheckInListQueryMixin: @@ -163,20 +163,20 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList if e.last_entry: if isinstance(e.last_entry, str): # Apparently only happens on SQLite - e.last_entry_aware = make_aware(dateutil.parser.parse(e.last_entry), UTC) + e.last_entry_aware = make_aware(dateutil.parser.parse(e.last_entry), timezone.utc) elif not is_aware(e.last_entry): # Apparently only happens on MySQL - e.last_entry_aware = make_aware(e.last_entry, UTC) + e.last_entry_aware = make_aware(e.last_entry, timezone.utc) else: # This would be correct, so guess on which database it works… Yes, it's PostgreSQL. e.last_entry_aware = e.last_entry if e.last_exit: if isinstance(e.last_exit, str): # Apparently only happens on SQLite - e.last_exit_aware = make_aware(dateutil.parser.parse(e.last_exit), UTC) + e.last_exit_aware = make_aware(dateutil.parser.parse(e.last_exit), timezone.utc) elif not is_aware(e.last_exit): # Apparently only happens on MySQL - e.last_exit_aware = make_aware(e.last_exit, UTC) + e.last_exit_aware = make_aware(e.last_exit, timezone.utc) else: # This would be correct, so guess on which database it works… Yes, it's PostgreSQL. e.last_exit_aware = e.last_exit diff --git a/src/pretix/control/views/dashboards.py b/src/pretix/control/views/dashboards.py index 50746d131..31cdb8b6d 100644 --- a/src/pretix/control/views/dashboards.py +++ b/src/pretix/control/views/dashboards.py @@ -34,8 +34,8 @@ from datetime import timedelta from decimal import Decimal +from zoneinfo import ZoneInfo -import pytz from django.conf import settings from django.contrib.contenttypes.models import ContentType from django.db.models import ( @@ -510,7 +510,7 @@ def widgets_for_event_qs(request, qs, user, nmax, lazy=False): for event in events: if not lazy: tzname = event.cache.get_or_set('timezone', lambda: event.settings.timezone) - tz = pytz.timezone(tzname) + tz = ZoneInfo(tzname) if event.has_subevents: if event.min_from is None: dr = pgettext("subevent", "No dates") diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 36cd75848..a1dd2dcd4 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -41,6 +41,7 @@ from decimal import Decimal from io import BytesIO from itertools import groupby from urllib.parse import urlsplit +from zoneinfo import ZoneInfo import bleach import qrcode @@ -67,7 +68,6 @@ from django.views.generic.base import TemplateView, View from django.views.generic.detail import SingleObjectMixin from i18nfield.strings import LazyI18nString from i18nfield.utils import I18nJSONEncoder -from pytz import timezone from pretix.base.channels import get_all_sales_channels from pretix.base.email import get_available_placeholders @@ -253,7 +253,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired self.item_meta_property_formset.is_valid() and self.confirm_texts_formset.is_valid() and \ self.footer_links_formset.is_valid(): # reset timezone - zone = timezone(self.sform.cleaned_data['timezone']) + zone = ZoneInfo(self.sform.cleaned_data['timezone']) event = form.instance event.date_from = self.reset_timezone(zone, event.date_from) event.date_to = self.reset_timezone(zone, event.date_to) @@ -266,7 +266,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired @staticmethod def reset_timezone(tz, dt): - return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None + return dt.replace(tzinfo=tz) if dt is not None else None @cached_property def item_meta_property_formset(self): diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index d824a6dab..2e70e0b39 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -60,8 +60,7 @@ from django.utils.timezone import get_current_timezone, now from django.utils.translation import gettext, gettext_lazy as _ from django.views import View from django.views.generic import ( - CreateView, DeleteView, DetailView, FormView, ListView, TemplateView, - UpdateView, + CreateView, DetailView, FormView, ListView, TemplateView, UpdateView, ) from pretix.api.models import ApiCall, WebHook @@ -1775,7 +1774,7 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView): instance = self.scheduled or ScheduledOrganizerExport( organizer=self.request.organizer, owner=self.request.user, - timezone=get_current_timezone().zone, + timezone=str(get_current_timezone()), ) if not self.scheduled: initial = { @@ -1811,7 +1810,7 @@ class ExportView(OrganizerPermissionRequiredMixin, ExportMixin, ListView): return ctx -class DeleteScheduledExportView(OrganizerPermissionRequiredMixin, ExportMixin, DeleteView): +class DeleteScheduledExportView(OrganizerPermissionRequiredMixin, ExportMixin, CompatDeleteView): template_name = 'pretixcontrol/organizers/export_delete.html' context_object_name = 'export' diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index 86a95d285..6f595dd00 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -260,7 +260,7 @@ class SubEventEditorMixin(MetaDataEditorMixin): form=SimpleCheckinListForm, formset=CheckinListFormSet, can_order=False, can_delete=True, extra=extra, ) - if self.object: + if self.object and self.object.pk: kwargs['queryset'] = self.object.checkinlist_set.prefetch_related('limit_products') return formsetclass(self.request.POST if self.request.method == "POST" else None, @@ -297,7 +297,7 @@ class SubEventEditorMixin(MetaDataEditorMixin): form=QuotaForm, formset=QuotaFormSet, min_num=1, validate_min=True, can_order=False, can_delete=True, extra=extra, ) - if self.object: + if self.object and self.object.pk: kwargs['queryset'] = self.object.quotas.prefetch_related('items', 'variations') return formsetclass( @@ -400,10 +400,10 @@ class SubEventEditorMixin(MetaDataEditorMixin): def itemvar_forms(self): se_item_instances = { sei.item_id: sei for sei in SubEventItem.objects.filter(subevent=self.object) - } + } if self.object and self.object.pk else {} se_var_instances = { sei.variation_id: sei for sei in SubEventItemVariation.objects.filter(subevent=self.object) - } + } if self.object and self.object.pk else {} if self.copy_from: se_item_instances = { @@ -821,18 +821,18 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Asyn for t in self.get_times(): se = copy.copy(form.instance) - se.date_from = make_aware(datetime.combine(rdate, t['time_from']), tz, is_dst=False) + se.date_from = make_aware(datetime.combine(rdate, t['time_from'].replace(fold=1)), tz) if t.get('time_to'): se.date_to = ( - make_aware(datetime.combine(rdate, t['time_to']), tz, is_dst=False) + make_aware(datetime.combine(rdate, t['time_to'].replace(fold=1)), tz) if t.get('time_to') > t.get('time_from') - else make_aware(datetime.combine(rdate + timedelta(days=1), t['time_to']), tz, is_dst=False) + else make_aware(datetime.combine(rdate + timedelta(days=1), t['time_to'].replace(fold=1)), tz) ) else: se.date_to = None se.date_admission = ( - make_aware(datetime.combine(rdate, t['time_admission'].replace(fold=1)), tz, is_dst=False) + make_aware(datetime.combine(rdate, t['time_admission'].replace(fold=1)), tz) if t.get('time_admission') else None ) @@ -1148,6 +1148,7 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie subevents = list(self.get_queryset().prefetch_related('checkinlist_set')) to_save_products = [] to_save_gates = [] + to_delete_list_ids = [] for f in self.list_formset.forms: if self.list_formset._should_delete_form(f) and f in self.list_formset.extra_forms: @@ -1159,7 +1160,7 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie log_entries += [ q.log_action(action='pretix.event.checkinlist.deleted', user=self.request.user, save=False), ] - q.delete() + to_delete_list_ids.append(q.pk) elif f in self.list_formset.extra_forms: change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} for se in subevents: @@ -1198,6 +1199,8 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie CheckinList.limit_products.through.objects.bulk_create(to_save_products) if to_save_gates: CheckinList.gates.through.objects.bulk_create(to_save_gates) + if to_delete_list_ids: + CheckinList.objects.filter(id__in=to_delete_list_ids).delete() def save_quota_formset(self, log_entries): if not self.quota_formset.has_changed(): @@ -1224,6 +1227,7 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie if to_delete_quota_ids: Quota.objects.filter(id__in=to_delete_quota_ids).delete() + to_delete_quota_ids = [] for f in self.quota_formset.forms: if self.quota_formset._should_delete_form(f) and f in self.quota_formset.extra_forms: @@ -1245,7 +1249,7 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie 'id': q.pk }, save=False) ] - q.delete() + to_delete_quota_ids.append(q.pk) elif f in self.quota_formset.extra_forms: change_data = {k: f.cleaned_data.get(k) for k in f.changed_data} for se in subevents: @@ -1288,6 +1292,8 @@ class SubEventBulkEdit(SubEventQueryMixin, EventPermissionRequiredMixin, FormVie Quota.items.through.objects.bulk_create(to_save_items) if to_save_variations: Quota.variations.through.objects.bulk_create(to_save_variations) + if to_delete_quota_ids: + Quota.objects.filter(id__in=to_delete_quota_ids).delete() def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) diff --git a/src/pretix/control/views/typeahead.py b/src/pretix/control/views/typeahead.py index d5a596c66..7045038dc 100644 --- a/src/pretix/control/views/typeahead.py +++ b/src/pretix/control/views/typeahead.py @@ -33,8 +33,8 @@ # License for the specific language governing permissions and limitations under the License. from datetime import datetime, time +from zoneinfo import ZoneInfo -import pytz from dateutil.parser import parse from django.core.exceptions import PermissionDenied from django.db.models import F, Max, Min, Q @@ -88,7 +88,7 @@ def serialize_event(e): if e.min_from is None: dr = pgettext('subevent', 'No dates') else: - tz = pytz.timezone(e.settings.timezone) + tz = ZoneInfo(e.settings.timezone) dr = _('Series:') + ' ' + daterange( e.min_from.astimezone(tz), (e.max_fromto or e.max_to or e.max_from).astimezone(tz) diff --git a/src/pretix/multidomain/event_domain_urlconf.py b/src/pretix/multidomain/event_domain_urlconf.py index 29987671c..5273d53d4 100644 --- a/src/pretix/multidomain/event_domain_urlconf.py +++ b/src/pretix/multidomain/event_domain_urlconf.py @@ -22,8 +22,7 @@ import importlib.util from django.apps import apps -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from pretix.multidomain.plugin_handler import plugin_event_urls from pretix.presale.urls import event_patterns, locale_patterns diff --git a/src/pretix/multidomain/maindomain_urlconf.py b/src/pretix/multidomain/maindomain_urlconf.py index 34fd8cc0e..8d932888f 100644 --- a/src/pretix/multidomain/maindomain_urlconf.py +++ b/src/pretix/multidomain/maindomain_urlconf.py @@ -35,8 +35,7 @@ import importlib.util from django.apps import apps -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from django.views.generic import TemplateView from pretix.multidomain.plugin_handler import plugin_event_urls diff --git a/src/pretix/multidomain/middlewares.py b/src/pretix/multidomain/middlewares.py index 004a36ed6..e5d3c0fa0 100644 --- a/src/pretix/multidomain/middlewares.py +++ b/src/pretix/multidomain/middlewares.py @@ -42,7 +42,9 @@ from django.contrib.sessions.middleware import ( from django.core.cache import cache from django.core.exceptions import DisallowedHost from django.http.request import split_domain_port -from django.middleware.csrf import CsrfViewMiddleware as BaseCsrfMiddleware +from django.middleware.csrf import ( + CSRF_SESSION_KEY, CsrfViewMiddleware as BaseCsrfMiddleware, +) from django.shortcuts import render from django.urls import set_urlconf from django.utils.cache import patch_vary_headers @@ -189,35 +191,25 @@ class CsrfViewMiddleware(BaseCsrfMiddleware): a custom domain. """ - def process_response(self, request, response): - if getattr(response, 'csrf_processing_done', False): - return response - - # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was - # never called, probably because a request middleware returned a response - # (for example, contrib.auth redirecting to a login page). - if request.META.get("CSRF_COOKIE") is None: - return response - - if not request.META.get("CSRF_COOKIE_USED", False): - return response - - # Set the CSRF cookie even if it's already set, so we renew - # the expiry timer. - set_cookie_without_samesite( - request, response, - settings.CSRF_COOKIE_NAME, - request.META["CSRF_COOKIE"], - max_age=settings.CSRF_COOKIE_AGE, - domain=get_cookie_domain(request), - path=settings.CSRF_COOKIE_PATH, - secure=request.scheme == 'https', - httponly=settings.CSRF_COOKIE_HTTPONLY - ) - # Content varies with the CSRF cookie, so set the Vary header. - patch_vary_headers(response, ('Cookie',)) - response.csrf_processing_done = True - return response + def _set_csrf_cookie(self, request, response): + if settings.CSRF_USE_SESSIONS: + if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]: + request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"] + else: + # Set the CSRF cookie even if it's already set, so we renew + # the expiry timer. + set_cookie_without_samesite( + request, response, + settings.CSRF_COOKIE_NAME, + request.META["CSRF_COOKIE"], + max_age=settings.CSRF_COOKIE_AGE, + domain=get_cookie_domain(request), + path=settings.CSRF_COOKIE_PATH, + secure=request.scheme == 'https', + httponly=settings.CSRF_COOKIE_HTTPONLY + ) + # Content varies with the CSRF cookie, so set the Vary header. + patch_vary_headers(response, ('Cookie',)) def get_cookie_domain(request): diff --git a/src/pretix/multidomain/organizer_domain_urlconf.py b/src/pretix/multidomain/organizer_domain_urlconf.py index 603e91e9c..7bdd08e87 100644 --- a/src/pretix/multidomain/organizer_domain_urlconf.py +++ b/src/pretix/multidomain/organizer_domain_urlconf.py @@ -22,8 +22,7 @@ import importlib.util from django.apps import apps -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from pretix.multidomain.plugin_handler import plugin_event_urls from pretix.presale.urls import ( diff --git a/src/pretix/multidomain/urlreverse.py b/src/pretix/multidomain/urlreverse.py index 04d9cf49f..edfe4b361 100644 --- a/src/pretix/multidomain/urlreverse.py +++ b/src/pretix/multidomain/urlreverse.py @@ -45,6 +45,9 @@ from .models import KnownDomain def get_event_domain(event, fallback=False, return_info=False): assert isinstance(event, Event) + if not event.pk: + # Can happen on the "event deleted" response + return (None, None) if return_info else None suffix = ('_fallback' if fallback else '') + ('_info' if return_info else '') domain = getattr(event, '_cached_domain' + suffix, None) or event.cache.get('domain' + suffix) if domain is None: diff --git a/src/pretix/plugins/badges/urls.py b/src/pretix/plugins/badges/urls.py index 13523a786..d79f0adca 100644 --- a/src/pretix/plugins/badges/urls.py +++ b/src/pretix/plugins/badges/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from pretix.api.urls import event_router from pretix.plugins.badges.api import BadgeItemViewSet, BadgeLayoutViewSet diff --git a/src/pretix/plugins/banktransfer/urls.py b/src/pretix/plugins/banktransfer/urls.py index 79442e775..968d0a9f1 100644 --- a/src/pretix/plugins/banktransfer/urls.py +++ b/src/pretix/plugins/banktransfer/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from pretix.api.urls import orga_router from pretix.plugins.banktransfer.api import BankImportJobViewSet diff --git a/src/pretix/plugins/checkinlists/exporters.py b/src/pretix/plugins/checkinlists/exporters.py index 03b1c9627..d40ccb206 100644 --- a/src/pretix/plugins/checkinlists/exporters.py +++ b/src/pretix/plugins/checkinlists/exporters.py @@ -33,6 +33,7 @@ # License for the specific language governing permissions and limitations under the License. from collections import OrderedDict +from datetime import timezone import bleach import dateutil.parser @@ -47,7 +48,6 @@ from django.utils.timezone import is_aware, make_aware, now from django.utils.translation import ( gettext as _, gettext_lazy, pgettext, pgettext_lazy, ) -from pytz import UTC from reportlab.lib.units import mm from reportlab.platypus import Flowable, Paragraph, Spacer, Table, TableStyle @@ -513,7 +513,7 @@ class CSVCheckinList(CheckInListMixin, ListExporter): elif op.last_checked_in: last_checked_in = op.last_checked_in if last_checked_in and not is_aware(last_checked_in): - last_checked_in = make_aware(last_checked_in, UTC) + last_checked_in = make_aware(last_checked_in, timezone.utc) last_checked_out = None if isinstance(op.last_checked_out, str): # SQLite @@ -521,7 +521,7 @@ class CSVCheckinList(CheckInListMixin, ListExporter): elif op.last_checked_out: last_checked_out = op.last_checked_out if last_checked_out and not is_aware(last_checked_out): - last_checked_out = make_aware(last_checked_out, UTC) + last_checked_out = make_aware(last_checked_out, timezone.utc) row = [ op.order.code, diff --git a/src/pretix/plugins/paypal/urls.py b/src/pretix/plugins/paypal/urls.py index 0a2900427..3b8dc501c 100644 --- a/src/pretix/plugins/paypal/urls.py +++ b/src/pretix/plugins/paypal/urls.py @@ -19,8 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from .views import abort, oauth_disconnect, redirect_view, success diff --git a/src/pretix/plugins/paypal2/signals.py b/src/pretix/plugins/paypal2/signals.py index fdd694d06..a1d3ef376 100644 --- a/src/pretix/plugins/paypal2/signals.py +++ b/src/pretix/plugins/paypal2/signals.py @@ -29,7 +29,7 @@ from django.http import HttpRequest, HttpResponse from django.template.loader import get_template from django.urls import resolve from django.utils.crypto import get_random_string -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, pgettext_lazy from pretix.base.forms import SecretKeySettingsField from pretix.base.middleware import _merge_csp, _parse_csp, _render_csp @@ -61,7 +61,10 @@ def pretixcontrol_logentry_display(sender, logentry, **kwargs): 'PAYMENT.SALE.REFUNDED': _('Payment refunded.'), 'PAYMENT.SALE.REVERSED': _('Payment reversed.'), 'PAYMENT.SALE.PENDING': _('Payment pending.'), - 'CHECKOUT.ORDER.APPROVED': _('Order approved.'), + 'CHECKOUT.ORDER.APPROVED': pgettext_lazy('paypal', 'Order approved.'), + 'CHECKOUT.ORDER.COMPLETED': pgettext_lazy('paypal', 'Order completed.'), + 'PAYMENT.CAPTURE.COMPLETED': pgettext_lazy('paypal', 'Capture completed.'), + 'PAYMENT.CAPTURE.PENDING': pgettext_lazy('paypal', 'Capture pending.'), } if event_type in plains: diff --git a/src/pretix/plugins/paypal2/urls.py b/src/pretix/plugins/paypal2/urls.py index f85b71864..11cb5715a 100644 --- a/src/pretix/plugins/paypal2/urls.py +++ b/src/pretix/plugins/paypal2/urls.py @@ -19,8 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from .views import ( PayView, XHRView, abort, isu_disconnect, isu_return, redirect_view, diff --git a/src/pretix/plugins/reports/exporters.py b/src/pretix/plugins/reports/exporters.py index 853e37ae3..1a33f6cc3 100644 --- a/src/pretix/plugins/reports/exporters.py +++ b/src/pretix/plugins/reports/exporters.py @@ -37,7 +37,6 @@ import tempfile from collections import OrderedDict, defaultdict from decimal import Decimal -import pytz from dateutil.parser import parse from django import forms from django.conf import settings @@ -123,7 +122,7 @@ class ReportlabExportMixin: return 'report-%s.pdf' % self.event.slug, 'application/pdf', self.create(form_data) def get_filename(self): - tz = pytz.timezone(self.event.settings.timezone) + tz = self.event.timezone return "%s-%s.pdf" % (self.name, now().astimezone(tz).strftime("%Y-%m-%d-%H-%M-%S")) @staticmethod @@ -491,7 +490,7 @@ class OrderTaxListReportPDF(Report): headlinestyle = self.get_style() headlinestyle.fontSize = 15 headlinestyle.fontName = 'OpenSansBd' - tz = pytz.timezone(self.event.settings.timezone) + tz = self.event.timezone tax_rates = set( a for a diff --git a/src/pretix/plugins/returnurl/urls.py b/src/pretix/plugins/returnurl/urls.py index 51b1a51d7..df5f9d176 100644 --- a/src/pretix/plugins/returnurl/urls.py +++ b/src/pretix/plugins/returnurl/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from .views import ReturnSettings diff --git a/src/pretix/plugins/sendmail/models.py b/src/pretix/plugins/sendmail/models.py index 18e53afbe..920420de7 100644 --- a/src/pretix/plugins/sendmail/models.py +++ b/src/pretix/plugins/sendmail/models.py @@ -21,6 +21,7 @@ # from datetime import datetime, time, timedelta +from dateutil.tz import datetime_exists from django.db import models from django.db.models import Exists, OuterRef, Q from django.utils import timezone @@ -69,6 +70,9 @@ class ScheduledMail(models.Model): def save(self, **kwargs): if not self.computed_datetime: self.recompute() + if 'update_fields' in kwargs: + kwargs['update_fields'] = {'computed_datetime', 'last_computed', 'state'}.union(kwargs['update_fields']) + super().save(**kwargs) def recompute(self): @@ -88,10 +92,14 @@ class ScheduledMail(models.Model): base_time = (e.date_to or e.date_from) if self.rule.offset_to_event_end else e.date_from d = base_time.astimezone(self.event.timezone).date() + offset self.computed_datetime = make_aware( - datetime.combine(d, time(hour=st.hour, minute=st.minute, second=st.second, microsecond=0)), + datetime.combine(d, time(hour=st.hour, minute=st.minute, second=st.second, microsecond=0, fold=1)), self.event.timezone, - is_dst=False, # prevent AmbiguousTimeError ) + if not datetime_exists(self.computed_datetime): + self.computed_datetime = make_aware( + datetime.combine(d, time(hour=st.hour, minute=st.minute, second=st.second, microsecond=0)) + timedelta(hours=1), + self.event.timezone, + ) if self.computed_datetime > timezone.now() and self.state == self.STATE_MISSED: self.state = self.STATE_SCHEDULED diff --git a/src/pretix/plugins/sendmail/urls.py b/src/pretix/plugins/sendmail/urls.py index d972f438d..757c7cb07 100644 --- a/src/pretix/plugins/sendmail/urls.py +++ b/src/pretix/plugins/sendmail/urls.py @@ -32,7 +32,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. -from django.conf.urls import re_path +from django.urls import re_path from pretix.api.urls import event_router diff --git a/src/pretix/plugins/statistics/urls.py b/src/pretix/plugins/statistics/urls.py index 8231d59fe..e7a08f3a0 100644 --- a/src/pretix/plugins/statistics/urls.py +++ b/src/pretix/plugins/statistics/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from . import views diff --git a/src/pretix/plugins/stripe/urls.py b/src/pretix/plugins/stripe/urls.py index dbcf9d45c..a601a7f19 100644 --- a/src/pretix/plugins/stripe/urls.py +++ b/src/pretix/plugins/stripe/urls.py @@ -19,8 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from pretix.multidomain import event_url diff --git a/src/pretix/plugins/ticketoutputpdf/urls.py b/src/pretix/plugins/ticketoutputpdf/urls.py index 18ec9b370..e3b158012 100644 --- a/src/pretix/plugins/ticketoutputpdf/urls.py +++ b/src/pretix/plugins/ticketoutputpdf/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from pretix.api.urls import event_router from pretix.plugins.ticketoutputpdf.api import ( diff --git a/src/pretix/plugins/webcheckin/urls.py b/src/pretix/plugins/webcheckin/urls.py index 5fa2b7875..40551d546 100644 --- a/src/pretix/plugins/webcheckin/urls.py +++ b/src/pretix/plugins/webcheckin/urls.py @@ -19,7 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from django.conf.urls import re_path +from django.urls import re_path from .views import IndexView diff --git a/src/pretix/presale/forms/renderers.py b/src/pretix/presale/forms/renderers.py index a94ca72c7..527b6ae72 100644 --- a/src/pretix/presale/forms/renderers.py +++ b/src/pretix/presale/forms/renderers.py @@ -19,7 +19,6 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from bootstrap3.renderers import FieldRenderer from bootstrap3.text import text_value from bootstrap3.utils import add_css_class from django.forms import CheckboxInput, CheckboxSelectMultiple, RadioSelect @@ -28,6 +27,8 @@ from django.utils.html import escape, format_html, strip_tags from django.utils.safestring import mark_safe from django.utils.translation import pgettext +from pretix.base.forms.renderers import FieldRenderer + def render_label(content, label_for=None, label_class=None, label_title='', label_id='', optional=False, is_valid=None, attrs=None): """ diff --git a/src/pretix/presale/ical.py b/src/pretix/presale/ical.py index 21f475259..056fcaf1f 100644 --- a/src/pretix/presale/ical.py +++ b/src/pretix/presale/ical.py @@ -22,7 +22,6 @@ import datetime from urllib.parse import urlparse -import pytz import vobject from django.conf import settings from django.utils.formats import date_format @@ -41,11 +40,11 @@ def get_public_ical(events): """ cal = vobject.iCalendar() cal.add('prodid').value = '-//pretix//{}//'.format(settings.PRETIX_INSTANCE_NAME.replace(" ", "_")) - creation_time = datetime.datetime.now(pytz.utc) + creation_time = datetime.datetime.now(datetime.timezone.utc) for ev in events: event = ev if isinstance(ev, Event) else ev.event - tz = pytz.timezone(event.settings.timezone) + tz = event.timezone if isinstance(ev, Event): url = build_absolute_uri(event, 'presale:event.index') else: @@ -114,9 +113,9 @@ def get_private_icals(event, positions): - It would be pretty hard to implement it in a way that doesn't require us to use distinct settings fields for emails to customers and to attendees, which feels like an overcomplication. """ - tz = pytz.timezone(event.settings.timezone) + tz = event.timezone - creation_time = datetime.datetime.now(pytz.utc) + creation_time = datetime.datetime.now(datetime.timezone.utc) calobjects = [] evs = set(p.subevent or event for p in positions) diff --git a/src/pretix/presale/urls.py b/src/pretix/presale/urls.py index edf34b5ba..1afd9dcb1 100644 --- a/src/pretix/presale/urls.py +++ b/src/pretix/presale/urls.py @@ -32,8 +32,7 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from django.views.decorators.csrf import csrf_exempt import pretix.presale.views.cart diff --git a/src/pretix/presale/views/oidc_op.py b/src/pretix/presale/views/oidc_op.py index d8a8401c2..5a388be17 100644 --- a/src/pretix/presale/views/oidc_op.py +++ b/src/pretix/presale/views/oidc_op.py @@ -29,7 +29,6 @@ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from Crypto.PublicKey import RSA from django.db import transaction from django.http import Http404, HttpResponse, JsonResponse -from django.middleware.csrf import _compare_masked_tokens from django.shortcuts import redirect, render from django.utils.crypto import get_random_string from django.utils.decorators import method_decorator @@ -103,8 +102,9 @@ class AuthorizeView(View): return self._process_auth_request(request, request.GET) def post(self, request, *args, **kwargs): - request_token = CsrfViewMiddleware(lambda: None)._get_token(request) - if not request_token or not _compare_masked_tokens(request.POST.get('csrfmiddlewaretoken', ''), request_token): + try: + CsrfViewMiddleware(lambda: None)._check_token(request) + except: # External request, we prefer GET and will redirect to prevent confusion with our login form return redirect(request.path + '?' + request.POST.urlencode()) return self._process_auth_request(request, request.GET) diff --git a/src/pretix/presale/views/organizer.py b/src/pretix/presale/views/organizer.py index 45cf1f14c..c6deb1083 100644 --- a/src/pretix/presale/views/organizer.py +++ b/src/pretix/presale/views/organizer.py @@ -38,10 +38,10 @@ from collections import defaultdict from datetime import date, datetime, time, timedelta from functools import reduce from urllib.parse import quote, urlencode +from zoneinfo import ZoneInfo import dateutil import isoweek -import pytz from django.conf import settings from django.core.cache import caches from django.db.models import Exists, Max, Min, OuterRef, Prefetch, Q @@ -392,7 +392,7 @@ class OrganizerIndex(OrganizerViewMixin, EventListMixin, ListView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) for event in ctx['events']: - event.tzname = pytz.timezone(event.cache.get_or_set('timezone', lambda: event.settings.timezone)) + event.tzname = ZoneInfo(event.cache.get_or_set('timezone', lambda: event.settings.timezone)) if event.has_subevents: event.daterange = daterange( event.min_from.astimezone(event.tzname), @@ -506,7 +506,7 @@ def add_subevents_for_days(qs, before, after, ebd, timezones, event=None, cart_n continue timezones.add(s.timezone) - tz = pytz.timezone(s.timezone) + tz = ZoneInfo(s.timezone) datetime_from = se.date_from.astimezone(tz) date_from = datetime_from.date() if s.show_date_to and se.date_to: @@ -623,8 +623,8 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView): raise Http404() tz = get_current_timezone() - before = tz.localize(datetime(self.year, self.month, 1, 0, 0, 0)) - timedelta(days=1) - after = tz.localize(datetime(self.year, self.month, ndays, 0, 0, 0)) + timedelta(days=1) + before = datetime(self.year, self.month, 1, 0, 0, 0, tzinfo=tz) - timedelta(days=1) + after = datetime(self.year, self.month, ndays, 0, 0, 0, tzinfo=tz) + timedelta(days=1) ctx['date'] = date(self.year, self.month, 1) ctx['before'] = before @@ -700,12 +700,12 @@ class WeekCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): tz = get_current_timezone() week = isoweek.Week(self.year, self.week) - before = tz.localize(datetime( - week.monday().year, week.monday().month, week.monday().day, 0, 0, 0, - )) - timedelta(days=1) - after = tz.localize(datetime( - week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, - )) + timedelta(days=1) + before = datetime( + week.monday().year, week.monday().month, week.monday().day, 0, 0, 0, tzinfo=tz, + ) - timedelta(days=1) + after = datetime( + week.sunday().year, week.sunday().month, week.sunday().day, 0, 0, 0, tzinfo=tz, + ) + timedelta(days=1) ctx['date'] = week.monday() ctx['before'] = before @@ -827,12 +827,12 @@ class DayCalendarView(OrganizerViewMixin, EventListMixin, TemplateView): ctx = super().get_context_data() tz = get_current_timezone() - before = tz.localize(datetime( - self.date.year, self.date.month, self.date.day, 0, 0, 0, - )) - timedelta(days=1) - after = tz.localize(datetime( - self.date.year, self.date.month, self.date.day, 0, 0, 0, - )) + timedelta(days=1) + before = datetime( + self.date.year, self.date.month, self.date.day, 0, 0, 0, tzinfo=tz, + ) - timedelta(days=1) + after = datetime( + self.date.year, self.date.month, self.date.day, 0, 0, 0, tzinfo=tz, + ) + timedelta(days=1) ctx['date'] = self.date ctx['cal_tz'] = self.tz diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index f41838249..b1e1d5d38 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -26,9 +26,9 @@ import logging from collections import defaultdict from datetime import date, datetime, timedelta from urllib.parse import urljoin +from zoneinfo import ZoneInfo import isoweek -import pytz from compressor.filters.jsmin import rJSMinFilter from django.conf import settings from django.contrib.staticfiles import finders @@ -438,7 +438,7 @@ class WidgetAPIProductList(EventListMixin, View): event = ev.event else: event = ev - tz = pytz.timezone(e['timezone']) + tz = ZoneInfo(e['timezone']) time = date_format(ev.date_from.astimezone(tz), 'TIME_FORMAT') if e.get('time') and event.settings.show_times else None if time and ev.date_to and ev.date_from.astimezone(tz).date() == ev.date_to.astimezone(tz).date() and event.settings.show_date_to: time += ' – ' + date_format(ev.date_to.astimezone(tz), 'TIME_FORMAT') @@ -626,7 +626,7 @@ class WidgetAPIProductList(EventListMixin, View): data['events'] = [] qs = self._get_event_queryset() for event in qs: - tz = pytz.timezone(event.cache.get_or_set('timezone', lambda: event.settings.timezone)) + tz = ZoneInfo(event.cache.get_or_set('timezone', lambda: event.settings.timezone)) if event.has_subevents: dr = daterange( event.min_from.astimezone(tz), diff --git a/src/pretix/settings.py b/src/pretix/settings.py index 63939e2d4..7a73b4e79 100644 --- a/src/pretix/settings.py +++ b/src/pretix/settings.py @@ -174,7 +174,7 @@ SITE_URL = config.get('pretix', 'url', fallback='http://localhost:8000') if SITE_URL.endswith('/'): SITE_URL = SITE_URL[:-1] -CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).hostname] +CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).scheme + '://' + urlparse(SITE_URL).hostname] TRUST_X_FORWARDED_FOR = config.get('pretix', 'trust_x_forwarded_for', fallback=False) USE_X_FORWARDED_HOST = config.get('pretix', 'trust_x_forwarded_host', fallback=False) @@ -638,8 +638,8 @@ CELERY_TASK_ROUTES = ([ BOOTSTRAP3 = { 'success_css_class': '', 'field_renderers': { - 'default': 'bootstrap3.renderers.FieldRenderer', - 'inline': 'bootstrap3.renderers.InlineFieldRenderer', + 'default': 'pretix.base.forms.renderers.FieldRenderer', + 'inline': 'pretix.base.forms.renderers.InlineFieldRenderer', 'control': 'pretix.control.forms.renderers.ControlFieldRenderer', 'bulkedit': 'pretix.control.forms.renderers.BulkEditFieldRenderer', 'bulkedit_inline': 'pretix.control.forms.renderers.InlineBulkEditFieldRenderer', diff --git a/src/pretix/static/colorpicker/bootstrap-colorpicker.scss b/src/pretix/static/colorpicker/bootstrap-colorpicker.scss index 616630a55..b5cda6852 100644 --- a/src/pretix/static/colorpicker/bootstrap-colorpicker.scss +++ b/src/pretix/static/colorpicker/bootstrap-colorpicker.scss @@ -227,4 +227,3 @@ .colorpicker-inline.colorpicker-visible { display: inline-block; } -/*# sourceMappingURL=bootstrap-colorpicker.css.map */ diff --git a/src/pretix/static/pdfjs/pdf.js b/src/pretix/static/pdfjs/pdf.js index 1afcab409..f3fa02781 100644 --- a/src/pretix/static/pdfjs/pdf.js +++ b/src/pretix/static/pdfjs/pdf.js @@ -15613,5 +15613,4 @@ const pdfjsBuild = 'd0823066c'; /******/ return __webpack_exports__; /******/ })() ; -}); -//# sourceMappingURL=pdf.js.map \ No newline at end of file +}); \ No newline at end of file diff --git a/src/pretix/static/pdfjs/pdf.worker.js b/src/pretix/static/pdfjs/pdf.worker.js index 340a80a59..4de6824c9 100644 --- a/src/pretix/static/pdfjs/pdf.worker.js +++ b/src/pretix/static/pdfjs/pdf.worker.js @@ -64052,5 +64052,4 @@ const pdfjsBuild = 'd0823066c'; /******/ return __webpack_exports__; /******/ })() ; -}); -//# sourceMappingURL=pdf.worker.js.map \ No newline at end of file +}); \ No newline at end of file diff --git a/src/pretix/static/pretixbase/scss/_theme.scss b/src/pretix/static/pretixbase/scss/_theme.scss index 2b27553c6..2ac15521b 100644 --- a/src/pretix/static/pretixbase/scss/_theme.scss +++ b/src/pretix/static/pretixbase/scss/_theme.scss @@ -177,7 +177,7 @@ input[type=number]::-webkit-outer-spin-button { } .pagination > li.page-current > a:hover, .pagination > li.page-current > a:focus { - color: #7f5a91; + color: $brand-primary; background-color: #fff; border: 1px solid #ddd; } diff --git a/src/pretix/static/rrule/rrule.js b/src/pretix/static/rrule/rrule.js index d89504a30..70a496883 100644 --- a/src/pretix/static/rrule/rrule.js +++ b/src/pretix/static/rrule/rrule.js @@ -3799,5 +3799,4 @@ var isFullyConvertible = totext.isFullyConvertible; /***/ }) /******/ ]); -}); -//# sourceMappingURL=rrule.js.map \ No newline at end of file +}); \ No newline at end of file diff --git a/src/pretix/urls.py b/src/pretix/urls.py index 3fd42ea80..205142e69 100644 --- a/src/pretix/urls.py +++ b/src/pretix/urls.py @@ -33,8 +33,7 @@ # License for the specific language governing permissions and limitations under the License. from django.conf import settings -from django.conf.urls import re_path -from django.urls import include +from django.urls import include, re_path from django.views.generic import RedirectView import pretix.control.urls diff --git a/src/setup.cfg b/src/setup.cfg index 08013b28c..f966605f1 100644 --- a/src/setup.cfg +++ b/src/setup.cfg @@ -28,11 +28,12 @@ filterwarnings = ignore::DeprecationWarning:kombu ignore::DeprecationWarning:django ignore::DeprecationWarning:cgi + ignore::DeprecationWarning:vat_moss + ignore::pytz_deprecation_shim._exceptions.PytzUsageWarning:vobject ignore:the load_module.* method is deprecated:DeprecationWarning ignore::ImportWarning ignore::ResourceWarning ignore:django.contrib.staticfiles.templatetags.static:DeprecationWarning - ignore::django.utils.deprecation.RemovedInDjango41Warning: ignore::DeprecationWarning:compressor ignore:pkg_resources is deprecated as an API: diff --git a/src/tests/api/conftest.py b/src/tests/api/conftest.py index cee074e98..8d537e7af 100644 --- a/src/tests/api/conftest.py +++ b/src/tests/api/conftest.py @@ -32,13 +32,12 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. -from datetime import datetime +from datetime import datetime, timezone import pytest from django.test import utils from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from rest_framework.test import APIClient from pretix.base.models import Device, Event, Organizer, Team, User @@ -67,7 +66,7 @@ def meta_prop(organizer): def event(organizer, meta_prop): e = Event.objects.create( organizer=organizer, name='Dummy', slug='dummy', - date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC), + date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf', is_public=True ) @@ -82,7 +81,7 @@ def event(organizer, meta_prop): def event2(organizer, meta_prop): e = Event.objects.create( organizer=organizer, name='Dummy2', slug='dummy2', - date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC), + date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf' ) e.meta_values.create(property=meta_prop, value="Conference") @@ -94,7 +93,7 @@ def event2(organizer, meta_prop): def event3(organizer, meta_prop): e = Event.objects.create( organizer=organizer, name='Dummy3', slug='dummy3', - date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC), + date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf' ) e.meta_values.create(property=meta_prop, value="Conference") @@ -173,7 +172,7 @@ def device_client(client, device): def subevent(event, meta_prop): event.has_subevents = True event.save() - se = event.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se = event.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc)) se.meta_values.create(property=meta_prop, value="Workshop") return se @@ -184,7 +183,7 @@ def subevent(event, meta_prop): def subevent2(event2, meta_prop): event2.has_subevents = True event2.save() - se = event2.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se = event2.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc)) se.meta_values.create(property=meta_prop, value="Workshop") return se diff --git a/src/tests/api/test_cart.py b/src/tests/api/test_cart.py index 8849d6448..4767411c2 100644 --- a/src/tests/api/test_cart.py +++ b/src/tests/api/test_cart.py @@ -28,7 +28,6 @@ import pytest from django.core.files.base import ContentFile from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.base.models import Question, SeatingPlan @@ -95,15 +94,15 @@ TEST_CARTPOSITION_RES = { @pytest.mark.django_db def test_cp_list(token_client, organizer, event, item, taxrule, question): - testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime cr = CartPosition.objects.create( event=event, cart_id="aaa", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -116,15 +115,15 @@ def test_cp_list(token_client, organizer, event, item, taxrule, question): @pytest.mark.django_db def test_cp_list_api(token_client, organizer, event, item, taxrule, question): - testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -137,15 +136,15 @@ def test_cp_list_api(token_client, organizer, event, item, taxrule, question): @pytest.mark.django_db def test_cp_detail(token_client, organizer, event, item, taxrule, question): - testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk @@ -158,21 +157,21 @@ def test_cp_detail(token_client, organizer, event, item, taxrule, question): @pytest.mark.django_db def test_cp_delete(token_client, organizer, event, item, taxrule, question): - testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime cr = CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) ) CartPosition.objects.create( event=event, cart_id="aaa@api", item=item, addon_to=cr, price=23, attendee_name_parts={'full_name': 'Peter'}, - datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=UTC) + datetime=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2018, 6, 11, 10, 0, 0, 0, tzinfo=datetime.timezone.utc) ) res = dict(TEST_CARTPOSITION_RES) res["id"] = cr.pk diff --git a/src/tests/api/test_checkin.py b/src/tests/api/test_checkin.py index 3314e61d0..b7ef1b46c 100644 --- a/src/tests/api/test_checkin.py +++ b/src/tests/api/test_checkin.py @@ -31,7 +31,6 @@ from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled from i18nfield.strings import LazyI18nString -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.api.serializers.item import QuestionSerializer @@ -57,15 +56,15 @@ def other_item(event): @pytest.fixture def order(event, item, other_item, taxrule): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PAID, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=46, locale='en' ) InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ')) @@ -258,7 +257,7 @@ def test_list_list(token_client, organizer, event, clist, item, subevent, django '/api/v1/organizers/{}/events/{}/checkinlists/?subevent_match={}'.format(organizer.slug, event.slug, subevent.pk)) assert [res] == resp.data['results'] with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) resp = token_client.get( '/api/v1/organizers/{}/events/{}/checkinlists/?subevent={}'.format(organizer.slug, event.slug, se2.pk)) assert [] == resp.data['results'] @@ -577,7 +576,7 @@ def test_list_all_items_positions(token_client, organizer, event, clist, clist_a @pytest.mark.django_db def test_list_all_items_positions_by_subevent(token_client, organizer, event, clist, clist_all, item, other_item, order, subevent): with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) pfirst = order.positions.get(positionid=1) pfirst.subevent = se2 pfirst.save() diff --git a/src/tests/api/test_checkinrpc.py b/src/tests/api/test_checkinrpc.py index 40a7d53b9..4bca40405 100644 --- a/src/tests/api/test_checkinrpc.py +++ b/src/tests/api/test_checkinrpc.py @@ -29,7 +29,6 @@ from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled from i18nfield.strings import LazyI18nString -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.api.serializers.item import QuestionSerializer @@ -59,15 +58,15 @@ def other_item(event): @pytest.fixture def order(event, item, other_item, taxrule): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PAID, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=46, locale='en' ) InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ')) @@ -106,15 +105,15 @@ def order(event, item, other_item, taxrule): @pytest.fixture def order2(event2, item_on_event2): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='BAR', event=event2, email='dummy@dummy.test', status=Order.STATUS_PAID, secret="ylptCPNOxTyA", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=46, locale='en' ) InvoiceAddress.objects.create(order=o, company="Sample company", country=Country('NZ')) diff --git a/src/tests/api/test_device_event_selection.py b/src/tests/api/test_device_event_selection.py index 9dc823d30..c8a309065 100644 --- a/src/tests/api/test_device_event_selection.py +++ b/src/tests/api/test_device_event_selection.py @@ -20,13 +20,13 @@ # . # from datetime import datetime +from zoneinfo import ZoneInfo import pytest -import pytz from django_scopes import scopes_disabled from freezegun import freeze_time -tz = pytz.timezone("Asia/Tokyo") +tz = ZoneInfo("Asia/Tokyo") @pytest.mark.django_db @@ -40,22 +40,22 @@ def test_choose_between_events(device_client, device): with scopes_disabled(): e1 = device.organizer.events.create( name="Event", slug="e1", live=True, - date_from=tz.localize(datetime(2020, 1, 10, 14, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 15, 0)), + date_from=datetime(2020, 1, 10, 14, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 15, 0, tzinfo=tz), ) cl1 = e1.checkin_lists.create(name="Same name") e2 = device.organizer.events.create( name="Event", slug="e2", live=True, - date_from=tz.localize(datetime(2020, 1, 10, 16, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 17, 0)), + date_from=datetime(2020, 1, 10, 16, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 17, 0, tzinfo=tz), ) e2.checkin_lists.create(name="Other name") cl2 = e2.checkin_lists.create(name="Same name") e2.checkin_lists.create(name="Yet another name") tomorrow = device.organizer.events.create( name="Event", slug="tomorrow", live=True, - date_from=tz.localize(datetime(2020, 1, 11, 15, 0)), - date_to=tz.localize(datetime(2020, 1, 11, 16, 0)), + date_from=datetime(2020, 1, 11, 15, 0, tzinfo=tz), + date_to=datetime(2020, 1, 11, 16, 0, tzinfo=tz), ) cl3 = tomorrow.checkin_lists.create(name="Just any name") for e in device.organizer.events.all(): @@ -117,7 +117,7 @@ def test_choose_between_events(device_client, device): assert resp.data['event']['slug'] == 'e2' # check for overlapping events - e2.date_admission = tz.localize(datetime(2020, 1, 10, 14, 45)) + e2.date_admission = datetime(2020, 1, 10, 14, 45, tzinfo=tz) e2.save() with freeze_time("2020-01-10T14:45:00+09:00"): resp = device_client.get('/api/v1/device/eventselection?current_event=e1') @@ -131,28 +131,28 @@ def test_choose_between_subevents(device_client, device): with scopes_disabled(): e = device.organizer.events.create( name="Event", slug="e1", live=True, - date_from=tz.localize(datetime(2020, 1, 10, 14, 0)), + date_from=datetime(2020, 1, 10, 14, 0, tzinfo=tz), has_subevents=True, ) e.settings.timezone = "Asia/Tokyo" se1 = e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 10, 14, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 15, 0)), + date_from=datetime(2020, 1, 10, 14, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 15, 0, tzinfo=tz), ) cl1 = e.checkin_lists.create(name="Same name", subevent=se1) se2 = e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 10, 16, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 17, 0)), + date_from=datetime(2020, 1, 10, 16, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 17, 0, tzinfo=tz), ) cl2 = e.checkin_lists.create(name="Same name", subevent=se2) cl3 = e.checkin_lists.create(name="Other name") e.checkin_lists.create(name="Yet another name", subevent=se2) se_tomorrow = e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 11, 15, 0)), - date_to=tz.localize(datetime(2020, 1, 11, 16, 0)), + date_from=datetime(2020, 1, 11, 15, 0, tzinfo=tz), + date_to=datetime(2020, 1, 11, 16, 0, tzinfo=tz), ) with freeze_time("2020-01-10T14:30:00+09:00"): resp = device_client.get(f'/api/v1/device/eventselection?current_event=e1¤t_subevent={se1.pk}') @@ -217,7 +217,7 @@ def test_choose_between_subevents(device_client, device): assert resp.data['subevent'] == se2.pk # check for overlapping events - se2.date_admission = tz.localize(datetime(2020, 1, 10, 14, 45)) + se2.date_admission = datetime(2020, 1, 10, 14, 45, tzinfo=tz) se2.save() with freeze_time("2020-01-10T14:45:00+09:00"): resp = device_client.get(f'/api/v1/device/eventselection?current_event=e1¤t_subevent={se1.pk}') @@ -234,25 +234,25 @@ def test_require_gate(device_client, device): device.save() e = device.organizer.events.create( name="Event", slug="e1", live=True, - date_from=tz.localize(datetime(2020, 1, 10, 14, 0)), + date_from=datetime(2020, 1, 10, 14, 0, tzinfo=tz), has_subevents=True, ) e.settings.timezone = "Asia/Tokyo" se0 = e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 10, 9, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 10, 0)), + date_from=datetime(2020, 1, 10, 9, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 10, 0, tzinfo=tz), ) e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 10, 14, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 15, 0)), + date_from=datetime(2020, 1, 10, 14, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 15, 0, tzinfo=tz), ) cl1 = e.checkin_lists.create(name="Same name", subevent=se0) se2 = e.subevents.create( name="Event", active=True, - date_from=tz.localize(datetime(2020, 1, 10, 16, 0)), - date_to=tz.localize(datetime(2020, 1, 10, 17, 0)), + date_from=datetime(2020, 1, 10, 16, 0, tzinfo=tz), + date_to=datetime(2020, 1, 10, 17, 0, tzinfo=tz), ) e.checkin_lists.create(name="Same name", subevent=se2) cl3 = e.checkin_lists.create(name="Other name", subevent=se2) diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index 6c0bcd0cf..71da2caa7 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -33,7 +33,7 @@ # License for the specific language governing permissions and limitations under the License. import copy -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal from unittest import mock @@ -43,7 +43,6 @@ from django.core.files.base import ContentFile from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.base.models import ( @@ -63,15 +62,15 @@ def variations(item): @pytest.fixture def order(event, item, taxrule): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc), + expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=timezone.utc), total=23, locale='en' ) o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'), diff --git a/src/tests/api/test_idempotency.py b/src/tests/api/test_idempotency.py index baec6dda3..9f961184e 100644 --- a/src/tests/api/test_idempotency.py +++ b/src/tests/api/test_idempotency.py @@ -24,7 +24,6 @@ import json import pytest from django.utils.timezone import now -from pytz import UTC from pretix.api.models import ApiCall from pretix.base.models import Order @@ -159,8 +158,8 @@ def order(event): return Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=23, locale='en' ) diff --git a/src/tests/api/test_invoices.py b/src/tests/api/test_invoices.py index a158e44a1..1659a2934 100644 --- a/src/tests/api/test_invoices.py +++ b/src/tests/api/test_invoices.py @@ -27,7 +27,6 @@ import freezegun import pytest from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import InvoiceAddress, Order, OrderPosition from pretix.base.models.orders import OrderFee @@ -75,7 +74,7 @@ def quota(event, item): @pytest.fixture def order(event, item, taxrule, question): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) event.plugins += ",pretix.plugins.stripe" event.save() @@ -84,8 +83,8 @@ def order(event, item, taxrule, question): o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=23, locale='en' ) p1 = o.payments.create( @@ -140,7 +139,7 @@ def order(event, item, taxrule, question): @pytest.fixture def invoice(order): - testtime = datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime diff --git a/src/tests/api/test_items.py b/src/tests/api/test_items.py index c0b4c3cd6..89d9bea6c 100644 --- a/src/tests/api/test_items.py +++ b/src/tests/api/test_items.py @@ -33,7 +33,7 @@ # License for the specific language governing permissions and limitations under the License. import os import time -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal from unittest import mock @@ -42,7 +42,6 @@ from django.conf import settings from django.core.files.base import ContentFile from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.base.channels import get_all_sales_channels @@ -73,15 +72,15 @@ def category3(event, item): @pytest.fixture def order(event, item, taxrule): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc), + expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=timezone.utc), total=23, locale='en' ) o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'), @@ -108,7 +107,7 @@ def order_position(item, order, taxrule, variations): @pytest.fixture def cart_position(event, item, variations): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime @@ -1786,7 +1785,7 @@ def test_quota_list(token_client, organizer, event, quota, item, subevent): '/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, subevent.pk)) assert [res] == resp.data['results'] with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime(2017, 12, 27, 10, 0, 0, tzinfo=timezone.utc)) resp = token_client.get( '/api/v1/organizers/{}/events/{}/quotas/?subevent={}'.format(organizer.slug, event.slug, se2.pk)) assert [] == resp.data['results'] diff --git a/src/tests/api/test_membership.py b/src/tests/api/test_membership.py index a7d52770d..356762cb8 100644 --- a/src/tests/api/test_membership.py +++ b/src/tests/api/test_membership.py @@ -19,10 +19,9 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from datetime import datetime +from datetime import datetime, timezone import pytest -import pytz from django_scopes import scopes_disabled from i18nfield.strings import LazyI18nString @@ -54,8 +53,8 @@ def customer(organizer): def membership(organizer, customer, membershiptype): return customer.memberships.create( membership_type=membershiptype, - date_start=datetime(2021, 4, 1, 0, 0, 0, 0, tzinfo=pytz.UTC), - date_end=datetime(2021, 4, 8, 23, 59, 59, 999999, tzinfo=pytz.UTC), + date_start=datetime(2021, 4, 1, 0, 0, 0, 0, tzinfo=timezone.utc), + date_end=datetime(2021, 4, 8, 23, 59, 59, 999999, tzinfo=timezone.utc), attendee_name_parts={ "_scheme": "given_family", 'given_name': 'John', diff --git a/src/tests/api/test_order_change.py b/src/tests/api/test_order_change.py index 27cd6f4e8..192cba732 100644 --- a/src/tests/api/test_order_change.py +++ b/src/tests/api/test_order_change.py @@ -30,7 +30,6 @@ from django.core.files.base import ContentFile from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.base.models import ( @@ -90,7 +89,7 @@ def seat(event, organizer, item): @pytest.fixture def order(event, item, taxrule, question): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) event.plugins += ",pretix.plugins.stripe" event.save() @@ -99,8 +98,8 @@ def order(event, item, taxrule, question): o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=23, locale='en' ) p1 = o.payments.create( @@ -720,7 +719,7 @@ def test_orderposition_price_calculation_subevent(token_client, organizer, event def test_orderposition_price_calculation_subevent_with_override(token_client, organizer, event, order, subevent): with scopes_disabled(): item2 = event.items.create(name="Budget Ticket", default_price=23) - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) se2.subeventitem_set.create(item=item2, price=12) op = order.positions.first() op.subevent = subevent @@ -1262,7 +1261,7 @@ def test_position_update_change_item_variation_mismatch(token_client, organizer, @pytest.mark.django_db def test_position_update_change_subevent(token_client, organizer, event, order, quota, item, subevent): with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) q2 = se2.quotas.create(name="foo", size=1, event=event) q2.items.add(item) op = order.positions.first() @@ -1284,7 +1283,7 @@ def test_position_update_change_subevent(token_client, organizer, event, order, @pytest.mark.django_db def test_position_update_change_subevent_quota_empty(token_client, organizer, event, order, quota, item, subevent): with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) q2 = se2.quotas.create(name="foo", size=0, event=event) q2.items.add(item) op = order.positions.first() @@ -1365,7 +1364,7 @@ def test_position_update_change_subevent_keep_seat(token_client, organizer, even with scopes_disabled(): seat.subevent = subevent seat.save() - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) seat2 = event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se2) q2 = se2.quotas.create(name="foo", size=1, event=event) q2.items.add(item) @@ -1392,7 +1391,7 @@ def test_position_update_change_subevent_missing_seat(token_client, organizer, e with scopes_disabled(): seat.subevent = subevent seat.save() - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) q2 = se2.quotas.create(name="foo", size=1, event=event) q2.items.add(item) op = order.positions.first() diff --git a/src/tests/api/test_order_create.py b/src/tests/api/test_order_create.py index 60dffd61c..e382dab28 100644 --- a/src/tests/api/test_order_create.py +++ b/src/tests/api/test_order_create.py @@ -31,7 +31,6 @@ from django.core.files.base import ContentFile from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from tests.const import SAMPLE_PNG from pretix.base.models import ( @@ -101,7 +100,7 @@ def quota(event, item): @pytest.fixture def order(event, item, taxrule, question): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) event.plugins += ",pretix.plugins.stripe" event.save() @@ -110,8 +109,8 @@ def order(event, item, taxrule, question): o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=23, locale='en' ) p1 = o.payments.create( diff --git a/src/tests/api/test_orders.py b/src/tests/api/test_orders.py index a991aa21e..331664880 100644 --- a/src/tests/api/test_orders.py +++ b/src/tests/api/test_orders.py @@ -30,7 +30,6 @@ from django.core import mail as djmail from django.utils.timezone import now from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from stripe.error import APIConnectionError from tests.plugins.stripe.test_provider import MockedCharge @@ -77,7 +76,7 @@ def quota(event, item): @pytest.fixture def order(event, item, taxrule, question): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) event.plugins += ",pretix.plugins.stripe" event.save() @@ -86,8 +85,8 @@ def order(event, item, taxrule, question): o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), + expires=datetime.datetime(2017, 12, 10, 10, 0, 0, tzinfo=datetime.timezone.utc), total=23, locale='en' ) p1 = o.payments.create( @@ -947,8 +946,8 @@ def test_orderposition_list(token_client, organizer, event, order, item, subeven with scopes_disabled(): cl = event.checkin_lists.create(name="Default") - c = op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC), list=cl) - op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=UTC), list=cl, successful=False) + c = op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=datetime.timezone.utc), list=cl) + op.checkins.create(datetime=datetime.datetime(2017, 12, 26, 10, 0, 0, tzinfo=datetime.timezone.utc), list=cl, successful=False) res['checkins'] = [{ # successful only 'id': c.pk, 'datetime': '2017-12-26T10:00:00Z', diff --git a/src/tests/api/test_sendmail.py b/src/tests/api/test_sendmail.py index 964347374..f19abefe9 100644 --- a/src/tests/api/test_sendmail.py +++ b/src/tests/api/test_sendmail.py @@ -22,7 +22,6 @@ import datetime import pytest -from django.utils.timezone import utc from django_scopes import scopes_disabled from pretix.plugins.sendmail.models import Rule @@ -31,7 +30,7 @@ from pretix.plugins.sendmail.models import Rule @pytest.fixture def rule(event): return event.sendmail_rules.create(subject='test', template='foo', - send_date=datetime.datetime(2021, 7, 8, tzinfo=utc)) + send_date=datetime.datetime(2021, 7, 8, tzinfo=datetime.timezone.utc)) TEST_RULE_RES = { @@ -131,7 +130,7 @@ def test_sendmail_rule_create_minimal(token_client, organizer, event): 'send_date': '2018-03-17T13:31Z', } ) - assert r.send_date == datetime.datetime(2018, 3, 17, 13, 31, tzinfo=utc) + assert r.send_date == datetime.datetime(2018, 3, 17, 13, 31, tzinfo=datetime.timezone.utc) @scopes_disabled() diff --git a/src/tests/api/test_shredders.py b/src/tests/api/test_shredders.py index 938ee132c..ca518c95f 100644 --- a/src/tests/api/test_shredders.py +++ b/src/tests/api/test_shredders.py @@ -34,13 +34,12 @@ import copy import uuid -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from decimal import Decimal import pytest from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import Order, OrderPosition @@ -56,8 +55,8 @@ def order(event, item): o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc), + expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=timezone.utc), total=23, locale='en' ) OrderPosition.objects.create( diff --git a/src/tests/api/test_subevents.py b/src/tests/api/test_subevents.py index 03846183f..f6258c21d 100644 --- a/src/tests/api/test_subevents.py +++ b/src/tests/api/test_subevents.py @@ -19,14 +19,13 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from datetime import datetime +from datetime import datetime, timezone from decimal import Decimal from unittest import mock import pytest from django_countries.fields import Country from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import ( InvoiceAddress, Order, OrderPosition, SeatingPlan, SubEvent, @@ -52,15 +51,15 @@ def variations2(item2): @pytest.fixture def order(event, item, taxrule): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime o = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1", - datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC), - expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=UTC), + datetime=datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc), + expires=datetime(2017, 12, 10, 10, 0, 0, tzinfo=timezone.utc), total=23, locale='en' ) o.fees.create(fee_type=OrderFee.FEE_TYPE_PAYMENT, value=Decimal('0.25'), tax_rate=Decimal('19.00'), @@ -344,8 +343,8 @@ def test_subevent_update(token_client, organizer, event, subevent, item, item2, assert resp.status_code == 200 with scopes_disabled(): subevent = event.subevents.get(id=subevent.id) - assert subevent.date_from == datetime(2018, 12, 27, 10, 0, tzinfo=UTC) - assert subevent.date_to == datetime(2018, 12, 28, 10, 0, tzinfo=UTC) + assert subevent.date_from == datetime(2018, 12, 27, 10, 0, tzinfo=timezone.utc) + assert subevent.date_to == datetime(2018, 12, 28, 10, 0, tzinfo=timezone.utc) resp = token_client.patch( '/api/v1/organizers/{}/events/{}/subevents/{}/'.format(organizer.slug, event.slug, subevent.pk), diff --git a/src/tests/api/test_vouchers.py b/src/tests/api/test_vouchers.py index aaf1253d2..44d467f56 100644 --- a/src/tests/api/test_vouchers.py +++ b/src/tests/api/test_vouchers.py @@ -37,10 +37,8 @@ import datetime from decimal import Decimal import pytest -from django.utils import timezone from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import Event, SeatingPlan, Voucher @@ -234,7 +232,7 @@ def test_voucher_list(token_client, organizer, event, voucher, item, quota, sube assert [res] == resp.data['results'] voucher.redeemed = 0 - voucher.valid_until = (timezone.now() - datetime.timedelta(days=1)).replace(microsecond=0) + voucher.valid_until = (now() - datetime.timedelta(days=1)).replace(microsecond=0) voucher.save() res['valid_until'] = voucher.valid_until.isoformat().replace('+00:00', 'Z') res['redeemed'] = 0 @@ -251,7 +249,7 @@ def test_voucher_list(token_client, organizer, event, voucher, item, quota, sube '/api/v1/organizers/{}/events/{}/vouchers/?subevent={}'.format(organizer.slug, event.slug, subevent.pk)) assert [res] == resp.data['results'] with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) resp = token_client.get( '/api/v1/organizers/{}/events/{}/vouchers/?subevent={}'.format(organizer.slug, event.slug, se2.pk)) @@ -613,7 +611,7 @@ def test_change_to_item_of_other_event(token_client, organizer, event, item): organizer=organizer, name='Dummy2', slug='dummy2', - date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC), + date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc), plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf' ) ticket2 = e2.items.create(name='Late-bird ticket', default_price=23) @@ -1256,8 +1254,8 @@ def test_set_seat_subevent(token_client, organizer, event, seatingplan, seat1, i with scopes_disabled(): event.has_subevents = True event.save() - se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) - se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) + se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) seat1 = event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se1) event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se2) v = event.vouchers.create(item=item) @@ -1279,8 +1277,8 @@ def test_set_seat_subevent_required(token_client, organizer, event, seatingplan, with scopes_disabled(): event.has_subevents = True event.save() - se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) - se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) + se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) seat1 = event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se1) event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se2) event.vouchers.create(item=item, seat=seat1) @@ -1299,8 +1297,8 @@ def test_set_seat_subevent_invalid(token_client, organizer, event, seatingplan, with scopes_disabled(): event.has_subevents = True event.save() - se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) - se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se1 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) + se2 = event.subevents.create(name="Baz", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) seat1 = event.seats.create(seat_number="A1", product=item, seat_guid="A1", subevent=se1) event.seats.create(seat_number="B1", product=item, seat_guid="B1", subevent=se2) event.vouchers.create(item=item, seat=seat1, subevent=se2) diff --git a/src/tests/api/test_waitinglist.py b/src/tests/api/test_waitinglist.py index cf99b126a..b4a7a4855 100644 --- a/src/tests/api/test_waitinglist.py +++ b/src/tests/api/test_waitinglist.py @@ -25,7 +25,6 @@ from unittest import mock import pytest from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import WaitingListEntry @@ -44,7 +43,7 @@ def quota(event, item): @pytest.fixture def wle(event, item): - testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime.datetime(2017, 12, 1, 10, 0, 0, tzinfo=datetime.timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime @@ -140,7 +139,7 @@ def test_wle_list(token_client, organizer, event, wle, item, subevent): '/api/v1/organizers/{}/events/{}/waitinglistentries/?subevent={}'.format(organizer.slug, event.slug, subevent.pk)) assert [res] == resp.data['results'] with scopes_disabled(): - se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=UTC)) + se2 = event.subevents.create(name="Foobar", date_from=datetime.datetime(2017, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)) resp = token_client.get( '/api/v1/organizers/{}/events/{}/waitinglistentries/?subevent={}'.format(organizer.slug, event.slug, se2.pk)) diff --git a/src/tests/base/test_checkin.py b/src/tests/base/test_checkin.py index eeaa0ee23..0435c00fd 100644 --- a/src/tests/base/test_checkin.py +++ b/src/tests/base/test_checkin.py @@ -135,7 +135,7 @@ def test_checkin_blocked_position(position, clist): @pytest.mark.django_db def test_checkin_valid_from(event, position, clist): - position.valid_from = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) + position.valid_from = datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) position.save() with freeze_time("2020-01-01 10:45:00"): with pytest.raises(CheckInError) as excinfo: @@ -153,7 +153,7 @@ def test_checkin_valid_from(event, position, clist): @pytest.mark.django_db def test_checkin_valid_until(event, position, clist): - position.valid_until = event.timezone.localize(datetime(2020, 1, 1, 9, 0, 0)) + position.valid_until = datetime(2020, 1, 1, 9, 0, 0, tzinfo=event.timezone) position.save() with freeze_time("2020-01-01 10:45:00"): with pytest.raises(CheckInError) as excinfo: @@ -712,7 +712,7 @@ def test_rules_scan_days(event, position, clist): def test_rules_time_isafter_tolerance(event, position, clist): # Ticket is valid starting 10 minutes before admission time event.settings.timezone = 'Europe/Berlin' - event.date_admission = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) + event.date_admission = datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) event.save() clist.rules = {"isAfter": [{"var": "now"}, {"buildTime": ["date_admission"]}, 10]} clist.save() @@ -732,7 +732,7 @@ def test_rules_time_isafter_tolerance(event, position, clist): def test_rules_time_isafter_no_tolerance(event, position, clist): # Ticket is valid only after admission time event.settings.timezone = 'Europe/Berlin' - event.date_from = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) + event.date_from = datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) # also tests that date_admission falls back to date_from event.save() clist.rules = {"isAfter": [{"var": "now"}, {"buildTime": ["date_admission"]}]} @@ -753,7 +753,7 @@ def test_rules_time_isafter_no_tolerance(event, position, clist): def test_rules_time_isbefore_with_tolerance(event, position, clist): # Ticket is valid until 10 minutes after end time event.settings.timezone = 'Europe/Berlin' - event.date_to = event.timezone.localize(datetime(2020, 1, 1, 12, 0, 0)) + event.date_to = datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) event.save() clist.rules = {"isBefore": [{"var": "now"}, {"buildTime": ["date_to"]}, 10]} clist.save() @@ -809,7 +809,7 @@ def test_rules_isafter_subevent(position, clist, event): event.has_subevents = True event.save() event.settings.timezone = 'Europe/Berlin' - se1 = event.subevents.create(name="Foo", date_from=event.timezone.localize(datetime(2020, 2, 1, 12, 0, 0))) + se1 = event.subevents.create(name="Foo", date_from=datetime(2020, 2, 1, 12, 0, 0, tzinfo=event.timezone)) position.subevent = se1 position.save() clist.rules = {"isAfter": [{"var": "now"}, {"buildTime": ["date_admission"]}]} @@ -949,7 +949,7 @@ def test_position_queries(django_assert_num_queries, position, clist): @pytest.mark.django_db(transaction=True) def test_auto_checkout_at_correct_time(event, position, clist): - clist.exit_all_at = event.timezone.localize(datetime(2020, 1, 2, 3, 0)) + clist.exit_all_at = datetime(2020, 1, 2, 3, 0, tzinfo=event.timezone) clist.save() with freeze_time("2020-01-01 10:00:00+01:00"): perform_checkin(position, clist, {}) @@ -962,12 +962,12 @@ def test_auto_checkout_at_correct_time(event, position, clist): assert position.checkins.count() == 2 assert position.checkins.first().type == Checkin.TYPE_EXIT clist.refresh_from_db() - assert clist.exit_all_at == event.timezone.localize(datetime(2020, 1, 3, 3, 0)) + assert clist.exit_all_at == datetime(2020, 1, 3, 3, 0, tzinfo=event.timezone) @pytest.mark.django_db(transaction=True) def test_auto_check_out_only_if_checked_in(event, position, clist): - clist.exit_all_at = event.timezone.localize(datetime(2020, 1, 2, 3, 0)) + clist.exit_all_at = datetime(2020, 1, 2, 3, 0, tzinfo=event.timezone) clist.save() with freeze_time("2020-01-02 03:05:00+01:00"): process_exit_all(sender=None) @@ -985,7 +985,7 @@ def test_auto_check_out_only_if_checked_in(event, position, clist): @pytest.mark.django_db(transaction=True) def test_auto_check_out_only_if_checked_in_before_exit_all_at(event, position, clist): - clist.exit_all_at = event.timezone.localize(datetime(2020, 1, 2, 3, 0)) + clist.exit_all_at = datetime(2020, 1, 2, 3, 0, tzinfo=event.timezone) clist.save() with freeze_time("2020-01-02 04:05:00+01:00"): perform_checkin(position, clist, {}) @@ -999,37 +999,37 @@ def test_auto_check_out_dst(event, position, clist): event.settings.timezone = 'Europe/Berlin' # Survive across a shift that doesn't affect the time in question - clist.exit_all_at = event.timezone.localize(datetime(2021, 3, 28, 1, 0)) + clist.exit_all_at = datetime(2021, 3, 28, 1, 0, tzinfo=event.timezone) clist.save() with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 29, 1, 0)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 3, 29, 1, 0, tzinfo=event.timezone) # Survive across a shift that makes the time in question ambigous - clist.exit_all_at = event.timezone.localize(datetime(2021, 10, 30, 2, 30)) + clist.exit_all_at = datetime(2021, 10, 30, 2, 30, tzinfo=event.timezone) clist.save() with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 10, 31, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 10, 31, 2, 30, tzinfo=event.timezone) with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 11, 1, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 11, 1, 2, 30, tzinfo=event.timezone) - # Doesn't survive across a shift that makes the time in question non-existant - clist.exit_all_at = event.timezone.localize(datetime(2021, 3, 27, 2, 30)) + # Moves back after a shift that makes the time in question non-existant + clist.exit_all_at = datetime(2021, 3, 27, 2, 30, tzinfo=event.timezone) clist.save() with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 28, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 3, 28, 3, 30, tzinfo=event.timezone) with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 29, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 3, 29, 2, 30, tzinfo=event.timezone) with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 30, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == datetime(2021, 3, 30, 2, 30, tzinfo=event.timezone) diff --git a/src/tests/base/test_export.py b/src/tests/base/test_export.py index 892f98f5c..684cae062 100644 --- a/src/tests/base/test_export.py +++ b/src/tests/base/test_export.py @@ -19,10 +19,9 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from datetime import datetime, time, timedelta +from datetime import datetime, time, timedelta, timezone import pytest -import pytz from django.core import mail as djmail from django.utils.timezone import now from django_scopes import scope @@ -39,7 +38,7 @@ def event(): o = Organizer.objects.create(name='Dummy', slug='dummy') event = Event.objects.create( organizer=o, name='Dummy', slug='dummy', - date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC), + date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer' ) o.settings.timezone = "Europe/Berlin" @@ -70,7 +69,7 @@ def test_event_run_sets_new_time(event, user): run_scheduled_exports(None) s.refresh_from_db() - assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 19, 2, 30, 0)) + assert s.schedule_next_run == datetime(2023, 1, 19, 2, 30, 0, tzinfo=event.timezone) @pytest.mark.django_db @@ -79,13 +78,13 @@ def test_event_not_run_when_failed_5_times(event, user): s = ScheduledEventExport(event=event, owner=user) s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO" s.schedule_rrule_time = time(2, 30, 0) - s.schedule_next_run = event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0)) + s.schedule_next_run = datetime(2023, 1, 18, 2, 30, 0, tzinfo=event.timezone) s.error_counter = 5 s.save() run_scheduled_exports(None) s.refresh_from_db() - assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0)) + assert s.schedule_next_run == datetime(2023, 1, 18, 2, 30, 0, tzinfo=event.timezone) @pytest.mark.django_db @@ -200,7 +199,7 @@ def test_organizer_run_sets_new_time(event, user): run_scheduled_exports(None) s.refresh_from_db() - assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 19, 2, 30, 0)) + assert s.schedule_next_run == datetime(2023, 1, 19, 2, 30, 0, tzinfo=event.timezone) @pytest.mark.django_db @@ -209,13 +208,13 @@ def test_organizer_not_run_when_failed_5_times(event, user): s = ScheduledOrganizerExport(organizer=event.organizer, owner=user) s.schedule_rrule = "DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO" s.schedule_rrule_time = time(2, 30, 0) - s.schedule_next_run = event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0)) + s.schedule_next_run = datetime(2023, 1, 18, 2, 30, 0, tzinfo=event.timezone) s.error_counter = 5 s.save() run_scheduled_exports(None) s.refresh_from_db() - assert s.schedule_next_run == event.timezone.localize(datetime(2023, 1, 18, 2, 30, 0)) + assert s.schedule_next_run == datetime(2023, 1, 18, 2, 30, 0, tzinfo=event.timezone) @pytest.mark.django_db @@ -304,7 +303,7 @@ def test_organizer_limited_to_events(event, user, team): event2 = Event.objects.create( organizer=event.organizer, name='Dummy', slug='dummy2', - date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC), + date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer' ) team.all_events = False @@ -344,7 +343,7 @@ def test_organizer_ok(event, user, team): Event.objects.create( organizer=event.organizer, name='Dummy', slug='dummy2', - date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=pytz.UTC), + date_from=datetime(2023, 1, 19, 2, 30, 0, tzinfo=timezone.utc), plugins='pretix.plugins.banktransfer' ) diff --git a/src/tests/base/test_item_validity.py b/src/tests/base/test_item_validity.py index ec42419ac..c18ba130f 100644 --- a/src/tests/base/test_item_validity.py +++ b/src/tests/base/test_item_validity.py @@ -20,17 +20,17 @@ # . # from datetime import datetime +from zoneinfo import ZoneInfo import pytest -import pytz from pretix.base.models import Item -tz = pytz.timezone("Europe/Berlin") +tz = ZoneInfo("Europe/Berlin") -def dt(*args, is_dst=None, **kwargs): - return tz.localize(datetime(*args, **kwargs), is_dst=is_dst) +def dt(*args, **kwargs): + return datetime(*args, **kwargs, tzinfo=tz) @pytest.mark.parametrize("minutes,hours,days,months,start,expected_end", [ @@ -62,7 +62,7 @@ def dt(*args, is_dst=None, **kwargs): (30, 2, 1, 0, dt(2023, 3, 25, 10, 30, 0), dt(2023, 3, 26, 3, 29, 59)), # Day + hour with ambiguous end time during DST change - (30, 2, 1, 0, dt(2023, 10, 28, 10, 30, 0), dt(2023, 10, 29, 2, 29, 59, is_dst=True)), + (30, 2, 1, 0, dt(2023, 10, 28, 10, 30, 0), dt(2023, 10, 29, 2, 29, 59, fold=1)), # Month with short month following (0, 0, 0, 1, dt(2023, 1, 31, 10, 30, 0), dt(2023, 2, 28, 23, 59, 59)), diff --git a/src/tests/base/test_memberships.py b/src/tests/base/test_memberships.py index 93f1ce0a4..5650b9af7 100644 --- a/src/tests/base/test_memberships.py +++ b/src/tests/base/test_memberships.py @@ -21,9 +21,9 @@ # from datetime import datetime, timedelta from decimal import Decimal +from zoneinfo import ZoneInfo import pytest -import pytz from django.conf import settings from django.core.exceptions import ValidationError from django.utils.timezone import now @@ -42,7 +42,7 @@ from pretix.base.services.orders import ( ) from pretix.plugins.banktransfer.payment import BankTransfer -TZ = pytz.timezone('Europe/Berlin') +TZ = ZoneInfo('Europe/Berlin') @pytest.fixture(scope='function') @@ -51,9 +51,9 @@ def event(): o.settings.customer_accounts = True event = Event.objects.create( organizer=o, name='Dummy', slug='dummy', - date_from=TZ.localize(datetime(2021, 4, 27, 10, 0, 0, 0)), - date_to=TZ.localize(datetime(2021, 4, 28, 10, 0, 0, 0)), - presale_end=TZ.localize(datetime(2221, 4, 28, 10, 0, 0, 0)), + date_from=datetime(2021, 4, 27, 10, 0, 0, 0, tzinfo=TZ), + date_to=datetime(2021, 4, 28, 10, 0, 0, 0, tzinfo=TZ), + presale_end=datetime(2221, 4, 28, 10, 0, 0, 0, tzinfo=TZ), plugins='pretix.plugins.banktransfer' ) event.settings.timezone = 'Europe/Berlin' @@ -75,8 +75,8 @@ def membership_type(event): def membership(event, membership_type, customer): return customer.memberships.create( membership_type=membership_type, - date_start=TZ.localize(datetime(2021, 4, 1, 0, 0, 0, 0)), - date_end=TZ.localize(datetime(2021, 4, 30, 23, 59, 59, 999999)), + date_start=datetime(2021, 4, 1, 0, 0, 0, 0, tzinfo=TZ), + date_end=datetime(2021, 4, 30, 23, 59, 59, 999999, tzinfo=TZ), ) @@ -107,7 +107,7 @@ def subevent(event): event.has_subevents = True return event.subevents.create( name='Foo', - date_from=TZ.localize(datetime(2021, 4, 29, 10, 0, 0, 0)), + date_from=datetime(2021, 4, 29, 10, 0, 0, 0, tzinfo=TZ), ) @@ -115,8 +115,8 @@ def subevent(event): def test_validity_membership_duration_like_event(event, granting_ticket, membership_type): granting_ticket.grant_membership_duration_like_event = True assert membership_validity(granting_ticket, None, event) == ( - TZ.localize(datetime(2021, 4, 27, 10, 0, 0, 0)), - TZ.localize(datetime(2021, 4, 28, 10, 0, 0, 0)), + datetime(2021, 4, 27, 10, 0, 0, 0, tzinfo=TZ), + datetime(2021, 4, 28, 10, 0, 0, 0, tzinfo=TZ), ) @@ -124,8 +124,8 @@ def test_validity_membership_duration_like_event(event, granting_ticket, members def test_validity_membership_duration_like_subevent_without_end(event, granting_ticket, subevent, membership_type): granting_ticket.grant_membership_duration_like_event = True assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 4, 29, 10, 0, 0, 0)), - TZ.localize(datetime(2021, 4, 29, 23, 59, 59, 999999)), + datetime(2021, 4, 29, 10, 0, 0, 0, tzinfo=TZ), + datetime(2021, 4, 29, 23, 59, 59, 999999, tzinfo=TZ), ) @@ -135,8 +135,8 @@ def test_validity_membership_duration_days(event, granting_ticket, membership_ty granting_ticket.grant_membership_duration_days = 3 with freeze_time("2021-04-10T11:00:00+02:00"): assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 4, 10, 0, 0, 0, 0)), - TZ.localize(datetime(2021, 4, 12, 23, 59, 59, 999999)), + datetime(2021, 4, 10, 0, 0, 0, 0, tzinfo=TZ), + datetime(2021, 4, 12, 23, 59, 59, 999999, tzinfo=TZ), ) @@ -146,13 +146,13 @@ def test_validity_membership_duration_months(event, granting_ticket, membership_ granting_ticket.grant_membership_duration_months = 1 with freeze_time("2021-02-01T11:00:00+01:00"): assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 2, 1, 0, 0, 0, 0)), - TZ.localize(datetime(2021, 2, 28, 23, 59, 59, 999999)), + datetime(2021, 2, 1, 0, 0, 0, 0, tzinfo=TZ), + datetime(2021, 2, 28, 23, 59, 59, 999999, tzinfo=TZ), ) with freeze_time("2021-02-28T11:00:00+01:00"): assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 2, 28, 0, 0, 0, 0)), - TZ.localize(datetime(2021, 3, 27, 23, 59, 59, 999999)), + datetime(2021, 2, 28, 0, 0, 0, 0, tzinfo=TZ), + datetime(2021, 3, 27, 23, 59, 59, 999999, tzinfo=TZ), ) @@ -163,13 +163,13 @@ def test_validity_membership_duration_months_plus_days(event, granting_ticket, m granting_ticket.grant_membership_duration_days = 2 with freeze_time("2021-02-01T11:00:00+01:00"): assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 2, 1, 0, 0, 0, 0)), - TZ.localize(datetime(2021, 3, 2, 23, 59, 59, 999999)), + datetime(2021, 2, 1, 0, 0, 0, 0, tzinfo=TZ), + datetime(2021, 3, 2, 23, 59, 59, 999999, tzinfo=TZ), ) with freeze_time("2021-02-28T11:00:00+01:00"): assert membership_validity(granting_ticket, subevent, event) == ( - TZ.localize(datetime(2021, 2, 28, 0, 0, 0, 0)), - TZ.localize(datetime(2021, 3, 29, 23, 59, 59, 999999)), + datetime(2021, 2, 28, 0, 0, 0, 0, tzinfo=TZ), + datetime(2021, 3, 29, 23, 59, 59, 999999, tzinfo=TZ), ) @@ -420,7 +420,7 @@ def test_validate_membership_max_usages(event, customer, membership, requiring_t def test_validate_membership_parallel(event, customer, membership, subevent, requiring_ticket, membership_type): se2 = event.subevents.create( name='Foo', - date_from=TZ.localize(datetime(2021, 4, 28, 10, 0, 0, 0)), + date_from=datetime(2021, 4, 28, 10, 0, 0, 0, tzinfo=TZ), ) membership_type.allow_parallel_usage = False @@ -585,5 +585,5 @@ def test_grant_when_paid_and_changed(event, customer, granting_ticket): m = customer.memberships.get() assert m.granted_in == order.positions.first() assert m.membership_type == granting_ticket.grant_membership_type - assert m.date_start == TZ.localize(datetime(2021, 4, 27, 10, 0, 0, 0)) - assert m.date_end == TZ.localize(datetime(2021, 4, 28, 10, 0, 0, 0)) + assert m.date_start == datetime(2021, 4, 27, 10, 0, 0, 0, tzinfo=TZ) + assert m.date_end == datetime(2021, 4, 28, 10, 0, 0, 0, tzinfo=TZ) diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index 222e44a41..99f919ebb 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -39,7 +39,6 @@ from datetime import date, timedelta from decimal import Decimal import pytest -import pytz from dateutil.tz import tzoffset from django.conf import settings from django.core.exceptions import ValidationError @@ -1204,13 +1203,13 @@ class OrderTestCase(BaseQuotaTestCase): @classscope(attr='o') def test_payment_term_last_relative(self): self.event.settings.set('payment_term_last', date(2017, 5, 3)) - assert self.order.payment_term_last == datetime.datetime(2017, 5, 3, 23, 59, 59, tzinfo=pytz.UTC) - self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC) + assert self.order.payment_term_last == datetime.datetime(2017, 5, 3, 23, 59, 59, tzinfo=datetime.timezone.utc) + self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc) self.event.save() self.event.settings.set('payment_term_last', RelativeDateWrapper( RelativeDate(days_before=2, time=None, base_date_name='date_from', minutes_before=None) )) - assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=pytz.UTC) + assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=datetime.timezone.utc) @classscope(attr='o') def test_payment_term_last_subevent(self): @@ -1235,14 +1234,14 @@ class OrderTestCase(BaseQuotaTestCase): @classscope(attr='o') def test_ticket_download_date_relative(self): - self.event.settings.set('ticket_download_date', datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC)) - assert self.order.ticket_download_date == datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=pytz.UTC) - self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=pytz.UTC) + self.event.settings.set('ticket_download_date', datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=datetime.timezone.utc)) + assert self.order.ticket_download_date == datetime.datetime(2017, 5, 3, 12, 59, 59, tzinfo=datetime.timezone.utc) + self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc) self.event.save() self.event.settings.set('ticket_download_date', RelativeDateWrapper( RelativeDate(days_before=2, time=None, base_date_name='date_from', minutes_before=None) )) - assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=pytz.UTC) + assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=datetime.timezone.utc) @classscope(attr='o') def test_ticket_download_date_subevent(self): @@ -2150,7 +2149,7 @@ class EventTest(TestCase): @classscope(attr='organizer') def test_presale_has_ended(self): - event = Event( + event = Event.objects.create( organizer=self.organizer, name='Download', slug='download', date_from=now() ) @@ -2907,15 +2906,15 @@ class ScheduledExportTestCase(TestCase): with freeze_time("2023-01-18 15:08:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 1, 19, 6, 30, 0)) + assert s.schedule_next_run == datetime.datetime(2023, 1, 19, 6, 30, 0, tzinfo=self.event.timezone) with freeze_time("2023-01-19 06:28:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 1, 19, 6, 30, 0)) + assert s.schedule_next_run == datetime.datetime(2023, 1, 19, 6, 30, 0, tzinfo=self.event.timezone) with freeze_time("2023-01-19 06:30:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 1, 24, 6, 30, 0)) + assert s.schedule_next_run == datetime.datetime(2023, 1, 24, 6, 30, 0, tzinfo=self.event.timezone) with freeze_time("2024-01-18 15:08:00+01:00"): s.compute_next_run() @@ -2928,10 +2927,10 @@ class ScheduledExportTestCase(TestCase): s.schedule_rrule_time = datetime.time(2, 30, 0) with freeze_time("2023-03-25 18:00:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 3, 26, 3, 30, 0)) + assert s.schedule_next_run == datetime.datetime(2023, 3, 26, 3, 30, 0, tzinfo=self.event.timezone) with freeze_time("2023-03-26 18:00:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 3, 27, 2, 30, 0)) + assert s.schedule_next_run == datetime.datetime(2023, 3, 27, 2, 30, 0, tzinfo=self.event.timezone) with freeze_time("2023-10-28 18:00:00+01:00"): s.compute_next_run() - assert s.schedule_next_run == self.event.timezone.localize(datetime.datetime(2023, 10, 29, 2, 30, 0, fold=0)) + assert s.schedule_next_run == datetime.datetime(2023, 10, 29, 2, 30, 0, fold=0, tzinfo=self.event.timezone) diff --git a/src/tests/base/test_orderimport.py b/src/tests/base/test_orderimport.py index e30a23737..a56bb9071 100644 --- a/src/tests/base/test_orderimport.py +++ b/src/tests/base/test_orderimport.py @@ -747,7 +747,7 @@ def test_import_subevent_by_date(user, event, item): event.has_subevents = True event.save() event.settings.timezone = 'Europe/Berlin' - s = event.subevents.create(name='Test', date_from=event.timezone.localize(datetime.datetime(2021, 6, 28, 11, 0, 0, 0)), active=True) + s = event.subevents.create(name='Test', date_from=datetime.datetime(2021, 6, 28, 11, 0, 0, 0, tzinfo=event.timezone), active=True) settings['item'] = 'static:{}'.format(item.pk) settings['subevent'] = 'csv:J' diff --git a/src/tests/base/test_orders.py b/src/tests/base/test_orders.py index 63b394880..5c2e02881 100644 --- a/src/tests/base/test_orders.py +++ b/src/tests/base/test_orders.py @@ -22,9 +22,9 @@ import json from datetime import datetime, timedelta from decimal import Decimal +from zoneinfo import ZoneInfo import pytest -import pytz from django.core import mail as djmail from django.db.models import F, Sum from django.test import TestCase @@ -256,9 +256,9 @@ def test_expiry_last_relative_subevents(event): @pytest.mark.django_db def test_expiry_dst(event): event.settings.set('timezone', 'Europe/Berlin') - tz = pytz.timezone('Europe/Berlin') - utc = pytz.timezone('UTC') - today = tz.localize(datetime(2016, 10, 29, 12, 0, 0)).astimezone(utc) + tz = ZoneInfo('Europe/Berlin') + utc = ZoneInfo('UTC') + today = datetime(2016, 10, 29, 12, 0, 0, tzinfo=tz).astimezone(utc) order = _create_order(event, email='dummy@example.org', positions=[], now_dt=today, payment_requests=[{ @@ -3089,6 +3089,9 @@ def test_autocheckin(clist_autocheckin, event): clist_autocheckin.auto_checkin_sales_channels = [] clist_autocheckin.save() + cp1 = CartPosition.objects.create( + item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" + ) order = _create_order(event, email='dummy@example.org', positions=[cp1], now_dt=today, payment_requests=[{ @@ -3129,6 +3132,9 @@ def test_saleschannel_testmode_restriction(event): locale='de', sales_channel='web')[0] assert not order.testmode + cp1 = CartPosition.objects.create( + item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" + ) order = _create_order(event, email='dummy@example.org', positions=[cp1], now_dt=today, payment_requests=[{ @@ -3143,6 +3149,9 @@ def test_saleschannel_testmode_restriction(event): locale='de', sales_channel=FoobazSalesChannel.identifier)[0] assert not order.testmode + cp1 = CartPosition.objects.create( + item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" + ) event.testmode = True order = _create_order(event, email='dummy@example.org', positions=[cp1], now_dt=today, @@ -3158,6 +3167,9 @@ def test_saleschannel_testmode_restriction(event): locale='de', sales_channel='web')[0] assert order.testmode + cp1 = CartPosition.objects.create( + item=ticket, price=23, expires=now() + timedelta(days=1), event=event, cart_id="123" + ) order = _create_order(event, email='dummy@example.org', positions=[cp1], now_dt=today, payment_requests=[{ diff --git a/src/tests/base/test_payment.py b/src/tests/base/test_payment.py index a6d830f35..f3983ac20 100644 --- a/src/tests/base/test_payment.py +++ b/src/tests/base/test_payment.py @@ -34,9 +34,9 @@ import datetime from decimal import Decimal +from zoneinfo import ZoneInfo import pytest -import pytz from django.utils.timezone import now from django_scopes import scope from tests.testdummy.payment import DummyPaymentProvider @@ -112,18 +112,18 @@ def test_availability_date_not_available(event): @pytest.mark.django_db def test_availability_date_relative(event): event.settings.set('timezone', 'US/Pacific') - tz = pytz.timezone('US/Pacific') - event.date_from = tz.localize(datetime.datetime(2016, 12, 3, 12, 0, 0)) + tz = ZoneInfo('US/Pacific') + event.date_from = datetime.datetime(2016, 12, 3, 12, 0, 0, tzinfo=tz) event.save() prov = DummyPaymentProvider(event) prov.settings.set('_availability_date', RelativeDateWrapper( RelativeDate(days_before=2, time=None, base_date_name='date_from', minutes_before=None) )) - utc = pytz.timezone('UTC') - assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc)) - assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc)) - assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc)) + utc = datetime.timezone.utc + assert prov._is_still_available(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc)) + assert prov._is_still_available(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc)) + assert not prov._is_still_available(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc)) @pytest.mark.django_db @@ -132,11 +132,11 @@ def test_availability_date_timezones(event): prov = DummyPaymentProvider(event) prov.settings.set('_availability_date', '2016-12-01') - tz = pytz.timezone('US/Pacific') - utc = pytz.timezone('UTC') - assert prov._is_still_available(tz.localize(datetime.datetime(2016, 11, 30, 23, 0, 0)).astimezone(utc)) - assert prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 1, 23, 59, 0)).astimezone(utc)) - assert not prov._is_still_available(tz.localize(datetime.datetime(2016, 12, 2, 0, 0, 1)).astimezone(utc)) + tz = ZoneInfo('US/Pacific') + utc = ZoneInfo('UTC') + assert prov._is_still_available(datetime.datetime(2016, 11, 30, 23, 0, 0, tzinfo=tz).astimezone(utc)) + assert prov._is_still_available(datetime.datetime(2016, 12, 1, 23, 59, 0, tzinfo=tz).astimezone(utc)) + assert not prov._is_still_available(datetime.datetime(2016, 12, 2, 0, 0, 1, tzinfo=tz).astimezone(utc)) @pytest.mark.django_db diff --git a/src/tests/base/test_reldate.py b/src/tests/base/test_reldate.py index f1498ad88..301dc781d 100644 --- a/src/tests/base/test_reldate.py +++ b/src/tests/base/test_reldate.py @@ -20,16 +20,16 @@ # . # from datetime import datetime, time +from zoneinfo import ZoneInfo import pytest -import pytz from django_scopes import scope from pretix.base.models import Event, Organizer from pretix.base.reldate import RelativeDate, RelativeDateWrapper -TOKYO = pytz.timezone('Asia/Tokyo') -BERLIN = pytz.timezone('Europe/Berlin') +TOKYO = ZoneInfo('Asia/Tokyo') +BERLIN = ZoneInfo('Europe/Berlin') @pytest.fixture @@ -37,8 +37,8 @@ def event(): o = Organizer.objects.create(name='Dummy', slug='dummy') event = Event.objects.create( organizer=o, name='Dummy', slug='dummy', - date_from=TOKYO.localize(datetime(2017, 12, 27, 5, 0, 0)), - presale_start=TOKYO.localize(datetime(2017, 12, 1, 5, 0, 0)), + date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO), + presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO), plugins='pretix.plugins.banktransfer' ) @@ -57,7 +57,7 @@ def test_absolute_date(event): @pytest.mark.django_db def test_relative_date_without_time(event): rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from', minutes_before=None)) - assert rdw.datetime(event).astimezone(TOKYO) == TOKYO.localize(datetime(2017, 12, 26, 5, 0, 0)) + assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO) assert rdw.to_string() == 'RELDATE/1/-/date_from/' @@ -65,64 +65,64 @@ def test_relative_date_without_time(event): def test_relative_date_other_base_point(event): with scope(organizer=event.organizer): rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start', minutes_before=None)) - assert rdw.datetime(event) == TOKYO.localize(datetime(2017, 11, 30, 5, 0, 0)) + assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO) assert rdw.to_string() == 'RELDATE/1/-/presale_start/' # presale_end is unset, defaults to date_from rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end', minutes_before=None)) - assert rdw.datetime(event) == TOKYO.localize(datetime(2017, 12, 26, 5, 0, 0)) + assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO) assert rdw.to_string() == 'RELDATE/1/-/presale_end/' # subevent base - se = event.subevents.create(name="SE1", date_from=TOKYO.localize(datetime(2017, 11, 27, 5, 0, 0))) + se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO)) rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='date_from', minutes_before=None)) - assert rdw.datetime(se) == TOKYO.localize(datetime(2017, 11, 26, 5, 0, 0)) + assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO) # presale_start is unset on subevent, default to event rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_start', minutes_before=None)) - assert rdw.datetime(se) == TOKYO.localize(datetime(2017, 11, 30, 5, 0, 0)) + assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO) # presale_end is unset on all, default to date_from of subevent rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=None, base_date_name='presale_end', minutes_before=None)) - assert rdw.datetime(se) == TOKYO.localize(datetime(2017, 11, 26, 5, 0, 0)) + assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO) @pytest.mark.django_db def test_relative_date_in_minutes(event): rdw = RelativeDateWrapper(RelativeDate(days_before=0, time=None, base_date_name='date_from', minutes_before=60)) assert rdw.to_string() == 'RELDATE/minutes/60/date_from/' - assert rdw.datetime(event) == TOKYO.localize(datetime(2017, 12, 27, 4, 0, 0)) + assert rdw.datetime(event) == datetime(2017, 12, 27, 4, 0, 0, tzinfo=TOKYO) @pytest.mark.django_db def test_relative_date_with_time(event): rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=time(8, 5, 13), base_date_name='date_from', minutes_before=None)) assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/' - assert rdw.datetime(event) == TOKYO.localize(datetime(2017, 12, 26, 8, 5, 13)) + assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO) @pytest.mark.django_db def test_relative_date_with_time_around_dst(event): event.settings.timezone = "Europe/Berlin" - event.date_from = BERLIN.localize(datetime(2020, 3, 29, 18, 0, 0)) + event.date_from = datetime(2020, 3, 29, 18, 0, 0, tzinfo=BERLIN) rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=time(18, 0, 0), base_date_name='date_from', minutes_before=None)) assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/' - assert rdw.datetime(event) == BERLIN.localize(datetime(2020, 3, 28, 18, 0, 0)) + assert rdw.datetime(event) == datetime(2020, 3, 28, 18, 0, 0, tzinfo=BERLIN) rdw = RelativeDateWrapper(RelativeDate(days_before=0, time=time(2, 30, 0), base_date_name='date_from', minutes_before=None)) assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/' - assert rdw.datetime(event) == BERLIN.localize(datetime(2020, 3, 29, 2, 30, 0)) + assert rdw.datetime(event) == datetime(2020, 3, 29, 2, 30, 0, tzinfo=BERLIN) - event.date_from = BERLIN.localize(datetime(2020, 10, 25, 18, 0, 0)) + event.date_from = datetime(2020, 10, 25, 18, 0, 0, tzinfo=BERLIN) rdw = RelativeDateWrapper(RelativeDate(days_before=1, time=time(18, 0, 0), base_date_name='date_from', minutes_before=None)) assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/' - assert rdw.datetime(event) == BERLIN.localize(datetime(2020, 10, 24, 18, 0, 0)) + assert rdw.datetime(event) == datetime(2020, 10, 24, 18, 0, 0, tzinfo=BERLIN) rdw = RelativeDateWrapper(RelativeDate(days_before=0, time=time(2, 30, 0), base_date_name='date_from', minutes_before=None)) assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/' - assert rdw.datetime(event) == BERLIN.localize(datetime(2020, 10, 25, 2, 30, 0)) + assert rdw.datetime(event) == datetime(2020, 10, 25, 2, 30, 0, tzinfo=BERLIN) def test_unserialize(): diff --git a/src/tests/base/test_timeframes.py b/src/tests/base/test_timeframes.py index 0f9f454d9..246873143 100644 --- a/src/tests/base/test_timeframes.py +++ b/src/tests/base/test_timeframes.py @@ -21,20 +21,20 @@ # from datetime import date, datetime +from zoneinfo import ZoneInfo import pytest -import pytz from pretix.base.timeframes import ( REPORTING_DATE_TIMEFRAMES, resolve_timeframe_to_dates_inclusive, resolve_timeframe_to_datetime_start_inclusive_end_exclusive, ) -tz = pytz.timezone("Europe/Berlin") +tz = ZoneInfo("Europe/Berlin") def dt(*args): - return tz.localize(datetime(*args)) + return datetime(*args, tzinfo=tz) ref_date = date(2023, 3, 28) diff --git a/src/tests/base/test_timeline.py b/src/tests/base/test_timeline.py index 5fcfaea03..c61befa1e 100644 --- a/src/tests/base/test_timeline.py +++ b/src/tests/base/test_timeline.py @@ -21,16 +21,16 @@ # from datetime import datetime from decimal import Decimal +from zoneinfo import ZoneInfo import pytest -import pytz from django_scopes import scope from pretix.base.i18n import language from pretix.base.models import Event, Organizer from pretix.base.timeline import timeline_for_event -tz = pytz.timezone('Europe/Berlin') +tz = ZoneInfo('Europe/Berlin') def one(iterable): diff --git a/src/tests/control/test_checkins.py b/src/tests/control/test_checkins.py index d22d954da..4640ef89e 100644 --- a/src/tests/control/test_checkins.py +++ b/src/tests/control/test_checkins.py @@ -499,7 +499,7 @@ class CheckinListFormTest(SoupTest): form_data) assert doc.select(".alert-success") cl.refresh_from_db() - assert cl.exit_all_at == self.event1.timezone.localize(datetime(2020, 1, 2, 3, 0)) + assert cl.exit_all_at == datetime(2020, 1, 2, 3, 0, tzinfo=self.event1.timezone) @freeze_time("2020-01-02 03:05:00+01:00") def test_update_exit_all_at_next_day(self): @@ -512,7 +512,7 @@ class CheckinListFormTest(SoupTest): form_data) assert doc.select(".alert-success") cl.refresh_from_db() - assert cl.exit_all_at == self.event1.timezone.localize(datetime(2020, 1, 3, 3, 0)) + assert cl.exit_all_at == datetime(2020, 1, 3, 3, 0, tzinfo=self.event1.timezone) def test_delete(self): with scopes_disabled(): diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py index 0d56f75d3..6072b398c 100644 --- a/src/tests/control/test_events.py +++ b/src/tests/control/test_events.py @@ -37,14 +37,13 @@ import datetime import time from decimal import Decimal from smtplib import SMTPResponseException +from zoneinfo import ZoneInfo import pytest -import pytz from django.test.utils import override_settings from django.utils.timezone import now from django_scopes import scopes_disabled from i18nfield.strings import LazyI18nString -from pytz import timezone from tests.base import SoupTest, extract_form_fields from pretix.base.models import Event, LogEntry, Order, Organizer, Team, User @@ -835,11 +834,11 @@ class EventsTest(SoupTest): assert ev.location == LazyI18nString({'de': 'Hamburg', 'en': 'Hamburg'}) assert Team.objects.filter(limit_events=ev, members=self.user).exists() - berlin_tz = timezone('Europe/Berlin') - assert ev.date_from == berlin_tz.localize(datetime.datetime(2016, 12, 27, 10, 0, 0)).astimezone(pytz.utc) - assert ev.date_to == berlin_tz.localize(datetime.datetime(2016, 12, 30, 19, 0, 0)).astimezone(pytz.utc) - assert ev.presale_start == berlin_tz.localize(datetime.datetime(2016, 11, 1, 10, 0, 0)).astimezone(pytz.utc) - assert ev.presale_end == berlin_tz.localize(datetime.datetime(2016, 11, 30, 18, 0, 0)).astimezone(pytz.utc) + berlin_tz = ZoneInfo('Europe/Berlin') + assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.date_to == datetime.datetime(2016, 12, 30, 19, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_start == datetime.datetime(2016, 11, 1, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_end == datetime.datetime(2016, 11, 30, 18, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) assert ev.tax_rules.filter(rate=Decimal('19.00')).exists() @@ -954,11 +953,11 @@ class EventsTest(SoupTest): assert Team.objects.filter(limit_events=ev, members=self.user).exists() assert ev.items.count() == 1 - berlin_tz = timezone('Europe/Berlin') - assert ev.date_from == berlin_tz.localize(datetime.datetime(2016, 12, 27, 10, 0, 0)).astimezone(pytz.utc) - assert ev.date_to == berlin_tz.localize(datetime.datetime(2016, 12, 30, 19, 0, 0)).astimezone(pytz.utc) - assert ev.presale_start == berlin_tz.localize(datetime.datetime(2016, 11, 1, 10, 0, 0)).astimezone(pytz.utc) - assert ev.presale_end == berlin_tz.localize(datetime.datetime(2016, 11, 30, 18, 0, 0)).astimezone(pytz.utc) + berlin_tz = ZoneInfo('Europe/Berlin') + assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.date_to == datetime.datetime(2016, 12, 30, 19, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_start == datetime.datetime(2016, 11, 1, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_end == datetime.datetime(2016, 11, 30, 18, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) assert ev.tax_rules.filter(rate=Decimal('19.00')).count() == 1 i = ev.items.get() @@ -1027,11 +1026,11 @@ class EventsTest(SoupTest): assert Team.objects.filter(limit_events=ev, members=self.user).exists() assert ev.items.count() == 1 - berlin_tz = timezone('Europe/Berlin') - assert ev.date_from == berlin_tz.localize(datetime.datetime(2016, 12, 27, 10, 0, 0)).astimezone(pytz.utc) - assert ev.date_to == berlin_tz.localize(datetime.datetime(2016, 12, 30, 19, 0, 0)).astimezone(pytz.utc) - assert ev.presale_start == berlin_tz.localize(datetime.datetime(2016, 11, 1, 10, 0, 0)).astimezone(pytz.utc) - assert ev.presale_end == berlin_tz.localize(datetime.datetime(2016, 11, 30, 18, 0, 0)).astimezone(pytz.utc) + berlin_tz = ZoneInfo('Europe/Berlin') + assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.date_to == datetime.datetime(2016, 12, 30, 19, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_start == datetime.datetime(2016, 11, 1, 10, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) + assert ev.presale_end == datetime.datetime(2016, 11, 30, 18, 0, 0, tzinfo=berlin_tz).astimezone(datetime.timezone.utc) assert ev.tax_rules.filter(rate=Decimal('19.00')).count() == 1 @@ -1079,7 +1078,7 @@ class EventsTest(SoupTest): assert ev.organizer == self.orga1 assert ev.location == LazyI18nString({'en': 'Hamburg'}) assert Team.objects.filter(limit_events=ev, members=self.user).exists() - assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=pytz.utc) + assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc) assert ev.date_to is None assert ev.presale_start is None assert ev.presale_end is None @@ -1128,7 +1127,7 @@ class EventsTest(SoupTest): assert ev.location == LazyI18nString({'en': 'Hamburg'}) team = Team.objects.filter(limit_events=ev, members=self.user).first() assert team == self.team2 - assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=pytz.utc) + assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=datetime.timezone.utc) assert ev.date_to is None assert ev.presale_start is None assert ev.presale_end is None diff --git a/src/tests/helpers/test_daterange.py b/src/tests/helpers/test_daterange.py index c1b2c1ba2..42be63234 100644 --- a/src/tests/helpers/test_daterange.py +++ b/src/tests/helpers/test_daterange.py @@ -57,8 +57,8 @@ def test_same_day_english(): def test_same_day_spanish(): with translation.override('es'): df = date(2003, 2, 1) - assert daterange(df, df) == "1 de Febrero de 2003" - assert daterange(df, df, as_html=True) == '' + assert daterange(df, df) == "1 de febrero de 2003" + assert daterange(df, df, as_html=True) == '' def test_same_day_other_lang(): @@ -88,8 +88,8 @@ def test_same_month_spanish(): with translation.override('es'): df = date(2003, 2, 1) dt = date(2003, 2, 3) - assert daterange(df, dt) == "1 - 3 de Febrero de 2003" - assert daterange(df, dt, as_html=True) == ' - ' + assert daterange(df, dt) == "1 - 3 de febrero de 2003" + assert daterange(df, dt, as_html=True) == ' - ' def test_same_year_german(): @@ -112,8 +112,8 @@ def test_same_year_spanish(): with translation.override('es'): df = date(2003, 2, 1) dt = date(2003, 4, 3) - assert daterange(df, dt) == "1 de Febrero - 3 de Abril de 2003" - assert daterange(df, dt, as_html=True) == ' - ' + assert daterange(df, dt) == "1 de febrero - 3 de abril de 2003" + assert daterange(df, dt, as_html=True) == ' - ' def test_different_dates_german(): @@ -136,9 +136,9 @@ def test_different_dates_spanish(): with translation.override('es'): df = date(2003, 2, 1) dt = date(2005, 4, 3) - assert daterange(df, dt) == "1 de Febrero de 2003 – 3 de Abril de 2005" - assert daterange(df, dt, as_html=True) == ' – ' \ - '' + assert daterange(df, dt) == "1 de febrero de 2003 – 3 de abril de 2005" + assert daterange(df, dt, as_html=True) == ' – ' \ + '' def test_different_dates_other_lang(): diff --git a/src/tests/plugins/banktransfer/test_api.py b/src/tests/plugins/banktransfer/test_api.py index 1c1e70ac1..3d496f0dd 100644 --- a/src/tests/plugins/banktransfer/test_api.py +++ b/src/tests/plugins/banktransfer/test_api.py @@ -21,13 +21,12 @@ # import copy import json -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from unittest import mock import pytest from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import ( Event, Item, Order, OrderPosition, Organizer, Quota, Team, User, @@ -91,7 +90,7 @@ RES_JOB = { @pytest.mark.django_db def test_api_list(env, client): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime @@ -111,7 +110,7 @@ def test_api_list(env, client): @pytest.mark.django_db def test_api_detail(env, client): - testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=UTC) + testtime = datetime(2017, 12, 1, 10, 0, 0, tzinfo=timezone.utc) with mock.patch('django.utils.timezone.now') as mock_now: mock_now.return_value = testtime diff --git a/src/tests/plugins/sendmail/test_rules.py b/src/tests/plugins/sendmail/test_rules.py index c44a5172c..c20f0b1a3 100644 --- a/src/tests/plugins/sendmail/test_rules.py +++ b/src/tests/plugins/sendmail/test_rules.py @@ -21,11 +21,11 @@ # import datetime +from zoneinfo import ZoneInfo import pytest -import pytz from django.core import mail as djmail -from django.utils.timezone import now, utc +from django.utils.timezone import now from django_scopes import scopes_disabled from pretix.base.models import InvoiceAddress, Order @@ -46,8 +46,8 @@ def test_sendmail_rule_create_single(event): dt_now = now() -NZ = pytz.timezone('NZ') -Berlin = pytz.timezone('Europe/Berlin') +NZ = ZoneInfo('NZ') +Berlin = ZoneInfo('Europe/Berlin') @pytest.mark.django_db @@ -71,62 +71,62 @@ Berlin = pytz.timezone('Europe/Berlin') (dt_now + datetime.timedelta(days=1)).replace(hour=9, minute=0, second=0, microsecond=0) ), ( # "Relative, before event start" - datetime.datetime(2021, 5, 17, 12, 14, 0, tzinfo=utc), + datetime.datetime(2021, 5, 17, 12, 14, 0, tzinfo=datetime.timezone.utc), None, 'UTC', Rule(date_is_absolute=False, send_offset_days=2, send_offset_time=datetime.time(hour=0)), - datetime.datetime(2021, 5, 15, 0, tzinfo=utc) + datetime.datetime(2021, 5, 15, 0, tzinfo=datetime.timezone.utc) ), ( # "Relative, after event end" - datetime.datetime(2021, 5, 17, 18, tzinfo=utc), - datetime.datetime(2021, 5, 18, 5, tzinfo=utc), + datetime.datetime(2021, 5, 17, 18, tzinfo=datetime.timezone.utc), + datetime.datetime(2021, 5, 18, 5, tzinfo=datetime.timezone.utc), 'UTC', Rule(date_is_absolute=False, offset_to_event_end=True, offset_is_after=True, send_offset_days=1, send_offset_time=datetime.time(hour=10)), - datetime.datetime(2021, 5, 19, 10, tzinfo=utc) + datetime.datetime(2021, 5, 19, 10, tzinfo=datetime.timezone.utc) ), ( # "Relative, before event end" - datetime.datetime(2021, 5, 17, 18, tzinfo=utc), - datetime.datetime(2021, 5, 22, 5, tzinfo=utc), + datetime.datetime(2021, 5, 17, 18, tzinfo=datetime.timezone.utc), + datetime.datetime(2021, 5, 22, 5, tzinfo=datetime.timezone.utc), 'UTC', Rule(date_is_absolute=False, offset_to_event_end=True, offset_is_after=False, send_offset_days=1, send_offset_time=datetime.time(hour=10)), - datetime.datetime(2021, 5, 21, 10, tzinfo=utc) + datetime.datetime(2021, 5, 21, 10, tzinfo=datetime.timezone.utc) ), # Tests for timezone quirks ( # Test sending on leap day - datetime.datetime(2020, 2, 27, 9, tzinfo=utc), + datetime.datetime(2020, 2, 27, 9, tzinfo=datetime.timezone.utc), None, 'UTC', Rule(date_is_absolute=False, offset_is_after=True, send_offset_days=2, send_offset_time=datetime.time(hour=9)), - datetime.datetime(2020, 2, 29, 9, tzinfo=utc) + datetime.datetime(2020, 2, 29, 9, tzinfo=datetime.timezone.utc) ), ( # Test timezone far off from UTC - NZ.localize(datetime.datetime(2021, 5, 17, 22)), + datetime.datetime(2021, 5, 17, 22, tzinfo=NZ), None, 'NZ', Rule(date_is_absolute=False, offset_is_after=True, send_offset_days=1, send_offset_time=datetime.time(hour=9)), - NZ.localize(datetime.datetime(2021, 5, 18, 9)) + datetime.datetime(2021, 5, 18, 9, tzinfo=NZ) ), ( # Test across DST change - Berlin.localize(datetime.datetime(2021, 10, 29, 16, 30)), + datetime.datetime(2021, 10, 29, 16, 30, tzinfo=Berlin), None, 'Europe/Berlin', Rule(date_is_absolute=False, offset_is_after=True, send_offset_days=4, send_offset_time=datetime.time(hour=2, minute=30)), - Berlin.localize(datetime.datetime(2021, 11, 2, 2, 30)) + datetime.datetime(2021, 11, 2, 2, 30, tzinfo=Berlin) ), ( # Test ambiguous time at DST change - Berlin.localize(datetime.datetime(2021, 10, 29, 18, 30)), + datetime.datetime(2021, 10, 29, 18, 30, tzinfo=Berlin), None, 'Europe/Berlin', Rule(date_is_absolute=False, offset_is_after=True, send_offset_days=2, send_offset_time=datetime.time(hour=2, minute=30)), - datetime.datetime(2021, 10, 31, 1, 30, tzinfo=utc) + datetime.datetime(2021, 10, 31, 1, 30, tzinfo=datetime.timezone.utc) ), ( # Test non-existing time at DST change - Berlin.localize(datetime.datetime(2021, 3, 29, 14, 30)), + datetime.datetime(2021, 3, 29, 14, 30, tzinfo=Berlin), None, 'Europe/Berlin', Rule(date_is_absolute=False, offset_is_after=False, send_offset_days=1, send_offset_time=datetime.time(hour=2, minute=30)), - datetime.datetime(2021, 3, 28, 1, 30, tzinfo=utc) + datetime.datetime(2021, 3, 28, 1, 30, tzinfo=datetime.timezone.utc) ), ]) @@ -145,7 +145,7 @@ def test_sendmail_rule_send_time(event_from, event_to, event_tz, rule, expected, rule.save() m = ScheduledMail.objects.filter(rule=rule).get() - assert m.computed_datetime.astimezone(event.timezone) == expected + assert m.computed_datetime.astimezone(event.timezone) == expected.astimezone(event.timezone) @pytest.mark.django_db @@ -166,7 +166,7 @@ def test_sendmail_rule_recompute(event): sendmail_run_rules(None) m = ScheduledMail.objects.filter(rule=rule).first() - assert m.computed_datetime.astimezone(utc) == expected + assert m.computed_datetime.astimezone(datetime.timezone.utc) == expected @pytest.mark.django_db diff --git a/src/tests/plugins/test_checkinlist.py b/src/tests/plugins/test_checkinlist.py index b96246fd1..af98b7f3a 100644 --- a/src/tests/plugins/test_checkinlist.py +++ b/src/tests/plugins/test_checkinlist.py @@ -23,7 +23,6 @@ import datetime from decimal import Decimal import pytest -import pytz from django.utils.timezone import now from django_scopes import scope @@ -51,7 +50,7 @@ def event(): order_paid = Order.objects.create( code='FOO', event=event, email='dummy@dummy.test', phone="+498912345678", status=Order.STATUS_PAID, - datetime=datetime.datetime(2019, 2, 22, 14, 0, 0, tzinfo=pytz.UTC), expires=now() + datetime.timedelta(days=10), + datetime=datetime.datetime(2019, 2, 22, 14, 0, 0, tzinfo=datetime.timezone.utc), expires=now() + datetime.timedelta(days=10), total=33, locale='en' ) item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True) @@ -150,7 +149,7 @@ def test_csv_order_by_inherited_name_parts(event): # noqa order2 = Order.objects.create( code='BAR', event=event, email='dummy@dummy.test', phone='+498912345678', status=Order.STATUS_PAID, - datetime=datetime.datetime(2019, 2, 22, 14, 0, 0, tzinfo=pytz.UTC), expires=now() + datetime.timedelta(days=10), + datetime=datetime.datetime(2019, 2, 22, 14, 0, 0, tzinfo=datetime.timezone.utc), expires=now() + datetime.timedelta(days=10), total=33, locale='en' ) OrderPosition.objects.create( diff --git a/src/tests/presale/test_event.py b/src/tests/presale/test_event.py index 9b06f8d20..974af9b21 100644 --- a/src/tests/presale/test_event.py +++ b/src/tests/presale/test_event.py @@ -37,6 +37,7 @@ import datetime import re from decimal import Decimal from json import loads +from zoneinfo import ZoneInfo from bs4 import BeautifulSoup from django.conf import settings @@ -45,7 +46,6 @@ from django.core.exceptions import ValidationError from django.test import TestCase from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import timezone from tests.base import SoupTest from tests.testdummy.signals import FoobarSalesChannel @@ -1393,11 +1393,11 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest): fmt = '%Y%m%dT%H%M%S' self.assertIn('DTSTART;TZID=%s:%s' % (self.event.settings.timezone, - self.event.date_from.astimezone(timezone(self.event.settings.timezone)).strftime(fmt)), + self.event.date_from.astimezone(ZoneInfo(self.event.settings.timezone)).strftime(fmt)), ical, 'incorrect start time') self.assertIn('DTEND;TZID=%s:%s' % (self.event.settings.timezone, - self.event.date_to.astimezone(timezone(self.event.settings.timezone)).strftime(fmt)), + self.event.date_to.astimezone(ZoneInfo(self.event.settings.timezone)).strftime(fmt)), ical, 'incorrect end time') self.assertIn('TZID:%s' % self.event.settings.timezone, ical, 'missing VCALENDAR') @@ -1416,7 +1416,7 @@ class EventIcalDownloadTest(EventTestMixin, SoupTest): fmt = '%Y%m%dT%H%M%S' self.assertIn('DTSTART;TZID=%s:%s' % (self.event.settings.timezone, - self.event.date_from.astimezone(timezone(self.event.settings.timezone)).strftime(fmt)), + self.event.date_from.astimezone(ZoneInfo(self.event.settings.timezone)).strftime(fmt)), ical, 'incorrect start time') self.assertNotIn('DTEND', ical, 'unexpected end time attribute') diff --git a/src/tests/presale/test_organizer_page.py b/src/tests/presale/test_organizer_page.py index 164bb3a56..b8811f235 100644 --- a/src/tests/presale/test_organizer_page.py +++ b/src/tests/presale/test_organizer_page.py @@ -19,12 +19,11 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import pytest from django.utils.timezone import now from django_scopes import scopes_disabled -from pytz import UTC from pretix.base.models import Event, Organizer @@ -149,7 +148,7 @@ def test_calendar(env, client): env[0].settings.event_list_type = 'calendar' e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=False ) r = client.get('/mrmcd/?style=calendar') @@ -169,7 +168,7 @@ def test_week_calendar(env, client): env[0].settings.event_list_type = 'calendar' e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=False ) r = client.get('/mrmcd/?style=week') @@ -187,7 +186,7 @@ def test_attributes_in_calendar(env, client): env[0].settings.event_list_type = 'calendar' e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=True ) prop = env[0].meta_properties.create(name='loc') @@ -203,7 +202,7 @@ def test_attributes_in_calendar(env, client): def test_ics(env, client): e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=False ) r = client.get('/mrmcd/events/ical/') @@ -218,7 +217,7 @@ def test_ics(env, client): def test_ics_subevents(env, client): e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=True, has_subevents=True ) with scopes_disabled(): @@ -232,12 +231,12 @@ def test_ics_subevents(env, client): def test_ics_subevents_attributes(env, client): e0 = Event.objects.create( organizer=env[0], name='DS2017', slug='DS2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=True ) e = Event.objects.create( organizer=env[0], name='MRMCD2017', slug='2017', - date_from=datetime(now().year + 1, 9, 1, tzinfo=UTC), + date_from=datetime(now().year + 1, 9, 1, tzinfo=timezone.utc), live=True, is_public=True, has_subevents=True ) with scopes_disabled():