Add event meta-data

This commit is contained in:
Raphael Michel
2017-08-28 11:46:35 +02:00
parent 454ca27c54
commit 88f5af3e77
23 changed files with 470 additions and 27 deletions

View File

@@ -1,4 +1,5 @@
from django_countries.serializers import CountryFieldMixin
from rest_framework.fields import Field
from pretix.api.serializers.i18n import I18nAwareModelSerializer
from pretix.base.models import Event, TaxRule
@@ -6,12 +7,22 @@ from pretix.base.models.event import SubEvent
from pretix.base.models.items import SubEventItem, SubEventItemVariation
class MetaDataField(Field):
def to_representation(self, value):
return {
v.property.name: v.value for v in value.meta_values.all()
}
class EventSerializer(I18nAwareModelSerializer):
meta_data = MetaDataField(source='*')
class Meta:
model = Event
fields = ('name', 'slug', 'live', 'currency', 'date_from',
'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'has_subevents')
'presale_end', 'location', 'has_subevents', 'meta_data')
class SubEventItemSerializer(I18nAwareModelSerializer):
@@ -29,12 +40,13 @@ class SubEventItemVariationSerializer(I18nAwareModelSerializer):
class SubEventSerializer(I18nAwareModelSerializer):
item_price_overrides = SubEventItemSerializer(source='subeventitem_set', many=True)
variation_price_overrides = SubEventItemVariationSerializer(source='subeventitemvariation_set', many=True)
meta_data = MetaDataField(source='*')
class Meta:
model = SubEvent
fields = ('id', 'name', 'date_from', 'date_to', 'active', 'date_admission',
'presale_start', 'presale_end', 'location',
'item_price_overrides', 'variation_price_overrides')
'item_price_overrides', 'variation_price_overrides', 'meta_data')
class TaxRuleSerializer(CountryFieldMixin, I18nAwareModelSerializer):

View File

@@ -15,7 +15,7 @@ class EventViewSet(viewsets.ReadOnlyModelViewSet):
lookup_url_kwarg = 'event'
def get_queryset(self):
return self.request.organizer.events.all()
return self.request.organizer.events.prefetch_related('meta_values', 'meta_values__property')
class SubEventFilter(FilterSet):

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-08-28 09:01
from __future__ import unicode_literals
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
import pretix.base.models.base
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0074_auto_20170825_1258'),
]
operations = [
migrations.CreateModel(
name='EventMetaProperty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True, help_text='Can not contain spaces or special characters execpt underscores', max_length=50, validators=[django.core.validators.RegexValidator(message='The property name may only contain letters, numbers and underscores.', regex='^[a-zA-Z0-9_]+$')], verbose_name='Name')),
('default', models.TextField()),
('organizer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_properties', to='pretixbase.Organizer')),
],
options={
'abstract': False,
},
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='EventMetaValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.TextField()),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_values', to='pretixbase.Event')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_values', to='pretixbase.EventMetaProperty')),
],
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='SubEventMetaValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.TextField()),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subevent_values', to='pretixbase.EventMetaProperty')),
('subevent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_values', to='pretixbase.SubEvent')),
],
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.AlterUniqueTogether(
name='subeventmetavalue',
unique_together=set([('subevent', 'property')]),
),
migrations.AlterUniqueTogether(
name='eventmetavalue',
unique_together=set([('event', 'property')]),
),
]

View File

