normalize runid

duplicating runid in three tables was bad practice and made filtering
very difficult
pull/1/head
eric 2016-09-30 14:04:41 -04:00
parent fada8e0222
commit 2761d28fed
13 changed files with 194 additions and 63 deletions

View File

@ -61,7 +61,7 @@ class QuestionnaireAdmin(admin.ModelAdmin):
class RunInfoAdmin(admin.ModelAdmin): class RunInfoAdmin(admin.ModelAdmin):
list_display = ['random', 'runid', 'subject', 'created', 'emailsent', 'lastemailerror'] list_display = ['random', 'run', 'subject', 'created', 'emailsent', 'lastemailerror']
pass pass
@ -70,10 +70,10 @@ class RunInfoHistoryAdmin(admin.ModelAdmin):
class AnswerAdmin(admin.ModelAdmin): class AnswerAdmin(admin.ModelAdmin):
search_fields = ['subject__email', 'runid', 'question__number', 'answer'] search_fields = ['subject__email', 'run__id', 'question__number', 'answer']
list_display = ['id', 'runid', 'subject', 'question'] list_display = ['id', 'run', 'subject', 'question']
list_filter = ['subject', 'runid'] list_filter = ['subject', 'run__id']
ordering = [ 'id', 'subject', 'runid', 'question', ] ordering = [ 'id', 'subject', 'run__id', 'question', ]
from django.contrib import admin from django.contrib import admin

View File

@ -121,7 +121,7 @@ def dep_check(expr, runinfo, answerdict):
else: else:
# retrieve from database # retrieve from database
answer_object = Answer.objects.filter(question=check_question, answer_object = Answer.objects.filter(question=check_question,
runid=runinfo.runid, run=runinfo.run,
subject=runinfo.subject) subject=runinfo.subject)
if answer_object: if answer_object:
actual_answer = answer_object[0].split_answer() actual_answer = answer_object[0].split_answer()

View File

@ -49,16 +49,11 @@ def _new_runinfo(subject, questionset):
""" """
nextrun = subject.nextrun nextrun = subject.nextrun
runid = str(nextrun.year) runid = str(nextrun.year)
entries = list(RunInfo.objects.filter(runid=runid, subject=subject)) (run, created) = Run.objects.get_or_create(runid=runid)
if len(entries)>0: (r, created) = RunInfo.objects.get_or_create(run=run, subject=subject)
r = entries[0] if created:
else:
r = RunInfo()
r.random = _new_random(subject) r.random = _new_random(subject)
r.subject = subject
r.runid = runid
r.emailcount = 0 r.emailcount = 0
r.created = datetime.now()
r.questionset = questionset r.questionset = questionset
r.save() r.save()
if nextrun.month == 2 and nextrun.day == 29: # the only exception? if nextrun.month == 2 and nextrun.day == 29: # the only exception?
@ -79,7 +74,7 @@ def _send_email(runinfo):
c['gender'] = subject.gender c['gender'] = subject.gender
c['email'] = subject.email c['email'] = subject.email
c['random'] = runinfo.random c['random'] = runinfo.random
c['runid'] = runinfo.runid c['runid'] = runinfo.run.runid
c['created'] = runinfo.created c['created'] = runinfo.created
c['site'] = getattr(settings, 'QUESTIONNAIRE_URL', '(settings.QUESTIONNAIRE_URL not set)') c['site'] = getattr(settings, 'QUESTIONNAIRE_URL', '(settings.QUESTIONNAIRE_URL not set)')
email = tmpl.render(c) email = tmpl.render(c)
@ -143,18 +138,18 @@ def send_emails(request=None, qname=None):
WEEKAGO = time.time() - (60 * 60 * 24 * 7) # one week ago WEEKAGO = time.time() - (60 * 60 * 24 * 7) # one week ago
outlog = [] outlog = []
for r in runinfos: for r in runinfos:
if r.runid.startswith('test:'): if r.run.runid.startswith('test:'):
continue continue
if r.emailcount == -1: if r.emailcount == -1:
continue continue
if r.emailcount == 0 or time.mktime(r.emailsent.timetuple()) < WEEKAGO: if r.emailcount == 0 or time.mktime(r.emailsent.timetuple()) < WEEKAGO:
try: try:
if _send_email(r): if _send_email(r):
outlog.append(u"[%s] %s, %s: OK" % (r.runid, r.subject.surname, r.subject.givenname)) outlog.append(u"[%s] %s, %s: OK" % (r.run.runid, r.subject.surname, r.subject.givenname))
else: else:
outlog.append(u"[%s] %s, %s: %s" % (r.runid, r.subject.surname, r.subject.givenname, r.lastemailerror)) outlog.append(u"[%s] %s, %s: %s" % (r.run.runid, r.subject.surname, r.subject.givenname, r.lastemailerror))
except Exception, e: except Exception, e:
outlog.append("Exception: [%s] %s: %s" % (r.runid, r.subject.surname, str(e))) outlog.append("Exception: [%s] %s: %s" % (r.run.runid, r.subject.surname, str(e)))
if request: if request:
return HttpResponse("Sent Questionnaire Emails:\n " return HttpResponse("Sent Questionnaire Emails:\n "
+"\n ".join(outlog), content_type="text/plain") +"\n ".join(outlog), content_type="text/plain")

