Merge pull request #22 from juliomenendez/clean_urls_with_session
Use sessions instead of putting the run key and page in the URLs.EmailTemplateFixes
commit
7ef7ad3eff
|
@ -68,7 +68,7 @@ STATIC_URL = '/static/'
|
|||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
os.path.abspath('./static'),
|
||||
os.path.abspath('../questionnaire/static/')
|
||||
os.path.abspath('../questionnaire/static/')
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
|
@ -129,15 +129,15 @@ LANGUAGES = (
|
|||
# the possible options are 'default', 'async' and 'none'
|
||||
#
|
||||
# 'default'
|
||||
# The progressbar will be rendered in each questionset together with the
|
||||
# questions. This is a good choice for smaller questionnaires as the
|
||||
# The progressbar will be rendered in each questionset together with the
|
||||
# questions. This is a good choice for smaller questionnaires as the
|
||||
# progressbar will always be up to date.
|
||||
#
|
||||
# 'async'
|
||||
# The progressbar value is updated using ajax once the questions have been
|
||||
# rendered. This approach is the right choice for bigger questionnaires which
|
||||
# result in a long time spent on updating the progressbar with each request.
|
||||
# (The progress calculation is by far the most time consuming method in
|
||||
# (The progress calculation is by far the most time consuming method in
|
||||
# bigger questionnaires as all questionsets and questions need to be
|
||||
# parsed to decide if they play a role in the current run or not)
|
||||
#
|
||||
|
@ -146,5 +146,12 @@ LANGUAGES = (
|
|||
# questionnaire is so huge that even the ajax request takes too long.
|
||||
QUESTIONNAIRE_PROGRESS = 'async'
|
||||
|
||||
# Defines how the questionnaire and questionset id are passed around.
|
||||
# If False, the default value, the ids are part of the URLs and visible to the
|
||||
# user answering the questions.
|
||||
# If True the ids are set in the session and the URL remains unchanged as the
|
||||
# user goes through the steps of the question set.
|
||||
QUESTIONNAIRE_USE_SESSION = False
|
||||
|
||||
try: from local_settings import *
|
||||
except: pass
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{% for x in jsinclude %}
|
||||
<script type="text/javascript" src="{{ x }}"></script>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for x in cssinclude %}
|
||||
<link rel="stylesheet" href="{{ x }}" type="text/css" />
|
||||
{% endfor %}
|
||||
|
@ -44,15 +44,15 @@
|
|||
</div>
|
||||
|
||||
<form name="qform" id="qform" action="{{ request.path }}" method="POST">
|
||||
|
||||
|
||||
{% csrf_token %}
|
||||
|
||||
<input type="hidden" name="questionset_id" value="{{ questionset.id }}">
|
||||
|
||||
|
||||
{% for question, qdict in qlist %}
|
||||
{% with errors|dictget:question.number as error %}
|
||||
|
||||
<div class="question type_{{ qdict.qtype }} {% if error %} error prepend-top{% endif %}{{ qdict.qnum_class }}{{ qdict.qalpha_class }}" id="qc_{{ question.number }}" {{qdict.checkstring|safe}} style="{{qdict.css_style}}">
|
||||
<div class="question type_{{ qdict.qtype }} {% if error %} error prepend-top{% endif %}{{ qdict.qnum_class }}{{ qdict.qalpha_class }}" id="qc_{{ question.number }}" {{qdict.checkstring|safe}} style="{{qdict.css_style}}">
|
||||
{% if request.user.is_staff %}
|
||||
<span class="pull-right">
|
||||
<a href="/admin/questionnaire/question/{{ question.id }}/">
|
||||
|
@ -60,7 +60,7 @@
|
|||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if qdict.custom %}
|
||||
{% if error %}
|
||||
<div class="error">
|
||||
|
@ -70,7 +70,7 @@
|
|||
{% include qdict.template %}
|
||||
{% else %}
|
||||
<div class="question-text {% if qdict.required %}required{% endif %}">
|
||||
<span class="qnumber">{{ question.display_number }}.</span>
|
||||
<span class="qnumber">{{ question.display_number }}.</span>
|
||||
{{ question.text }}
|
||||
</div>
|
||||
<div class="answer">
|
||||
|
@ -89,16 +89,16 @@
|
|||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div style="text-align: center;" class="well questionset-submit">
|
||||
<input class="btn large primary" name="submit" type="submit" value="{% trans "Continue" %}">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% if questionset.prev %}
|
||||
<a class="back-link pull-left" href="javascript:history.back();">{% trans "return to previous page" %}</a>
|
||||
<a class="back-link pull-left" href="{{ prev_url }}">{% trans "return to previous page" %}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
|
@ -106,11 +106,11 @@
|
|||
{% for trigger in triggers %}
|
||||
addtrigger("{{trigger}}");
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for k,v in qvalues.items %}
|
||||
qvalues['{{ k|escapejs }}'] = '{{ v|escapejs }}';
|
||||
{% endfor %}
|
||||
|
||||
|
||||
for(key in qvalues) {
|
||||
valchanged(key, qvalues[key]);
|
||||
}
|
||||
|
|
|
@ -3,15 +3,31 @@
|
|||
from django.conf.urls import *
|
||||
from views import *
|
||||
|
||||
urlpatterns = patterns('',
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$',
|
||||
questionnaire, name='questionnaire_noargs'),
|
||||
questionnaire, name='questionnaire_noargs'),
|
||||
url(r'^csv/(?P<qid>\d+)/$',
|
||||
export_csv, name='export_csv'),
|
||||
url(r'^(?P<runcode>[^/]+)/progress/$',
|
||||
get_async_progress, name='progress'),
|
||||
url(r'^(?P<runcode>[^/]+)/(?P<qs>[-]{0,1}\d+)/$',
|
||||
questionnaire, name='questionset'),
|
||||
url(r'^(?P<runcode>[^/]+)/$',
|
||||
questionnaire, name='questionnaire'),
|
||||
export_csv, name='export_csv'),
|
||||
url(r'^(?P<runcode>[^/]+)/progress/$',
|
||||
get_async_progress, name='progress'),
|
||||
)
|
||||
|
||||
if not use_session:
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
url(r'^(?P<runcode>[^/]+)/(?P<qs>[-]{0,1}\d+)/$',
|
||||
questionnaire, name='questionset'),
|
||||
url(r'^(?P<runcode>[^/]+)/$',
|
||||
questionnaire, name='questionnaire'),
|
||||
)
|
||||
else:
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
url(r'^$',
|
||||
questionnaire, name='questionnaire'),
|
||||
url(r'^prev/$',
|
||||
redirect_to_prev_questionnaire,
|
||||
name='redirect_to_prev_questionnaire')
|
||||
)
|
||||
|
|
|
@ -27,6 +27,13 @@ import random
|
|||
from hashlib import md5
|
||||
import re
|
||||
|
||||
|
||||
try:
|
||||
use_session = settings.QUESTIONNAIRE_USE_SESSION
|
||||
except AttributeError:
|
||||
use_session = False
|
||||
|
||||
|
||||
def r2r(tpl, request, **contextdict):
|
||||
"Shortcut to use RequestContext instead of Context in templates"
|
||||
contextdict['request'] = request
|
||||
|
@ -51,7 +58,7 @@ def delete_answer(question, subject, runid):
|
|||
def add_answer(runinfo, question, answer_dict):
|
||||
"""
|
||||
Add an Answer to a Question for RunInfo, given the relevant form input
|
||||
|
||||
|
||||
answer_dict contains the POST'd elements for this question, minus the
|
||||
question_{number} prefix. The question_{number} form value is accessible
|
||||
with the ANSWER key.
|
||||
|
@ -76,7 +83,7 @@ def add_answer(runinfo, question, answer_dict):
|
|||
|
||||
# then save the new answer to the database
|
||||
answer.save(runinfo)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def check_parser(runinfo, exclude=[]):
|
||||
|
@ -101,7 +108,7 @@ def check_parser(runinfo, exclude=[]):
|
|||
checks = parse_checks(checks)
|
||||
|
||||
for check, value in checks.items():
|
||||
if check in fnmap:
|
||||
if check in fnmap:
|
||||
value = value and value.strip()
|
||||
if not fnmap[check](value):
|
||||
return False
|
||||
|
@ -130,9 +137,9 @@ def questionset_satisfies_checks(questionset, runinfo, checks=None):
|
|||
"""Return True if the runinfo passes the checks specified in the QuestionSet
|
||||
|
||||
Checks is an optional dictionary with the keys being questionset.pk and the
|
||||
values being the checks of the contained questions.
|
||||
|
||||
This, in conjunction with fetch_checks allows for fewer
|
||||
values being the checks of the contained questions.
|
||||
|
||||
This, in conjunction with fetch_checks allows for fewer
|
||||
db roundtrips and greater performance.
|
||||
|
||||
Sadly, checks cannot be hashed and therefore the request cache is useless
|
||||
|
@ -169,7 +176,7 @@ def questionset_satisfies_checks(questionset, runinfo, checks=None):
|
|||
def get_progress(runinfo):
|
||||
|
||||
position, total = 0, 0
|
||||
|
||||
|
||||
current = runinfo.questionset
|
||||
sets = current.questionnaire.questionsets()
|
||||
|
||||
|
@ -190,27 +197,34 @@ def get_progress(runinfo):
|
|||
progress = 1
|
||||
else:
|
||||
progress = float(position) / float(total) * 100.00
|
||||
|
||||
|
||||
# progress is always at least one percent
|
||||
progress = progress >= 1.0 and progress or 1
|
||||
|
||||
return int(progress)
|
||||
|
||||
def get_async_progress(request, runcode, *args, **kwargs):
|
||||
def get_async_progress(request, *args, **kwargs):
|
||||
""" Returns the progress as json for use with ajax """
|
||||
|
||||
if 'runcode' in kwargs:
|
||||
runcode = kwargs['runcode']
|
||||
else:
|
||||
session_runcode = request.session.get('runcode', None)
|
||||
if session_runcode is not None:
|
||||
runcode = session_runcode
|
||||
|
||||
runinfo = get_runinfo(runcode)
|
||||
response = dict(progress=get_progress(runinfo))
|
||||
|
||||
cache.set('progress' + runinfo.random, response['progress'])
|
||||
response = HttpResponse(json.dumps(response),
|
||||
response = HttpResponse(json.dumps(response),
|
||||
mimetype='application/javascript');
|
||||
response["Cache-Control"] = "no-cache"
|
||||
return response
|
||||
|
||||
def fetch_checks(questionsets):
|
||||
ids = [qs.pk for qs in questionsets]
|
||||
|
||||
|
||||
query = Question.objects.filter(questionset__pk__in=ids)
|
||||
query = query.values('questionset_id', 'checks', 'number')
|
||||
|
||||
|
@ -225,7 +239,8 @@ def fetch_checks(questionsets):
|
|||
|
||||
return checks
|
||||
|
||||
def redirect_to_qs(runinfo):
|
||||
|
||||
def redirect_to_qs(runinfo, request=None):
|
||||
"Redirect to the correct and current questionset URL for this RunInfo"
|
||||
|
||||
# cache current questionset
|
||||
|
@ -233,12 +248,12 @@ def redirect_to_qs(runinfo):
|
|||
|
||||
# skip questionsets that don't pass
|
||||
if not questionset_satisfies_checks(runinfo.questionset, runinfo):
|
||||
|
||||
|
||||
next = runinfo.questionset.next()
|
||||
|
||||
while next and not questionset_satisfies_checks(next, runinfo):
|
||||
next = next.next()
|
||||
|
||||
|
||||
runinfo.questionset = next
|
||||
runinfo.save()
|
||||
|
||||
|
@ -251,10 +266,36 @@ def redirect_to_qs(runinfo):
|
|||
logging.warn('no questionset in questionnaire which passes the check')
|
||||
return finish_questionnaire(runinfo, qs.questionnaire)
|
||||
|
||||
url = reverse("questionset",
|
||||
args=[ runinfo.random, runinfo.questionset.sortid ])
|
||||
if not use_session:
|
||||
args = [runinfo.random, runinfo.questionset.sortid]
|
||||
urlname = 'questionset'
|
||||
else:
|
||||
args = []
|
||||
request.session['qs'] = runinfo.questionset.sortid
|
||||
request.session['runcode'] = runinfo.random
|
||||
urlname = 'questionnaire'
|
||||
url = reverse(urlname, args=args)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
def redirect_to_prev_questionnaire(request):
|
||||
"""
|
||||
Used only when ```QUESTIONNAIRE_USE_SESSION``` is True.
|
||||
Takes the questionnaire set in the session and redirects to the
|
||||
previous questionnaire if any.
|
||||
"""
|
||||
runcode = request.session.get('runcode', None)
|
||||
if runcode is not None:
|
||||
runinfo = get_runinfo(runcode)
|
||||
prev_qs = runinfo.questionset.prev()
|
||||
if runinfo and prev_qs:
|
||||
request.session['runcode'] = runinfo.random
|
||||
request.session['qs'] = prev_qs.sortid
|
||||
return HttpResponseRedirect(reverse('questionnaire'))
|
||||
|
||||
return HttpResponseRedirect('/')
|
||||
|
||||
|
||||
@transaction.commit_on_success
|
||||
def questionnaire(request, runcode=None, qs=None):
|
||||
"""
|
||||
|
@ -269,6 +310,14 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
We only commit on success, to maintain consistency. We also specifically
|
||||
rollback if there were errors processing the answers for this questionset.
|
||||
"""
|
||||
if use_session:
|
||||
session_runcode = request.session.get('runcode', None)
|
||||
if session_runcode is not None:
|
||||
runcode = session_runcode
|
||||
|
||||
session_qs = request.session.get('qs', None)
|
||||
if session_qs is not None:
|
||||
qs = session_qs
|
||||
|
||||
# if runcode provided as query string, redirect to the proper page
|
||||
if not runcode:
|
||||
|
@ -276,7 +325,12 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
if not runcode:
|
||||
return HttpResponseRedirect("/")
|
||||
else:
|
||||
return HttpResponseRedirect(reverse("questionnaire",args=[runcode]))
|
||||
if not use_session:
|
||||
args = [runcode, ]
|
||||
else:
|
||||
request.session['runcode'] = runcode
|
||||
args = []
|
||||
return HttpResponseRedirect(reverse("questionnaire", args=args))
|
||||
|
||||
runinfo = get_runinfo(runcode)
|
||||
|
||||
|
@ -300,7 +354,7 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
return set_language(request, runinfo, request.path)
|
||||
|
||||
# --------------------------------
|
||||
# --- Handle non-POST requests ---
|
||||
# --- Handle non-POST requests ---
|
||||
# --------------------------------
|
||||
|
||||
if request.method != "POST":
|
||||
|
@ -310,13 +364,13 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
pass # ok for testing
|
||||
elif qs.sortid > runinfo.questionset.sortid:
|
||||
# you may jump back, but not forwards
|
||||
return redirect_to_qs(runinfo)
|
||||
return redirect_to_qs(runinfo, request)
|
||||
runinfo.questionset = qs
|
||||
runinfo.save()
|
||||
transaction.commit()
|
||||
# no questionset id in URL, so redirect to the correct URL
|
||||
if qs is None:
|
||||
return redirect_to_qs(runinfo)
|
||||
return redirect_to_qs(runinfo, request)
|
||||
return show_questionnaire(request, runinfo)
|
||||
|
||||
# -------------------------------------
|
||||
|
@ -325,7 +379,7 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
|
||||
# if the submitted page is different to what runinfo says, update runinfo
|
||||
# XXX - do we really want this?
|
||||
qs = request.POST.get('questionset_id', None)
|
||||
qs = request.POST.get('questionset_id', qs)
|
||||
try:
|
||||
qsobj = QuestionSet.objects.filter(pk=qs)[0]
|
||||
if qsobj.questionnaire == runinfo.questionset.questionnaire:
|
||||
|
@ -410,12 +464,14 @@ def questionnaire(request, runcode=None, qs=None):
|
|||
next = next.next()
|
||||
runinfo.questionset = next
|
||||
runinfo.save()
|
||||
if use_session:
|
||||
request.session['prev_runcode'] = runinfo.random
|
||||
|
||||
if next is None: # we are finished
|
||||
return finish_questionnaire(runinfo, questionnaire)
|
||||
|
||||
transaction.commit()
|
||||
return redirect_to_qs(runinfo)
|
||||
return redirect_to_qs(runinfo, request)
|
||||
|
||||
def finish_questionnaire(runinfo, questionnaire):
|
||||
hist = RunInfoHistory()
|
||||
|
@ -462,7 +518,7 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
|
||||
show_all = request.GET.get('show_all') == '1' # for debugging purposes in some cases we may want to show all questions on one screen.
|
||||
questionset = runinfo.questionset
|
||||
questions = questionset.questionnaire.questions() if show_all else questionset.questions()
|
||||
questions = questionset.questionnaire.questions() if show_all else questionset.questions()
|
||||
|
||||
qlist = []
|
||||
jsinclude = [] # js files to include
|
||||
|
@ -470,7 +526,7 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
jstriggers = []
|
||||
qvalues = {}
|
||||
|
||||
# initialize qvalues
|
||||
# initialize qvalues
|
||||
cookiedict = runinfo.get_cookiedict()
|
||||
|
||||
for k,v in cookiedict.items():
|
||||
|
@ -496,7 +552,7 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
'qalpha_class' : _qalpha and (ord(_qalpha[-1]) % 2 \
|
||||
and ' alodd' or ' aleven') or '',
|
||||
}
|
||||
|
||||
|
||||
# substitute answer texts
|
||||
substitute_answer(qvalues, question)
|
||||
|
||||
|
@ -522,9 +578,9 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
jstriggers.extend(qdict['jstriggers'])
|
||||
if 'qvalue' in qdict and not question.number in cookiedict:
|
||||
qvalues[question.number] = qdict['qvalue']
|
||||
|
||||
|
||||
qlist.append( (question, qdict) )
|
||||
|
||||
|
||||
try:
|
||||
has_progress = settings.QUESTIONNAIRE_PROGRESS in ('async', 'default')
|
||||
async_progress = settings.QUESTIONNAIRE_PROGRESS == 'async'
|
||||
|
@ -551,6 +607,10 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
else:
|
||||
qvalues[s[1]] = v
|
||||
|
||||
if use_session:
|
||||
prev_url = reverse('redirect_to_prev_questionnaire')
|
||||
else:
|
||||
prev_url = 'javascript:history.back();'
|
||||
r = r2r("questionnaire/questionset.html", request,
|
||||
questionset=runinfo.questionset,
|
||||
runinfo=runinfo,
|
||||
|
@ -562,7 +622,8 @@ def show_questionnaire(request, runinfo, errors={}):
|
|||
jsinclude=jsinclude,
|
||||
cssinclude=cssinclude,
|
||||
async_progress=async_progress,
|
||||
async_url=reverse('progress', args=[runinfo.random])
|
||||
async_url=reverse('progress', args=[runinfo.random]),
|
||||
prev_url=prev_url,
|
||||
)
|
||||
r['Cache-Control'] = 'no-cache'
|
||||
r['Expires'] = "Thu, 24 Jan 1980 00:00:00 GMT"
|
||||
|
@ -578,7 +639,7 @@ def substitute_answer(qvalues, obj):
|
|||
Only answers with 'store' in their check will work with this.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
if qvalues and obj.text:
|
||||
magic = 'subst_with_ans_'
|
||||
regex =r'subst_with_ans_(\S+)'
|
||||
|
@ -587,14 +648,14 @@ def substitute_answer(qvalues, obj):
|
|||
text_attributes = [a for a in dir(obj) if a.startswith('text_')]
|
||||
|
||||
for answerid in replacements:
|
||||
|
||||
|
||||
target = magic + answerid
|
||||
replacement = qvalues.get(answerid.lower(), '')
|
||||
|
||||
for attr in text_attributes:
|
||||
oldtext = getattr(obj, attr)
|
||||
newtext = oldtext.replace(target, replacement)
|
||||
|
||||
|
||||
setattr(obj, attr, newtext)
|
||||
|
||||
|
||||
|
@ -715,14 +776,14 @@ def answer_export(questionnaire, answers=None):
|
|||
questionnaire -- questionnaire model for export
|
||||
answers -- query set of answers to include in export, defaults to all
|
||||
|
||||
Return a flat dump of column headings and all the answers for a
|
||||
questionnaire (in query set answers) in the form (headings, answers)
|
||||
Return a flat dump of column headings and all the answers for a
|
||||
questionnaire (in query set answers) in the form (headings, answers)
|
||||
where headings is:
|
||||
['question1 number', ...]
|
||||
and answers is:
|
||||
[(subject1, 'runid1', ['answer1.1', ...]), ... ]
|
||||
|
||||
The headings list might include items with labels like
|
||||
The headings list might include items with labels like
|
||||
'questionnumber-freeform'. Those columns will contain all the freeform
|
||||
answers for that question (separated from the other answer data).
|
||||
|
||||
|
@ -731,7 +792,7 @@ def answer_export(questionnaire, answers=None):
|
|||
|
||||
The items in the answers list are unicode strings or empty strings
|
||||
if no answer was given. The number of elements in each answer list will
|
||||
always match the number of headings.
|
||||
always match the number of headings.
|
||||
"""
|
||||
if answers is None:
|
||||
answers = Answer.objects.all()
|
||||
|
@ -756,14 +817,14 @@ def answer_export(questionnaire, answers=None):
|
|||
row = []
|
||||
for answer in answers:
|
||||
if answer.runid != runid or answer.subject != subject:
|
||||
if row:
|
||||
if row:
|
||||
out.append((subject, runid, row))
|
||||
runid = answer.runid
|
||||
subject = answer.subject
|
||||
row = [""] * len(headings)
|
||||
ans = answer.split_answer()
|
||||
if type(ans) == int:
|
||||
ans = str(ans)
|
||||
ans = str(ans)
|
||||
for choice in ans:
|
||||
col = None
|
||||
if type(choice) == list:
|
||||
|
@ -781,7 +842,7 @@ def answer_export(questionnaire, answers=None):
|
|||
if col is not None:
|
||||
row[col] = choice
|
||||
# and don't forget about the last one
|
||||
if row:
|
||||
if row:
|
||||
out.append((subject, runid, row))
|
||||
return headings, out
|
||||
|
||||
|
@ -791,13 +852,13 @@ def answer_summary(questionnaire, answers=None):
|
|||
answers -- query set of answers to include in summary, defaults to all
|
||||
|
||||
Return a summary of the answer totals in answer_qs in the form:
|
||||
[('q1', 'question1 text',
|
||||
[('choice1', 'choice1 text', num), ...],
|
||||
[('q1', 'question1 text',
|
||||
[('choice1', 'choice1 text', num), ...],
|
||||
['freeform1', ...]), ...]
|
||||
|
||||
questions are returned in questionnaire order
|
||||
choices are returned in question order
|
||||
freeform options are case-insensitive sorted
|
||||
freeform options are case-insensitive sorted
|
||||
"""
|
||||
|
||||
if answers is None:
|
||||
|
@ -834,7 +895,7 @@ def answer_summary(questionnaire, answers=None):
|
|||
summary.append((question.number, question.text, [
|
||||
(n, t, choice_totals[n]) for (n, t) in choices], freeforms))
|
||||
return summary
|
||||
|
||||
|
||||
def has_tag(tag, runinfo):
|
||||
""" Returns true if the given runinfo contains the given tag. """
|
||||
return tag in (t.strip() for t in runinfo.tags.split(','))
|
||||
|
@ -859,7 +920,7 @@ def dep_check(expr, runinfo, answerdict):
|
|||
When looking up the answer, it first checks if it's in the answerdict,
|
||||
then it checks runinfo's cookies, then it does a database lookup to find
|
||||
the answer.
|
||||
|
||||
|
||||
The use of the comma separator is purely historical.
|
||||
"""
|
||||
|
||||
|
@ -908,7 +969,7 @@ def dep_check(expr, runinfo, answerdict):
|
|||
if not actual_answer:
|
||||
if check_question.getcheckdict():
|
||||
actual_answer = check_question.getcheckdict().get('default')
|
||||
|
||||
|
||||
if actual_answer is None:
|
||||
actual_answer = u''
|
||||
if check_answer[0:1] in "<>":
|
||||
|
@ -969,6 +1030,9 @@ def generate_run(request, questionnaire_id):
|
|||
|
||||
run = RunInfo(subject=su, random=key, runid=key, questionset=qs)
|
||||
run.save()
|
||||
|
||||
return HttpResponseRedirect(reverse('questionnaire', kwargs={'runcode': key}))
|
||||
|
||||
if not use_session:
|
||||
kwargs = {'runcode': key}
|
||||
else:
|
||||
kwargs = {}
|
||||
request.session['runcode'] = key
|
||||
return HttpResponseRedirect(reverse('questionnaire', kwargs=kwargs))
|
||||
|
|
Loading…
Reference in New Issue