updates from gluejar/reqluit
parent
2c5a44beb8
commit
eae65d7467
|
@ -333,7 +333,8 @@ class EbookForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ebook
|
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 = {
|
widgets = {
|
||||||
'edition': forms.HiddenInput,
|
'edition': forms.HiddenInput,
|
||||||
'user': forms.HiddenInput,
|
'user': forms.HiddenInput,
|
||||||
|
@ -347,7 +348,7 @@ class EbookForm(forms.ModelForm):
|
||||||
url = self.cleaned_data['url']
|
url = self.cleaned_data['url']
|
||||||
new_provider = Ebook.infer_provider(url)
|
new_provider = Ebook.infer_provider(url)
|
||||||
if url and not new_provider:
|
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"
|
return new_provider if new_provider else "Unglue.it"
|
||||||
|
|
||||||
def clean_url(self):
|
def clean_url(self):
|
||||||
|
@ -359,7 +360,7 @@ class EbookForm(forms.ModelForm):
|
||||||
raise forms.ValidationError(_("There's already an ebook with that url."))
|
raise forms.ValidationError(_("There's already an ebook with that url."))
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
format = self.cleaned_data['format']
|
format = self.cleaned_data.get('format', '')
|
||||||
the_file = self.cleaned_data.get('file', None)
|
the_file = self.cleaned_data.get('file', None)
|
||||||
url = self.cleaned_data.get('url', None)
|
url = self.cleaned_data.get('url', None)
|
||||||
test_file(the_file)
|
test_file(the_file)
|
||||||
|
|
|
@ -27,7 +27,7 @@ Completed {{ landing.runinfohistory_set.all.count }} times</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<a href="{% url 'new_survey' work.id %}">Set up a new survey</a> for this work.<br />
|
<a href="{% url 'new_survey' work.id %}">Set up a new survey</a> for this work.<br />
|
||||||
{% for survey in surveys %}
|
{% for survey in surveys %}
|
||||||
<a href="{% url 'survey_answers' survey.id work.id %}">Export answers to {{ survey }}</a> for this work.<br />
|
<a href="{% url 'survey_answers' survey.id work.id %}">Export</a> or <a href="{% url 'survey_summary' survey.id work.id %}">Summarize</a> answers to {{ survey }} for this work.<br />
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</dd>
|
</dd>
|
||||||
|
@ -40,7 +40,11 @@ Completed {{ landing.runinfohistory_set.all.count }} times</dd>
|
||||||
<a href="{% url 'new_survey' '' %}">Set up a survey</a> using isbn. <br />
|
<a href="{% url 'new_survey' '' %}">Set up a survey</a> using isbn. <br />
|
||||||
{% for survey in surveys %}
|
{% for survey in surveys %}
|
||||||
<a href="{% url 'survey_answers' survey.id '' %}">Export all my answers to {{ survey }}</a>.<br />
|
<a href="{% url 'survey_answers' survey.id '' %}">Export all my answers to {{ survey }}</a>.<br />
|
||||||
<a href="{% url 'survey_answers' survey.id '0' %}">{% if request.user.is_staff %}Export ALL answers to {{ survey }}</a>.<br />{% endif %}
|
<a href="{% url 'survey_summary' survey.id '' %}">Summarize my responses to {{ survey }}</a>.<br />
|
||||||
|
|
||||||
|
{% if request.user.is_staff %}<a href="{% url 'survey_answers' survey.id '0' %}">Export ALL answers to {{ survey }}</a>.<br />
|
||||||
|
<a href="{% url 'survey_summary' survey.id '0' %}">Summarize ALL responses to {{ survey }}</a>.<br />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ urlpatterns = [
|
||||||
url(r"^rightsholders/surveys/$", views.surveys, name="surveys"),
|
url(r"^rightsholders/surveys/$", views.surveys, name="surveys"),
|
||||||
url(r"^rightsholders/new_survey/(?P<work_id>\d*)/?$", views.new_survey, name="new_survey"),
|
url(r"^rightsholders/new_survey/(?P<work_id>\d*)/?$", views.new_survey, name="new_survey"),
|
||||||
url(r"^rightsholders/surveys/answers_(?P<qid>\d+)_(?P<work_id>\d*).csv$", views.export_surveys, name="survey_answers"),
|
url(r"^rightsholders/surveys/answers_(?P<qid>\d+)_(?P<work_id>\d*).csv$", views.export_surveys, name="survey_answers"),
|
||||||
|
url(r"^rightsholders/surveys/summary_(?P<qid>\d+)_(?P<work_id>\d*).csv$", views.surveys_summary, name="survey_summary"),
|
||||||
url(r"^rh_admin/$", views.rh_admin, name="rh_admin"),
|
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/accepted/$", views.rh_admin, {'facet': 'accepted'}, name="accepted"),
|
||||||
url(r"^rh_admin/claims/$", views.rh_admin, {'facet': 'claims'}, name="claims"),
|
url(r"^rh_admin/claims/$", views.rh_admin, {'facet': 'claims'}, name="claims"),
|
||||||
|
|
|
@ -135,7 +135,7 @@ from regluit.libraryauth.views import Authenticator, superlogin, login_user
|
||||||
from regluit.libraryauth.models import Library
|
from regluit.libraryauth.models import Library
|
||||||
from regluit.marc.views import qs_marc_records
|
from regluit.marc.views import qs_marc_records
|
||||||
from regluit.questionnaire.models import Landing, Questionnaire
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -1842,7 +1842,7 @@ def works_user_can_admin(user):
|
||||||
Q(claim__user = user) | Q(claim__rights_holder__owner = 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):
|
def work_survey_filter(answers):
|
||||||
works = works_user_can_admin(request.user)
|
works = works_user_can_admin(request.user)
|
||||||
if work_id == '0' and request.user.is_staff:
|
if work_id == '0' and request.user.is_staff:
|
||||||
|
@ -1855,30 +1855,42 @@ def export_surveys(request, qid, work_id):
|
||||||
return answers.none()
|
return answers.none()
|
||||||
else:
|
else:
|
||||||
return answers.filter(run__run_info_histories__landing__works__in=works)
|
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):
|
def extra_entries(subject, run):
|
||||||
landing = None
|
landing = completed = None
|
||||||
try:
|
try:
|
||||||
landing = run.run_info_histories.all()[0].landing
|
landing = run.run_info_histories.all()[0].landing
|
||||||
|
completed = run.run_info_histories.all()[0].completed
|
||||||
except IndexError:
|
except IndexError:
|
||||||
try:
|
try:
|
||||||
landing = run.run_infos.all()[0].landing
|
landing = run.run_infos.all()[0].landing
|
||||||
|
completed = run.run_infos.all()[0].created
|
||||||
except IndexError:
|
except IndexError:
|
||||||
label = wid = "error"
|
label = wid = "error"
|
||||||
if landing:
|
if landing:
|
||||||
label = landing.label
|
label = landing.label
|
||||||
wid = landing.object_id
|
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() :
|
if not request.user.is_authenticated() :
|
||||||
return HttpResponseRedirect(reverse('surveys'))
|
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,
|
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_entries=extra_entries,
|
||||||
extra_headings=extra_headings,
|
extra_headings=extra_headings,
|
||||||
filecode=work_id)
|
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):
|
def new_survey(request, work_id):
|
||||||
if not request.user.is_authenticated() :
|
if not request.user.is_authenticated() :
|
||||||
return HttpResponseRedirect(reverse('surveys'))
|
return HttpResponseRedirect(reverse('surveys'))
|
||||||
|
|
|
@ -53,8 +53,11 @@ class QuestionnaireAdmin(admin.ModelAdmin):
|
||||||
readonly_fields = ('export',)
|
readonly_fields = ('export',)
|
||||||
|
|
||||||
def export(self, obj):
|
def export(self, obj):
|
||||||
csv_url= reverse("export_csv", args=[obj.id,])
|
csv_url = reverse("export_csv", args=[obj.id,])
|
||||||
return '<a href="%s">%s</a>' % (csv_url, _("Download data"))
|
summary_url = reverse("export_summary", args=[obj.id,])
|
||||||
|
return '<a href="{}">{}</a> <a href="{}">{}</a>'.format(
|
||||||
|
csv_url, _("Download data"), summary_url, _("Show summary")
|
||||||
|
)
|
||||||
|
|
||||||
export.allow_tags = True
|
export.allow_tags = True
|
||||||
export.short_description = _('Export to CSV')
|
export.short_description = _('Export to CSV')
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends "base-questionnaire.html" %}
|
||||||
|
{% block questionnaire %}
|
||||||
|
<h1>
|
||||||
|
Survey Results Summary
|
||||||
|
</h1>
|
||||||
|
{% for summary in summaries %}
|
||||||
|
|
||||||
|
<h2>Question</h2>
|
||||||
|
<p>
|
||||||
|
{{summary.1|safe}}
|
||||||
|
</p>
|
||||||
|
{% if summary.2 %}
|
||||||
|
<h3>Choices</h3>
|
||||||
|
<ul>
|
||||||
|
{% for option in summary.2 %}
|
||||||
|
<li>
|
||||||
|
{{option.1}}: <b>{{option.2}}</b>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<h3>Free Text Answers</h3>
|
||||||
|
<ul>
|
||||||
|
{% for answer in summary.3 %}
|
||||||
|
{% if answer %}
|
||||||
|
<li>
|
||||||
|
{{answer}}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -9,6 +9,8 @@ urlpatterns = [
|
||||||
questionnaire, name='questionnaire_noargs'),
|
questionnaire, name='questionnaire_noargs'),
|
||||||
url(r'^csv/(?P<qid>\d+)/$',
|
url(r'^csv/(?P<qid>\d+)/$',
|
||||||
export_csv, name='export_csv'),
|
export_csv, name='export_csv'),
|
||||||
|
url(r'^summary/(?P<qid>\d+)/$',
|
||||||
|
export_summary, name='export_summary'),
|
||||||
url(r'^(?P<runcode>[^/]+)/progress/$',
|
url(r'^(?P<runcode>[^/]+)/progress/$',
|
||||||
get_async_progress, name='progress'),
|
get_async_progress, name='progress'),
|
||||||
url(r'^take/(?P<questionnaire_id>[0-9]+)/$', generate_run),
|
url(r'^take/(?P<questionnaire_id>[0-9]+)/$', generate_run),
|
||||||
|
@ -19,8 +21,6 @@ urlpatterns = [
|
||||||
url(r'^landing/(?P<nonce>\w+)/$', SurveyView.as_view(), name="landing"),
|
url(r'^landing/(?P<nonce>\w+)/$', SurveyView.as_view(), name="landing"),
|
||||||
url(r'^(?P<runcode>[^/]+)/(?P<qs>[-]{0,1}\d+)/$',
|
url(r'^(?P<runcode>[^/]+)/(?P<qs>[-]{0,1}\d+)/$',
|
||||||
questionnaire, name='questionset'),
|
questionnaire, name='questionset'),
|
||||||
url(r'^q/manage/csv/(\d+)/',
|
|
||||||
export_csv, name="export_csv"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if not use_session:
|
if not use_session:
|
||||||
|
|
|
@ -41,7 +41,7 @@ def numal_sort(a, b):
|
||||||
bnum, bstr = split_numal(b)
|
bnum, bstr = split_numal(b)
|
||||||
cmpnum = cmp(anum, bnum)
|
cmpnum = cmp(anum, bnum)
|
||||||
if(cmpnum == 0):
|
if(cmpnum == 0):
|
||||||
return cmp(astr, bstr)
|
return cmp(astr.lower(), bstr.lower())
|
||||||
return cmpnum
|
return cmpnum
|
||||||
|
|
||||||
def numal0_sort(a, b):
|
def numal0_sort(a, b):
|
||||||
|
|
|
@ -812,22 +812,25 @@ def _table_headers(questions):
|
||||||
This will create separate columns for each multiple-choice possiblity
|
This will create separate columns for each multiple-choice possiblity
|
||||||
and freeform options, to avoid mixing data types and make charting easier.
|
and freeform options, to avoid mixing data types and make charting easier.
|
||||||
"""
|
"""
|
||||||
ql = list(questions)
|
ql = list(questions.order_by(
|
||||||
ql.sort(lambda x, y: numal_sort(x.number, y.number))
|
'questionset__sortid', 'number')
|
||||||
|
)
|
||||||
|
#ql.sort(lambda x, y: numal_sort(x.number, y.number))
|
||||||
columns = []
|
columns = []
|
||||||
for q in ql:
|
for q in ql:
|
||||||
|
qnum = '{}.{}'.format(q.questionset.sortid, q.number)
|
||||||
if q.type.startswith('choice-yesnocomment'):
|
if q.type.startswith('choice-yesnocomment'):
|
||||||
columns.extend([q.number, q.number + "-freeform"])
|
columns.extend([qnum, qnum + "-freeform"])
|
||||||
elif q.type.startswith('choice-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'):
|
elif q.type.startswith('choice-multiple'):
|
||||||
cl = [c.value for c in q.choice_set.all()]
|
cl = [c.value for c in q.choice_set.all()]
|
||||||
cl.sort(numal_sort)
|
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':
|
if q.type == 'choice-multiple-freeform':
|
||||||
columns.append(q.number + '-freeform')
|
columns.append(qnum + '-freeform')
|
||||||
else:
|
else:
|
||||||
columns.append(q.number)
|
columns.append(qnum)
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
default_extra_headings = [u'subject', u'run id']
|
default_extra_headings = [u'subject', u'run id']
|
||||||
|
@ -934,18 +937,19 @@ def answer_export(questionnaire, answers=None, answer_filter=None):
|
||||||
ans = str(ans)
|
ans = str(ans)
|
||||||
for choice in ans:
|
for choice in ans:
|
||||||
col = None
|
col = None
|
||||||
|
qnum = '{}.{}'.format(answer.question.questionset.sortid, answer.question.number)
|
||||||
if type(choice) == list:
|
if type(choice) == list:
|
||||||
# freeform choice
|
# freeform choice
|
||||||
choice = choice[0]
|
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)
|
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 col is None: # single-choice items
|
||||||
if ((not qchoicedict[answer.question.id]) or
|
if ((not qchoicedict[answer.question.id]) or
|
||||||
choice in qchoicedict[answer.question.id]):
|
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
|
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:
|
if col is not None:
|
||||||
row[col] = choice
|
row[col] = choice
|
||||||
# and don't forget about the last one
|
# 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))
|
out.append((subject, run, row))
|
||||||
return headings, out
|
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
|
questionnaire -- questionnaire model for summary
|
||||||
answers -- query set of answers to include in summary, defaults to all
|
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:
|
if answers is None:
|
||||||
answers = Answer.objects.all()
|
answers = Answer.objects.all()
|
||||||
|
if answer_filter:
|
||||||
|
answers = answer_filter(answers)
|
||||||
answers = answers.filter(question__questionset__questionnaire=questionnaire)
|
answers = answers.filter(question__questionset__questionnaire=questionnaire)
|
||||||
questions = Question.objects.filter(
|
questions = Question.objects.filter(
|
||||||
questionset__questionnaire=questionnaire).order_by(
|
questionset__questionnaire=questionnaire).order_by(
|
||||||
|
|
Loading…
Reference in New Issue