diff --git a/frontend/forms.py b/frontend/forms.py
index 5498be46..b333d366 100644
--- a/frontend/forms.py
+++ b/frontend/forms.py
@@ -333,7 +333,8 @@ class EbookForm(forms.ModelForm):
class Meta:
model = Ebook
- exclude = ('created', 'download_count', 'active', 'filesize', 'version_iter')
+ #exclude = ('created', 'download_count', 'active', 'filesize', 'version_iter')
+ fields = ('url', 'format', 'provider', 'version_label', 'rights', 'edition', 'user')
widgets = {
'edition': forms.HiddenInput,
'user': forms.HiddenInput,
@@ -347,7 +348,7 @@ class EbookForm(forms.ModelForm):
url = self.cleaned_data['url']
new_provider = Ebook.infer_provider(url)
if url and not new_provider:
- raise forms.ValidationError(_("At this time, ebook URLs must point at Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, or Google Books."))
+ raise forms.ValidationError(_("At this time, ebook URLs must point at Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, Google Books, or OApen."))
return new_provider if new_provider else "Unglue.it"
def clean_url(self):
@@ -359,7 +360,7 @@ class EbookForm(forms.ModelForm):
raise forms.ValidationError(_("There's already an ebook with that url."))
def clean(self):
- format = self.cleaned_data['format']
+ format = self.cleaned_data.get('format', '')
the_file = self.cleaned_data.get('file', None)
url = self.cleaned_data.get('url', None)
test_file(the_file)
diff --git a/frontend/templates/surveys.html b/frontend/templates/surveys.html
index 6f133bf2..7a470498 100644
--- a/frontend/templates/surveys.html
+++ b/frontend/templates/surveys.html
@@ -27,7 +27,7 @@ Completed {{ landing.runinfohistory_set.all.count }} times
Set up a new survey for this work.
{% for survey in surveys %}
- Export answers to {{ survey }} for this work.
+ Export or Summarize answers to {{ survey }} for this work.
{% endfor %}
@@ -40,7 +40,11 @@ Completed {{ landing.runinfohistory_set.all.count }} times
Set up a survey using isbn.
{% for survey in surveys %}
Export all my answers to {{ survey }}.
- {% if request.user.is_staff %}Export ALL answers to {{ survey }}.
{% endif %}
+ Summarize my responses to {{ survey }}.
+
+ {% if request.user.is_staff %}Export ALL answers to {{ survey }}.
+ Summarize ALL responses to {{ survey }}.
+ {% endif %}
{% endfor %}
diff --git a/frontend/urls.py b/frontend/urls.py
index 2f456fe4..f7b249c8 100644
--- a/frontend/urls.py
+++ b/frontend/urls.py
@@ -35,6 +35,7 @@ urlpatterns = [
url(r"^rightsholders/surveys/$", views.surveys, name="surveys"),
url(r"^rightsholders/new_survey/(?P\d*)/?$", views.new_survey, name="new_survey"),
url(r"^rightsholders/surveys/answers_(?P\d+)_(?P\d*).csv$", views.export_surveys, name="survey_answers"),
+ url(r"^rightsholders/surveys/summary_(?P\d+)_(?P\d*).csv$", views.surveys_summary, name="survey_summary"),
url(r"^rh_admin/$", views.rh_admin, name="rh_admin"),
url(r"^rh_admin/accepted/$", views.rh_admin, {'facet': 'accepted'}, name="accepted"),
url(r"^rh_admin/claims/$", views.rh_admin, {'facet': 'claims'}, name="claims"),
diff --git a/frontend/views.py b/frontend/views.py
index 76653c8b..0e702bfb 100755
--- a/frontend/views.py
+++ b/frontend/views.py
@@ -135,7 +135,7 @@ from regluit.libraryauth.views import Authenticator, superlogin, login_user
from regluit.libraryauth.models import Library
from regluit.marc.views import qs_marc_records
from regluit.questionnaire.models import Landing, Questionnaire
-from regluit.questionnaire.views import export_csv as export_answers
+from regluit.questionnaire.views import export_summary as answer_summary, export_csv as export_answers
logger = logging.getLogger(__name__)
@@ -1842,7 +1842,7 @@ def works_user_can_admin(user):
Q(claim__user = user) | Q(claim__rights_holder__owner = user)
)
-def export_surveys(request, qid, work_id):
+def works_user_can_admin_filter(request, work_id):
def work_survey_filter(answers):
works = works_user_can_admin(request.user)
if work_id == '0' and request.user.is_staff:
@@ -1855,29 +1855,41 @@ def export_surveys(request, qid, work_id):
return answers.none()
else:
return answers.filter(run__run_info_histories__landing__works__in=works)
-
-
+ return work_survey_filter
+
+def export_surveys(request, qid, work_id):
def extra_entries(subject, run):
- landing = None
+ landing = completed = None
try:
landing = run.run_info_histories.all()[0].landing
+ completed = run.run_info_histories.all()[0].completed
except IndexError:
try:
landing = run.run_infos.all()[0].landing
+ completed = run.run_infos.all()[0].created
except IndexError:
label = wid = "error"
if landing:
label = landing.label
wid = landing.object_id
- return [wid, subject.ip_address, run.id, label]
+ return [wid, subject.ip_address, run.id, completed, label]
if not request.user.is_authenticated() :
return HttpResponseRedirect(reverse('surveys'))
- extra_headings = [u'work id', u'subject ip address', u'run id', u'landing label']
+ extra_headings = [u'work id', u'subject ip address', u'run id', u'date completed', u'landing label']
return export_answers(request, qid,
- answer_filter=work_survey_filter,
+ answer_filter=works_user_can_admin_filter(request, work_id),
extra_entries=extra_entries,
extra_headings=extra_headings,
filecode=work_id)
+
+def surveys_summary(request, qid, work_id):
+ if not request.user.is_authenticated() :
+ return HttpResponseRedirect(reverse('surveys'))
+ return answer_summary(
+ request,
+ qid,
+ answer_filter=works_user_can_admin_filter(request, work_id),
+ )
def new_survey(request, work_id):
if not request.user.is_authenticated() :
diff --git a/questionnaire/admin.py b/questionnaire/admin.py
index 08984e56..c82ec5cc 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 00000000..e52ffa96
--- /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
+
+{% for option in summary.2 %}
+-
+{{option.1}}: {{option.2}}
+
+{% endfor %}
+
+{% endif %}
+Free Text Answers
+
+{% for answer in summary.3 %}
+{% if answer %}
+-
+{{answer}}
+
+{% endif %}
+{% endfor %}
+
+
+{% endfor %}
+
+{% endblock %}
diff --git a/questionnaire/urls.py b/questionnaire/urls.py
index a43c1e81..7439038d 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 345f05c9..fabb9b25 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 61b44542..6af14f5a 100644
--- a/questionnaire/views.py
+++ b/questionnaire/views.py
@@ -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']
@@ -934,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
@@ -953,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
@@ -971,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(