mirror of
https://github.com/pretix/pretix.git
synced 2026-11-10 01:17:07 +00:00
QuestionnaireChild -> QuestionnaireRow
This commit is contained in:
1 parent
c8964fad9d
commit
2c88efceb1
19 files changed
+166
-166
No files matched your search
@@ -61,7 +61,7 @@ from pretix.base.models import (
|
||||
ItemVariation, ItemVariationMetaValue, Question, QuestionAnswer,
|
||||
QuestionOption, Quota, SalesChannel,
|
||||
)
|
||||
from pretix.base.models.items import Questionnaire, QuestionnaireChild
|
||||
from pretix.base.models.items import Questionnaire, QuestionnaireRow
|
||||
from pretix.base.settings import (
|
||||
PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
|
||||
)
|
||||
@@ -353,7 +353,7 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
self.fields['variations'].child.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
|
||||
|
||||
def get_personalized(self, item):
|
||||
return QuestionnaireChild.objects.filter(questionnaire__items=item, system_datafield__in=('@attendee_name_parts', '@attendee_email')).exists()
|
||||
return QuestionnaireRow.objects.filter(questionnaire__items=item, system_datafield__in=('@attendee_name_parts', '@attendee_email')).exists()
|
||||
|
||||
def put_personalized(self, item, new_value):
|
||||
if item and new_value != self.get_personalized(item):
|
||||
@@ -588,7 +588,7 @@ class DatafieldRelatedField(serializers.SlugRelatedField):
|
||||
if data is None:
|
||||
return {'user_datafield': None, 'system_datafield': None}
|
||||
elif type(data) == str and data.startswith('@'):
|
||||
if data not in QuestionnaireChild.SystemDatafield.values:
|
||||
if data not in QuestionnaireRow.SystemDatafield.values:
|
||||
self.fail("invalid_choice", input=data)
|
||||
return {'user_datafield': None, 'system_datafield': data}
|
||||
elif type(data) == str:
|
||||
@@ -631,7 +631,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
|
||||
options = InlineQuestionOptionSerializer(source='user_datafield.options', many=True, required=False, default=[])
|
||||
|
||||
class Meta:
|
||||
model = QuestionnaireChild
|
||||
model = QuestionnaireRow
|
||||
fields = ('datafield', 'label', 'type', 'required', 'help_text', 'items', 'options', 'position',
|
||||
'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
|
||||
'print_on_invoice', 'valid_number_min',
|
||||
@@ -646,7 +646,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
|
||||
if isinstance(instance, QuestionAnswer):
|
||||
position = instance.cartposition if instance.cartposition_id else instance.orderposition
|
||||
print('searching ', repr(instance), repr(position), repr(instance.question), instance.pk, position.item_id, self.context) # TODO remove print
|
||||
qcs = list(QuestionnaireChild.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
|
||||
qcs = list(QuestionnaireRow.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
|
||||
return next((qc for qc in qcs if qc.questionnaire.type == self.context.get('preferred_questionnaire_type', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE)), qcs[0])
|
||||
|
||||
return super().get_attribute(instance)
|
||||
@@ -681,7 +681,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
|
||||
part["type"] = Question.FieldType.CHOICE
|
||||
part["options"] = [{"id": idx + 1, "identifier": k, "answer": i18n_all_from_gettext(v, settings.locales), "position": idx + 1} for idx, (k, v) in
|
||||
enumerate(PERSON_NAME_SALUTATIONS)]
|
||||
part["id"] = QuestionnaireChild.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
|
||||
part["id"] = QuestionnaireRow.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
|
||||
part["required"] = part["required"] and k in REQUIRED_NAME_PARTS
|
||||
mangled_results.append(part)
|
||||
pos_offset += 1
|
||||
@@ -720,7 +720,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
|
||||
options = InlineQuestionOptionSerializer(source='user_datafield.options', many=True, required=False, default=[])
|
||||
|
||||
class Meta:
|
||||
model = QuestionnaireChild
|
||||
model = QuestionnaireRow
|
||||
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
|
||||
'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
|
||||
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
|
||||
@@ -735,7 +735,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
|
||||
if isinstance(instance, QuestionAnswer):
|
||||
position = instance.cartposition if instance.cartposition_id else instance.orderposition
|
||||
print('searching ', repr(instance), repr(position), repr(instance.question), instance.pk, position.item_id, self.context) # TODO remove print
|
||||
qcs = list(QuestionnaireChild.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
|
||||
qcs = list(QuestionnaireRow.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
|
||||
return next((qc for qc in qcs if qc.questionnaire.type == self.context.get('preferred_questionnaire_type', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE)), qcs[0])
|
||||
|
||||
return super().get_attribute(instance)
|
||||
@@ -775,7 +775,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
|
||||
part["type"] = Question.FieldType.CHOICE
|
||||
part["options"] = [{"id": idx + 1, "identifier": k, "answer": i18n_all_from_gettext(v, settings.locales), "position": idx + 1} for idx, (k, v) in
|
||||
enumerate(PERSON_NAME_SALUTATIONS)]
|
||||
part["id"] = QuestionnaireChild.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
|
||||
part["id"] = QuestionnaireRow.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
|
||||
part["required"] = part["required"] and k in REQUIRED_NAME_PARTS
|
||||
mangled_results.append(part)
|
||||
pos_offset += 1
|
||||
@@ -890,14 +890,14 @@ class RenderedMarkdownField(serializers.CharField):
|
||||
return rich_text(value)
|
||||
|
||||
|
||||
class InlineQuestionnaireChildSerializer(I18nAwareModelSerializer):
|
||||
class InlineQuestionnaireRowSerializer(I18nAwareModelSerializer):
|
||||
datafield = DatafieldRelatedField(allow_null=True, source='*', queryset=Question.objects.none())
|
||||
dependency_question = DatafieldRelatedField(allow_null=True, required=False, queryset=Question.objects.none())
|
||||
rendered_help_text = RenderedMarkdownField(read_only=True, source='help_text')
|
||||
cooked_id = serializers.IntegerField(read_only=True, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = QuestionnaireChild
|
||||
model = QuestionnaireRow
|
||||
fields = ('datafield', 'required', 'label', 'help_text', 'dependency_question', 'dependency_values', 'rendered_help_text', 'cooked_id')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -925,10 +925,10 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Questionnaire
|
||||
fields = ('id', 'type', 'internal_name', 'items', 'position', 'all_sales_channels', 'limit_sales_channels', 'children')
|
||||
fields = ('id', 'type', 'internal_name', 'items', 'position', 'all_sales_channels', 'limit_sales_channels', 'rows')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.fields['children'] = InlineQuestionnaireChildSerializer(many=True, required=True, context=kwargs['context'], partial=False)
|
||||
self.fields['rows'] = InlineQuestionnaireRowSerializer(many=True, required=True, context=kwargs['context'], partial=False)
|
||||
self.fields['items'].child_relation.queryset = kwargs['context']['event'].items.all()
|
||||
self.fields['limit_sales_channels'].child_relation.queryset = kwargs['context']['event'].organizer.sales_channels.all()
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -955,14 +955,14 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
|
||||
#Question.clean_items(event, full_data.get('items') or [])
|
||||
|
||||
if (full_data.get('type') == Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN
|
||||
and any(c['dependency_question'] for c in full_data['children'])):
|
||||
and any(c['dependency_question'] for c in full_data['rows'])):
|
||||
raise ValidationError('Dependencies are not supported during check-in.')
|
||||
|
||||
if (not full_data.get('type').startswith('position_')
|
||||
and any(c['system_datafield'] for c in full_data['children'])):
|
||||
and any(c['system_datafield'] for c in full_data['rows'])):
|
||||
raise ValidationError('System data fields are only supported on order positions.')
|
||||
|
||||
system_fields = set(c['system_datafield'] for c in full_data['children'])
|
||||
system_fields = set(c['system_datafield'] for c in full_data['rows'])
|
||||
if ('zipcode' in system_fields or 'state' in system_fields or 'street' in system_fields or 'city' in system_fields) and 'country' not in system_fields:
|
||||
raise ValidationError('The system data fields "street", "zip code", "city", and "state" can only be used in combination with the "country" field.')
|
||||
|
||||
@@ -970,9 +970,9 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
children_data = validated_data.pop('children') if 'children' in validated_data else []
|
||||
rows_data = validated_data.pop('rows') if 'rows' in validated_data else []
|
||||
questionnaire = super().create(validated_data)
|
||||
self.set_children(questionnaire, children_data)
|
||||
self.set_rows(questionnaire, rows_data)
|
||||
try:
|
||||
Questionnaire.check_constraints(questionnaire.event)
|
||||
except ValidationError as e:
|
||||
@@ -981,20 +981,20 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
|
||||
|
||||
@transaction.atomic
|
||||
def update(self, instance, validated_data):
|
||||
children_data = validated_data.pop('children', None)
|
||||
rows_data = validated_data.pop('rows', None)
|
||||
questionnaire = super().update(instance, validated_data)
|
||||
if children_data is not None:
|
||||
self.set_children(questionnaire, children_data)
|
||||
if rows_data is not None:
|
||||
self.set_rows(questionnaire, rows_data)
|
||||
try:
|
||||
Questionnaire.check_constraints(questionnaire.event)
|
||||
except ValidationError as e:
|
||||
raise DrfValidationError(as_serializer_error(e))
|
||||
return questionnaire
|
||||
|
||||
def set_children(self, questionnaire, new_data):
|
||||
def set_rows(self, questionnaire, new_data):
|
||||
result = {}
|
||||
child_serializer = self.fields['children'].child
|
||||
existing_children = questionnaire.children.all()
|
||||
child_serializer = self.fields['rows'].child
|
||||
existing_rows = questionnaire.rows.all()
|
||||
def q(qc):
|
||||
return qc['user_datafield'] or qc['system_datafield']
|
||||
for i, d in enumerate(new_data):
|
||||
@@ -1004,7 +1004,7 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
|
||||
d.setdefault('help_text', None)
|
||||
d.setdefault('dependency_question', None)
|
||||
d.setdefault('dependency_values', None)
|
||||
for existing, update_data in zip_longest(existing_children, new_data):
|
||||
for existing, update_data in zip_longest(existing_rows, new_data):
|
||||
if update_data:
|
||||
if update_data.get('dependency_question'):
|
||||
try:
|
||||
|
||||
@@ -58,7 +58,7 @@ from pretix.base.media import MEDIA_TYPES
|
||||
from pretix.base.models import (
|
||||
CachedFile, Checkin, Customer, Device, GiftCard, Invoice, InvoiceAddress,
|
||||
InvoiceLine, Item, ItemVariation, Order, OrderPosition, Question,
|
||||
QuestionAnswer, QuestionnaireChild, ReusableMedium, SalesChannel, Seat,
|
||||
QuestionAnswer, QuestionnaireRow, ReusableMedium, SalesChannel, Seat,
|
||||
SubEvent, TaxRule, Voucher,
|
||||
)
|
||||
from pretix.base.models.orders import (
|
||||
@@ -242,7 +242,7 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
def to_representation(self, instance):
|
||||
answers = super().to_representation(instance.answers)
|
||||
answers_dict = {answer["question"]: answer for answer in answers}
|
||||
for qc in QuestionnaireChild.objects.filter(questionnaire__items__id=instance.item_id):
|
||||
for qc in QuestionnaireRow.objects.filter(questionnaire__items__id=instance.item_id):
|
||||
if qc.user_datafield_id:
|
||||
if answer := answers_dict.get(qc.user_datafield_id):
|
||||
answer = dict(answer)
|
||||
@@ -251,7 +251,7 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
answer["user_datafield_id"] = qc.user_datafield_id
|
||||
answers.append(answer)
|
||||
elif (sys_answer := instance.get_system_answer(qc.system_datafield)) is not None:
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
name_parts = instance.attendee_name_parts
|
||||
settings = self.context['event'].settings
|
||||
for k, v, w, *__ in PERSON_NAME_SCHEMES.get(settings.name_scheme)['fields']:
|
||||
@@ -269,7 +269,7 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
except ValueError:
|
||||
pass
|
||||
answers.append({
|
||||
"question": QuestionnaireChild.cook_id_from_parts(qc.questionnaire_id, None, f'{qc.system_datafield}:{k}'),
|
||||
"question": QuestionnaireRow.cook_id_from_parts(qc.questionnaire_id, None, f'{qc.system_datafield}:{k}'),
|
||||
"answer": name_parts[k],
|
||||
"question_identifier": f'{qc.system_datafield}:{k}',
|
||||
"options": options,
|
||||
@@ -293,9 +293,9 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
settings = self.context['event'].settings
|
||||
for answer in data["answers"]:
|
||||
try:
|
||||
qid, dfid, sys_df = QuestionnaireChild.uncook_id(answer["question"])
|
||||
qid, dfid, sys_df = QuestionnaireRow.uncook_id(answer["question"])
|
||||
except ValueError:
|
||||
if type(answer["question"]) is str and answer["question"] in QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS:
|
||||
if type(answer["question"]) is str and answer["question"] in QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS:
|
||||
qid, dfid, sys_df = None, None, answer["question"]
|
||||
else:
|
||||
raise ValidationError('Invalid question ID')
|
||||
|
||||
@@ -65,7 +65,7 @@ from pretix.api.views.order import OrderPositionFilter
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedFile, Checkin, CheckinList, Device, Event, Order, OrderPosition,
|
||||
Question, Questionnaire, QuestionnaireChild, ReusableMedium,
|
||||
Question, Questionnaire, QuestionnaireRow, ReusableMedium,
|
||||
RevokedTicketSecret, TeamAPIToken,
|
||||
)
|
||||
from pretix.base.models.orders import PrintLog
|
||||
@@ -799,7 +799,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
# 5. Pre-validate all incoming answers, handle file upload
|
||||
given_answers = {}
|
||||
if answers_data:
|
||||
for qc in QuestionnaireChild.objects.filter(
|
||||
for qc in QuestionnaireRow.objects.filter(
|
||||
questionnaire__items__in=[op.item], questionnaire__type=Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN
|
||||
):
|
||||
if qc.user_datafield_id and (str(qc.user_datafield_id) in answers_data or str(qc.cooked_id) in answers_data):
|
||||
|
||||
@@ -60,7 +60,7 @@ from pretix.base.models import (
|
||||
CartPosition, Device, Item, ItemAddOn, ItemBundle, ItemCategory,
|
||||
ItemProgramTime, ItemVariation, Question, QuestionOption, Quota,
|
||||
)
|
||||
from pretix.base.models.items import Questionnaire, QuestionnaireChild
|
||||
from pretix.base.models.items import Questionnaire, QuestionnaireRow
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
from pretix.helpers.i18n import i18ncomp
|
||||
@@ -472,7 +472,7 @@ with scopes_disabled():
|
||||
ask_during_checkin = django_filters.BooleanFilter(method='ask_during_checkin_qs')
|
||||
|
||||
class Meta:
|
||||
model = QuestionnaireChild
|
||||
model = QuestionnaireRow
|
||||
fields = ['required']
|
||||
|
||||
def ask_during_checkin_qs(self, qs, name, value):
|
||||
@@ -494,7 +494,7 @@ class DeprecationWarningMixin:
|
||||
|
||||
class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelViewSet):
|
||||
serializer_class = POSCompatQuestionSerializer
|
||||
queryset = QuestionnaireChild.objects.none()
|
||||
queryset = QuestionnaireRow.objects.none()
|
||||
permission = None
|
||||
sales_channel_ident = None
|
||||
|
||||
@@ -511,7 +511,7 @@ class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelVi
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return QuestionnaireChild.objects.filter(
|
||||
return QuestionnaireRow.objects.filter(
|
||||
# only matching sales channel
|
||||
Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels__identifier=self.sales_channel_ident),
|
||||
# only real questions, no text blocks
|
||||
@@ -533,7 +533,7 @@ class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelVi
|
||||
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
|
||||
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
print(self.kwargs[lookup_url_kwarg], qid, dfid, sys_df)
|
||||
if qid and sys_df:
|
||||
obj = get_object_or_404(queryset, questionnaire__pk=qid, system_datafield=sys_df)
|
||||
@@ -608,7 +608,7 @@ class BaseDatafieldViewSet(viewsets.ModelViewSet):
|
||||
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
|
||||
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
self.via_questionnaire_id = qid
|
||||
obj = get_object_or_404(queryset, pk=dfid)
|
||||
|
||||
@@ -833,7 +833,7 @@ class QuestionnaireViewSet(ConditionalListView, viewsets.ModelViewSet):
|
||||
write_permission = 'event.items:write'
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.questionnaires.prefetch_related('children').all()
|
||||
return self.request.event.questionnaires.prefetch_related('rows').all()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(event=self.request.event)
|
||||
@@ -871,7 +871,7 @@ class QuestionFormView(generics.GenericAPIView):
|
||||
# like POSCompatQuestionViewSet, but less compat
|
||||
|
||||
serializer_class = QuestionFormSerializer
|
||||
queryset = QuestionnaireChild.objects.none()
|
||||
queryset = QuestionnaireRow.objects.none()
|
||||
permission = None
|
||||
sales_channel_ident = None
|
||||
|
||||
@@ -888,7 +888,7 @@ class QuestionFormView(generics.GenericAPIView):
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
return QuestionnaireChild.objects.filter(
|
||||
return QuestionnaireRow.objects.filter(
|
||||
Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels__identifier=self.sales_channel_ident),
|
||||
questionnaire__event=self.request.event,
|
||||
# legacy compat only for position-level questions
|
||||
@@ -907,7 +907,7 @@ class QuestionFormView(generics.GenericAPIView):
|
||||
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
|
||||
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
|
||||
print(self.kwargs[lookup_url_kwarg], qid, dfid, sys_df)
|
||||
if qid and sys_df:
|
||||
obj = get_object_or_404(queryset, questionnaire__pk=qid, system_datafield=sys_df)
|
||||
|
||||
@@ -83,7 +83,7 @@ from pretix.base.invoicing.transmission import (
|
||||
get_transmission_types, transmission_types,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
InvoiceAddress, Item, Question, Questionnaire, QuestionnaireChild,
|
||||
InvoiceAddress, Item, Question, Questionnaire, QuestionnaireRow,
|
||||
QuestionOption,
|
||||
)
|
||||
from pretix.base.models.tax import ask_for_vat_id
|
||||
@@ -963,7 +963,7 @@ class OrderLevelQuestionsForm(BaseQuestionsForm):
|
||||
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=container.sales_channel.identifier),
|
||||
event=event, type=Questionnaire.QuestionnaireType.ORDER_SALE,
|
||||
).order_by('position').prefetch_related(
|
||||
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
|
||||
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
|
||||
Prefetch('user_datafield', Question.objects.prefetch_related(
|
||||
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
|
||||
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
|
||||
@@ -975,18 +975,18 @@ class OrderLevelQuestionsForm(BaseQuestionsForm):
|
||||
)))
|
||||
))
|
||||
),
|
||||
to_attr='childlist')
|
||||
to_attr='questionnairerowlist')
|
||||
)
|
||||
answerlist = container.answers.prefetch_related('options')
|
||||
|
||||
idx = 0
|
||||
for questionnaire in questionnaires:
|
||||
for child in getattr(questionnaire, 'childlist', questionnaire.children.all()):
|
||||
if child.user_datafield:
|
||||
df = child.user_datafield
|
||||
self.fields[child.form_field_name] = self.build_user_question_field(request, event, answerlist, child, df)
|
||||
for qrow in getattr(questionnaire, 'questionnairerowlist', questionnaire.rows.all()):
|
||||
if qrow.user_datafield:
|
||||
df = qrow.user_datafield
|
||||
self.fields[qrow.form_field_name] = self.build_user_question_field(request, event, answerlist, qrow, df)
|
||||
else:
|
||||
self.fields[child.form_field_name] = self.build_text_block(request, event, child)
|
||||
self.fields[qrow.form_field_name] = self.build_text_block(request, event, qrow)
|
||||
idx += 1
|
||||
|
||||
def clean(self):
|
||||
@@ -1025,14 +1025,14 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
self.fields['requested_valid_from'] = self.build_requested_valid_from_field(event, pos, item)
|
||||
|
||||
for questionnaire in questionnaires:
|
||||
for child in getattr(questionnaire, 'childlist', questionnaire.children.all()):
|
||||
if child.user_datafield:
|
||||
df = child.user_datafield
|
||||
self.fields[child.form_field_name] = self.build_user_question_field(request, event, pos.answerlist, child, df)
|
||||
elif child.system_datafield:
|
||||
self.fields[child.form_field_name] = self.build_system_question_field(request, event, pos, child)
|
||||
for qrow in getattr(questionnaire, 'questionnairerowlist', questionnaire.rows.all()):
|
||||
if qrow.user_datafield:
|
||||
df = qrow.user_datafield
|
||||
self.fields[qrow.form_field_name] = self.build_user_question_field(request, event, pos.answerlist, qrow, df)
|
||||
elif qrow.system_datafield:
|
||||
self.fields[qrow.form_field_name] = self.build_system_question_field(request, event, pos, qrow)
|
||||
else:
|
||||
self.fields[child.form_field_name] = self.build_text_block(request, event, child)
|
||||
self.fields[qrow.form_field_name] = self.build_text_block(request, event, qrow)
|
||||
|
||||
responses = question_form_fields.send(sender=event, position=pos)
|
||||
data = pos.meta_info_data
|
||||
@@ -1097,7 +1097,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
)
|
||||
|
||||
def build_system_question_field(self, request, event, pos, qc):
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
return NamePartsFormField(
|
||||
max_length=255,
|
||||
required=qc.required and not self.all_optional,
|
||||
@@ -1107,7 +1107,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
help_text=rich_text(qc.help_text),
|
||||
initial=pos.attendee_name_parts,
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_EMAIL:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_EMAIL:
|
||||
return forms.EmailField(
|
||||
required=qc.required and not self.all_optional,
|
||||
label=escape(qc.label),
|
||||
@@ -1119,7 +1119,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
}
|
||||
)
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.COMPANY:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.COMPANY:
|
||||
return forms.CharField(
|
||||
required=qc.required and not self.all_optional,
|
||||
label=escape(qc.label),
|
||||
@@ -1128,7 +1128,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
initial=pos.company,
|
||||
)
|
||||
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.STREET:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.STREET:
|
||||
return forms.CharField(
|
||||
required=qc.required and not self.all_optional,
|
||||
label=escape(qc.label),
|
||||
@@ -1140,7 +1140,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
}),
|
||||
initial=pos.street,
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ZIPCODE:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ZIPCODE:
|
||||
return forms.CharField(
|
||||
required=False,
|
||||
max_length=30,
|
||||
@@ -1151,7 +1151,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
'autocomplete': 'postal-code',
|
||||
}),
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.CITY:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.CITY:
|
||||
return forms.CharField(
|
||||
required=False,
|
||||
label=escape(qc.label),
|
||||
@@ -1162,7 +1162,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
'autocomplete': 'address-level2',
|
||||
}),
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.COUNTRY:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.COUNTRY:
|
||||
country = pos.country or guess_country_from_request(request, event)
|
||||
return CountryField(
|
||||
countries=CachedCountries
|
||||
@@ -1176,7 +1176,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
|
||||
'data-trigger-address-info': 'on',
|
||||
}),
|
||||
)
|
||||
if qc.system_datafield == QuestionnaireChild.SystemDatafield.STATE:
|
||||
if qc.system_datafield == QuestionnaireRow.SystemDatafield.STATE:
|
||||
country = pos.country or guess_country_from_request(request, event)
|
||||
c = [('', '---')]
|
||||
fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else ''
|
||||
|
||||
@@ -52,7 +52,7 @@ def migrate_questions_forward(apps, schema_editor):
|
||||
Item = apps.get_model("pretixbase", "Item")
|
||||
Question = apps.get_model("pretixbase", "Question")
|
||||
Questionnaire = apps.get_model("pretixbase", "Questionnaire")
|
||||
QuestionnaireChild = apps.get_model("pretixbase", "QuestionnaireChild")
|
||||
QuestionnaireRow = apps.get_model("pretixbase", "QuestionnaireRow")
|
||||
EventSettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
|
||||
|
||||
qc_candidate = namedtuple('qc_candidate', ['item_info', 'question_id', 'question'])
|
||||
@@ -83,33 +83,33 @@ def migrate_questions_forward(apps, schema_editor):
|
||||
else:
|
||||
internal_name = _('personalized products')
|
||||
|
||||
# create questionnaires and children
|
||||
# create questionnaires and rows
|
||||
questionnaire = Questionnaire.objects.create(
|
||||
event=event, type=questionnaire_type, position=0, all_sales_channels=True,
|
||||
internal_name=internal_name
|
||||
)
|
||||
questionnaire.items.set([ii.id for ii in item_infos])
|
||||
deps = {}
|
||||
for position, child in enumerate(questions):
|
||||
print(position, child)
|
||||
if isinstance(child, FakeQuestion):
|
||||
QuestionnaireChild.objects.create(
|
||||
for position, question in enumerate(questions):
|
||||
print(position, question)
|
||||
if isinstance(question, FakeQuestion):
|
||||
QuestionnaireRow.objects.create(
|
||||
questionnaire=questionnaire,
|
||||
position=position + 1,
|
||||
system_datafield=child.id,
|
||||
required=child.required,
|
||||
label=child.question,
|
||||
system_datafield=question.id,
|
||||
required=question.required,
|
||||
label=question.question,
|
||||
)
|
||||
else:
|
||||
deps[child.id] = QuestionnaireChild.objects.create(
|
||||
deps[question.id] = QuestionnaireRow.objects.create(
|
||||
questionnaire=questionnaire,
|
||||
position=position + 1,
|
||||
user_datafield=child,
|
||||
required=child.required,
|
||||
label=child.question,
|
||||
help_text=child.help_text,
|
||||
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
|
||||
dependency_values=child.dependency_values,
|
||||
user_datafield=question,
|
||||
required=question.required,
|
||||
label=question.question,
|
||||
help_text=question.help_text,
|
||||
dependency_question=deps[question.dependency_question.id] if question.dependency_question else None,
|
||||
dependency_values=question.dependency_values,
|
||||
)
|
||||
|
||||
for event in Event.objects.iterator():
|
||||
@@ -127,7 +127,7 @@ def migrate_questions_forward(apps, schema_editor):
|
||||
personalized_items = event.items.filter(personalized=True).values_list('id', 'internal_name', 'name', 'personalized', named=True)
|
||||
|
||||
# [position_checkout] presale
|
||||
children = chain((
|
||||
rows = chain((
|
||||
qc_candidate(item, 0, q)
|
||||
for q in get_fake_questions(settings)
|
||||
for item in personalized_items
|
||||
@@ -137,45 +137,45 @@ def migrate_questions_forward(apps, schema_editor):
|
||||
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
|
||||
))
|
||||
|
||||
create_grouped_item_questionnaires(event, children, '', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, personalized_items)
|
||||
create_grouped_item_questionnaires(event, rows, '', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, personalized_items)
|
||||
|
||||
# [position_checkin] checkin
|
||||
children = chain((
|
||||
rows = chain((
|
||||
qc_candidate(item, q.id, q)
|
||||
for q in questions.filter(ask_during_checkin=True)
|
||||
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
|
||||
))
|
||||
|
||||
create_grouped_item_questionnaires(event, children, _('Check-in questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN, personalized_items)
|
||||
create_grouped_item_questionnaires(event, rows, _('Check-in questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN, personalized_items)
|
||||
|
||||
# [position_hidden] hidden
|
||||
children = chain((
|
||||
rows = chain((
|
||||
qc_candidate(item, q.id, q)
|
||||
for q in questions.filter(hidden=True, ask_during_checkin=False)
|
||||
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
|
||||
))
|
||||
|
||||
create_grouped_item_questionnaires(event, children, _('Hidden questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_HIDDEN, personalized_items)
|
||||
create_grouped_item_questionnaires(event, rows, _('Hidden questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_HIDDEN, personalized_items)
|
||||
|
||||
# [O] get all order-level questions
|
||||
questions = list(event.questions.filter(container_type='O', hidden=False).order_by('position'))
|
||||
if questions:
|
||||
# create questionnaires and children
|
||||
# create questionnaires and rows
|
||||
questionnaire = Questionnaire.objects.create(
|
||||
event=event, type=Questionnaire.QuestionnaireType.ORDER_SALE, position=0, all_sales_channels=True,
|
||||
internal_name=_('Per-order questions'),
|
||||
)
|
||||
deps = {}
|
||||
for position, child in enumerate(questions):
|
||||
deps[child.id] = QuestionnaireChild.objects.create(
|
||||
for position, question in enumerate(questions):
|
||||
deps[question.id] = QuestionnaireRow.objects.create(
|
||||
questionnaire=questionnaire,
|
||||
position=position + 1,
|
||||
user_datafield=child,
|
||||
required=child.required,
|
||||
label=child.question,
|
||||
help_text=child.help_text,
|
||||
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
|
||||
dependency_values=child.dependency_values,
|
||||
user_datafield=question,
|
||||
required=question.required,
|
||||
label=question.question,
|
||||
help_text=question.help_text,
|
||||
dependency_question=deps[question.dependency_question.id] if question.dependency_question else None,
|
||||
dependency_values=question.dependency_values,
|
||||
)
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class Migration(migrations.Migration):
|
||||
bases=(models.Model, pretix.base.models.base.LoggingMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='QuestionnaireChild',
|
||||
name='QuestionnaireRow',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('position', models.PositiveIntegerField(default=0)),
|
||||
@@ -217,8 +217,8 @@ class Migration(migrations.Migration):
|
||||
('label', i18nfield.fields.I18nTextField()),
|
||||
('help_text', i18nfield.fields.I18nTextField(null=True)),
|
||||
('dependency_values', pretix.base.models.fields.MultiStringField(default=[])),
|
||||
('dependency_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dependent_questions', to='pretixbase.questionnairechild')),
|
||||
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='children', to='pretixbase.questionnaire')),
|
||||
('dependency_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dependent_questions', to='pretixbase.questionnairerow')),
|
||||
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rows', to='pretixbase.questionnaire')),
|
||||
('user_datafield', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='references', to='pretixbase.question')),
|
||||
],
|
||||
options={
|
||||
|
||||
@@ -37,7 +37,7 @@ from .invoices import Invoice, InvoiceLine, invoice_filename
|
||||
from .items import (
|
||||
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue,
|
||||
ItemProgramTime, ItemVariation, ItemVariationMetaValue, Question,
|
||||
Questionnaire, QuestionnaireChild, QuestionOption, Quota, SubEventItem,
|
||||
Questionnaire, QuestionnaireRow, QuestionOption, Quota, SubEventItem,
|
||||
SubEventItemVariation, itempicture_upload_to,
|
||||
)
|
||||
from .log import LogEntry
|
||||
|
||||
@@ -76,7 +76,7 @@ from pretix.helpers.daterange import daterange
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
from pretix.helpers.json import CustomJSONEncoder, safe_string
|
||||
from pretix.helpers.thumb import get_thumbnail
|
||||
from . import QuestionnaireChild
|
||||
from . import QuestionnaireRow
|
||||
|
||||
from ..settings import settings_hierarkey
|
||||
from .organizer import Organizer, Team
|
||||
@@ -740,7 +740,7 @@ class Event(EventMixin, LoggedModel):
|
||||
self.settings.mail_send_download_reminder_attendee = True
|
||||
from . import Questionnaire
|
||||
q = self.questionnaires.create(internal_name=str(_('Attendee data')), type=Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, position=0)
|
||||
q.children.create(label=str(_('Attendee name')), system_datafield=QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS, position=0)
|
||||
q.rows.create(label=str(_('Attendee name')), system_datafield=QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS, position=0)
|
||||
|
||||
@property
|
||||
def social_image(self):
|
||||
|
||||
@@ -1680,7 +1680,7 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
|
||||
choices=FieldType.choices,
|
||||
verbose_name=_("Question type")
|
||||
)
|
||||
tbd_required = models.BooleanField( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
|
||||
tbd_required = models.BooleanField( # TODO(questionnaires) : to be removed, -> QuestionnaireRow
|
||||
default=False,
|
||||
verbose_name=_("Required question"),
|
||||
db_column="required",
|
||||
@@ -1692,7 +1692,7 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
|
||||
# blank=True,
|
||||
# help_text=_('This question will be asked to buyers of the selected products')
|
||||
# )
|
||||
tbd_position = models.PositiveIntegerField( # TODO(questionnaires) : to be removed, -> Questionnaire + QuestionnaireChild
|
||||
tbd_position = models.PositiveIntegerField( # TODO(questionnaires) : to be removed, -> Questionnaire + QuestionnaireRow
|
||||
default=0,
|
||||
verbose_name=_("Position"),
|
||||
db_column="position",
|
||||
@@ -1717,10 +1717,10 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
|
||||
verbose_name=_('Print answer on invoices'),
|
||||
default=False
|
||||
)
|
||||
dependency_question = models.ForeignKey( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
|
||||
dependency_question = models.ForeignKey( # TODO(questionnaires) : to be removed, -> QuestionnaireRow
|
||||
'Question', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
|
||||
)
|
||||
dependency_values = MultiStringField(default=[]) # TODO(questionnaires) : to be removed, -> QuestionnaireChild
|
||||
dependency_values = MultiStringField(default=[]) # TODO(questionnaires) : to be removed, -> QuestionnaireRow
|
||||
valid_number_min = models.DecimalField(decimal_places=6, max_digits=30, null=True, blank=True,
|
||||
verbose_name=_('Minimum value'),
|
||||
help_text=_('Currently not supported in our apps and during check-in'))
|
||||
@@ -2066,14 +2066,14 @@ class Questionnaire(LoggedModel):
|
||||
@staticmethod
|
||||
def check_constraints(event):
|
||||
errors = set()
|
||||
all_questionnaires = Questionnaire.objects.filter(event=event).prefetch_related('children', 'items')
|
||||
all_questionnaires = Questionnaire.objects.filter(event=event).prefetch_related('rows', 'items')
|
||||
all_sales_channels = event.sales_channels.all()
|
||||
for (sales_channel, type, item_id, sdf, udf), fields in groupby(sorted(
|
||||
(sales_channel, q.type, item.id, c.system_datafield or '', c.user_datafield_id or 0, q, item)
|
||||
for q in all_questionnaires
|
||||
for sales_channel in (q.limit_sales_channels if not q.all_sales_channels else all_sales_channels).values_list('pk', flat=True)
|
||||
for item in q.items.all()
|
||||
for c in q.children.all()
|
||||
for c in q.rows.all()
|
||||
), key=lambda d: (d[0], d[1], d[2], d[3], d[4])):
|
||||
fields = list(fields)
|
||||
if len(fields) > 1:
|
||||
@@ -2092,7 +2092,7 @@ class Questionnaire(LoggedModel):
|
||||
raise ValidationError(list(errors))
|
||||
|
||||
|
||||
class QuestionnaireChild(LoggedModel):
|
||||
class QuestionnaireRow(LoggedModel):
|
||||
class SystemDatafield(models.TextChoices):
|
||||
ATTENDEE_NAME_PARTS = '@attendee_name_parts', _('Attendee name')
|
||||
ATTENDEE_EMAIL = '@attendee_email', _('Attendee email')
|
||||
@@ -2122,7 +2122,7 @@ class QuestionnaireChild(LoggedModel):
|
||||
|
||||
questionnaire = models.ForeignKey(
|
||||
Questionnaire,
|
||||
related_name="children",
|
||||
related_name="rows",
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
position = models.PositiveIntegerField(
|
||||
@@ -2153,7 +2153,7 @@ class QuestionnaireChild(LoggedModel):
|
||||
null=True, blank=True,
|
||||
)
|
||||
dependency_question = models.ForeignKey(
|
||||
'QuestionnaireChild', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
|
||||
'pretix.base.models.items.QuestionnaireRow', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
|
||||
)
|
||||
dependency_values = MultiStringField(default=[])
|
||||
|
||||
@@ -2172,13 +2172,13 @@ class QuestionnaireChild(LoggedModel):
|
||||
@property
|
||||
def cooked_id(self):
|
||||
if self.user_datafield_id or self.system_datafield:
|
||||
return QuestionnaireChild.cook_id_from_parts(self.questionnaire_id, self.user_datafield_id, self.system_datafield)
|
||||
return QuestionnaireRow.cook_id_from_parts(self.questionnaire_id, self.user_datafield_id, self.system_datafield)
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cook_id_from_parts(qid, dfid, sys_df):
|
||||
df_id = dfid + 100 if dfid else QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS.index(sys_df)
|
||||
df_id = dfid + 100 if dfid else QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS.index(sys_df)
|
||||
assert qid < 1000000000
|
||||
return qid * 1000000000 + df_id
|
||||
|
||||
@@ -2190,7 +2190,7 @@ class QuestionnaireChild(LoggedModel):
|
||||
if dfid >= 100:
|
||||
return qid, dfid - 100, None
|
||||
else:
|
||||
return qid, None, QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS[dfid]
|
||||
return qid, None, QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS[dfid]
|
||||
else:
|
||||
return None, id, None
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ from ._transactions import (
|
||||
from .base import LockModel, LoggedModel
|
||||
from .event import Event, SubEvent
|
||||
from .items import (
|
||||
Item, ItemVariation, Question, QuestionnaireChild, QuestionOption, Quota,
|
||||
Item, ItemVariation, Question, QuestionnaireRow, QuestionOption, Quota,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -1608,37 +1608,37 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
|
||||
# answers of other items in the same cart if the question objects have been
|
||||
# selected via prefetch_related
|
||||
if hasattr(self.item, 'relevant_questionnaires'):
|
||||
children = list(copy.copy(qc) for qq in self.item.relevant_questionnaires for qc in qq.childlist)
|
||||
qrows = list(copy.copy(qc) for qq in self.item.relevant_questionnaires for qc in qq.questionnairerowlist)
|
||||
else:
|
||||
children = QuestionnaireChild.objects.filter(questionnaire__event_id=self.item.event_id, questionnaire__items__in=[self.item.id])
|
||||
qrows = QuestionnaireRow.objects.filter(questionnaire__event_id=self.item.event_id, questionnaire__items__in=[self.item.id])
|
||||
if questionnaire_type is not None:
|
||||
children = children.filter(questionnaire__type=questionnaire_type)
|
||||
qrows = qrows.filter(questionnaire__type=questionnaire_type)
|
||||
if sales_channel is not None:
|
||||
children = children.filter(Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels=sales_channel))
|
||||
qrows = qrows.filter(Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels=sales_channel))
|
||||
|
||||
children = list(children)
|
||||
qrows = list(qrows)
|
||||
|
||||
qc_cache = {
|
||||
q.pk: q for q in children
|
||||
row_cache = {
|
||||
q.pk: q for q in qrows
|
||||
}
|
||||
|
||||
def qc_is_visible(parentid, qvals):
|
||||
if parentid not in qc_cache:
|
||||
def row_is_visible(parentid, qvals):
|
||||
if parentid not in row_cache:
|
||||
return False
|
||||
parentqc = qc_cache[parentid]
|
||||
if parentqc.dependency_question_id and not qc_is_visible(parentqc.dependency_question_id, parentqc.dependency_values):
|
||||
parentrow = row_cache[parentid]
|
||||
if parentrow.dependency_question_id and not row_is_visible(parentrow.dependency_question_id, parentrow.dependency_values):
|
||||
return False
|
||||
answer_values = self.get_dependency_answer_values(parentqc)
|
||||
answer_values = self.get_dependency_answer_values(parentrow)
|
||||
return answer_values and any(qval in answer_values for qval in qvals)
|
||||
|
||||
self.questions = []
|
||||
for qc in children:
|
||||
for qrow in qrows:
|
||||
try:
|
||||
qc.answer = self.answer_cache[qc.user_datafield_id or qc.system_datafield]
|
||||
qrow.answer = self.answer_cache[qrow.user_datafield_id or qrow.system_datafield]
|
||||
except KeyError:
|
||||
continue
|
||||
if not qc.dependency_question_id or qc_is_visible(qc.dependency_question_id, qc.dependency_values):
|
||||
self.questions.append(qc)
|
||||
if not qrow.dependency_question_id or row_is_visible(qrow.dependency_question_id, qrow.dependency_values):
|
||||
self.questions.append(qrow)
|
||||
return self.questions
|
||||
|
||||
@cached_property
|
||||
@@ -1667,21 +1667,21 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
|
||||
raise ValueError('Questionnaire child without datafield has no answer')
|
||||
|
||||
def get_system_answer(self, system_datafield_name):
|
||||
if system_datafield_name == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
if system_datafield_name == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
|
||||
return self.attendee_name
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.ATTENDEE_EMAIL:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.ATTENDEE_EMAIL:
|
||||
return self.attendee_email
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.COMPANY:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.COMPANY:
|
||||
return self.company
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.STREET:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.STREET:
|
||||
return self.street
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.ZIPCODE:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.ZIPCODE:
|
||||
return self.zipcode
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.CITY:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.CITY:
|
||||
return self.city
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.STATE:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.STATE:
|
||||
return self.state
|
||||
elif system_datafield_name == QuestionnaireChild.SystemDatafield.COUNTRY:
|
||||
elif system_datafield_name == QuestionnaireRow.SystemDatafield.COUNTRY:
|
||||
return str(self.country)
|
||||
else:
|
||||
raise ValueError('Unknown system datafield name')
|
||||
|
||||
@@ -1410,7 +1410,7 @@ class CartManager:
|
||||
for k, v in self._widget_data.items():
|
||||
if not k.startswith('question-'):
|
||||
continue
|
||||
# TODO(questionnaires) - switch to QuestionnaireChild
|
||||
# TODO(questionnaires) - switch to QuestionnaireRow
|
||||
q = cp.item.questions.filter(ask_during_checkin=False, identifier__iexact=k[9:]).first()
|
||||
if q:
|
||||
try:
|
||||
|
||||
@@ -55,7 +55,7 @@ from django_scopes import scope, scopes_disabled
|
||||
|
||||
from pretix.base.models import (
|
||||
Checkin, CheckinList, Device, Event, Gate, Item, ItemVariation, Order,
|
||||
OrderPosition, Questionnaire, QuestionnaireChild, QuestionOption,
|
||||
OrderPosition, Questionnaire, QuestionnaireRow, QuestionOption,
|
||||
)
|
||||
from pretix.base.signals import checkin_created, periodic_task
|
||||
from pretix.helpers import OF_SELF
|
||||
@@ -1033,7 +1033,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
||||
|
||||
# Do this outside of transaction so it is saved even if the checkin fails for some other reason
|
||||
checkin_questions = list(
|
||||
QuestionnaireChild.objects.filter(
|
||||
QuestionnaireRow.objects.filter(
|
||||
questionnaire__event=clist.event, questionnaire__type=Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN,
|
||||
questionnaire__items__in=[op.item_id],
|
||||
).select_related('user_datafield', 'questionnaire')
|
||||
|
||||
@@ -37,7 +37,7 @@ from pretix.base.forms.questions import (
|
||||
)
|
||||
from pretix.base.models import (
|
||||
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
|
||||
Questionnaire, QuestionnaireChild, QuestionOption,
|
||||
Questionnaire, QuestionnaireRow, QuestionOption,
|
||||
)
|
||||
from pretix.base.models.customers import AttendeeProfile
|
||||
from pretix.base.models.orders import CheckoutSession, Order
|
||||
@@ -364,7 +364,7 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
|
||||
to_attr='answerlist'),
|
||||
Prefetch('item__questionnaires',
|
||||
qqs.prefetch_related(
|
||||
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
|
||||
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
|
||||
Prefetch('user_datafield', Question.objects.prefetch_related(
|
||||
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
|
||||
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
|
||||
@@ -376,7 +376,7 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
|
||||
)))
|
||||
))
|
||||
),
|
||||
to_attr='childlist')
|
||||
to_attr='questionnairerowlist')
|
||||
),
|
||||
to_attr='relevant_questionnaires')
|
||||
))
|
||||
|
||||
@@ -67,7 +67,7 @@ from pretix.api.serializers.item import (
|
||||
from pretix.base.forms import I18nFormSet
|
||||
from pretix.base.models import (
|
||||
CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation, LogEntry,
|
||||
OrderPosition, Question, QuestionAnswer, QuestionnaireChild,
|
||||
OrderPosition, Question, QuestionAnswer, QuestionnaireRow,
|
||||
QuestionOption, Quota, SeatCategoryMapping, Voucher,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
@@ -720,7 +720,7 @@ class QuestionnairesEditor(EventPermissionRequiredMixin, TemplateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['questionnaire_type_choices'] = textchoices_to_json(Questionnaire.QuestionnaireType, self.request.event)
|
||||
ctx['system_question_choices'] = textchoices_to_json(QuestionnaireChild.SystemDatafield, self.request.event)
|
||||
ctx['system_question_choices'] = textchoices_to_json(QuestionnaireRow.SystemDatafield, self.request.event)
|
||||
ctx['question_type_choices'] = textchoices_to_json(Question.FieldType, self.request.event)
|
||||
return ctx
|
||||
|
||||
|
||||
@@ -1093,7 +1093,7 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
|
||||
for cp in self._positions_for_questions:
|
||||
qc_cache = {
|
||||
qc.pk: qc for qq in cp.item.relevant_questionnaires for qc in qq.childlist
|
||||
qc.pk: qc for qq in cp.item.relevant_questionnaires for qc in qq.questionnairerowlist
|
||||
}
|
||||
|
||||
def question_is_visible(parentid, qvals):
|
||||
@@ -1113,7 +1113,7 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep):
|
||||
|
||||
if not self.all_optional:
|
||||
for qq in cp.item.relevant_questionnaires:
|
||||
for qc in qq.childlist:
|
||||
for qc in qq.questionnairerowlist:
|
||||
if qc.user_datafield_id and question_is_required(qc) and qc.user_datafield_id not in cp.answer_cache:
|
||||
if warn:
|
||||
messages.warning(request, _('Please fill in answers to all required questions.'))
|
||||
|
||||
@@ -54,7 +54,7 @@ from pretix.base.models import (
|
||||
CartPosition, Customer, InvoiceAddress, ItemAddOn, OrderFee, Question,
|
||||
QuestionAnswer, Questionnaire, QuestionOption, TaxRule,
|
||||
)
|
||||
from pretix.base.models.items import QuestionnaireChild
|
||||
from pretix.base.models.items import QuestionnaireRow
|
||||
from pretix.base.models.orders import CheckoutSession
|
||||
from pretix.base.services.cart import get_fees
|
||||
from pretix.base.services.pricing import apply_rounding
|
||||
@@ -435,7 +435,7 @@ def get_cart_positions(request):
|
||||
to_attr='answerlist'),
|
||||
Prefetch('item__questionnaires',
|
||||
qqs.prefetch_related(
|
||||
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
|
||||
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
|
||||
Prefetch('user_datafield', Question.objects.prefetch_related(
|
||||
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
|
||||
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
|
||||
@@ -447,7 +447,7 @@ def get_cart_positions(request):
|
||||
)))
|
||||
))
|
||||
),
|
||||
to_attr='childlist')
|
||||
to_attr='questionnairerowlist')
|
||||
),
|
||||
to_attr='relevant_questionnaires')
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ function saveQuestionnaire(questionnaire) {
|
||||
}
|
||||
result = result.then(d => {
|
||||
questionnaire.id = d.id
|
||||
questionnaire.children = d.children
|
||||
questionnaire.rows = d.rows
|
||||
questionnaire._err_mes = null
|
||||
questionnaire._loading = false
|
||||
return d;
|
||||
@@ -55,14 +55,14 @@ function saveQuestionnaire(questionnaire) {
|
||||
}
|
||||
function addPositionQuestionnaire () {
|
||||
position_questionnaires.value.push({
|
||||
all_sales_channels: true, children: [], limit_sales_channels: [], position: position_questionnaires.value.length,
|
||||
all_sales_channels: true, rows: [], limit_sales_channels: [], position: position_questionnaires.value.length,
|
||||
items: [], internal_name: "Unnamed questionnaire", type: "position_purchase",
|
||||
_new_id: Date.now(),
|
||||
});
|
||||
}
|
||||
function addOrderQuestionnaire () {
|
||||
order_questionnaires.value.push({
|
||||
all_sales_channels: true, children: [], limit_sales_channels: [], position: order_questionnaires.value.length,
|
||||
all_sales_channels: true, rows: [], limit_sales_channels: [], position: order_questionnaires.value.length,
|
||||
items: [], internal_name: "Unnamed questionnaire", type: "order_purchase",
|
||||
_new_id: Date.now(),
|
||||
});
|
||||
|
||||
@@ -34,14 +34,14 @@ const emit = defineEmits(['update', 'invalidate:datafields', 'invalidate:questio
|
||||
const sales_channels: Ref<SalesChannel[]> = inject('pretix:env:organizer:sales_channels')
|
||||
|
||||
let nextId = 1;
|
||||
watch(() => props.questionnaire.children, () => {
|
||||
for (let qc of props.questionnaire.children) {
|
||||
watch(() => props.questionnaire.rows, () => {
|
||||
for (let qc of props.questionnaire.rows) {
|
||||
if (!qc._cid) qc._cid = id + (++nextId);
|
||||
}
|
||||
})
|
||||
|
||||
function addExistingDatafield (field) {
|
||||
props.questionnaire.children.push({
|
||||
props.questionnaire.rows.push({
|
||||
_cid: useId(),
|
||||
datafield: field.identifier,
|
||||
required: false,
|
||||
@@ -61,7 +61,7 @@ function showAddTextblockDialog () {
|
||||
}
|
||||
|
||||
function addTextblock () {
|
||||
props.questionnaire.children.push({
|
||||
props.questionnaire.rows.push({
|
||||
_cid: useId(),
|
||||
datafield: null,
|
||||
required: false,
|
||||
@@ -163,14 +163,14 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
|
||||
<p v-else>{{ questionnaire._err_mes }}</p>
|
||||
</div>
|
||||
<div class="form-horizontal" :id="`questionListParent${props.questionnaire.id}`">
|
||||
<SlickList axis="y" v-model:list="props.questionnaire.children" useDragHandle :appendTo="`#questionListParent${props.questionnaire.id}`" @update:list="emit('update')">
|
||||
<SlickItem v-for="(child, index) in props.questionnaire.children" :key="child._cid" :index="index">
|
||||
<SlickList axis="y" v-model:list="props.questionnaire.rows" useDragHandle :appendTo="`#questionListParent${props.questionnaire.id}`" @update:list="emit('update')">
|
||||
<SlickItem v-for="(child, index) in props.questionnaire.rows" :key="child._cid" :index="index">
|
||||
<QuestionElement
|
||||
:datafields="props.datafields"
|
||||
:question="child"
|
||||
:editable="true"
|
||||
:possible_dependencies="props.questionnaire.children.slice(0, index)"
|
||||
@remove-self="questionnaire.children.splice(index, 1); emit('update')"
|
||||
:possible_dependencies="props.questionnaire.rows.slice(0, index)"
|
||||
@remove-self="questionnaire.rows.splice(index, 1); emit('update')"
|
||||
@update="emit('update')"
|
||||
@invalidate:datafields="emit('invalidate:datafields')"/>
|
||||
</SlickItem>
|
||||
|
||||
@@ -45,10 +45,10 @@ export type Questionnaire = {
|
||||
position: number,
|
||||
all_sales_channels: boolean,
|
||||
limit_sales_channels: string[],
|
||||
children: QuestionnaireChild[],
|
||||
rows: QuestionnaireRow[],
|
||||
}
|
||||
|
||||
export type QuestionnaireChild = {
|
||||
export type QuestionnaireRow = {
|
||||
datafield: string | null,
|
||||
required: boolean,
|
||||
label: I18nString,
|
||||
|
||||
Reference in new issue
Block a user