From 72790b421d67485b805481d26d9a8205a722d2c8 Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 26 Jul 2016 17:08:23 -0400 Subject: [PATCH] general renovations --- README.md | 51 +- questionnaire/__init__.py | 23 +- questionnaire/admin.py | 15 +- questionnaire/apps.py | 29 + questionnaire/dependency_checker.py | 2 +- questionnaire/emails.py | 11 +- questionnaire/fixtures/sampleSurvey.json | 512 ++++++++++++++++++ questionnaire/legacy_test_urls.py | 2 +- questionnaire/legacy_tests.py | 2 +- .../management/commands/make_survey_nonces.py | 14 + .../commands/questionnaire_emails.py | 2 +- questionnaire/migrations/0001_initial.py | 109 ++-- .../migrations/0002_question_sort_id.py | 20 - .../migrations/0003_auto_20151129_0727.py | 44 -- .../migrations/0004_auto_20151202_0230.py | 24 - .../migrations/0005_questionset_parse_html.py | 19 - .../0006_questionnaire_parse_html.py | 19 - .../migrations/0007_auto_20151207_1045.py | 24 - questionnaire/migrations/0008_dbstylesheet.py | 21 - .../0009_dbstylesheet_inclusion_tag.py | 20 - .../migrations/0010_index_choice_value.py | 18 - ...ndex_on_question_mod_number_questionset.py | 18 - ...stionset_questionnaire_and_sortid_index.py | 18 - .../0013__answer_index_subject_run_id.py | 18 - .../migrations/0014__runinfo_index_random.py | 18 - .../0015_questionnaire_index_sortid.py | 18 - .../0016_subject_given_sur_name_index.py | 18 - .../migrations/0017_auto_20160209_0228.py | 31 -- .../migrations/0018_auto_20160216_0507.py | 19 - questionnaire/models.py | 84 ++- questionnaire/modelutils.py | 11 +- questionnaire/page/admin.py | 2 +- questionnaire/page/views.py | 9 +- questionnaire/qprocessors/__init__.py | 15 +- questionnaire/qprocessors/choice.py | 29 +- questionnaire/qprocessors/custom.py | 4 +- questionnaire/qprocessors/range_or_number.py | 11 +- questionnaire/qprocessors/simple.py | 28 +- questionnaire/qprocessors/timeperiod.py | 2 +- .../static/bootstrap/bootstrap.min.css | 362 +------------ questionnaire/static/questionnaire.css | 26 +- .../templates/base-questionnaire.html | 57 ++ questionnaire/templates/page.html | 4 +- questionnaire/templates/pages/complete.html | 8 + questionnaire/templates/pages/generic.html | 14 + .../questionnaire/choice-freeform.html | 8 +- .../choice-multiple-freeform.html | 22 +- .../questionnaire/choice-yesnocomment.html | 10 +- .../templates/questionnaire/choice.html | 2 +- .../templates/questionnaire/complete.de.html | 4 +- .../templates/questionnaire/complete.en.html | 18 + .../templates/questionnaire/complete.html | 10 - .../questionnaire/open-textfield.html | 2 +- .../templates/questionnaire/questionset.html | 43 +- questionnaire/templatetags/landings.py | 15 + questionnaire/templatetags/questionnaire.py | 9 +- questionnaire/tests.py | 2 +- questionnaire/urls.py | 16 +- questionnaire/utils.py | 21 +- questionnaire/utils_noncircular.py | 2 +- questionnaire/views.py | 86 +-- setup.py | 12 +- 62 files changed, 1078 insertions(+), 1009 deletions(-) create mode 100644 questionnaire/apps.py create mode 100644 questionnaire/fixtures/sampleSurvey.json create mode 100644 questionnaire/management/commands/make_survey_nonces.py delete mode 100644 questionnaire/migrations/0002_question_sort_id.py delete mode 100644 questionnaire/migrations/0003_auto_20151129_0727.py delete mode 100644 questionnaire/migrations/0004_auto_20151202_0230.py delete mode 100644 questionnaire/migrations/0005_questionset_parse_html.py delete mode 100644 questionnaire/migrations/0006_questionnaire_parse_html.py delete mode 100644 questionnaire/migrations/0007_auto_20151207_1045.py delete mode 100644 questionnaire/migrations/0008_dbstylesheet.py delete mode 100644 questionnaire/migrations/0009_dbstylesheet_inclusion_tag.py delete mode 100644 questionnaire/migrations/0010_index_choice_value.py delete mode 100644 questionnaire/migrations/0011_index_on_question_mod_number_questionset.py delete mode 100644 questionnaire/migrations/0012_questionset_questionnaire_and_sortid_index.py delete mode 100644 questionnaire/migrations/0013__answer_index_subject_run_id.py delete mode 100644 questionnaire/migrations/0014__runinfo_index_random.py delete mode 100644 questionnaire/migrations/0015_questionnaire_index_sortid.py delete mode 100644 questionnaire/migrations/0016_subject_given_sur_name_index.py delete mode 100644 questionnaire/migrations/0017_auto_20160209_0228.py delete mode 100644 questionnaire/migrations/0018_auto_20160216_0507.py create mode 100644 questionnaire/templates/base-questionnaire.html create mode 100644 questionnaire/templates/pages/complete.html create mode 100644 questionnaire/templates/pages/generic.html create mode 100644 questionnaire/templates/questionnaire/complete.en.html delete mode 100644 questionnaire/templates/questionnaire/complete.html create mode 100644 questionnaire/templatetags/landings.py diff --git a/README.md b/README.md index d93f292..c950626 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,23 @@ -ED Questionnaire +FEF Questionnaire ===================== Introduction ------------ -ED Questionnaire is a Django questionnaire app which is easily customizable +FEF Questionnaire is a Django questionnaire app which is easily customizable and includes advanced dependency support using boolean expressions. It allows an administrator to create and edit questionnaires in the Django admin interface, with support for multiple languages. +It can be run either as a survey where subjects are solicited by email, or as a web-based poll. + +In either mode, an instance can be linked to an arbitrary object via the django content-types module. + History ------- -The questionnaire app was originally developed by [Seantis](https://github.com/seantis), itself derived from [rmt](https://github.com/rmt). We picked up the project because we had been using it and the Seantis version had entered a steady state of development. There are several feature changes we wanted and decided to head up the maintenance ourselves. +The questionnaire app was originally developed by [Seantis](https://github.com/seantis), itself derived from [rmt](https://github.com/rmt). Eldest Daughter picked up the project and named it [ED-questionnaire](git://github.com/eldest-daughter/ed-questionnaire) because they had been using it and the Seantis version had entered a steady state of development. There are several feature changes they wanted and decided to head up the maintenance themselves. The old versions are tagged as follows: @@ -22,12 +26,14 @@ The old versions are tagged as follows: * tag 2.0 - original updated trunk from Seantis version * tag 2.5 - contains the original Seantis version and all PRs merged in as of 12/09/15. It's considered to be the backwards compatible version of the repository. -The new version is the current trunk and is dubbed v3.0. It should be considered a new project and thus will contain backwards incompatible changes. When possible, we'll try and backport fixes to the v2.x branches, but it will not be a priority. +The "ED-questionnaire" version was dubbed v3.0. It is not compatible with the v2.x branches. + +The "FEF-questionnaire" was created to add the ability to link the questionnaire to individual books in a book database. We'll call this v4.0 About this Manual ----------------- -ED Questionnaire is not a very well documented app so far to say the least. This manual should give you a general idea of the layout and concepts of it, but it is not as comprehensive as it should be. +FEF Questionnaire is not a very well documented app so far to say the least. This manual should give you a general idea of the layout and concepts of it, but it is not as comprehensive as it should be. What it does cover is the following: @@ -70,11 +76,11 @@ Create a place for the questionnare Clone the questionnaire source - git clone git://github.com/eldest-daughter/ed-questionnaire.git + git clone git://github.com/EbookFoundation/fef-questionnaire.git You should now have a ed-questionnaire folder in your apps folder - cd ed-questionnaire + cd fef-questionnaire The next step is to install the questionnaire. @@ -102,7 +108,7 @@ We will use that below for the setup of the folders. In the same file add the questionnaire static directory to your STATICFILES_DIRS: STATICFILES_DIRS = ( - os.path.abspath('./apps/ed-questionnaire/questionnaire/static/'), + os.path.abspath('./apps/fef-questionnaire/questionnaire/static/'), ) Also add the locale and request cache middleware to MIDDLEWARE_CLASSES: @@ -116,7 +122,7 @@ otherwise you will get an error when trying to start the server. Add the questionnaire template directory as well as your own to TEMPLATE_DIRS: - os.path.abspath('./apps/ed-questionnaire/questionnaire/templates'), + os.path.abspath('./apps/fef-questionnaire/questionnaire/templates'), os.path.abspath('./templates'), And finally, add `transmeta`, `questionnaire` to your INSTALLED_APPS: @@ -144,12 +150,6 @@ For an empty site with enabled admin interface you add: # questionnaire urls url(r'q/', include('questionnaire.urls')), - - url(r'^take/(?P[0-9]+)/$', 'questionnaire.views.generate_run'), - url(r'^$', 'questionnaire.page.views.page', {'page_to_render' : 'index'}), - url(r'^(?P..)/(?P.*)\.html$', 'questionnaire.page.views.langpage'), - url(r'^(?P.*)\.html$', 'questionnaire.page.views.page'), - url(r'^setlang/$', 'questionnaire.views.set_language'), ) Having done that we can initialize our database. (For this to work you must have setup your DATABASES in `settings.py`.). First, in your CLI navigate back to the `mysite` folder: @@ -161,19 +161,19 @@ The check that you are in the proper folder, type `ls`: if you can see `manage.p python manage.py syncdb python manage.py migrate -The questionnaire expects a `base.html` template to be there, with certain stylesheets and blocks inside. Have a look at `./apps/ed-questionnaire/example/templates/base.html`. +The questionnaire expects a `base.html` template to be there, with certain stylesheets and blocks inside. Have a look at `./apps/fef-questionnaire/example/templates/base.html`. For now you might want to just copy the `base.html` to your own template folder. mkdir templates cd templates - cp ../apps/ed-questionnaire/example/templates/base.html . + cp ../apps/fef-questionnaire/example/templates/base.html . Congratulations, you have setup the basics of the questionnaire! At this point this site doesn't really do anything, as there are no questionnaires defined. To see an example questionnaire you can do the following (Note: this will only work if you have both English and German defined as Languages in `settings.py`): - python manage.py loaddata ./apps/ed-questionnaire/example/fixtures/initial_data.yaml + python manage.py loaddata ./apps/fef-questionnaire/example/fixtures/initial_data.yaml You may then start your development server: @@ -194,6 +194,7 @@ The ED Questionnaire has the following tables, described in detail below. * QuestionSet * Questionnaire * Answer + * Landing ### Subject @@ -289,6 +290,9 @@ Contains the answer to a question. The value of the answer is stored as JSON. A questionnaire is a group of questionsets together. +### Landing + +In Poll mode, the landing url links a Questionnaire to an Object and a User to a Subject. Migration of 1.x to 2.0 ----------------------- @@ -340,4 +344,15 @@ There are a few that do some simple testing, but more are needed. More tests wou Django admin is a nice feature to have, but we either don't leverage it well enough, or it is not the right tool for the questionnaire. In any case, if you are expecting your customer to work with the questionnaire's structure you might have to write your own admin interface. The current one is not good enough. +4.0 Changes +-------------- +Version 4.0 has not been tested for compatibility with previous versions. + +* Broken back links have been fixed. The application works in session mode and non-session mode. +* We've updated to Bootstrap 3.3.6 and implemented label tags for accessibility +* "landings" have been added so that survey responses can be linked to arbitrary models in an application. template tags have been added that allow questions and answers to refer to those models. +* question types have been added so that choices can be offered without making the question required. +* styling of required questions has been spiffed up + + diff --git a/questionnaire/__init__.py b/questionnaire/__init__.py index e22f3a6..d11a5a9 100644 --- a/questionnaire/__init__.py +++ b/questionnaire/__init__.py @@ -7,13 +7,13 @@ Create flexible questionnaires. Author: Robert Thomson """ -from django.conf import settings from django.dispatch import Signal -import imp __all__ = ['question_proc', 'answer_proc', 'add_type', 'AnswerException', 'questionset_done', 'questionnaire_done', ] +default_app_config = '{}.apps.QuestionnaireConfig'.format(__name__) + QuestionChoices = [] QuestionProcessors = {} # supply additional information to the templates Processors = {} # for processing answers @@ -27,8 +27,7 @@ questionnaire_done = Signal(providing_args=["runinfo", "questionnaire"]) class AnswerException(Exception): """Thrown from an answer processor to generate an error message""" pass - - + def question_proc(*names): """ Decorator to create a question processor for one or more @@ -82,19 +81,3 @@ def add_type(id, name): QuestionChoices.append((id, name)) -import questionnaire.qprocessors # make sure ours are imported first - -add_type('sameas', 'Same as Another Question (put sameas=question.number in checks or sameasid=question.id)') - -for app in settings.INSTALLED_APPS: - try: - app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__ - except AttributeError: - continue - - try: - imp.find_module('qprocessors', app_path) - except ImportError: - continue - - __import__("%s.qprocessors" % app) diff --git a/questionnaire/admin.py b/questionnaire/admin.py index 5d3bbe8..ab3bc4b 100644 --- a/questionnaire/admin.py +++ b/questionnaire/admin.py @@ -1,6 +1,8 @@ from django.utils.translation import ugettext as _ from django.contrib import admin -from models import * +from django.core.urlresolvers import reverse +from .models import (Choice, Questionnaire, Question, QuestionSet, Subject, + RunInfo, RunInfoHistory, Answer, DBStylesheet, Landing) adminsite = admin.site @@ -51,7 +53,8 @@ class QuestionnaireAdmin(admin.ModelAdmin): readonly_fields = ('export',) def export(self, obj): - return '%s' % (obj.id, _("Download data")) + csv_url= reverse("export_csv", args=[obj.id,]) + return '%s' % (csv_url, _("Download data")) export.allow_tags = True export.short_description = _('Export to CSV') @@ -71,6 +74,13 @@ class AnswerAdmin(admin.ModelAdmin): list_display = ['id', 'runid', 'subject', 'question'] list_filter = ['subject', 'runid'] ordering = [ 'id', 'subject', 'runid', 'question', ] + +from django.contrib import admin + +# new in dj1.7 +# @admin.register(Landing) +class LandingAdmin(admin.ModelAdmin): + pass adminsite.register(Questionnaire, QuestionnaireAdmin) adminsite.register(Question, QuestionAdmin) @@ -79,4 +89,5 @@ adminsite.register(Subject, SubjectAdmin) adminsite.register(RunInfo, RunInfoAdmin) adminsite.register(RunInfoHistory, RunInfoHistoryAdmin) adminsite.register(Answer, AnswerAdmin) +adminsite.register(Landing, LandingAdmin) adminsite.register(DBStylesheet) diff --git a/questionnaire/apps.py b/questionnaire/apps.py new file mode 100644 index 0000000..d4c12d9 --- /dev/null +++ b/questionnaire/apps.py @@ -0,0 +1,29 @@ +# questionnaire/apps.py +import imp +from django.conf import settings + +from . import qprocessors, add_type # make sure ours are imported first # noqa +from . import __name__ as app_name +from django.apps import AppConfig + +class QuestionnaireConfig(AppConfig): + name = app_name + verbose_name = "FEF Questionnaire" + label = 'questionnaire' + + def ready(self): + + add_type('sameas', 'Same as Another Question (put sameas=question.number in checks or sameasid=question.id)') + + for app in settings.INSTALLED_APPS: + try: + app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__ + except AttributeError: + continue + + try: + imp.find_module('qprocessors', app_path) + except ImportError: + continue + + __import__("%s.qprocessors" % app) diff --git a/questionnaire/dependency_checker.py b/questionnaire/dependency_checker.py index 8b81436..e0fcb03 100644 --- a/questionnaire/dependency_checker.py +++ b/questionnaire/dependency_checker.py @@ -1,4 +1,4 @@ -from questionnaire.models import Question, Answer +from .models import Question, Answer import logging diff --git a/questionnaire/emails.py b/questionnaire/emails.py index d4f5662..2ce5547 100644 --- a/questionnaire/emails.py +++ b/questionnaire/emails.py @@ -3,18 +3,19 @@ Functions to send email reminders to users. """ +import random, time, smtplib, rfc822 +from datetime import datetime +from email.Header import Header +from email.Utils import formataddr, parseaddr from django.core.mail import get_connection, EmailMessage from django.contrib.auth.decorators import login_required from django.template import Context, loader from django.utils import translation from django.conf import settings from django.http import Http404, HttpResponse -from models import Subject, QuestionSet, RunInfo, Questionnaire -from datetime import datetime from django.shortcuts import render_to_response, get_object_or_404 -import random, time, smtplib, rfc822 -from email.Header import Header -from email.Utils import formataddr, parseaddr +from .models import Subject, QuestionSet, RunInfo, Questionnaire + try: from hashlib import md5 except: from md5 import md5 diff --git a/questionnaire/fixtures/sampleSurvey.json b/questionnaire/fixtures/sampleSurvey.json new file mode 100644 index 0000000..b45f2d9 --- /dev/null +++ b/questionnaire/fixtures/sampleSurvey.json @@ -0,0 +1,512 @@ +[ + { + "fields": { + "anonymous": false, + "email": "test@example.com", + "formtype": "email", + "gender": "male", + "givenname": "TestGivenname", + "ip_address": null, + "language": "de", + "nextrun": "2009-05-15", + "state": "active", + "surname": "TestSurname" + }, + "model": "questionnaire.subject", + "pk": 1 + }, + { + "fields": { + "admin_access_only": false, + "html": "survey html here", + "name": "MappingSurvey", + "parse_html": false, + "redirect_url": "" + }, + "model": "questionnaire.questionnaire", + "pk": 3 + }, + { + "fields": { + "content_type": 22, + "label": "Open Book Publishers", + "nonce": "xxxxxx", + "object_id": 81834, + "questionnaire": 3 + }, + "model": "questionnaire.landing", + "pk": 1234 + }, + { + "fields": { + "checks": "", + "heading": "Open Access Ebooks (Part 1)", + "parse_html": true, + "questionnaire": 3, + "sortid": 1, + "text_en": "

Introduction

\r\n

\r\nWelcome, reader of {{ landing_object.title }}! And thanks for visiting Unglue.it to help us out with this \u2026\r\n

\r\n

\r\nAs Open Access publishers, {{ landing_object.claim.all.0.rights_holder }} are truly committed to making academic research broadly accessible - so we want to understand how people like you are actually accessing and using our Open Access titles. \r\n

\r\n

\r\nWe have a bunch of questions for you (well - only 9 actually) about how you found this book and what you\u2019re going to do with it. Please tell us the things you think are interesting or relevant. We really want to know!\r\n

\r\n

\r\n[Privacy policy: There are no marketing traps, we\u2019re not going to surreptitiously drop cookies on you to carry around for us, or swamp you with emails afterwards, or tell our \u201cfriends\u201d about you - we\u2019re just going to store your answers to create a database of usage examples that can be used to understand what Open Access publishing enables." + }, + "model": "questionnaire.questionset", + "pk": 5 + }, + { + "fields": { + "checks": "", + "heading": "Now About You...", + "parse_html": true, + "questionnaire": 3, + "sortid": 2, + "text_en": "

And now, three questions about you as well ...

" + }, + "model": "questionnaire.questionset", + "pk": 6 + }, + { + "fields": { + "checks": "", + "heading": "Follow-up", + "parse_html": true, + "questionnaire": 3, + "sortid": 3, + "text_en": "

We would really like to be able to follow up with some of the respondents to this questionnaire to ask them a few more questions - particularly if you\u2019ve told us something really interesting in a comment (for example). [There will also be a little reward (a free book no less!) for those of you we do contact in this way.]

\r\n\r\n

Thanks so much for your time and efforts answering these questions for us - we love you for it!

\r\n\r\n

We hope you enjoy {{ landing_object.title }}.

\r\n\r\n

{{ landing_object.claim.all.0.rights_holder }} and Unglue.it

\r\n" + }, + "model": "questionnaire.questionset", + "pk": 7 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "1", + "parse_html": true, + "questionset": 5, + "sort_id": 1, + "text_en": "How did you find out about this book in the first place?

\r\n\r\nFor example: Was it from a Google search? Following a wikipedia link? A tweet? Referenced in another book? A late night session with a friend (we don\u2019t need to know much more about that!)? - or in some other way?\r\n", + "type": "open" + }, + "model": "questionnaire.question", + "pk": 16 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "2", + "parse_html": false, + "questionset": 5, + "sort_id": 2, + "text_en": "How did you get hold of this particular copy? \r\n\r\nFor example: Did you download it from the publisher's website? Amazon or another retailer? Find it on academia.edu? Or somewhere like aaaaarg? Get it from a friend? ", + "type": "open" + }, + "model": "questionnaire.question", + "pk": 17 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "3", + "parse_html": true, + "questionset": 5, + "sort_id": 3, + "text_en": "Why are you interested in this book?", + "type": "choice-multiple-freeform" + }, + "model": "questionnaire.question", + "pk": 18 + }, + { + "fields": { + "checks": "", + "extra_en": "If Yes - why are you using this edition and not one of the other ones?", + "footer_en": "", + "number": "4", + "parse_html": false, + "questionset": 5, + "sort_id": 4, + "text_en": "Are you aware that this title is available in multiple different digital and printed formats?", + "type": "choice-yesnocomment" + }, + "model": "questionnaire.question", + "pk": 19 + }, + { + "fields": { + "checks": "", + "extra_en": "Please tell us in more detail:", + "footer_en": "\r\n\r\n\r\n\r\n\r\n\r\n\r\n", + "number": "5", + "parse_html": false, + "questionset": 5, + "sort_id": 5, + "text_en": "What are you going to do with it now you have it?", + "type": "choice-multiple-freeform" + }, + "model": "questionnaire.question", + "pk": 20 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "1", + "parse_html": false, + "questionset": 6, + "sort_id": null, + "text_en": "Where do you live?", + "type": "choice-freeform" + }, + "model": "questionnaire.question", + "pk": 21 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "2", + "parse_html": false, + "questionset": 6, + "sort_id": null, + "text_en": "What do you do for a living?", + "type": "open" + }, + "model": "questionnaire.question", + "pk": 22 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "\r\n\r\n \r\n\r\n", + "number": "3", + "parse_html": false, + "questionset": 6, + "sort_id": null, + "text_en": "When did you finish your formal education?", + "type": "choice-freeform" + }, + "model": "questionnaire.question", + "pk": 23 + }, + { + "fields": { + "checks": "required-no", + "extra_en": "", + "footer_en": "", + "number": "4", + "parse_html": false, + "questionset": 6, + "sort_id": null, + "text_en": "Is there anything else you would like to tell us, or think we should know?", + "type": "open-textfield" + }, + "model": "questionnaire.question", + "pk": 24 + }, + { + "fields": { + "checks": "", + "extra_en": "", + "footer_en": "", + "number": "1", + "parse_html": false, + "questionset": 7, + "sort_id": null, + "text_en": "If you\u2019re willing, then please leave us an email address where we could make contact with you (information which we won\u2019t share or make public).\r\n", + "type": "open" + }, + "model": "questionnaire.question", + "pk": 25 + }, + { + "fields": { + "question": 18, + "sortid": 1, + "tags": "", + "text_en": "For personal use - I\u2019m interested in the topic ", + "value": "personal" + }, + "model": "questionnaire.choice", + "pk": 17 + }, + { + "fields": { + "question": 18, + "sortid": 2, + "tags": "", + "text_en": "For my job - it relates to what I do ", + "value": "job" + }, + "model": "questionnaire.choice", + "pk": 18 + }, + { + "fields": { + "question": 18, + "sortid": 3, + "tags": "", + "text_en": "I need to read it for a course", + "value": "course" + }, + "model": "questionnaire.choice", + "pk": 19 + }, + { + "fields": { + "question": 20, + "sortid": 1, + "tags": "", + "text_en": "Save it, in case I need to use it in the future", + "value": "save" + }, + "model": "questionnaire.choice", + "pk": 20 + }, + { + "fields": { + "question": 20, + "sortid": 2, + "tags": "", + "text_en": "Skim through it and see if it\u2019s at all interesting", + "value": "skim" + }, + "model": "questionnaire.choice", + "pk": 21 + }, + { + "fields": { + "question": 20, + "sortid": 3, + "tags": "", + "text_en": "There\u2019s only really a section/chapter I\u2019m interested in - I\u2019ll probably just read that", + "value": "section" + }, + "model": "questionnaire.choice", + "pk": 22 + }, + { + "fields": { + "question": 20, + "sortid": 4, + "tags": "", + "text_en": "The whole book looks fascinating - I\u2019m going to read it all!", + "value": "whole" + }, + "model": "questionnaire.choice", + "pk": 23 + }, + { + "fields": { + "question": 20, + "sortid": 5, + "tags": "", + "text_en": "I\u2019m going to adapt it and use it (or, at least, parts of it) for another purpose (eg a student coursepack, lecture/briefing notes \u2026)", + "value": "adapt" + }, + "model": "questionnaire.choice", + "pk": 24 + }, + { + "fields": { + "question": 20, + "sortid": 6, + "tags": "", + "text_en": "Share it with my friends ", + "value": "share" + }, + "model": "questionnaire.choice", + "pk": 25 + }, + { + "fields": { + "question": 20, + "sortid": 7, + "tags": "", + "text_en": "Print it out and ceremoniously burn it!", + "value": "print" + }, + "model": "questionnaire.choice", + "pk": 26 + }, + { + "fields": { + "question": 20, + "sortid": 8, + "tags": "", + "text_en": "I\u2019m creating/collating a (online) library", + "value": "catalog" + }, + "model": "questionnaire.choice", + "pk": 27 + }, + { + "fields": { + "question": 20, + "sortid": 9, + "tags": "", + "text_en": "Something else entirely \u2026. ", + "value": "else" + }, + "model": "questionnaire.choice", + "pk": 28 + }, + { + "fields": { + "question": 21, + "sortid": 1, + "tags": "", + "text_en": "USA/Canada", + "value": "us" + }, + "model": "questionnaire.choice", + "pk": 29 + }, + { + "fields": { + "question": 21, + "sortid": 2, + "tags": "", + "text_en": "Europe", + "value": "eu" + }, + "model": "questionnaire.choice", + "pk": 30 + }, + { + "fields": { + "question": 21, + "sortid": 3, + "tags": "", + "text_en": "South America", + "value": "sa" + }, + "model": "questionnaire.choice", + "pk": 31 + }, + { + "fields": { + "question": 21, + "sortid": 4, + "tags": "", + "text_en": "Central America/ Caribbean", + "value": "ca" + }, + "model": "questionnaire.choice", + "pk": 32 + }, + { + "fields": { + "question": 21, + "sortid": 5, + "tags": "", + "text_en": "Asia", + "value": "as" + }, + "model": "questionnaire.choice", + "pk": 33 + }, + { + "fields": { + "question": 21, + "sortid": 6, + "tags": "", + "text_en": "Africa", + "value": "af" + }, + "model": "questionnaire.choice", + "pk": 34 + }, + { + "fields": { + "question": 21, + "sortid": 7, + "tags": "", + "text_en": "Middle East", + "value": "me" + }, + "model": "questionnaire.choice", + "pk": 35 + }, + { + "fields": { + "question": 21, + "sortid": 8, + "tags": "", + "text_en": "Another Planet", + "value": "ap" + }, + "model": "questionnaire.choice", + "pk": 36 + }, + { + "fields": { + "question": 23, + "sortid": 1, + "tags": "", + "text_en": "I haven\u2019t - I\u2019m still a student", + "value": "x" + }, + "model": "questionnaire.choice", + "pk": 37 + }, + { + "fields": { + "question": 23, + "sortid": 2, + "tags": "", + "text_en": "At primary school", + "value": "8" + }, + "model": "questionnaire.choice", + "pk": 38 + }, + { + "fields": { + "question": 23, + "sortid": 3, + "tags": "", + "text_en": "At high school", + "value": "h" + }, + "model": "questionnaire.choice", + "pk": 39 + }, + { + "fields": { + "question": 23, + "sortid": 4, + "tags": "", + "text_en": "After trade qualifications", + "value": "t" + }, + "model": "questionnaire.choice", + "pk": 40 + }, + { + "fields": { + "question": 23, + "sortid": 5, + "tags": "", + "text_en": "At College/Undergraduate Degree ", + "value": "c" + }, + "model": "questionnaire.choice", + "pk": 41 + }, + { + "fields": { + "question": 23, + "sortid": 6, + "tags": "", + "text_en": "At Grad School/post-graduate university", + "value": "g" + }, + "model": "questionnaire.choice", + "pk": 42 + } +] diff --git a/questionnaire/legacy_test_urls.py b/questionnaire/legacy_test_urls.py index fd62d5f..ac66ce4 100644 --- a/questionnaire/legacy_test_urls.py +++ b/questionnaire/legacy_test_urls.py @@ -2,7 +2,7 @@ import questionnaire from django.conf.urls.defaults import * -from views import * +from .views import * urlpatterns = patterns('', url(r'^q/(?P[^/]+)/(?P\d+)/$', diff --git a/questionnaire/legacy_tests.py b/questionnaire/legacy_tests.py index 9a33abd..831afbd 100644 --- a/questionnaire/legacy_tests.py +++ b/questionnaire/legacy_tests.py @@ -7,7 +7,7 @@ answers submitted to the DB. """ from django.test import TestCase from django.test.client import Client -from questionnaire.models import * +from .models import * from datetime import datetime import os diff --git a/questionnaire/management/commands/make_survey_nonces.py b/questionnaire/management/commands/make_survey_nonces.py new file mode 100644 index 0000000..1086739 --- /dev/null +++ b/questionnaire/management/commands/make_survey_nonces.py @@ -0,0 +1,14 @@ +from django.core.management.base import BaseCommand +from ...models import Landing + + +class Command(BaseCommand): + help = "make survey nonces with the specified label" + args = "