@@ -452,6 +452,12 @@ class Event(EventMixin, LoggedModel):
)
).order_by('date_from', 'name')
@property
def meta_data(self):
data = {p.name: p.default for p in self.organizer.meta_properties.all()}
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
return data
class SubEvent(EventMixin, LoggedModel):
"""
@@ -541,6 +547,12 @@ class SubEvent(EventMixin, LoggedModel):
for si in SubEventItemVariation.objects.filter(subevent=self, price__isnull=False)
}
@property
def meta_data(self):
data = self.event.meta_data
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
return data
def generate_invite_token():
return get_random_string(length=32, allowed_chars=string.ascii_lowercase + string.digits)
@@ -589,3 +601,74 @@ class RequiredAction(models.Model):
if response:
return response
return self.action_type
class EventMetaProperty(LoggedModel):
"""
An organizer account can have EventMetaProperty objects attached to define meta information fields
for its events. This information can be re-used for example in ticket layouts.
:param organizer: The organizer this property is defined for.
:type organizer: Organizer
:param name: Name
:type name: Name of the property, used in various places
:param default: Default value
:type default: str
"""
organizer = models.ForeignKey(Organizer, related_name="meta_properties", on_delete=models.CASCADE)
name = models.CharField(
max_length=50, db_index=True,
help_text=_(
"Can not contain spaces or special characters execpt underscores"
),
validators=[
RegexValidator(
regex="^[a-zA-Z0-9_]+$",
message=_("The property name may only contain letters, numbers and underscores."),
),
],
verbose_name=_("Name"),
)
default = models.TextField(blank=True)
class EventMetaValue(LoggedModel):
"""
A meta-data value assigned to an event.
:param event: The event this metadata is valid for
:type event: Event
:param property: The property this value belongs to
:type property: EventMetaProperty
:param value: The actual value
:type value: str
"""
event = models.ForeignKey('Event', on_delete=models.CASCADE,
related_name='meta_values')
property = models.ForeignKey('EventMetaProperty', on_delete=models.CASCADE,
related_name='event_values')
value = models.TextField()
class Meta:
unique_together = ('event', 'property')
class SubEventMetaValue(LoggedModel):
"""
A meta-data value assigned to a sub-event.
:param event: The event this metadata is valid for
:type event: Event
:param property: The property this value belongs to
:type property: EventMetaProperty
:param value: The actual value
:type value: str
"""
subevent = models.ForeignKey('SubEvent', on_delete=models.CASCADE,
related_name='meta_values')
property = models.ForeignKey('EventMetaProperty', on_delete=models.CASCADE,
related_name='subevent_values')
value = models.TextField()
class Meta:
unique_together = ('subevent', 'property')

View File

@@ -10,6 +10,7 @@ from pytz import common_timezones, timezone
from pretix.base.forms import I18nModelForm, PlaceholderValidator, SettingsForm
from pretix.base.models import Event, Organizer, TaxRule
from pretix.base.models.event import EventMetaValue
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
from pretix.control.forms import ExtFileField, SlugWidget
from pretix.multidomain.urlreverse import build_absolute_uri
@@ -163,6 +164,22 @@ class EventWizardCopyForm(forms.Form):
)
class EventMetaValueForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.property = kwargs.pop('property')
super().__init__(*args, **kwargs)
self.fields['value'].required = False
self.fields['value'].widget.attrs['placeholder'] = self.property.default
class Meta:
model = EventMetaValue
fields = ['value']
widgets = {
'value': forms.TextInput
}
class EventUpdateForm(I18nModelForm):
def clean_slug(self):
return self.instance.slug

View File

@@ -71,6 +71,14 @@ class OrganizerUpdateForm(OrganizerForm):
return instance
class EventMetaPropertyForm(forms.ModelForm):
class Meta:
fields = ['name', 'default']
widgets = {
'default': forms.TextInput()
}
class TeamForm(forms.ModelForm):
def __init__(self, *args, **kwargs):

View File

@@ -3,7 +3,7 @@ from django.utils.functional import cached_property
from i18nfield.forms import I18nInlineFormSet
from pretix.base.forms import I18nModelForm
from pretix.base.models.event import SubEvent
from pretix.base.models.event import SubEvent, SubEventMetaValue
from pretix.base.models.items import SubEventItem
@@ -99,3 +99,20 @@ class QuotaFormSet(I18nInlineFormSet):
)
self.add_fields(form, None)
return form
class SubEventMetaValueForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.property = kwargs.pop('property')
self.default = kwargs.pop('default', None)
super().__init__(*args, **kwargs)
self.fields['value'].required = False
self.fields['value'].widget.attrs['placeholder'] = self.default or self.property.default
class Meta:
model = SubEventMetaValue
fields = ['value']
widgets = {
'value': forms.TextInput
}

View File

@@ -15,6 +15,26 @@
{% bootstrap_field form.date_admission layout="horizontal" %}
{% bootstrap_field form.currency layout="horizontal" %}
{% bootstrap_field form.is_public layout="horizontal" %}
{% if meta_forms %}
<div class="form-group metadata-group">
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
<div class="col-md-9">
{% for form in meta_forms %}
<div class="row">
<div class="col-md-4">
<label for="{{ form.value.id_for_label }}">
{{ form.property.name }}
</label>
</div>
<div class="col-md-8">
{% bootstrap_form form layout="inline" %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Display settings" %}</legend>

View File

@@ -1,6 +1,7 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block title %}{% trans "Organizer" %}{% endblock %}
{% block content %}
<h1>{% trans "Organizer" %}</h1>
@@ -28,6 +29,65 @@
{% bootstrap_form_errors sform %}
{% bootstrap_field sform.organizer_info_text layout="horizontal" %}
</fieldset>
<fieldset>
<legend>{% trans "Event metadata (advanced)" %}</legend>
<p>
{% blocktrans trimmed %}
You can here define a set of metadata properties (i.e. variables) that you can later set for your
events and re-use in places like ticket layouts. This is an useful timesaver if you create lots and
lots of events.
{% endblocktrans %}
</p>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_form_errors form %}
{% bootstrap_field form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_field formset.empty_form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field formset.empty_form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add property" %}</button>
</p>
</div>
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -27,6 +27,25 @@
{% bootstrap_field form.location layout="horizontal" %}
{% bootstrap_field form.date_admission layout="horizontal" %}
{% bootstrap_field form.frontpage_text layout="horizontal" %}
{% if meta_forms %}
<div class="form-group metadata-group">
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
<div class="col-md-9">
{% for form in meta_forms %}
<div class="row">
<div class="col-md-4">
<label for="{{ form.value.id_for_label }}">
{{ form.property.name }}
</label>
</div>
<div class="col-md-8">
{% bootstrap_form form layout="inline" %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Timeline" %}</legend>

View File

@@ -28,13 +28,14 @@ from pretix.base.models import (
CachedTicket, Event, Item, ItemVariation, LogEntry, Order, OrderPosition,
RequiredAction, TaxRule, Voucher,
)
from pretix.base.models.event import EventMetaValue
from pretix.base.services import tickets
from pretix.base.services.invoices import build_preview_invoice_pdf
from pretix.base.signals import event_live_issues, register_ticket_outputs
from pretix.control.forms.event import (
CommentForm, DisplaySettingsForm, EventSettingsForm, EventUpdateForm,
InvoiceSettingsForm, MailSettingsForm, PaymentSettingsForm, ProviderForm,
TaxRuleForm, TicketSettingsForm,
CommentForm, DisplaySettingsForm, EventMetaValueForm, EventSettingsForm,
EventUpdateForm, InvoiceSettingsForm, MailSettingsForm,
PaymentSettingsForm, ProviderForm, TaxRuleForm, TicketSettingsForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.helpers.urls import build_absolute_uri
@@ -44,7 +45,39 @@ from . import CreateView, UpdateView
from ..logdisplay import OVERVIEW_BLACKLIST
class EventUpdate(EventPermissionRequiredMixin, UpdateView):
class MetaDataEditorMixin:
meta_form = EventMetaValueForm
meta_model = EventMetaValue
@cached_property
def meta_forms(self):
val_instances = {
v.property_id: v for v in self.object.meta_values.all()
}
formlist = []
for p in self.request.organizer.meta_properties.all():
formlist.append(self._make_meta_form(p, val_instances))
return formlist
def _make_meta_form(self, p, val_instances):
return self.meta_form(
prefix='prop-{}'.format(p.pk),
property=p,
instance=val_instances.get(p.pk, self.meta_model(property=p, event=self.object)),
data=(self.request.POST if self.request.method == "POST" else None)
)
def save_meta(self):
for f in self.meta_forms:
if f.cleaned_data.get('value'):
f.save()
elif f.instance and f.instance.pk:
f.delete()
class EventUpdate(EventPermissionRequiredMixin, MetaDataEditorMixin, UpdateView):
model = Event
form_class = EventUpdateForm
template_name = 'pretixcontrol/event/settings.html'
@@ -68,11 +101,14 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['sform'] = self.sform
context['meta_forms'] = self.meta_forms
return context
@transaction.atomic
def form_valid(self, form):
self.sform.save()
self.save_meta()
if self.sform.has_changed():
self.request.event.log_action('pretix.event.settings', user=self.request.user, data={
k: self.request.event.settings.get(k) for k in self.sform.changed_data
@@ -92,7 +128,7 @@ class EventUpdate(EventPermissionRequiredMixin, UpdateView):
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and self.sform.is_valid():
if form.is_valid() and self.sform.is_valid() and all([f.is_valid() for f in self.meta_forms]):
# reset timezone
zone = timezone(self.sform.cleaned_data['timezone'])
event = form.instance

View File

@@ -5,6 +5,7 @@ from django.core.files import File
from django.core.urlresolvers import reverse
from django.db import transaction
from django.db.models import Count
from django.forms import inlineformset_factory
from django.shortcuts import get_object_or_404, redirect
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
@@ -13,10 +14,12 @@ from django.views.generic import (
)
from pretix.base.models import Organizer, Team, TeamInvite, User
from pretix.base.models.event import EventMetaProperty
from pretix.base.models.organizer import TeamAPIToken
from pretix.base.services.mail import SendMailException, mail
from pretix.control.forms.organizer import (
OrganizerForm, OrganizerSettingsForm, OrganizerUpdateForm, TeamForm,
EventMetaPropertyForm, OrganizerForm, OrganizerSettingsForm,
OrganizerUpdateForm, TeamForm,
)
from pretix.control.permissions import OrganizerPermissionRequiredMixin
from pretix.control.signals import nav_organizer
@@ -108,10 +111,12 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['sform'] = self.sform
context['formset'] = self.formset
return context
@transaction.atomic
def form_valid(self, form):
self.save_formset(self.object)
self.sform.save()
if self.sform.has_changed():
self.request.organizer.log_action(
@@ -145,12 +150,40 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
})
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid() and self.sform.is_valid():
if form.is_valid() and self.sform.is_valid() and self.formset.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
@cached_property
def formset(self):
formsetclass = inlineformset_factory(
Organizer, EventMetaProperty,
form=EventMetaPropertyForm, can_order=False, can_delete=True, extra=0
)
return formsetclass(self.request.POST if self.request.method == "POST" else None,
instance=self.object, queryset=self.object.meta_properties.all())
def save_formset(self, obj):
for form in self.formset.initial_forms:
if form in self.formset.deleted_forms:
if not form.instance.pk:
continue
form.instance.delete()
form.instance.pk = None
elif form.has_changed():
form.save()
for form in self.formset.extra_forms:
if not form.has_changed():
continue
if self.formset._should_delete_form(form):
continue
form.instance.organizer = obj
form.save()
class OrganizerCreate(CreateView):
model = Organizer

View File

@@ -9,14 +9,16 @@ from django.utils.functional import cached_property
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from django.views.generic import CreateView, DeleteView, ListView, UpdateView
from pretix.base.models.event import SubEvent
from pretix.base.models.event import SubEvent, SubEventMetaValue
from pretix.base.models.items import Quota, SubEventItem, SubEventItemVariation
from pretix.control.forms.filter import SubEventFilterForm
from pretix.control.forms.item import QuotaForm
from pretix.control.forms.subevents import (
QuotaFormSet, SubEventForm, SubEventItemForm, SubEventItemVariationForm,
SubEventMetaValueForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.control.views.event import MetaDataEditorMixin
class SubEventList(EventPermissionRequiredMixin, ListView):
@@ -85,7 +87,22 @@ class SubEventDelete(EventPermissionRequiredMixin, DeleteView):
})
class SubEventEditorMixin:
class SubEventEditorMixin(MetaDataEditorMixin):
meta_form = SubEventMetaValueForm
meta_model = SubEventMetaValue
def _make_meta_form(self, p, val_instances):
if not hasattr(self, '_default_meta'):
self._default_meta = self.request.event.meta_data
return self.meta_form(
prefix='prop-{}'.format(p.pk),
property=p,
default=self._default_meta.get(p.name, ''),
instance=val_instances.get(p.pk, self.meta_model(property=p, subevent=self.object)),
data=(self.request.POST if self.request.method == "POST" else None)
)
@cached_property
def formset(self):
extra = 0
@@ -159,6 +176,7 @@ class SubEventEditorMixin:
ctx = super().get_context_data(**kwargs)
ctx['formset'] = self.formset
ctx['itemvar_forms'] = self.itemvar_forms
ctx['meta_forms'] = self.meta_forms
return ctx
@cached_property
@@ -210,7 +228,9 @@ class SubEventEditorMixin:
return formlist
def is_valid(self, form):
return form.is_valid() and all([f.is_valid() for f in self.itemvar_forms]) and self.formset.is_valid()
return form.is_valid() and all([f.is_valid() for f in self.itemvar_forms]) and self.formset.is_valid() and (
all([f.is_valid() for f in self.meta_forms])
)
class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateView):
@@ -239,6 +259,7 @@ class SubEventUpdate(EventPermissionRequiredMixin, SubEventEditorMixin, UpdateVi
@transaction.atomic
def form_valid(self, form):
self.save_formset(self.object)
self.save_meta()
for f in self.itemvar_forms:
f.save()
@@ -308,5 +329,6 @@ class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateVi
for f in self.itemvar_forms:
f.instance.subevent = form.instance
f.save()
self.object = form.instance
self.save_meta()
return ret

View File

@@ -137,7 +137,7 @@ var editor = {
if (d.content === "other") {
o.setText(d.text);
} else {
o.setText(editor.text_samples[d.content]);
o.setText(editor._get_text_sample(d.content));
}
}
@@ -182,6 +182,13 @@ var editor = {
"addons": gettext("Addon 1\nAddon 2"),
},
_get_text_sample: function (key) {
if (key.startsWith('meta:')) {
return key.substr(5);
}
return editor.text_samples[key];
},
_load_pdf: function (dump) {
// TODO: Loading indicators
var url = editor.pdf_url;
@@ -362,7 +369,7 @@ var editor = {
if ($("#toolbox-content").val() === "other") {
o.setText($("#toolbox-content-other").val());
} else {
o.setText(editor.text_samples[$("#toolbox-content").val()]);
o.setText(editor._get_text_sample($("#toolbox-content").val()));
}
}

View File

@@ -304,6 +304,11 @@
<option value="addons">{% trans "List of Add-Ons" %}</option>
<option value="organizer">{% trans "Organizer name" %}</option>
<option value="organizer_info_text">{% trans "Organizer info text" %}</option>
{% for p in request.organizer.meta_properties.all %}
<option value="meta:{{ p.name }}">
{% trans "Event attribute:" %} {{ p.name }}
</option>
{% endfor %}
<option value="other">{% trans "Other…" %}</option>
</select>
<textarea type="text" value="" class="input-block-level form-control"

View File

@@ -69,6 +69,8 @@ class PdfTicketOutput(BaseTicketOutput):
ev = op.subevent or order.event
if o['content'] == 'other':
return o['text'].replace("\n", "<br/>\n")
elif o['content'].startswith('meta:'):
return ev.meta_data.get(o['content'][5:])
elif o['content'] == 'order':
return order.code
elif o['content'] == 'item':

View File

@@ -175,3 +175,11 @@ pre.mail-preview {
}
}
.metadata-group {
.form-group {
margin: 0 0 10px 0;
}
.row label {
padding-top: 7px;
}
}

View File

@@ -18,12 +18,19 @@ def organizer():
@pytest.fixture
def event(organizer):
return Event.objects.create(
def meta_prop(organizer):
return organizer.meta_properties.create(name="type", default="Concert")
@pytest.fixture
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),
plugins='pretix.plugins.banktransfer,pretix.plugins.ticketoutputpdf'
)
e.meta_values.create(property=meta_prop, value="Conference")
return e
@pytest.fixture
@@ -48,11 +55,14 @@ def token_client(client, team):
@pytest.fixture
def subevent(event):
def subevent(event, meta_prop):
event.has_subevents = True
event.save()
return 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=UTC))
se.meta_values.create(property=meta_prop, value="Workshop")
return se
@pytest.fixture

View File

@@ -13,6 +13,7 @@ TEST_EVENT_RES = {
"location": None,
"slug": "dummy",
"has_subevents": False,
"meta_data": {"type": "Conference"}
}

View File

@@ -11,7 +11,8 @@ TEST_SUBEVENT_RES = {
'id': 1,
'variation_price_overrides': [],
'location': None,
'item_price_overrides': []
'item_price_overrides': [],
'meta_data': {'type': 'Workshop'}
}