Adding sort id to question display within questionset

EmailTemplateFixes
Griffin Caprio 2015-03-10 17:47:13 -05:00
parent c8b1a91875
commit 3068791744
5 changed files with 205 additions and 6 deletions

View File

@ -1,32 +1,34 @@
#!/usr/bin/python
# vim: set fileencoding=utf-8
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.contrib import admin from django.contrib import admin
from models import * from models import *
adminsite = admin.site adminsite = admin.site
class SubjectAdmin(admin.ModelAdmin): class SubjectAdmin(admin.ModelAdmin):
search_fields = ['surname', 'givenname', 'email'] search_fields = ['surname', 'givenname', 'email']
list_display = ['surname', 'givenname', 'email'] list_display = ['surname', 'givenname', 'email']
class ChoiceAdmin(admin.ModelAdmin): class ChoiceAdmin(admin.ModelAdmin):
list_display = ['sortid', 'text', 'value', 'question'] list_display = ['sortid', 'text', 'value', 'question']
class ChoiceInline(admin.TabularInline): class ChoiceInline(admin.TabularInline):
ordering = ['sortid'] ordering = ['sortid']
model = Choice model = Choice
extra = 5 extra = 5
class QuestionSetAdmin(admin.ModelAdmin): class QuestionSetAdmin(admin.ModelAdmin):
ordering = ['questionnaire', 'sortid', ] ordering = ['questionnaire', 'sortid', ]
list_filter = ['questionnaire', ] list_filter = ['questionnaire', ]
list_display = ['questionnaire', 'heading', 'sortid', ] list_display = ['questionnaire', 'heading', 'sortid', ]
list_editable = ['sortid', ] list_editable = ['sortid', ]
class QuestionAdmin(admin.ModelAdmin): class QuestionAdmin(admin.ModelAdmin):
ordering = ['questionset__questionnaire', 'questionset', 'number'] ordering = ['questionset__questionnaire', 'questionset', 'sort_id', 'number']
inlines = [ChoiceInline] inlines = [ChoiceInline]
list_filter = ['questionset__questionnaire'] list_filter = ['questionset__questionnaire']
@ -43,6 +45,7 @@ class QuestionAdmin(admin.ModelAdmin):
extra_context['questionnaires'] = Questionnaire.objects.filter(**args).order_by('name') extra_context['questionnaires'] = Questionnaire.objects.filter(**args).order_by('name')
return super(QuestionAdmin, self).changelist_view(request, extra_context) return super(QuestionAdmin, self).changelist_view(request, extra_context)
class QuestionnaireAdmin(admin.ModelAdmin): class QuestionnaireAdmin(admin.ModelAdmin):
list_display = ('name', 'redirect_url', 'export') list_display = ('name', 'redirect_url', 'export')
readonly_fields = ('export',) readonly_fields = ('export',)
@ -53,13 +56,16 @@ class QuestionnaireAdmin(admin.ModelAdmin):
export.allow_tags = True export.allow_tags = True
export.short_description = _('Export to CSV') export.short_description = _('Export to CSV')
class RunInfoAdmin(admin.ModelAdmin): class RunInfoAdmin(admin.ModelAdmin):
list_display = ['random', 'runid', 'subject', 'created', 'emailsent', 'lastemailerror'] list_display = ['random', 'runid', 'subject', 'created', 'emailsent', 'lastemailerror']
pass pass
class RunInfoHistoryAdmin(admin.ModelAdmin): class RunInfoHistoryAdmin(admin.ModelAdmin):
pass pass
class AnswerAdmin(admin.ModelAdmin): class AnswerAdmin(admin.ModelAdmin):
search_fields = ['subject__email', 'runid', 'question__number', 'answer'] search_fields = ['subject__email', 'runid', 'question__number', 'answer']
list_display = ['runid', 'subject', 'question'] list_display = ['runid', 'subject', 'question']

View File

