From 48d0a4fbf15711db3e5811fecedc80faca21b51e Mon Sep 17 00:00:00 2001 From: eric Date: Thu, 8 Jun 2017 17:28:18 -0400 Subject: [PATCH] updates from Gluejar/regluit --- questionnaire/admin.py | 7 ++- questionnaire/templates/pages/summaries.html | 35 ++++++++++++ questionnaire/urls.py | 4 +- questionnaire/utils.py | 2 +- questionnaire/views.py | 59 +++++++++++++++----- 5 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 questionnaire/templates/pages/summaries.html diff --git a/questionnaire/admin.py b/questionnaire/admin.py index 08984e5..c82ec5c 100644 --- a/questionnaire/admin.py +++ b/questionnaire/admin.py @@ -53,8 +53,11 @@ class QuestionnaireAdmin(admin.ModelAdmin): readonly_fields = ('export',) def export(self, obj): - csv_url= reverse("export_csv", args=[obj.id,]) - return '%s' % (csv_url, _("Download data")) + csv_url = reverse("export_csv", args=[obj.id,]) + summary_url = reverse("export_summary", args=[obj.id,]) + return '{} {}'.format( + csv_url, _("Download data"), summary_url, _("Show summary") + ) export.allow_tags = True export.short_description = _('Export to CSV') diff --git a/questionnaire/templates/pages/summaries.html b/questionnaire/templates/pages/summaries.html new file mode 100644 index 0000000..e52ffa9 --- /dev/null +++ b/questionnaire/templates/pages/summaries.html @@ -0,0 +1,35 @@ +{% extends "base-questionnaire.html" %} +{% block questionnaire %} +

+ Survey Results Summary +

+{% for summary in summaries %} + +

Question

+

+{{summary.1|safe}} +

+{% if summary.2 %} +

Choices

+ +{% endif %} +

Free Text Answers

