forked from CGM_Public/pretix_original
Integrate hierarkey package (#460)
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
import logging
|
||||
|
||||
import i18nfield.forms
|
||||
from django import forms
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.files.uploadedfile import UploadedFile
|
||||
from django.forms.models import ModelFormMetaclass
|
||||
from django.utils import six
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from hierarkey.forms import HierarkeyForm
|
||||
|
||||
from pretix.base.models import Event
|
||||
|
||||
@@ -49,67 +45,22 @@ class I18nInlineFormSet(i18nfield.forms.I18nInlineFormSet):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class SettingsForm(i18nfield.forms.I18nForm):
|
||||
"""
|
||||
This form is meant to be used for modifying EventSettings or OrganizerSettings. It takes
|
||||
care of loading the current values of the fields and saving the field inputs to the
|
||||
settings storage. It also deals with setting the available languages for internationalized
|
||||
fields.
|
||||
|
||||
:param obj: The event or organizer object which should be used for the settings storage
|
||||
"""
|
||||
|
||||
BOOL_CHOICES = (
|
||||
('False', _('disabled')),
|
||||
('True', _('enabled')),
|
||||
)
|
||||
class SettingsForm(i18nfield.forms.I18nFormMixin, HierarkeyForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.obj = kwargs.pop('obj', None)
|
||||
self.obj = kwargs.get('obj', None)
|
||||
self.locales = kwargs.pop('locales', None)
|
||||
kwargs['attribute_name'] = 'settings'
|
||||
kwargs['locales'] = self.obj.settings.get('locales') if self.obj else self.locales
|
||||
kwargs['initial'] = self.obj.settings.freeze()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
Performs the save operation
|
||||
"""
|
||||
for name, field in self.fields.items():
|
||||
value = self.cleaned_data[name]
|
||||
if isinstance(value, UploadedFile):
|
||||
# Delete old file
|
||||
fname = self.obj.settings.get(name, as_type=File)
|
||||
if fname:
|
||||
try:
|
||||
default_storage.delete(fname.name)
|
||||
except OSError:
|
||||
logger.error('Deleting file %s failed.' % fname.name)
|
||||
|
||||
# Create new file
|
||||
nonce = get_random_string(length=8)
|
||||
if isinstance(self.obj, Event):
|
||||
fname = '%s/%s/%s.%s.%s' % (
|
||||
self.obj.organizer.slug, self.obj.slug, name, nonce, value.name.split('.')[-1]
|
||||
)
|
||||
else:
|
||||
fname = '%s/%s.%s.%s' % (self.obj.slug, name, nonce, value.name.split('.')[-1])
|
||||
newname = default_storage.save(fname, value)
|
||||
value._name = newname
|
||||
self.obj.settings.set(name, value)
|
||||
elif isinstance(value, File):
|
||||
# file is unchanged
|
||||
continue
|
||||
elif isinstance(field, forms.FileField):
|
||||
# file is deleted
|
||||
fname = self.obj.settings.get(name, as_type=File)
|
||||
if fname:
|
||||
try:
|
||||
default_storage.delete(fname.name)
|
||||
except OSError:
|
||||
logger.error('Deleting file %s failed.' % fname.name)
|
||||
del self.obj.settings[name]
|
||||
elif value is None:
|
||||
del self.obj.settings[name]
|
||||
elif self.obj.settings.get(name, as_type=type(value)) != value:
|
||||
self.obj.settings.set(name, value)
|
||||
def get_new_filename(self, name: str) -> str:
|
||||
nonce = get_random_string(length=8)
|
||||
if isinstance(self.obj, Event):
|
||||
fname = '%s/%s/%s.%s.%s' % (
|
||||
self.obj.organizer.slug, self.obj.slug, name, nonce, name.split('.')[-1]
|
||||
)
|
||||
else:
|
||||
fname = '%s/%s.%s.%s' % (self.obj.slug, name, nonce, name.split('.')[-1])
|
||||
return fname
|
||||
|
||||
59
src/pretix/base/migrations/0053_auto_20170409_1651.py
Normal file
59
src/pretix/base/migrations/0053_auto_20170409_1651.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-04-09 16:51
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_global_settings(apps, schema_editor):
|
||||
GlobalSetting = apps.get_model('pretixbase', 'GlobalSetting')
|
||||
GlobalSettingsObject_SettingsStore = apps.get_model('pretixbase', 'GlobalSettingsObject_SettingsStore')
|
||||
|
||||
l = []
|
||||
for s in GlobalSetting.objects.all():
|
||||
l.append(GlobalSettingsObject_SettingsStore(key=s.key, value=s.value))
|
||||
|
||||
GlobalSettingsObject_SettingsStore.objects.bulk_create(l)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0052_auto_20170324_1506'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='EventSetting',
|
||||
new_name='Event_SettingsStore',
|
||||
),
|
||||
migrations.RenameModel(
|
||||
old_name='OrganizerSetting',
|
||||
new_name='Organizer_SettingsStore',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GlobalSettingsObject_SettingsStore',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('key', models.CharField(db_index=True, max_length=255)),
|
||||
('value', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.RunPython(
|
||||
migrate_global_settings, migrations.RunPython.noop
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='GlobalSetting',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event_settingsstore',
|
||||
name='object',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='_settings_objects', to='pretixbase.Event'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organizer_settingsstore',
|
||||
name='object',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='_settings_objects', to='pretixbase.Organizer'),
|
||||
),
|
||||
]
|
||||
@@ -1,8 +1,9 @@
|
||||
from ..settings import GlobalSettingsObject_SettingsStore
|
||||
from .auth import U2FDevice, User
|
||||
from .base import CachedFile, LoggedModel, cachedfile_name
|
||||
from .checkin import Checkin
|
||||
from .event import (
|
||||
Event, EventLock, EventPermission, EventSetting, RequiredAction,
|
||||
Event, Event_SettingsStore, EventLock, EventPermission, RequiredAction,
|
||||
generate_invite_token,
|
||||
)
|
||||
from .invoices import Invoice, InvoiceLine, invoice_filename
|
||||
@@ -17,6 +18,6 @@ from .orders import (
|
||||
cachedcombinedticket_name, cachedticket_name, generate_position_secret,
|
||||
generate_secret,
|
||||
)
|
||||
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
||||
from .organizer import Organizer, Organizer_SettingsStore, OrganizerPermission
|
||||
from .vouchers import Voucher
|
||||
from .waitinglist import WaitingListEntry
|
||||
|
||||
@@ -11,22 +11,21 @@ from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.template.defaultfilters import date as _date
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from i18nfield.fields import I18nCharField
|
||||
|
||||
from pretix.base.email import CustomSMTPBackend
|
||||
from pretix.base.models.base import LoggedModel
|
||||
from pretix.base.settings import SettingsProxy
|
||||
from pretix.base.validators import EventSlugBlacklistValidator
|
||||
from pretix.helpers.daterange import daterange
|
||||
|
||||
from ..settings import settings_hierarkey
|
||||
from .auth import User
|
||||
from .organizer import Organizer
|
||||
from .settings import EventSetting
|
||||
|
||||
|
||||
@settings_hierarkey.add(parent_field='organizer', cache_namespace='event')
|
||||
class Event(LoggedModel):
|
||||
"""
|
||||
This model represents an event. An event is anything you can buy
|
||||
@@ -183,17 +182,6 @@ class Event(LoggedModel):
|
||||
|
||||
return ObjectRelatedCache(self)
|
||||
|
||||
@cached_property
|
||||
def settings(self) -> SettingsProxy:
|
||||
"""
|
||||
Returns an object representing this event's settings.
|
||||
"""
|
||||
try:
|
||||
return SettingsProxy(self, type=EventSetting, parent=self.organizer)
|
||||
except Organizer.DoesNotExist:
|
||||
# Should only happen when creating new events
|
||||
return SettingsProxy(self, type=EventSetting)
|
||||
|
||||
@property
|
||||
def presale_has_ended(self):
|
||||
if self.presale_end and now() > self.presale_end:
|
||||
@@ -290,7 +278,7 @@ class Event(LoggedModel):
|
||||
o.question = q
|
||||
o.save()
|
||||
|
||||
for s in EventSetting.objects.filter(object=other):
|
||||
for s in other.settings._objects.all():
|
||||
s.object = self
|
||||
s.pk = None
|
||||
if s.value.startswith('file://'):
|
||||
|
||||
@@ -3,17 +3,16 @@ import string
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from pretix.base.models.base import LoggedModel
|
||||
from pretix.base.settings import SettingsProxy
|
||||
from pretix.base.validators import OrganizerSlugBlacklistValidator
|
||||
|
||||
from ..settings import settings_hierarkey
|
||||
from .auth import User
|
||||
from .settings import OrganizerSetting
|
||||
|
||||
|
||||
@settings_hierarkey.add(cache_namespace='organizer')
|
||||
class Organizer(LoggedModel):
|
||||
"""
|
||||
This model represents an entity organizing events, e.g. a company, institution,
|
||||
@@ -59,14 +58,6 @@ class Organizer(LoggedModel):
|
||||
self.get_cache().clear()
|
||||
return obj
|
||||
|
||||
@cached_property
|
||||
def settings(self) -> SettingsProxy:
|
||||
"""
|
||||
Returns an object representing this organizer's settings
|
||||
"""
|
||||
from pretix.base.settings import GlobalSettingsObject
|
||||
return SettingsProxy(self, type=OrganizerSetting, parent=GlobalSettingsObject())
|
||||
|
||||
def get_cache(self) -> "pretix.base.cache.ObjectRelatedCache":
|
||||
"""
|
||||
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class GlobalSetting(models.Model):
|
||||
"""
|
||||
A global setting is a key-value setting which can be set for a
|
||||
pretix instance. It will be inherited by all events and organizers.
|
||||
It is filled via the register_global_settings signal.
|
||||
"""
|
||||
key = models.CharField(max_length=255, primary_key=True)
|
||||
value = models.TextField()
|
||||
|
||||
def __init__(self, *args, object=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class OrganizerSetting(models.Model):
|
||||
"""
|
||||
An organizer setting is a key-value setting which can be set for an
|
||||
organizer. It will be inherited by the events of this organizer
|
||||
"""
|
||||
object = models.ForeignKey('Organizer', related_name='setting_objects', on_delete=models.CASCADE)
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
|
||||
|
||||
class EventSetting(models.Model):
|
||||
"""
|
||||
An event setting is a key-value setting which can be set for a
|
||||
specific event
|
||||
"""
|
||||
object = models.ForeignKey('Event', related_name='setting_objects', on_delete=models.CASCADE)
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
@@ -54,7 +54,7 @@ def assign_automatically(event_id: int, user_id: int=None):
|
||||
|
||||
@receiver(signal=periodic_task)
|
||||
def process_waitinglist(sender, **kwargs):
|
||||
qs = Event.objects.prefetch_related('setting_objects', 'organizer__setting_objects').select_related('organizer')
|
||||
qs = Event.objects.prefetch_related('_settings_objects', 'organizer___settings_objects').select_related('organizer')
|
||||
for e in qs:
|
||||
if e.settings.waiting_list_enabled and e.settings.waiting_list_auto:
|
||||
assign_automatically.apply_async(args=(e.pk,))
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import decimal
|
||||
import json
|
||||
from datetime import date, datetime, time
|
||||
from datetime import datetime
|
||||
|
||||
from django.core.cache import cache
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import dateutil.parser
|
||||
from django.conf import settings
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Model
|
||||
from django.utils.translation import ugettext_noop
|
||||
|
||||
from hierarkey.models import GlobalSettingsBase, Hierarkey
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pretix.base.models.settings import GlobalSetting
|
||||
from typing import Any
|
||||
|
||||
DEFAULTS = {
|
||||
'max_items_per_order': {
|
||||
@@ -382,194 +377,27 @@ Your {event} team"""))
|
||||
}
|
||||
}
|
||||
|
||||
settings_hierarkey = Hierarkey(attribute_name='settings')
|
||||
|
||||
class SettingsProxy:
|
||||
"""
|
||||
This object allows convenient access to settings stored in the
|
||||
EventSettings/OrganizerSettings database model. It exposes all settings as
|
||||
properties and it will do all the nasty inheritance and defaults stuff for
|
||||
you.
|
||||
"""
|
||||
for k, v in DEFAULTS.items():
|
||||
settings_hierarkey.add_default(k, v['default'], v['type'])
|
||||
|
||||
def __init__(self, obj: Model, parent: Optional[Model]=None, type=None):
|
||||
self._obj = obj
|
||||
self._parent = parent
|
||||
self._cached_obj = None
|
||||
self._write_cached_obj = None
|
||||
self._type = type
|
||||
|
||||
def _cache(self) -> Dict[str, Any]:
|
||||
if self._cached_obj is None:
|
||||
self._cached_obj = cache.get_or_set(
|
||||
'settings_{}_{}'.format(self._obj.settings_namespace, self._obj.pk),
|
||||
lambda: {s.key: s.value for s in self._obj.setting_objects.all()},
|
||||
timeout=1800
|
||||
)
|
||||
return self._cached_obj
|
||||
def i18n_uns(v):
|
||||
try:
|
||||
return LazyI18nString(json.loads(v))
|
||||
except ValueError:
|
||||
return LazyI18nString(str(v))
|
||||
|
||||
def _write_cache(self) -> Dict[str, Any]:
|
||||
if self._write_cached_obj is None:
|
||||
self._write_cached_obj = {
|
||||
s.key: s for s in self._obj.setting_objects.all()
|
||||
}
|
||||
return self._write_cached_obj
|
||||
|
||||
def _flush(self) -> None:
|
||||
self._cached_obj = None
|
||||
self._write_cached_obj = None
|
||||
self._flush_external_cache()
|
||||
settings_hierarkey.add_type(LazyI18nString,
|
||||
serialize=lambda s: json.dumps(s.data),
|
||||
unserialize=i18n_uns)
|
||||
|
||||
def _flush_external_cache(self):
|
||||
cache.delete('settings_{}_{}'.format(self._obj.settings_namespace, self._obj.pk))
|
||||
|
||||
def freeze(self) -> dict:
|
||||
"""
|
||||
Returns a dictionary of all settings set for this object, including
|
||||
any default values of its parents or hardcoded in pretix.
|
||||
"""
|
||||
settings = {}
|
||||
for key, v in DEFAULTS.items():
|
||||
settings[key] = self._unserialize(v['default'], v['type'])
|
||||
if self._parent:
|
||||
settings.update(self._parent.settings.freeze())
|
||||
for key in self._cache():
|
||||
settings[key] = self.get(key)
|
||||
return settings
|
||||
|
||||
def _unserialize(self, value: str, as_type: type, binary_file=False) -> Any:
|
||||
if as_type is None and value is not None and value.startswith('file://'):
|
||||
as_type = File
|
||||
|
||||
if as_type is not None and isinstance(value, as_type):
|
||||
return value
|
||||
elif value is None:
|
||||
return None
|
||||
elif as_type == int or as_type == float or as_type == decimal.Decimal:
|
||||
return as_type(value)
|
||||
elif as_type == dict or as_type == list:
|
||||
return json.loads(value)
|
||||
elif as_type == bool or value in ('True', 'False'):
|
||||
return value == 'True'
|
||||
elif as_type == File:
|
||||
try:
|
||||
fi = default_storage.open(value[7:], 'rb' if binary_file else 'r')
|
||||
fi.url = default_storage.url(value[7:])
|
||||
return fi
|
||||
except OSError:
|
||||
return False
|
||||
elif as_type == datetime:
|
||||
return dateutil.parser.parse(value)
|
||||
elif as_type == date:
|
||||
return dateutil.parser.parse(value).date()
|
||||
elif as_type == time:
|
||||
return dateutil.parser.parse(value).time()
|
||||
elif as_type == LazyI18nString and not isinstance(value, LazyI18nString):
|
||||
try:
|
||||
return LazyI18nString(json.loads(value))
|
||||
except ValueError:
|
||||
return LazyI18nString(str(value))
|
||||
elif as_type is not None and issubclass(as_type, Model):
|
||||
return as_type.objects.get(pk=value)
|
||||
return value
|
||||
|
||||
def _serialize(self, value: Any) -> str:
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
elif isinstance(value, int) or isinstance(value, float) \
|
||||
or isinstance(value, bool) or isinstance(value, decimal.Decimal):
|
||||
return str(value)
|
||||
elif isinstance(value, list) or isinstance(value, dict):
|
||||
return json.dumps(value)
|
||||
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
|
||||
return value.isoformat()
|
||||
elif isinstance(value, Model):
|
||||
return value.pk
|
||||
elif isinstance(value, LazyI18nString):
|
||||
return json.dumps(value.data)
|
||||
elif isinstance(value, File):
|
||||
return 'file://' + value.name
|
||||
|
||||
raise TypeError('Unable to serialize %s into a setting.' % str(type(value)))
|
||||
|
||||
def get(self, key: str, default=None, as_type: type=None, binary_file=False):
|
||||
"""
|
||||
Get a setting specified by key ``key``. Normally, settings are strings, but
|
||||
if you put non-strings into the settings object, you can request unserialization
|
||||
by specifying ``as_type``. If the key does not have a harcdoded type in the pretix source,
|
||||
omitting ``as_type`` always will get you a string.
|
||||
|
||||
If the setting with the specified name does not exist on this object, any parent object
|
||||
will be queried (e.g. the organizer of an event). If still no value is found, a default
|
||||
value hardcoded will be returned if one exists. If not, the value of the ``default`` argument
|
||||
will be returned instead.
|
||||
"""
|
||||
if as_type is None and key in DEFAULTS:
|
||||
as_type = DEFAULTS[key]['type']
|
||||
|
||||
if key in self._cache():
|
||||
value = self._cache()[key]
|
||||
else:
|
||||
value = None
|
||||
if self._parent:
|
||||
value = self._parent.settings.get(key, as_type=str)
|
||||
if value is None and key in DEFAULTS:
|
||||
value = DEFAULTS[key]['default']
|
||||
if value is None and default is not None:
|
||||
value = default
|
||||
|
||||
return self._unserialize(value, as_type, binary_file=binary_file)
|
||||
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
return self.get(key)
|
||||
|
||||
def __getattr__(self, key: str) -> Any:
|
||||
if key.startswith('_'):
|
||||
return super().__getattr__(key)
|
||||
return self.get(key)
|
||||
|
||||
def __setattr__(self, key: str, value: Any) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__setattr__(key, value)
|
||||
self.set(key, value)
|
||||
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
self.set(key, value)
|
||||
|
||||
def set(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
Stores a setting to the database of its object.
|
||||
"""
|
||||
wc = self._write_cache()
|
||||
if key in wc:
|
||||
s = wc[key]
|
||||
else:
|
||||
s = self._type(object=self._obj, key=key)
|
||||
s.value = self._serialize(value)
|
||||
s.save()
|
||||
self._cache()[key] = s.value
|
||||
wc[key] = s
|
||||
self._flush_external_cache()
|
||||
|
||||
def __delattr__(self, key: str) -> None:
|
||||
if key.startswith('_'):
|
||||
return super().__delattr__(key)
|
||||
self.delete(key)
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
self.delete(key)
|
||||
|
||||
def delete(self, key: str) -> None:
|
||||
"""
|
||||
Deletes a setting from this object's storage.
|
||||
"""
|
||||
if key in self._write_cache():
|
||||
self._write_cache()[key].delete()
|
||||
del self._write_cache()[key]
|
||||
|
||||
if key in self._cache():
|
||||
del self._cache()[key]
|
||||
|
||||
self._flush_external_cache()
|
||||
@settings_hierarkey.set_global(cache_namespace='global')
|
||||
class GlobalSettingsObject(GlobalSettingsBase):
|
||||
slug = '_global'
|
||||
|
||||
|
||||
class SettingsSandbox:
|
||||
@@ -614,13 +442,3 @@ class SettingsSandbox:
|
||||
|
||||
def set(self, key: str, value: Any):
|
||||
self._event.settings.set(self._convert_key(key), value)
|
||||
|
||||
|
||||
class GlobalSettingsObject():
|
||||
settings_namespace = 'global'
|
||||
|
||||
def __init__(self):
|
||||
self.settings = SettingsProxy(self, type=GlobalSetting)
|
||||
self.setting_objects = GlobalSetting.objects
|
||||
self.slug = '_global'
|
||||
self.pk = '_global'
|
||||
|
||||
@@ -22,13 +22,13 @@ class EventList(ListView):
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_superuser:
|
||||
return Event.objects.all().select_related("organizer").prefetch_related(
|
||||
"setting_objects", "organizer__setting_objects"
|
||||
"_settings_objects", "organizer___settings_objects"
|
||||
)
|
||||
else:
|
||||
return Event.objects.filter(
|
||||
permitted__id__exact=self.request.user.pk
|
||||
).select_related("organizer").prefetch_related(
|
||||
"setting_objects", "organizer__setting_objects"
|
||||
"_settings_objects", "organizer___settings_objects"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ pep8-naming
|
||||
flake8
|
||||
codecov
|
||||
coverage
|
||||
pytest==2.9.*
|
||||
pytest==3.0.*
|
||||
pytest-django
|
||||
isort
|
||||
pytest-mock
|
||||
pytest-mock==1.4.*
|
||||
pytest-rerunfailures
|
||||
pytest-warnings
|
||||
responses
|
||||
|
||||
@@ -5,6 +5,7 @@ pytz
|
||||
django-bootstrap3==8.0.*
|
||||
django-formset-js-improved==0.5.0.1
|
||||
django-compressor==2.1
|
||||
django-hierarkey==1.0.*
|
||||
reportlab==3.2.*
|
||||
PyPDF2==1.26.*
|
||||
easy-thumbnails==2.*
|
||||
|
||||
@@ -68,6 +68,7 @@ setup(
|
||||
'django-bootstrap3==7.1.*',
|
||||
'django-formset-js-improved==0.5.0.1',
|
||||
'django-compressor==2.1',
|
||||
'django-hierarkey==1.0.*',
|
||||
'reportlab==3.2.*',
|
||||
'easy-thumbnails==2.*',
|
||||
'PyPDF2==1.26.*',
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
from datetime import date, datetime, time
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import TestCase
|
||||
from django.utils.timezone import now
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base import settings
|
||||
from pretix.base.models import Event, Organizer, User
|
||||
from pretix.base.models import Event, Organizer
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.control.forms.global_settings import GlobalSettingsObject
|
||||
|
||||
@@ -21,243 +15,24 @@ class SettingsTestCase(TestCase):
|
||||
'type': str
|
||||
}
|
||||
self.global_settings = GlobalSettingsObject()
|
||||
self.global_settings.settings._flush()
|
||||
self.global_settings.settings.flush()
|
||||
self.organizer = Organizer.objects.create(name='Dummy', slug='dummy')
|
||||
self.organizer.settings._flush()
|
||||
self.organizer.settings.flush()
|
||||
self.event = Event.objects.create(
|
||||
organizer=self.organizer, name='Dummy', slug='dummy',
|
||||
date_from=now(),
|
||||
)
|
||||
self.event.settings._flush()
|
||||
|
||||
def test_global_set_explicit(self):
|
||||
self.global_settings.settings.test = 'foo'
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.global_settings = GlobalSettingsObject()
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
|
||||
def test_organizer_set_explicit(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
|
||||
def test_event_set_explicit(self):
|
||||
self.event.settings.test = 'foo'
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.event = Event.objects.get(id=self.event.id)
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_event_set_twice(self):
|
||||
self.event.settings.test = 'bar'
|
||||
self.event.settings.test = 'foo'
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.event = Event.objects.get(id=self.event.id)
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_organizer_set_on_global(self):
|
||||
self.global_settings.settings.test = 'foo'
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.global_settings = GlobalSettingsObject()
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
|
||||
def test_event_set_on_global(self):
|
||||
self.global_settings.settings.test = 'foo'
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.global_settings = GlobalSettingsObject()
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_event_set_on_organizer(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
# Reload object
|
||||
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
def test_event_override_organizer(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.event.settings.test = 'bar'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
# Reload object
|
||||
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||
self.event = Event.objects.get(id=self.event.id)
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
def test_event_override_global(self):
|
||||
self.global_settings.settings.test = 'foo'
|
||||
self.event.settings.test = 'bar'
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
# Reload object
|
||||
self.global_settings = GlobalSettingsObject()
|
||||
self.event = Event.objects.get(id=self.event.id)
|
||||
self.assertEqual(self.global_settings.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
def test_default(self):
|
||||
self.assertEqual(self.global_settings.settings.test_default, 'def')
|
||||
self.assertEqual(self.organizer.settings.test_default, 'def')
|
||||
self.assertEqual(self.event.settings.test_default, 'def')
|
||||
self.assertEqual(self.event.settings.get('nonexistant', default='abc'), 'abc')
|
||||
|
||||
def test_default_typing(self):
|
||||
self.assertIs(type(self.event.settings.get('nonexistant', as_type=Decimal, default=0)), Decimal)
|
||||
|
||||
def test_item_access(self):
|
||||
self.event.settings['foo'] = 'abc'
|
||||
self.assertEqual(self.event.settings['foo'], 'abc')
|
||||
del self.event.settings['foo']
|
||||
self.assertIsNone(self.event.settings['foo'])
|
||||
|
||||
def test_delete(self):
|
||||
self.organizer.settings.test = 'foo'
|
||||
self.event.settings.test = 'bar'
|
||||
self.assertEqual(self.organizer.settings.test, 'foo')
|
||||
self.assertEqual(self.event.settings.test, 'bar')
|
||||
|
||||
del self.event.settings.test
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
self.event = Event.objects.get(id=self.event.id)
|
||||
self.assertEqual(self.event.settings.test, 'foo')
|
||||
|
||||
del self.organizer.settings.test
|
||||
self.assertIsNone(self.organizer.settings.test)
|
||||
|
||||
self.organizer = Organizer.objects.get(id=self.organizer.id)
|
||||
self.assertIsNone(self.organizer.settings.test)
|
||||
|
||||
def test_serialize_str(self):
|
||||
self._test_serialization('ABC', as_type=str)
|
||||
|
||||
def test_serialize_float(self):
|
||||
self._test_serialization(2.3, float)
|
||||
|
||||
def test_serialize_int(self):
|
||||
self._test_serialization(2, int)
|
||||
|
||||
def test_serialize_datetime(self):
|
||||
self._test_serialization(now(), datetime)
|
||||
|
||||
def test_serialize_time(self):
|
||||
self._test_serialization(now().time(), time)
|
||||
|
||||
def test_serialize_date(self):
|
||||
self._test_serialization(now().date(), date)
|
||||
|
||||
def test_serialize_decimal(self):
|
||||
self._test_serialization(Decimal('2.3'), Decimal)
|
||||
|
||||
def test_serialize_dict(self):
|
||||
self._test_serialization({'a': 'b', 'c': 'd'}, dict)
|
||||
|
||||
def test_serialize_list(self):
|
||||
self._test_serialization([1, 2, 'a'], list)
|
||||
|
||||
def test_serialize_lazyi18nstring(self):
|
||||
self._test_serialization(LazyI18nString({'de': 'Hallo', 'en': 'Hello'}), LazyI18nString)
|
||||
|
||||
def test_serialize_bool(self):
|
||||
self._test_serialization(True, bool)
|
||||
self._test_serialization(False, bool)
|
||||
|
||||
def test_serialize_bool_implicit(self):
|
||||
self.event.settings.set('test', True)
|
||||
self.event.settings._flush()
|
||||
self.assertIs(self.event.settings.get('test', as_type=None), True)
|
||||
self.event.settings.set('test', False)
|
||||
self.event.settings._flush()
|
||||
self.assertIs(self.event.settings.get('test', as_type=None), False)
|
||||
|
||||
def test_serialize_versionable(self):
|
||||
self._test_serialization(self.event, Event)
|
||||
|
||||
def test_serialize_model(self):
|
||||
self._test_serialization(User.objects.create_user('dummy@dummy.dummy', 'dummy'), User)
|
||||
|
||||
def test_serialize_unknown(self):
|
||||
class Type:
|
||||
pass
|
||||
|
||||
try:
|
||||
self._test_serialization(Type(), Type)
|
||||
self.assertTrue(False, 'No exception thrown!')
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
def test_serialize_file(self):
|
||||
val = SimpleUploadedFile("sample_invalid_image.jpg", b"file_content", content_type="image/jpeg")
|
||||
default_storage.save(val.name, val)
|
||||
val.close()
|
||||
self.event.settings.set('test', val)
|
||||
self.event.settings._flush()
|
||||
f = self.event.settings.get('test', as_type=File)
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertTrue(f.name.endswith(val.name))
|
||||
f.close()
|
||||
|
||||
def test_unserialize_file_value(self):
|
||||
val = SimpleUploadedFile("sample_invalid_image.jpg", b"file_content", content_type="image/jpeg")
|
||||
default_storage.save(val.name, val)
|
||||
val.close()
|
||||
self.event.settings.set('test', val)
|
||||
self.event.settings._flush()
|
||||
f = self.event.settings.get('test', as_type=File)
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertTrue(f.name.endswith(val.name))
|
||||
f.close()
|
||||
|
||||
def test_autodetect_file_value(self):
|
||||
val = SimpleUploadedFile("sample_invalid_image.jpg", b"file_content", content_type="image/jpeg")
|
||||
default_storage.save(val.name, val)
|
||||
val.close()
|
||||
self.event.settings.set('test', val)
|
||||
self.event.settings._flush()
|
||||
f = self.event.settings.get('test')
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertTrue(f.name.endswith(val.name))
|
||||
f.close()
|
||||
|
||||
def test_autodetect_file_value_of_parent(self):
|
||||
val = SimpleUploadedFile("sample_invalid_image.jpg", b"file_content", content_type="image/jpeg")
|
||||
default_storage.save(val.name, val)
|
||||
val.close()
|
||||
self.organizer.settings.set('test', val)
|
||||
self.organizer.settings._flush()
|
||||
f = self.event.settings.get('test')
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertTrue(f.name.endswith(val.name))
|
||||
f.close()
|
||||
self.event.settings.flush()
|
||||
|
||||
def _test_serialization(self, val, as_type):
|
||||
self.event.settings.set('test', val)
|
||||
self.event.settings._flush()
|
||||
self.event.settings.flush()
|
||||
self.assertEqual(self.event.settings.get('test', as_type=as_type), val)
|
||||
self.assertIsInstance(self.event.settings.get('test', as_type=as_type), as_type)
|
||||
|
||||
def test_serialize_lazyi18nstring(self):
|
||||
self._test_serialization(LazyI18nString({'de': 'Hallo', 'en': 'Hello'}), LazyI18nString)
|
||||
|
||||
def test_sandbox(self):
|
||||
sandbox = SettingsSandbox('testing', 'foo', self.event)
|
||||
sandbox.set('foo', 'bar')
|
||||
@@ -278,26 +53,3 @@ class SettingsTestCase(TestCase):
|
||||
|
||||
self.assertIsNone(sandbox.bar)
|
||||
self.assertIsNone(sandbox['baz'])
|
||||
|
||||
def test_freeze(self):
|
||||
olddef = settings.DEFAULTS
|
||||
settings.DEFAULTS = {
|
||||
'test_default': {
|
||||
'default': 'def',
|
||||
'type': str
|
||||
}
|
||||
}
|
||||
self.event.organizer.settings.set('bar', 'baz')
|
||||
self.event.organizer.settings.set('foo', 'baz')
|
||||
self.event.settings.set('foo', 'bar')
|
||||
frozen = self.event.settings.freeze()
|
||||
self.event.settings.set('foo', 'notfrozen')
|
||||
|
||||
try:
|
||||
self.assertEqual(frozen, {
|
||||
'test_default': 'def',
|
||||
'bar': 'baz',
|
||||
'foo': 'bar'
|
||||
})
|
||||
finally:
|
||||
settings.DEFAULTS = olddef
|
||||
|
||||
@@ -135,7 +135,7 @@ class EventsTest(SoupTest):
|
||||
'settings-payment_term_days': '2',
|
||||
'settings-tax_rate_default': '19.00',
|
||||
})
|
||||
self.event1.settings._flush()
|
||||
self.event1.settings.flush()
|
||||
assert self.event1.settings.get('payment_banktransfer__enabled', as_type=bool)
|
||||
assert self.event1.settings.get('payment_banktransfer__fee_abs', as_type=Decimal) == Decimal('12.23')
|
||||
|
||||
@@ -177,7 +177,7 @@ class EventsTest(SoupTest):
|
||||
doc = self.post_doc('/control/event/%s/%s/settings/invoice' % (self.orga1.slug, self.event1.slug),
|
||||
data, follow=True)
|
||||
assert doc.select('.alert-success')
|
||||
self.event1.settings._flush()
|
||||
self.event1.settings.flush()
|
||||
assert self.event1.settings.get('invoice_address_required', as_type=bool)
|
||||
|
||||
def test_display_settings(self):
|
||||
@@ -190,7 +190,7 @@ class EventsTest(SoupTest):
|
||||
doc = self.post_doc('/control/event/%s/%s/settings/display' % (self.orga1.slug, self.event1.slug),
|
||||
data, follow=True)
|
||||
assert doc.select('.alert-success')
|
||||
self.event1.settings._flush()
|
||||
self.event1.settings.flush()
|
||||
assert self.event1.settings.get('primary_color') == '#FF0000'
|
||||
mocked.assert_any_call(args=(self.event1.pk,))
|
||||
|
||||
@@ -204,7 +204,7 @@ class EventsTest(SoupTest):
|
||||
doc = self.post_doc('/control/event/%s/%s/settings/email' % (self.orga1.slug, self.event1.slug),
|
||||
data, follow=True)
|
||||
assert doc.select('.alert-success')
|
||||
self.event1.settings._flush()
|
||||
self.event1.settings.flush()
|
||||
assert mocked.called
|
||||
|
||||
def test_ticket_settings(self):
|
||||
@@ -214,7 +214,7 @@ class EventsTest(SoupTest):
|
||||
data['ticketoutput_testdummy__enabled'] = 'on'
|
||||
doc = self.post_doc('/control/event/%s/%s/settings/tickets' % (self.orga1.slug, self.event1.slug),
|
||||
data, follow=True)
|
||||
self.event1.settings._flush()
|
||||
self.event1.settings.flush()
|
||||
assert self.event1.settings.get('ticket_download', as_type=bool)
|
||||
|
||||
def test_create_event_unauthorized(self):
|
||||
|
||||
@@ -52,12 +52,12 @@ def test_settings(client, user):
|
||||
|
||||
client.post('/control/global/update/', {'update_check_email': 'test@example.org', 'update_check_perform': 'on'})
|
||||
gs = GlobalSettingsObject()
|
||||
gs.settings._flush()
|
||||
gs.settings.flush()
|
||||
assert gs.settings.update_check_perform
|
||||
assert gs.settings.update_check_email
|
||||
|
||||
client.post('/control/global/update/', {'update_check_email': '', 'update_check_perform': ''})
|
||||
gs.settings._flush()
|
||||
gs.settings.flush()
|
||||
assert not gs.settings.update_check_perform
|
||||
assert not gs.settings.update_check_email
|
||||
|
||||
@@ -78,5 +78,5 @@ def test_trigger(client, user):
|
||||
gs = GlobalSettingsObject()
|
||||
assert not gs.settings.update_check_last
|
||||
client.post('/control/global/update/', {'trigger': 'on'})
|
||||
gs.settings._flush()
|
||||
gs.settings.flush()
|
||||
assert gs.settings.update_check_last
|
||||
|
||||
@@ -44,11 +44,11 @@ def test_flush_key(client, env):
|
||||
env[0].settings.set('pretixdroid_key', 'abcdefg')
|
||||
|
||||
client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug))
|
||||
env[0].settings._flush()
|
||||
env[0].settings.flush()
|
||||
env[0].settings.get('pretixdroid_key') == 'abcdefg'
|
||||
|
||||
client.get('/control/event/%s/%s/pretixdroid/?flush_key=1' % (env[0].organizer.slug, env[0].slug))
|
||||
env[0].settings._flush()
|
||||
env[0].settings.flush()
|
||||
env[0].settings.get('pretixdroid_key') != 'abcdefg'
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user