@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Answer',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('runid', models.CharField(help_text='The RunID (ie. year)', max_length=32, verbose_name='RunID')),
('answer', models.TextField()),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Choice',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('sortid', models.IntegerField()),
('value', models.CharField(max_length=64, verbose_name='Short Value')),
('text_en', models.CharField(max_length=200, verbose_name='Choice Text')),
('tags', models.CharField(max_length=64, verbose_name='Tags', blank=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Question',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('number', models.CharField(help_text=b'eg. <tt>1</tt>, <tt>2a</tt>, <tt>2b</tt>, <tt>3c</tt><br /> Number is also used for ordering questions.', max_length=8)),
('text_en', models.TextField(verbose_name='Text', blank=True)),
('type', models.CharField(help_text="Determines the means of answering the question. An open question gives the user a single-line textfield, multiple-choice gives the user a number of choices he/she can choose from. If a question is multiple-choice, enter the choices this user can choose from below'.", max_length=32, verbose_name='Type of question', choices=[(b'open', b'Open Answer, single line [input]'), (b'open-textfield', b'Open Answer, multi-line [textarea]'), (b'choice-yesno', b'Yes/No Choice [radio]'), (b'choice-yesnocomment', b'Yes/No Choice with optional comment [radio, input]'), (b'choice-yesnodontknow', b"Yes/No/Don't know Choice [radio]"), (b'comment', b'Comment Only'), (b'choice', b'Choice [radio]'), (b'choice-freeform', b'Choice with a freeform option [radio]'), (b'dropdown', b'Dropdown choice [select]'), (b'choice-multiple', b'Multiple-Choice, Multiple-Answers [checkbox]'), (b'choice-multiple-freeform', b'Multiple-Choice, Multiple-Answers, plus freeform [checkbox, input]'), (b'range', b'Range of numbers [select]'), (b'number', b'Number [input]'), (b'timeperiod', b'Time Period [input, select]'), (b'custom', b'Custom field'), (b'sameas', b'Same as Another Question (put sameas=question.number in checks or sameasid=question.id)')])),
('extra_en', models.CharField(help_text='Extra information (use on question type)', max_length=512, null=True, verbose_name='Extra information', blank=True)),
('checks', models.CharField(help_text=b'Additional checks to be performed for this value (space separated) <br /><br />For text fields, <tt>required</tt> is a valid check.<br />For yes/no choice, <tt>required</tt>, <tt>required-yes</tt>, and <tt>required-no</tt> are valid.<br /><br />If this question is required only if another question\'s answer is something specific, use <tt>requiredif="QuestionNumber,Value"</tt> or <tt>requiredif="QuestionNumber,!Value"</tt> for anything but a specific value. You may also combine tests appearing in <tt>requiredif</tt> by joining them with the words <tt>and</tt> or <tt>or</tt>, eg. <tt>requiredif="Q1,A or Q2,B"</tt>', max_length=512, null=True, verbose_name='Additional checks', blank=True)),
('footer_en', models.TextField(help_text=b'Footer rendered below the question interpreted as textile', verbose_name='Footer', blank=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Questionnaire',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=128)),
('redirect_url', models.CharField(default=b'/static/complete.html', help_text=b'URL to redirect to when Questionnaire is complete. Macros: $SUBJECTID, $RUNID, $LANG', max_length=128)),
],
options={
'permissions': (('export', 'Can export questionnaire answers'), ('management', 'Management Tools')),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='QuestionSet',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('sortid', models.IntegerField()),
('heading', models.CharField(max_length=64)),
('checks', models.CharField(help_text=b'Current options are \'femaleonly\' or \'maleonly\' and shownif="QuestionNumber,Answer" which takes the same format as <tt>requiredif</tt> for questions.', max_length=256, blank=True)),
('text_en', models.TextField(help_text=b"This is interpreted as Textile: <a href='http://en.wikipedia.org/wiki/Textile_%28markup_language%29' target='_blank'>http://en.wikipedia.org/wiki/Textile_(markup_language)</a>", verbose_name='Text')),
('questionnaire', models.ForeignKey(to='questionnaire.Questionnaire')),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='RunInfo',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('random', models.CharField(max_length=32)),
('runid', models.CharField(max_length=32)),
('emailcount', models.IntegerField(default=0)),
('created', models.DateTimeField(auto_now_add=True)),
('emailsent', models.DateTimeField(null=True, blank=True)),
('lastemailerror', models.CharField(max_length=64, null=True, blank=True)),
('state', models.CharField(max_length=16, null=True, blank=True)),
('cookies', models.TextField(null=True, blank=True)),
('tags', models.TextField(help_text='Tags active on this run, separated by commas', blank=True)),
('skipped', models.TextField(help_text='A comma sepearted list of questions to skip', blank=True)),
('questionset', models.ForeignKey(blank=True, to='questionnaire.QuestionSet', null=True)),
],
options={
'verbose_name_plural': 'Run Info',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='RunInfoHistory',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('runid', models.CharField(max_length=32)),
('completed', models.DateField()),
('tags', models.TextField(help_text='Tags used on this run, separated by commas', blank=True)),
('skipped', models.TextField(help_text='A comma sepearted list of questions skipped by this run', blank=True)),
('questionnaire', models.ForeignKey(to='questionnaire.Questionnaire')),
],
options={
'verbose_name_plural': 'Run Info History',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Subject',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('state', models.CharField(default=b'inactive', max_length=16, verbose_name='State', choices=[(b'active', 'Active'), (b'inactive', 'Inactive')])),
('surname', models.CharField(max_length=64, null=True, verbose_name='Surname', blank=True)),
('givenname', models.CharField(max_length=64, null=True, verbose_name='Given name', blank=True)),
('email', models.EmailField(max_length=75, null=True, verbose_name='Email', blank=True)),
('gender', models.CharField(default=b'unset', max_length=8, verbose_name='Gender', blank=True, choices=[(b'unset', 'Unset'), (b'male', 'Male'), (b'female', 'Female')])),
('nextrun', models.DateField(null=True, verbose_name='Next Run', blank=True)),
('formtype', models.CharField(default=b'email', max_length=16, verbose_name='Form Type', choices=[(b'email', 'Subject receives emails'), (b'paperform', 'Subject is sent paper form')])),
('language', models.CharField(default=b'en', max_length=2, verbose_name='Language', choices=[(b'en', b'English')])),
],
options={
},
bases=(models.Model,),
),
migrations.AddField(
model_name='runinfohistory',
name='subject',
field=models.ForeignKey(to='questionnaire.Subject'),
preserve_default=True,
),
migrations.AddField(
model_name='runinfo',
name='subject',
field=models.ForeignKey(to='questionnaire.Subject'),
preserve_default=True,
),
migrations.AddField(
model_name='question',
name='questionset',
field=models.ForeignKey(to='questionnaire.QuestionSet'),
preserve_default=True,
),
migrations.AddField(
model_name='choice',
name='question',
field=models.ForeignKey(to='questionnaire.Question'),
preserve_default=True,
),
migrations.AddField(
model_name='answer',
name='question',
field=models.ForeignKey(help_text='The question that this is an answer to', to='questionnaire.Question'),
preserve_default=True,
),
migrations.AddField(
model_name='answer',
name='subject',
field=models.ForeignKey(help_text='The user who supplied this answer', to='questionnaire.Subject'),
preserve_default=True,
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('questionnaire', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='question',
name='sort_id',
field=models.IntegerField(help_text=b'Questions within a questionset are sorted by sort order first, question number second', null=True, blank=True),
preserve_default=True,
),
]

View File

View File

@ -89,6 +89,7 @@ class Questionnaire(models.Model):
("management", "Management Tools") ("management", "Management Tools")
) )
class QuestionSet(models.Model): class QuestionSet(models.Model):
__metaclass__ = TransMeta __metaclass__ = TransMeta
@ -105,7 +106,11 @@ class QuestionSet(models.Model):
def numeric_number(val): def numeric_number(val):
matches = re.findall(r'^\d+', val) matches = re.findall(r'^\d+', val)
return int(matches[0]) if matches else 0 return int(matches[0]) if matches else 0
self.__qcache = sorted(Question.objects.filter(questionset=self.id), key=lambda q: (numeric_number(q.number), q.number))
questions_with_sort_id = sorted(Question.objects.filter(questionset=self.id).exclude(sort_id__isnull=True), key=lambda q: q.sort_id)
questions_with_out_sort_id = sorted(Question.objects.filter(questionset=self.id, sort_id__isnull=True), key=lambda q: (numeric_number(q.number), q.number))
self.__qcache = questions_with_sort_id + questions_with_out_sort_id
return self.__qcache return self.__qcache
def next(self): def next(self):
@ -260,6 +265,7 @@ class RunInfoHistory(models.Model):
class Meta: class Meta:
verbose_name_plural = 'Run Info History' verbose_name_plural = 'Run Info History'
class Question(models.Model): class Question(models.Model):
__metaclass__ = TransMeta __metaclass__ = TransMeta
@ -267,6 +273,7 @@ class Question(models.Model):
number = models.CharField(max_length=8, help_text= number = models.CharField(max_length=8, help_text=
"eg. <tt>1</tt>, <tt>2a</tt>, <tt>2b</tt>, <tt>3c</tt><br /> " "eg. <tt>1</tt>, <tt>2a</tt>, <tt>2b</tt>, <tt>3c</tt><br /> "
"Number is also used for ordering questions.") "Number is also used for ordering questions.")
sort_id = models.IntegerField(null=True, blank=True, help_text="Questions within a questionset are sorted by sort order first, question number second")
text = models.TextField(blank=True, verbose_name=_("Text")) text = models.TextField(blank=True, verbose_name=_("Text"))
type = models.CharField(u"Type of question", max_length=32, type = models.CharField(u"Type of question", max_length=32,
choices = QuestionChoices, choices = QuestionChoices,
@ -291,7 +298,6 @@ class Question(models.Model):
'eg. <tt>requiredif="Q1,A or Q2,B"</tt>') 'eg. <tt>requiredif="Q1,A or Q2,B"</tt>')
footer = models.TextField(u"Footer", help_text="Footer rendered below the question interpreted as textile", blank=True) footer = models.TextField(u"Footer", help_text="Footer rendered below the question interpreted as textile", blank=True)
def questionnaire(self): def questionnaire(self):
return self.questionset.questionnaire return self.questionset.questionnaire