+ + +{% endfor %} + +{% endblock %} diff --git a/questionnaire/urls.py b/questionnaire/urls.py index a43c1e8..7439038 100644 --- a/questionnaire/urls.py +++ b/questionnaire/urls.py @@ -9,6 +9,8 @@ urlpatterns = [ questionnaire, name='questionnaire_noargs'), url(r'^csv/(?P\d+)/$', export_csv, name='export_csv'), + url(r'^summary/(?P\d+)/$', + export_summary, name='export_summary'), url(r'^(?P[^/]+)/progress/$', get_async_progress, name='progress'), url(r'^take/(?P[0-9]+)/$', generate_run), @@ -19,8 +21,6 @@ urlpatterns = [ url(r'^landing/(?P\w+)/$', SurveyView.as_view(), name="landing"), url(r'^(?P[^/]+)/(?P[-]{0,1}\d+)/$', questionnaire, name='questionset'), - url(r'^q/manage/csv/(\d+)/', - export_csv, name="export_csv"), ] if not use_session: diff --git a/questionnaire/utils.py b/questionnaire/utils.py index 345f05c..fabb9b2 100644 --- a/questionnaire/utils.py +++ b/questionnaire/utils.py @@ -41,7 +41,7 @@ def numal_sort(a, b): bnum, bstr = split_numal(b) cmpnum = cmp(anum, bnum) if(cmpnum == 0): - return cmp(astr, bstr) + return cmp(astr.lower(), bstr.lower()) return cmpnum def numal0_sort(a, b): diff --git a/questionnaire/views.py b/questionnaire/views.py index 9abb177..6af14f5 100644 --- a/questionnaire/views.py +++ b/questionnaire/views.py @@ -13,7 +13,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.template import RequestContext from django.core.urlresolvers import reverse from django.core.cache import cache -from django.contrib.auth.decorators import permission_required +from django.contrib.auth.decorators import login_required, permission_required from django.shortcuts import render, render_to_response, get_object_or_404 from django.views.generic.base import TemplateView from django.db import transaction @@ -812,22 +812,25 @@ def _table_headers(questions): This will create separate columns for each multiple-choice possiblity and freeform options, to avoid mixing data types and make charting easier. """ - ql = list(questions) - ql.sort(lambda x, y: numal_sort(x.number, y.number)) + ql = list(questions.order_by( + 'questionset__sortid', 'number') + ) + #ql.sort(lambda x, y: numal_sort(x.number, y.number)) columns = [] for q in ql: + qnum = '{}.{}'.format(q.questionset.sortid, q.number) if q.type.startswith('choice-yesnocomment'): - columns.extend([q.number, q.number + "-freeform"]) + columns.extend([qnum, qnum + "-freeform"]) elif q.type.startswith('choice-freeform'): - columns.extend([q.number, q.number + "-freeform"]) + columns.extend([qnum, qnum + "-freeform"]) elif q.type.startswith('choice-multiple'): cl = [c.value for c in q.choice_set.all()] cl.sort(numal_sort) - columns.extend([q.number + '-' + value for value in cl]) + columns.extend([qnum + '-' + value for value in cl]) if q.type == 'choice-multiple-freeform': - columns.append(q.number + '-freeform') + columns.append(qnum + '-freeform') else: - columns.append(q.number) + columns.append(qnum) return columns default_extra_headings = [u'subject', u'run id'] @@ -835,7 +838,8 @@ default_extra_headings = [u'subject', u'run id'] def default_extra_entries(subject, run): return ["%s/%s" % (subject.id, subject.ip_address), run.id] -@permission_required("questionnaire.export") + +@login_required def export_csv(request, qid, extra_headings=default_extra_headings, extra_entries=default_extra_entries, @@ -848,9 +852,12 @@ def export_csv(request, qid, qid -- questionnaire_id extra_headings -- customize the headings for extra columns, extra_entries -- function returning a list of extra column entries, - answer_filter -- custom filter for the answers + answer_filter -- custom filter for the answers. If this is present, the filter must manage access. filecode -- code for filename """ + if answer_filter is None and not request.user.has_perm("questionnaire.export"): + return HttpResponse('Sorry, you do not have export permissions', content_type="text/plain") + fd = tempfile.TemporaryFile() questionnaire = get_object_or_404(Questionnaire, pk=int(qid)) @@ -930,18 +937,19 @@ def answer_export(questionnaire, answers=None, answer_filter=None): ans = str(ans) for choice in ans: col = None + qnum = '{}.{}'.format(answer.question.questionset.sortid, answer.question.number) if type(choice) == list: # freeform choice choice = choice[0] - col = coldict.get(answer.question.number + '-freeform', None) + col = coldict.get(qnum + '-freeform', None) if col is None: # look for enumerated choice column (multiple-choice) - col = coldict.get(answer.question.number + '-' + unicode(choice), None) + col = coldict.get(qnum + '-' + unicode(choice), None) if col is None: # single-choice items if ((not qchoicedict[answer.question.id]) or choice in qchoicedict[answer.question.id]): - col = coldict.get(answer.question.number, None) + col = coldict.get(qnum, None) if col is None: # last ditch, if not found throw it in a freeform column - col = coldict.get(answer.question.number + '-freeform', None) + col = coldict.get(qnum + '-freeform', None) if col is not None: row[col] = choice # and don't forget about the last one @@ -949,8 +957,27 @@ def answer_export(questionnaire, answers=None, answer_filter=None): out.append((subject, run, row)) return headings, out +@login_required +def export_summary(request, qid, + answer_filter=None, + ): + """ + For a given questionnaire id, generate a CSV containing a summary of + answers for all subjects. + qid -- questionnaire_id + answer_filter -- custom filter for the answers. If this is present, the filter must manage access. + """ + if answer_filter is None and not request.user.has_perm("questionnaire.export"): + return HttpResponse('Sorry, you do not have export permissions', content_type="text/plain") + -def answer_summary(questionnaire, answers=None): + questionnaire = get_object_or_404(Questionnaire, pk=int(qid)) + summaries = answer_summary(questionnaire, answer_filter=answer_filter) + + return render(request, "pages/summaries.html", {'summaries':summaries}) + + +def answer_summary(questionnaire, answers=None, answer_filter=None): """ questionnaire -- questionnaire model for summary answers -- query set of answers to include in summary, defaults to all @@ -967,6 +994,8 @@ def answer_summary(questionnaire, answers=None): if answers is None: answers = Answer.objects.all() + if answer_filter: + answers = answer_filter(answers) answers = answers.filter(question__questionset__questionnaire=questionnaire) questions = Question.objects.filter( questionset__questionnaire=questionnaire).order_by(