View File

@ -60,6 +60,21 @@
text_de: shown with testtag, text_de: shown with testtag,
text_en: shown with testtag, text_en: shown with testtag,
} }
model: questionnaire.run
pk: 1
- fields: {
runid: 'test:test',
}
model: questionnaire.run
pk: 2
- fields: {
runid: 'test:withtags',
}
model: questionnaire.run
pk: 3
- fields: {
runid: 'test:withouttags',
}
model: questionnaire.questionset model: questionnaire.questionset
pk: 4 pk: 4
- fields: { - fields: {
@ -70,7 +85,7 @@
lastemailerror: null, lastemailerror: null,
questionset: 1, questionset: 1,
random: 'test:test', random: 'test:test',
runid: 'test:test', run: 1,
state: '', state: '',
subject: 1 subject: 1
} }
@ -84,7 +99,7 @@
lastemailerror: null, lastemailerror: null,
questionset: 3, questionset: 3,
random: 'test:withtags', random: 'test:withtags',
runid: 'test:withtags', run: 2,
state: '', state: '',
tags: 'testtag', tags: 'testtag',
subject: 1 subject: 1
@ -99,7 +114,7 @@
lastemailerror: null, lastemailerror: null,
questionset: 3, questionset: 3,
random: 'test:withouttags', random: 'test:withouttags',
runid: 'test:withouttags', run: 3,
state: '', state: '',
tags: '', tags: '',
subject: 1 subject: 1
@ -108,7 +123,7 @@
pk: 3 pk: 3
- fields: { - fields: {
completed: 2009-05-16, completed: 2009-05-16,
runid: 'test:test', run: 1,
subject: 1, subject: 1,
questionnaire: 1, questionnaire: 1,
} }

View File

