diff --git a/doc/api/resources/events.rst b/doc/api/resources/events.rst index 238cc248a0..ee13f2e0df 100644 --- a/doc/api/resources/events.rst +++ b/doc/api/resources/events.rst @@ -42,6 +42,7 @@ seating_plan integer If reserved sea plan. Otherwise ``null``. seat_category_mapping object An object mapping categories of the seating plan (strings) to items in the event (integers or ``null``). +timezone string Event timezone name ===================================== ========================== ======================================================= @@ -74,6 +75,10 @@ seat_category_mapping object An object mappi The attributes ``geo_lat`` and ``geo_lon`` have been added. +.. versionchanged:: 3.4 + + The attribute ``timezone`` has been added. + Endpoints --------- @@ -127,6 +132,7 @@ Endpoints "meta_data": {}, "seating_plan": null, "seat_category_mapping": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.banktransfer" "pretix.plugins.stripe" @@ -197,6 +203,7 @@ Endpoints "seating_plan": null, "seat_category_mapping": {}, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.banktransfer" "pretix.plugins.stripe" @@ -248,6 +255,7 @@ Endpoints "geo_lon": null, "has_subevents": false, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.stripe", "pretix.plugins.paypal" @@ -281,6 +289,7 @@ Endpoints "seat_category_mapping": {}, "has_subevents": false, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.stripe", "pretix.plugins.paypal" @@ -334,6 +343,7 @@ Endpoints "seat_category_mapping": {}, "has_subevents": false, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.stripe", "pretix.plugins.paypal" @@ -367,6 +377,7 @@ Endpoints "seating_plan": null, "seat_category_mapping": {}, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.stripe", "pretix.plugins.paypal" @@ -432,6 +443,7 @@ Endpoints "seating_plan": null, "seat_category_mapping": {}, "meta_data": {}, + "timezone": "Europe/Berlin", "plugins": [ "pretix.plugins.banktransfer", "pretix.plugins.stripe", diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index aebd34ebff..ea41161160 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -4,7 +4,8 @@ from django.db import transaction from django.utils.functional import cached_property from django.utils.translation import ugettext as _ from django_countries.serializers import CountryFieldMixin -from rest_framework.fields import Field +from pytz import common_timezones +from rest_framework.fields import ChoiceField, Field from rest_framework.relations import SlugRelatedField from pretix.api.serializers.i18n import I18nAwareModelSerializer @@ -61,17 +62,27 @@ class PluginsField(Field): } +class TimeZoneField(ChoiceField): + def get_attribute(self, instance): + return instance.cache.get_or_set( + 'timezone_name', + lambda: instance.settings.timezone, + 3600 + ) + + class EventSerializer(I18nAwareModelSerializer): meta_data = MetaDataField(required=False, source='*') plugins = PluginsField(required=False, source='*') seat_category_mapping = SeatCategoryMappingField(source='*', required=False) + timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones]) class Meta: model = Event fields = ('name', 'slug', 'live', 'testmode', 'currency', 'date_from', 'date_to', 'date_admission', 'is_public', 'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan', - 'plugins', 'seat_category_mapping') + 'plugins', 'seat_category_mapping', 'timezone') def validate(self, data): data = super().validate(data) @@ -156,8 +167,12 @@ class EventSerializer(I18nAwareModelSerializer): meta_data = validated_data.pop('meta_data', None) validated_data.pop('seat_category_mapping', None) plugins = validated_data.pop('plugins', settings.PRETIX_PLUGINS_DEFAULT.split(',')) + tz = validated_data.pop('timezone', None) event = super().create(validated_data) + if tz: + event.settings.timezone = tz + # Meta data if meta_data is not None: for key, value in meta_data.items(): @@ -182,8 +197,12 @@ class EventSerializer(I18nAwareModelSerializer): meta_data = validated_data.pop('meta_data', None) plugins = validated_data.pop('plugins', None) seat_category_mapping = validated_data.pop('seat_category_mapping', None) + tz = validated_data.pop('timezone', None) event = super().update(instance, validated_data) + if tz: + event.settings.timezone = tz + # Meta data if meta_data is not None: current = {mv.property: mv for mv in event.meta_values.select_related('property')} @@ -240,6 +259,7 @@ class CloneEventSerializer(EventSerializer): is_public = validated_data.pop('is_public', None) testmode = validated_data.pop('testmode', None) has_subevents = validated_data.pop('has_subevents', None) + tz = validated_data.pop('timezone', None) new_event = super().create(validated_data) event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first() @@ -254,6 +274,8 @@ class CloneEventSerializer(EventSerializer): if has_subevents is not None: new_event.has_subevents = has_subevents new_event.save() + if tz: + new_event.settings.timezone = tz return new_event diff --git a/src/tests/api/conftest.py b/src/tests/api/conftest.py index a3b2bdcc3d..9ff37af9fa 100644 --- a/src/tests/api/conftest.py +++ b/src/tests/api/conftest.py @@ -38,6 +38,7 @@ def event(organizer, meta_prop): is_public=True ) e.meta_values.create(property=meta_prop, value="Conference") + e.settings.timezone = 'Europe/Berlin' return e diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index d768877edf..21ff8b88f3 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -76,6 +76,7 @@ TEST_EVENT_RES = { "seating_plan": None, "seat_category_mapping": {}, "meta_data": {"type": "Conference"}, + 'timezone': 'Europe/Berlin', 'plugins': [ 'pretix.plugins.banktransfer', 'pretix.plugins.ticketoutputpdf' @@ -174,7 +175,8 @@ def test_event_create(token_client, organizer, event, meta_prop): "slug": "2030", "meta_data": { meta_prop.name: "Conference" - } + }, + "timezone": "Europe/Amsterdam" }, format='json' ) @@ -185,6 +187,7 @@ def test_event_create(token_client, organizer, event, meta_prop): property__name=meta_prop.name, value="Conference" ).exists() assert organizer.events.get(slug="2030").plugins == settings.PRETIX_PLUGINS_DEFAULT + assert organizer.events.get(slug="2030").settings.timezone == "Europe/Amsterdam" resp = token_client.post( '/api/v1/organizers/{}/events/'.format(organizer.slug), @@ -291,7 +294,8 @@ def test_event_create_with_clone(token_client, organizer, event, meta_prop): }, "plugins": [ "pretix.plugins.ticketoutputpdf" - ] + ], + "timezone": "Europe/Vienna" }, format='json' ) @@ -305,6 +309,7 @@ def test_event_create_with_clone(token_client, organizer, event, meta_prop): assert organizer.events.get(slug="2030").meta_values.filter( property__name=meta_prop.name, value="Conference" ).exists() + assert cloned_event.settings.timezone == "Europe/Vienna" resp = token_client.post( '/api/v1/organizers/{}/events/{}/clone/'.format(organizer.slug, event.slug),