mirror of
https://github.com/pretix/pretix.git
synced 2026-09-13 16:14:40 +00:00
Added statistic view on questions
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/sb-admin-2.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/sb-admin-2.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/main.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quota.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/question.js" %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{{ html_head|safe }}
|
{{ html_head|safe }}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|||||||
@@ -2,85 +2,84 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load formset_tags %}
|
{% load formset_tags %}
|
||||||
{% block title %}{% trans "Question" %}{% endblock %}
|
{% block title %}{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
<h1>{% trans "Question" %}</h1>
|
<h1>
|
||||||
<form action="" method="post" class="form-horizontal">
|
{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}
|
||||||
{% csrf_token %}
|
<a href="{% url "control:event.items.questions.edit" event=request.event.slug organizer=request.event.organizer.slug question=question.pk %}"
|
||||||
{% bootstrap_form_errors form %}
|
class="btn btn-default">
|
||||||
<fieldset>
|
<span class="fa fa-edit"></span>
|
||||||
<legend>{% trans "General information" %}</legend>
|
{% trans "Edit question" %}
|
||||||
{% bootstrap_field form.question layout="horizontal" %}
|
</a>
|
||||||
{% bootstrap_field form.type layout="horizontal" %}
|
</h1>
|
||||||
{% bootstrap_field form.required layout="horizontal" %}
|
<form class="form-inline helper-display-inline" action="" method="get">
|
||||||
</fieldset>
|
<p>
|
||||||
<fieldset>
|
<select name="status" class="form-control">
|
||||||
<legend>{% trans "Apply to products" %}</legend>
|
<option value="">{% trans "All orders" %}</option>
|
||||||
{% bootstrap_field form.items layout="horizontal" %}
|
<option value="p" {% if request.GET.status == "p" %}selected="selected"{% endif %}>{% trans "Paid" %}</option>
|
||||||
</fieldset>
|
<option value="n" {% if request.GET.status == "n" %}selected="selected"{% endif %}>{% trans "Pending" %}</option>
|
||||||
<div class="alert alert-info alert-required-boolean">
|
<option value="o" {% if request.GET.status == "o" %}selected="selected"{% endif %}>{% trans "Pending (overdue)" %}</option>
|
||||||
{% blocktrans trimmed %}
|
<option value="e" {% if request.GET.status == "e" %}selected="selected"{% endif %}>{% trans "Expired" %}</option>
|
||||||
If you mark a Yes/No question as required, it means that the user has to select Yes and No is not
|
<option value="ne" {% if request.GET.status == "ne" %}selected="selected"{% endif %}>{% trans "Pending or expired" %}</option>
|
||||||
accepted. If you want to allow both options, do not make this field required.
|
<option value="c" {% if request.GET.status == "c" %}selected="selected"{% endif %}>{% trans "Cancelled" %}</option>
|
||||||
{% endblocktrans %}
|
<option value="r" {% if request.GET.status == "r" %}selected="selected"{% endif %}>{% trans "Refunded" %}</option>
|
||||||
</div>
|
</select>
|
||||||
<fieldset id="answer-options">
|
<select name="item" class="form-control">
|
||||||
<legend>{% trans "Answer options" %}</legend>
|
<option value="">{% trans "All products" %}</option>
|
||||||
<noscript>
|
{% for item in items %}
|
||||||
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
|
<option value="{{ item.id }}"
|
||||||
</noscript>
|
{% if request.GET.item|add:0 == item.id %}selected="selected"{% endif %}>
|
||||||
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
|
{{ item.name }}
|
||||||
{{ formset.management_form }}
|
</option>
|
||||||
{% bootstrap_formset_errors formset %}
|
{% endfor %}
|
||||||
<div data-formset-body>
|
</select>
|
||||||
{% for form in formset %}
|
<button class="btn btn-primary" type="submit">{% trans "Filter" %}</button>
|
||||||
<div data-formset-form>
|
</p>
|
||||||
<div class="sr-only">
|
|
||||||
{{ form.id }}
|
|
||||||
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
|
|
||||||
</div>
|
|
||||||
<div class="row question-option-row">
|
|
||||||
<div class="col-xs-10">
|
|
||||||
{% bootstrap_form_errors form %}
|
|
||||||
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-2 text-right">
|
|
||||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
|
||||||
<i class="fa fa-trash"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
<script type="form-template" data-formset-empty-form>
|
|
||||||
{% escapescript %}
|
|
||||||
<div 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="row question-option-row">
|
|
||||||
<div class="col-xs-10">
|
|
||||||
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-2 text-right">
|
|
||||||
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
|
||||||
<i class="fa fa-trash"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endescapescript %}
|
|
||||||
</script>
|
|
||||||
<p>
|
|
||||||
<button type="button" class="btn btn-default" data-formset-add>
|
|
||||||
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div class="form-group submit-group">
|
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
|
||||||
{% trans "Save" %}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
{% if not stats %}
|
||||||
|
<div class="empty-collection">
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
No matching answers found.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
{% if not items %}
|
||||||
|
<p>
|
||||||
|
{% trans "You need to assign the question to a product to collect answers." %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a href="{% url "control:event.items.questions.edit" event=request.event.slug organizer=request.event.organizer.slug question=question.pk %}"
|
||||||
|
class="btn btn-primary btn-lg"><i class="fa fa-edit"></i> {% trans "Edit question" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row" id="question-stats">
|
||||||
|
<div class="col-md-6 col-xs-12">
|
||||||
|
<div class="chart" id="question_chart" data-type="{{ question.type }}">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script type="application/json" id="question-chart-data">{{ stats_json|safe }}</script>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-xs-12">
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Answer" %}</th>
|
||||||
|
<th>{% trans "Count" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for stat in stats %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{{ stat.answer }}
|
||||||
|
</td>
|
||||||
|
<td>{{ stat.count }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
{% extends "pretixcontrol/items/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load formset_tags %}
|
||||||
|
{% block title %}
|
||||||
|
{% if question %}
|
||||||
|
{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}
|
||||||
|
{% else %}
|
||||||
|
{% trans "Question" %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block inside %}
|
||||||
|
{% if question %}
|
||||||
|
<h1>{% blocktrans with name=question.question %}Question: {{ name }}{% endblocktrans %}</h1>
|
||||||
|
{% else %}
|
||||||
|
<h1>{% trans "Question" %}</h1>
|
||||||
|
{% endif %}
|
||||||
|
<form action="" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form_errors form %}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "General information" %}</legend>
|
||||||
|
{% bootstrap_field form.question layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.type layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.required layout="horizontal" %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Apply to products" %}</legend>
|
||||||
|
{% bootstrap_field form.items layout="horizontal" %}
|
||||||
|
</fieldset>
|
||||||
|
<div class="alert alert-info alert-required-boolean">
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
If you mark a Yes/No question as required, it means that the user has to select Yes and No is not
|
||||||
|
accepted. If you want to allow both options, do not make this field required.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
<fieldset id="answer-options">
|
||||||
|
<legend>{% trans "Answer options" %}</legend>
|
||||||
|
<noscript>
|
||||||
|
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
|
||||||
|
</noscript>
|
||||||
|
<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 data-formset-form>
|
||||||
|
<div class="sr-only">
|
||||||
|
{{ form.id }}
|
||||||
|
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="row question-option-row">
|
||||||
|
<div class="col-xs-10">
|
||||||
|
{% bootstrap_form_errors form %}
|
||||||
|
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2 text-right">
|
||||||
|
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||||
|
<i class="fa fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script type="form-template" data-formset-empty-form>
|
||||||
|
{% escapescript %}
|
||||||
|
<div 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="row question-option-row">
|
||||||
|
<div class="col-xs-10">
|
||||||
|
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-2 text-right">
|
||||||
|
<button type="button" class="btn btn-danger" data-formset-delete-button>
|
||||||
|
<i class="fa fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endescapescript %}
|
||||||
|
</script>
|
||||||
|
<p>
|
||||||
|
<button type="button" class="btn btn-default" data-formset-add>
|
||||||
|
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<div class="form-group submit-group">
|
||||||
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
|
{% trans "Save" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -38,15 +38,16 @@
|
|||||||
{% for q in questions %}
|
{% for q in questions %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><strong><a href="
|
<td><strong><a href="
|
||||||
{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">{{ q.question }}</a></strong>
|
{% url "control:event.items.questions.show" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}">{{ q.question }}</a></strong>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ q.get_type_display }}</td>
|
<td>{{ q.get_type_display }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url "control:event.items.questions.up" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
<a href="{% url "control:event.items.questions.up" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
||||||
<a href="{% url "control:event.items.questions.down" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
<a href="{% url "control:event.items.questions.down" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right"><a href="
|
<td class="text-right">
|
||||||
{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
<a href="{% url "control:event.items.questions.edit" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||||
|
<a href="{% url "control:event.items.questions.delete" organizer=request.event.organizer.slug event=request.event.slug question=q.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ urlpatterns = [
|
|||||||
url(r'^questions/(?P<question>\d+)/up$', item.question_move_up, name='event.items.questions.up'),
|
url(r'^questions/(?P<question>\d+)/up$', item.question_move_up, name='event.items.questions.up'),
|
||||||
url(r'^questions/(?P<question>\d+)/down$', item.question_move_down,
|
url(r'^questions/(?P<question>\d+)/down$', item.question_move_down,
|
||||||
name='event.items.questions.down'),
|
name='event.items.questions.down'),
|
||||||
url(r'^questions/(?P<question>\d+)/$', item.QuestionUpdate.as_view(),
|
url(r'^questions/(?P<question>\d+)/$', item.QuestionView.as_view(),
|
||||||
|
name='event.items.questions.show'),
|
||||||
|
url(r'^questions/(?P<question>\d+)/change$', item.QuestionUpdate.as_view(),
|
||||||
name='event.items.questions.edit'),
|
name='event.items.questions.edit'),
|
||||||
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
|
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
|
||||||
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
|
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
|
||||||
|
|||||||
@@ -4,19 +4,22 @@ from django.contrib import messages
|
|||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.core.urlresolvers import resolve, reverse
|
from django.core.urlresolvers import resolve, reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django.db.models import Count
|
||||||
from django.forms.models import ModelMultipleChoiceField, inlineformset_factory
|
from django.forms.models import ModelMultipleChoiceField, inlineformset_factory
|
||||||
from django.http import Http404, HttpResponseRedirect
|
from django.http import Http404, HttpResponseRedirect
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||||
from django.views.generic.edit import DeleteView
|
from django.views.generic.edit import DeleteView
|
||||||
|
|
||||||
from pretix.base.forms import I18nFormSet
|
from pretix.base.forms import I18nFormSet
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
Item, ItemCategory, ItemVariation, Question, QuestionOption, Quota,
|
Item, ItemCategory, ItemVariation, Order, Question, QuestionAnswer,
|
||||||
|
QuestionOption, Quota,
|
||||||
)
|
)
|
||||||
from pretix.control.forms.item import (
|
from pretix.control.forms.item import (
|
||||||
CategoryForm, ItemCreateForm, ItemUpdateForm, ItemVariationForm,
|
CategoryForm, ItemCreateForm, ItemUpdateForm, ItemVariationForm,
|
||||||
@@ -377,10 +380,71 @@ class QuestionMixin:
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionView(EventPermissionRequiredMixin, QuestionMixin, ChartContainingView, DetailView):
|
||||||
|
model = Question
|
||||||
|
template_name = 'pretixcontrol/items/question.html'
|
||||||
|
permission = 'can_change_items'
|
||||||
|
template_name_field = 'question'
|
||||||
|
|
||||||
|
def get_answer_statistics(self):
|
||||||
|
qs = QuestionAnswer.objects.filter(
|
||||||
|
question=self.object, orderposition__isnull=False,
|
||||||
|
orderposition__order__event=self.request.event
|
||||||
|
)
|
||||||
|
if self.request.GET.get("status", "") != "":
|
||||||
|
s = self.request.GET.get("status", "")
|
||||||
|
if s == 'o':
|
||||||
|
qs = qs.filter(orderposition__order__status=Order.STATUS_PENDING, expires__lt=now().date())
|
||||||
|
elif s == 'ne':
|
||||||
|
qs = qs.filter(orderposition__order__status__in=[Order.STATUS_PENDING, Order.STATUS_EXPIRED])
|
||||||
|
else:
|
||||||
|
qs = qs.filter(orderposition__order__status=s)
|
||||||
|
if self.request.GET.get("item", "") != "":
|
||||||
|
i = self.request.GET.get("item", "")
|
||||||
|
qs = qs.filter(orderposition__item_id__in=(i,))
|
||||||
|
|
||||||
|
if self.object.type in (Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE):
|
||||||
|
qs = qs.order_by('options').values('options', 'options__answer')\
|
||||||
|
.annotate(count=Count('id')).order_by('-count')
|
||||||
|
for a in qs:
|
||||||
|
a['answer'] = str(a['options__answer'])
|
||||||
|
del a['options__answer']
|
||||||
|
else:
|
||||||
|
qs = qs.order_by('answer').values('answer').annotate(count=Count('id')).order_by('-count')
|
||||||
|
|
||||||
|
if self.object.type == Question.TYPE_BOOLEAN:
|
||||||
|
for a in qs:
|
||||||
|
a['answer'] = ugettext('Yes') if a['answer'] == 'True' else ugettext('No')
|
||||||
|
a['answer_bool'] = a['answer'] == 'True'
|
||||||
|
|
||||||
|
return list(qs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
ctx = super().get_context_data()
|
||||||
|
ctx['items'] = self.object.items.all()
|
||||||
|
ctx['stats'] = self.get_answer_statistics()
|
||||||
|
ctx['stats_json'] = json.dumps(self.get_answer_statistics())
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
def get_object(self, queryset=None) -> Question:
|
||||||
|
try:
|
||||||
|
return self.request.event.questions.get(
|
||||||
|
id=self.kwargs['question']
|
||||||
|
)
|
||||||
|
except Question.DoesNotExist:
|
||||||
|
raise Http404(_("The requested question does not exist."))
|
||||||
|
|
||||||
|
def get_success_url(self) -> str:
|
||||||
|
return reverse('control:event.items.questions', kwargs={
|
||||||
|
'organizer': self.request.event.organizer.slug,
|
||||||
|
'event': self.request.event.slug,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class QuestionUpdate(EventPermissionRequiredMixin, QuestionMixin, UpdateView):
|
class QuestionUpdate(EventPermissionRequiredMixin, QuestionMixin, UpdateView):
|
||||||
model = Question
|
model = Question
|
||||||
form_class = QuestionForm
|
form_class = QuestionForm
|
||||||
template_name = 'pretixcontrol/items/question.html'
|
template_name = 'pretixcontrol/items/question_edit.html'
|
||||||
permission = 'can_change_items'
|
permission = 'can_change_items'
|
||||||
context_object_name = 'question'
|
context_object_name = 'question'
|
||||||
|
|
||||||
@@ -417,7 +481,7 @@ class QuestionUpdate(EventPermissionRequiredMixin, QuestionMixin, UpdateView):
|
|||||||
class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
|
class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
|
||||||
model = Question
|
model = Question
|
||||||
form_class = QuestionForm
|
form_class = QuestionForm
|
||||||
template_name = 'pretixcontrol/items/question.html'
|
template_name = 'pretixcontrol/items/question_edit.html'
|
||||||
permission = 'can_change_items'
|
permission = 'can_change_items'
|
||||||
context_object_name = 'question'
|
context_object_name = 'question'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
/*global $, Morris, gettext*/
|
||||||
|
$(function () {
|
||||||
|
if (!$("#question-stats").length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".chart").css("height", "250px");
|
||||||
|
var data_type = $("#question_chart").attr("data-type"),
|
||||||
|
data = JSON.parse($("#question-chart-data").html()),
|
||||||
|
others_sum = 0,
|
||||||
|
max_num = 8;
|
||||||
|
|
||||||
|
for (var i in data) {
|
||||||
|
data[i].value = data[i].count;
|
||||||
|
data[i].label = data[i].answer;
|
||||||
|
if (data[i].label.length > 20) {
|
||||||
|
data[i].label = data[i].label.substring(0, 20) + '…';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_type == 'N') {
|
||||||
|
// Sort
|
||||||
|
data.sort(function (a, b) {
|
||||||
|
if (parseFloat(a.label) > parseFloat(b.label)) {
|
||||||
|
return 1;
|
||||||
|
} else if (parseFloat(a.label) < parseFloat(b.label)) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
max_num = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit shown options
|
||||||
|
if (data.length > max_num) {
|
||||||
|
for (var i = max_num; i < data.length; i++) {
|
||||||
|
others_sum += data[i].count;
|
||||||
|
}
|
||||||
|
data = data.slice(0, max_num);
|
||||||
|
data.push({'value': others_sum, 'label': gettext('Others')});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_type === 'B') {
|
||||||
|
var colors;
|
||||||
|
if (data[0].answer_bool) {
|
||||||
|
colors = ['#41A351', '#BD362F'];
|
||||||
|
} else {
|
||||||
|
colors = ['#BD362F', '#41A351'];
|
||||||
|
}
|
||||||
|
new Morris.Donut({
|
||||||
|
element: 'question_chart',
|
||||||
|
data: data,
|
||||||
|
resize: true,
|
||||||
|
colors: colors
|
||||||
|
});
|
||||||
|
} else if (data_type === 'C') {
|
||||||
|
new Morris.Donut({
|
||||||
|
element: 'question_chart',
|
||||||
|
data: data,
|
||||||
|
resize: true
|
||||||
|
});
|
||||||
|
} else { // M, N, S, T
|
||||||
|
new Morris.Bar({
|
||||||
|
element: 'question_chart',
|
||||||
|
data: data,
|
||||||
|
resize: true,
|
||||||
|
xkey: 'label',
|
||||||
|
ykeys: ['count'],
|
||||||
|
labels: [gettext('Count')]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// N, S, T
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user