@ -40,8 +40,8 @@ class TypeTest(TestCase):
'question_12_multiple_1' : 'q12_choice1',# choice-multiple-freeform 'question_12_multiple_1' : 'q12_choice1',# choice-multiple-freeform
'question_12_more_1' : 'blah', # choice-multiple-freeform 'question_12_more_1' : 'blah', # choice-multiple-freeform
} }
runinfo = self.runinfo = RunInfo.objects.get(runid='test:test') runinfo = self.runinfo = RunInfo.objects.get(run__runid='test:test')
self.runid = runinfo.runid self.runid = runinfo.run.runid
self.subject_id = runinfo.subject_id self.subject_id = runinfo.subject_id
@ -66,7 +66,7 @@ class TypeTest(TestCase):
response = self.client.get('/q/test:test/1/') response = self.client.get('/q/test:test/1/')
assert "Don't Know" in response.content assert "Don't Know" in response.content
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
runinfo = RunInfo.objects.get(runid='test:test') runinfo = RunInfo.objects.get(run__runid='test:test')
self.assertEqual(runinfo.subject.language, 'en') self.assertEqual(runinfo.subject.language, 'en')
response = self.client.get('/q/test:test/1/', {"lang" : "de"}) response = self.client.get('/q/test:test/1/', {"lang" : "de"})
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
@ -74,7 +74,7 @@ class TypeTest(TestCase):
response = self.client.get('/q/test:test/1/') response = self.client.get('/q/test:test/1/')
assert "Weiss nicht" in response.content assert "Weiss nicht" in response.content
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
runinfo = RunInfo.objects.get(runid='test:test') runinfo = RunInfo.objects.get(run__runid='test:test')
self.assertEqual(runinfo.subject.language, 'de') self.assertEqual(runinfo.subject.language, 'de')
@ -105,9 +105,10 @@ class TypeTest(TestCase):
"POST complete answers for QuestionSet 1" "POST complete answers for QuestionSet 1"
c = self.client c = self.client
ansdict1 = self.ansdict1 ansdict1 = self.ansdict1
runinfo = RunInfo.objects.get(runid='test:test') runinfo = RunInfo.objects.get(run__runid='test:test')
runid = runinfo.random = runinfo.runid = '1real' runid = runinfo.random = runinfo.run.runid = '1real'
runinfo.save() runinfo.save()
runinfo.run.save()
response = c.get('/q/1real/1/') response = c.get('/q/1real/1/')
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -126,7 +127,7 @@ class TypeTest(TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], 'http://testserver/') self.assertEqual(response['Location'], 'http://testserver/')
self.assertEqual(RunInfo.objects.filter(runid='1real').count(), 0) self.assertEqual(RunInfo.objects.filter(run__runid='1real').count(), 0)
# TODO: The format of these answers seems very strange to me. It was # TODO: The format of these answers seems very strange to me. It was
# simpler before I changed it to get the test to work. # simpler before I changed it to get the test to work.
@ -148,7 +149,7 @@ class TypeTest(TestCase):
'12' : u'["q12_choice1", ["blah"]]', '12' : u'["q12_choice1", ["blah"]]',
} }
for k, v in dbvalues.items(): for k, v in dbvalues.items():
ans = Answer.objects.get(runid=runid, subject__id=self.subject_id, ans = Answer.objects.get(run__runid=runid, subject__id=self.subject_id,
question__number=k) question__number=k)
v = v.replace('\r', '\\r').replace('\n', '\\n') v = v.replace('\r', '\\r').replace('\n', '\\n')

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('questionnaire', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Run',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('runid', models.CharField(max_length=32, null=True)),
],
),
migrations.AddField(
model_name='answer',
name='run',
field=models.ForeignKey(related_name='answers', to='questionnaire.Run', null=True),
),
migrations.AddField(
model_name='runinfo',
name='run',
field=models.ForeignKey(related_name='run_infos', to='questionnaire.Run', null=True),
),
migrations.AddField(
model_name='runinfohistory',
name='run',
field=models.ForeignKey(related_name='run_info_histories', to='questionnaire.Run', null=True),
),
]

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def models_to_migrate(apps):
return [
apps.get_model('questionnaire', 'RunInfo'),
apps.get_model('questionnaire', 'RunInfoHistory'),
apps.get_model('questionnaire', 'Answer'),
]
class Migration(migrations.Migration):
def move_runids(apps, schema_editor):
Run = apps.get_model('questionnaire', 'Run')
for model in models_to_migrate(apps):
for instance in model.objects.all():
(run, created) = Run.objects.get_or_create(runid=instance.runid)
instance.run = run
instance.save()
def unmove_runids(apps, schema_editor):
for model in models_to_migrate(apps):
for instance in model.objects.all():
instance.runid = instance.run.runid
instance.save()
dependencies = [
('questionnaire', '0002_auto_20160929_1320'),
]
operations = [
migrations.RunPython(move_runids, reverse_code=unmove_runids, hints={'questionnaire': 'Run'}),
]

View File

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('questionnaire', '0003_auto_20160929_1321'),
]
operations = [
migrations.RemoveField(
model_name='runinfo',
name='runid',
),
migrations.RemoveField(
model_name='runinfohistory',
name='runid',
),
migrations.AlterField(
model_name='answer',
name='run',
field=models.ForeignKey(related_name='answers', default=1, to='questionnaire.Run'),
preserve_default=False,
),
migrations.AlterField(
model_name='runinfo',
name='run',
field=models.ForeignKey(related_name='run_infos', default=1, to='questionnaire.Run'),
preserve_default=False,
),
migrations.AlterField(
model_name='runinfohistory',
name='run',
field=models.ForeignKey(related_name='run_info_histories', to='questionnaire.Run'),
),
migrations.AlterIndexTogether(
name='answer',
index_together=set([('subject', 'run'), ('subject', 'run', 'id')]),
),
migrations.RemoveField(
model_name='answer',
name='runid',
),
]

