Adding sort id to question display within questionset
parent
c8b1a91875
commit
3068791744
|
@ -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']
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
]
|
|
@ -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,
|
||||||
|
),
|
||||||
|
]
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue