Fix #428 -- Timezone handling on event creation/update (#432)

* add event timezone during event creation

* add timezone handling in EventUpdate

* added event creation test cases & form cleaning bug fix
This commit is contained in:
jlwt90
2017-03-14 00:44:05 +09:00
committed by Raphael Michel
parent 811c498080
commit af3e811f94
3 changed files with 119 additions and 2 deletions

View File

@@ -5,7 +5,7 @@ from django.core.validators import RegexValidator
from django.utils.timezone import get_current_timezone_name from django.utils.timezone import get_current_timezone_name
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextarea from i18nfield.forms import I18nFormField, I18nTextarea
from pytz import common_timezones from pytz import common_timezones, timezone
from pretix.base.forms import I18nModelForm, SettingsForm from pretix.base.forms import I18nModelForm, SettingsForm
from pretix.base.models import Event, Organizer from pretix.base.models import Event, Organizer
@@ -80,8 +80,23 @@ class EventWizardBasicsForm(I18nModelForm):
raise ValidationError({ raise ValidationError({
'locale': _('Your default locale must also be enabled for your event (see box above).') 'locale': _('Your default locale must also be enabled for your event (see box above).')
}) })
if data.get('timezone') not in common_timezones:
raise ValidationError({
'timezone': _('Your default locale must be specified.')
})
# change timezone
zone = timezone(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'))
data['presale_end'] = self.reset_timezone(zone, data.get('presale_end'))
return data return data
@staticmethod
def reset_timezone(tz, dt):
return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None
def clean_slug(self): def clean_slug(self):
slug = self.cleaned_data['slug'] slug = self.cleaned_data['slug']
if Event.objects.filter(slug=slug, organizer=self.organizer).exists(): if Event.objects.filter(slug=slug, organizer=self.organizer).exists():

View File

@@ -14,6 +14,7 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, ListView from django.views.generic import FormView, ListView
from django.views.generic.base import TemplateView, View from django.views.generic.base import TemplateView, View
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from pytz import timezone
from pretix.base.forms import I18nModelForm from pretix.base.forms import I18nModelForm
from pretix.base.models import ( from pretix.base.models import (
@@ -88,10 +89,21 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
form = self.get_form() form = self.get_form()
if form.is_valid() and self.sform.is_valid(): if form.is_valid() and self.sform.is_valid():
# reset timezone
zone = timezone(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)
event.presale_start = self.reset_timezone(zone, event.presale_start)
event.presale_end = self.reset_timezone(zone, event.presale_end)
return self.form_valid(form) return self.form_valid(form)
else: else:
return self.form_invalid(form) return self.form_invalid(form)
@staticmethod
def reset_timezone(tz, dt):
return tz.localize(dt.replace(tzinfo=None)) if dt is not None else None
class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin): class EventPlugins(EventPermissionRequiredMixin, TemplateView, SingleObjectMixin):
model = Event model = Event

View File

@@ -1,7 +1,9 @@
import datetime import datetime
from decimal import Decimal from decimal import Decimal
import pytz
from i18nfield.strings import LazyI18nString from i18nfield.strings import LazyI18nString
from pytz import timezone
from tests.base import SoupTest, extract_form_fields from tests.base import SoupTest, extract_form_fields
from pretix.base.models import ( from pretix.base.models import (
@@ -45,7 +47,6 @@ class EventsTest(SoupTest):
doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug)) doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug))
doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00" doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00"
doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12" doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12"
print(extract_form_fields(doc.select('.container-fluid form')[0]))
doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug), doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug),
extract_form_fields(doc.select('.container-fluid form')[0])) extract_form_fields(doc.select('.container-fluid form')[0]))
@@ -53,6 +54,27 @@ class EventsTest(SoupTest):
assert doc.select("[name=date_to]")[0]['value'] == "2013-12-30 17:00:00" assert doc.select("[name=date_to]")[0]['value'] == "2013-12-30 17:00:00"
assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12" assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
def test_settings_timezone(self):
doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug))
doc.select("[name=date_to]")[0]['value'] = "2013-12-30 17:00:00"
doc.select("[name=settings-max_items_per_order]")[0]['value'] = "12"
doc.select("[name=settings-timezone]")[0]['value'] = "Asia/Tokyo"
doc.find('option', {"value": "Asia/Tokyo"})['selected'] = 'selected'
doc.find('option', {"value": "UTC"}).attrs.pop('selected')
doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug),
extract_form_fields(doc.select('.container-fluid form')[0]))
assert len(doc.select(".alert-success")) > 0
# date_to should not be changed even though the timezone is changed
assert doc.select("[name=date_to]")[0]['value'] == "2013-12-30 17:00:00"
assert doc.find('option', {"value": "Asia/Tokyo"})['selected'] == "selected"
assert doc.select("[name=settings-max_items_per_order]")[0]['value'] == "12"
self.event1.refresh_from_db()
# Asia/Tokyo -> GMT+9
assert self.event1.date_to.strftime('%Y-%m-%d %H:%M:%S') == "2013-12-30 08:00:00"
assert self.event1.settings.timezone == 'Asia/Tokyo'
def test_plugins(self): def test_plugins(self):
doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug)) doc = self.get_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug))
self.assertIn("PayPal", doc.select(".form-plugins")[0].text) self.assertIn("PayPal", doc.select(".form-plugins")[0].text)
@@ -295,4 +317,72 @@ class EventsTest(SoupTest):
assert ev.currency == 'EUR' assert ev.currency == 'EUR'
assert ev.settings.timezone == 'Europe/Berlin' assert ev.settings.timezone == 'Europe/Berlin'
assert ev.organizer == self.orga1 assert ev.organizer == self.orga1
assert ev.location == LazyI18nString({'de': 'Hamburg', 'en': 'Hamburg'})
assert EventPermission.objects.filter(event=ev, user=self.user).exists() assert EventPermission.objects.filter(event=ev, user=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)
def test_create_event_only_date_from(self):
# date_to, presale_start & presale_end are optional fields
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': 'en'
})
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-slug': '33c3',
'basics-date_from': '2016-12-27 10:00:00',
'basics-date_to': '',
'basics-location_0': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'UTC',
'basics-presale_start': '',
'basics-presale_end': '',
})
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'copy-copy_from_event': ''
})
ev = Event.objects.get(slug='33c3')
assert ev.name == LazyI18nString({'en': '33C3'})
assert ev.settings.locales == ['en']
assert ev.settings.locale == 'en'
assert ev.currency == 'EUR'
assert ev.settings.timezone == 'UTC'
assert ev.organizer == self.orga1
assert ev.location == LazyI18nString({'en': 'Hamburg'})
assert EventPermission.objects.filter(event=ev, user=self.user).exists()
assert ev.date_from == datetime.datetime(2016, 12, 27, 10, 0, 0, tzinfo=pytz.utc)
assert ev.date_to is None
assert ev.presale_start is None
assert ev.presale_end is None
def test_create_event_missing_date_from(self):
# date_from is mandatory
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'foundation-organizer': self.orga1.pk,
'foundation-locales': 'en'
})
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'basics-name_0': '33C3',
'basics-slug': '33c3',
'basics-date_from': '',
'basics-date_to': '2016-12-30 19:00:00',
'basics-location_0': 'Hamburg',
'basics-currency': 'EUR',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start': '2016-11-20 11:00:00',
'basics-presale_end': '2016-11-24 18:00:00',
})
assert doc.select(".alert-danger")