View File

@ -70,10 +70,10 @@ class Subject(models.Model):
return None return None
def history(self): def history(self):
return RunInfoHistory.objects.filter(subject=self).order_by('runid') return RunInfoHistory.objects.filter(subject=self).order_by('run__runid')
def pending(self): def pending(self):
return RunInfo.objects.filter(subject=self).order_by('runid') return RunInfo.objects.filter(subject=self).order_by('run__runid')
class Meta: class Meta:
index_together = [ index_together = [
@ -216,12 +216,14 @@ class QuestionSet(models.Model):
["questionnaire", "sortid"], ["questionnaire", "sortid"],
["sortid",] ["sortid",]
] ]
class Run(models.Model):
runid = models.CharField(max_length=32, null=True)
class RunInfo(models.Model): class RunInfo(models.Model):
"Store the active/waiting questionnaire runs here" "Store the active/waiting questionnaire runs here"
subject = models.ForeignKey(Subject) subject = models.ForeignKey(Subject)
random = models.CharField(max_length=32) # probably a randomized md5sum random = models.CharField(max_length=32) # probably a randomized md5sum
runid = models.CharField(max_length=32) run = models.ForeignKey(Run, related_name='run_infos')
landing = models.ForeignKey(Landing, null=True, blank=True) landing = models.ForeignKey(Landing, null=True, blank=True)
# questionset should be set to the first QuestionSet initially, and to null on completion # questionset should be set to the first QuestionSet initially, and to null on completion
# ... although the RunInfo entry should be deleted then anyway. # ... although the RunInfo entry should be deleted then anyway.
@ -301,7 +303,7 @@ class RunInfo(models.Model):
return self.__cookiecache return self.__cookiecache
def __unicode__(self): def __unicode__(self):
return "%s: %s, %s" % (self.runid, self.subject.surname, self.subject.givenname) return "%s: %s, %s" % (self.run.runid, self.subject.surname, self.subject.givenname)
class Meta: class Meta:
verbose_name_plural = 'Run Info' verbose_name_plural = 'Run Info'
@ -311,7 +313,7 @@ class RunInfo(models.Model):
class RunInfoHistory(models.Model): class RunInfoHistory(models.Model):
subject = models.ForeignKey(Subject) subject = models.ForeignKey(Subject)
runid = models.CharField(max_length=32) run = models.ForeignKey(Run, related_name='run_info_histories')
completed = models.DateTimeField() completed = models.DateTimeField()
landing = models.ForeignKey(Landing, null=True, blank=True) landing = models.ForeignKey(Landing, null=True, blank=True)
tags = models.TextField( tags = models.TextField(
@ -325,11 +327,11 @@ class RunInfoHistory(models.Model):
questionnaire = models.ForeignKey(Questionnaire) questionnaire = models.ForeignKey(Questionnaire)
def __unicode__(self): def __unicode__(self):
return "%s: %s on %s" % (self.runid, self.subject, self.completed) return "%s: %s on %s" % (self.run.runid, self.subject, self.completed)
def answers(self): def answers(self):
"Returns the query for the answers." "Returns the query for the answers."
return Answer.objects.filter(subject=self.subject, runid=self.runid) return Answer.objects.filter(subject=self.subject, run=self.run)
class Meta: class Meta:
verbose_name_plural = 'Run Info History' verbose_name_plural = 'Run Info History'
@ -448,7 +450,7 @@ class Question(models.Model):
return self.type == 'comment' return self.type == 'comment'
def get_value_for_run_question(self, runid): def get_value_for_run_question(self, runid):
runanswer = Answer.objects.filter(runid=runid,question=self) runanswer = Answer.objects.filter(run__runid=runid, question=self)
if len(runanswer) > 0: if len(runanswer) > 0:
return runanswer[0].answer return runanswer[0].answer
else: else:
@ -481,7 +483,7 @@ class Choice(models.Model):
class Answer(models.Model): class Answer(models.Model):
subject = models.ForeignKey(Subject, help_text = u'The user who supplied this answer') subject = models.ForeignKey(Subject, help_text = u'The user who supplied this answer')
question = models.ForeignKey(Question, help_text = u"The question that this is an answer to") question = models.ForeignKey(Question, help_text = u"The question that this is an answer to")
runid = models.CharField(u'RunID', help_text = u"The RunID ", max_length=32) run = models.ForeignKey(Run, related_name='answers')
answer = models.TextField() answer = models.TextField()
def __unicode__(self): def __unicode__(self):
@ -535,6 +537,6 @@ class Answer(models.Model):
class Meta: class Meta:
index_together = [ index_together = [
['subject', 'runid'], ['subject', 'run'],
['subject', 'runid', 'id'], ['subject', 'run', 'id'],
] ]

View File

@ -2,13 +2,13 @@
{% block after_field_sets %} {% block after_field_sets %}
{% for x in original.pending %} {% for x in original.pending %}
{% if forloop.first %}<h3>Pending:</h3><ul>{% endif %} {% if forloop.first %}<h3>Pending:</h3><ul>{% endif %}
<li><b>{{ x.runid }}: created {{ x.created }}, last email sent {{ x.emailsent }}</b></li> <li><b>{{ x.run.runid }}: created {{ x.created }}, last email sent {{ x.emailsent }}</b></li>
{% if forloop.last %}</ul>{% endif %} {% if forloop.last %}</ul>{% endif %}
{% endfor %} {% endfor %}
{% for x in original.history %} {% for x in original.history %}
{% if forloop.first %}<h3>History:</h3><ul>{% endif %} {% if forloop.first %}<h3>History:</h3><ul>{% endif %}
<li><b>{{ x.runid }}: completed {{ x.completed }}</b></li> <li><b>{{ x.run.runid }}: completed {{ x.completed }}</b></li>
{% if forloop.last %}</ul>{% endif %} {% if forloop.last %}</ul>{% endif %}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@ -54,7 +54,7 @@ def get_runid_from_request(request):
if use_session: if use_session:
return request.session.get('runcode', None) return request.session.get('runcode', None)
else: else:
return request.runinfo.runid return request.runinfo.run.runid
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@ -6,7 +6,7 @@ def get_completed_answers_for_questions(questionnaire_id, question_list):
completed_questionnaire_runs = RunInfoHistory.objects.filter(questionnaire__id=questionnaire_id) completed_questionnaire_runs = RunInfoHistory.objects.filter(questionnaire__id=questionnaire_id)
completed_answers = [] completed_answers = []
for run in completed_questionnaire_runs: for run in completed_questionnaire_runs:
specific_answers = Answer.objects.filter(runid=run.runid, question_id__in=question_list) specific_answers = Answer.objects.filter(run=run.run, question_id__in=question_list)
answer_set = [] answer_set = []
for answer in specific_answers: for answer in specific_answers:
if answer.answer != '[]': if answer.answer != '[]':

View File

@ -65,9 +65,9 @@ def get_question(number, questionset):
return res and res[0] or None return res and res[0] or None
def delete_answer(question, subject, runid): def delete_answer(question, subject, run):
"Delete the specified question/subject/runid combination from the Answer table" "Delete the specified question/subject/run combination from the Answer table"
Answer.objects.filter(subject=subject, runid=runid, question=question).delete() Answer.objects.filter(subject=subject, run=run, question=question).delete()
def add_answer(runinfo, question, answer_dict): def add_answer(runinfo, question, answer_dict):
@ -81,7 +81,7 @@ def add_answer(runinfo, question, answer_dict):
answer = Answer() answer = Answer()
answer.question = question answer.question = question
answer.subject = runinfo.subject answer.subject = runinfo.subject
answer.runid = runinfo.runid answer.run = runinfo.run
type = question.get_type() type = question.get_type()
@ -94,7 +94,7 @@ def add_answer(runinfo, question, answer_dict):
raise AnswerException("No Processor defined for question type %s" % type) raise AnswerException("No Processor defined for question type %s" % type)
# first, delete all existing answers to this question for this particular user+run # first, delete all existing answers to this question for this particular user+run
delete_answer(question, runinfo.subject, runinfo.runid) delete_answer(question, runinfo.subject, runinfo.run)
# then save the new answer to the database # then save the new answer to the database
answer.save(runinfo) answer.save(runinfo)
@ -483,7 +483,7 @@ def questionnaire(request, runcode=None, qs=None):
if not depparser.parse(depon): if not depparser.parse(depon):
# if check is not the same as answer, then we don't care # if check is not the same as answer, then we don't care
# about this question plus we should delete it from the DB # about this question plus we should delete it from the DB
delete_answer(question, runinfo.subject, runinfo.runid) delete_answer(question, runinfo.subject, runinfo.run)
if cd.get('store', False): if cd.get('store', False):
runinfo.set_cookie(question.number, None) runinfo.set_cookie(question.number, None)
continue continue
@ -522,7 +522,7 @@ def questionnaire(request, runcode=None, qs=None):
def finish_questionnaire(request, runinfo, questionnaire): def finish_questionnaire(request, runinfo, questionnaire):
hist = RunInfoHistory() hist = RunInfoHistory()
hist.subject = runinfo.subject hist.subject = runinfo.subject
hist.runid = runinfo.runid hist.run = runinfo.run
hist.completed = datetime.now() hist.completed = datetime.now()
hist.questionnaire = questionnaire hist.questionnaire = questionnaire
hist.tags = runinfo.tags hist.tags = runinfo.tags
@ -536,11 +536,11 @@ def finish_questionnaire(request, runinfo, questionnaire):
redirect_url = questionnaire.redirect_url redirect_url = questionnaire.redirect_url
for x, y in (('$LANG', lang), for x, y in (('$LANG', lang),
('$SUBJECTID', runinfo.subject.id), ('$SUBJECTID', runinfo.subject.id),
('$RUNID', runinfo.runid),): ('$RUNID', runinfo.run.runid),):
redirect_url = redirect_url.replace(x, str(y)) redirect_url = redirect_url.replace(x, str(y))
if runinfo.runid in ('12345', '54321') \ if runinfo.run.runid in ('12345', '54321') \
or runinfo.runid.startswith('test:'): or runinfo.run.runid.startswith('test:'):
runinfo.questionset = QuestionSet.objects.filter(questionnaire=questionnaire).order_by('sortid')[0] runinfo.questionset = QuestionSet.objects.filter(questionnaire=questionnaire).order_by('sortid')[0]
runinfo.save() runinfo.save()
else: else:
@ -724,7 +724,7 @@ def show_questionnaire(request, runinfo, errors={}):
current_answers = [] current_answers = []
if debug_questionnaire: if debug_questionnaire:
current_answers = Answer.objects.filter(subject=runinfo.subject, runid=runinfo.runid).order_by('id') current_answers = Answer.objects.filter(subject=runinfo.subject, run=runinfo.run).order_by('id')
r = r2r("questionnaire/questionset.html", request, r = r2r("questionnaire/questionset.html", request,
@ -883,7 +883,7 @@ def answer_export(questionnaire, answers=None):
answers = Answer.objects.all() answers = Answer.objects.all()
answers = answers.filter( answers = answers.filter(
question__questionset__questionnaire=questionnaire).order_by( question__questionset__questionnaire=questionnaire).order_by(
'subject', 'runid', 'question__questionset__sortid', 'question__number') 'subject', 'run__runid', 'question__questionset__sortid', 'question__number')
answers = answers.select_related() answers = answers.select_related()
questions = Question.objects.filter( questions = Question.objects.filter(
questionset__questionnaire=questionnaire) questionset__questionnaire=questionnaire)
@ -901,10 +901,10 @@ def answer_export(questionnaire, answers=None):
out = [] out = []
row = [] row = []
for answer in answers: for answer in answers:
if answer.runid != runid or answer.subject != subject: if answer.run.runid != runid or answer.subject != subject:
if row: if row:
out.append((subject, runid, row)) out.append((subject, runid, row))
runid = answer.runid runid = answer.run.runid
subject = answer.subject subject = answer.subject
row = [""] * len(headings) row = [""] * len(headings)
ans = answer.split_answer() ans = answer.split_answer()
@ -1023,9 +1023,8 @@ def generate_run(request, questionnaire_id, subject_id=None, context={}):
str_to_hash += settings.SECRET_KEY str_to_hash += settings.SECRET_KEY
key = md5(str_to_hash).hexdigest() key = md5(str_to_hash).hexdigest()
landing = context.get('landing', None) landing = context.get('landing', None)
r = Run.objects.create(runid=key)
run = RunInfo(subject=su, random=key, runid=key, questionset=qs, landing=landing) run = RunInfo.objects.create(subject=su, random=key, run=r, questionset=qs, landing=landing)
run.save()
if not use_session: if not use_session:
kwargs = {'runcode': key} kwargs = {'runcode': key}
else: else: