From b9f99a0f14861c1dece108a0b850f8408828d5a4 Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 16 Jun 2017 16:17:49 -0400 Subject: [PATCH 1/6] fix translation, modernize example settings --- README.md | 178 ++++++--------- example/settings.py | 208 +++++++++--------- questionnaire/page/views.py | 4 +- questionnaire/templates/page.html | 4 +- .../choice-multiple-freeform.html | 2 + questionnaire/templatetags/landings.py | 3 +- questionnaire/views.py | 8 +- setup.py | 4 +- 8 files changed, 185 insertions(+), 226 deletions(-) diff --git a/README.md b/README.md index 0e8764d..125b659 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -FEF Questionnaire -===================== +# FEF Questionnaire -Introduction ------------- +## Introduction FEF Questionnaire is a Django questionnaire app which is easily customizable and includes advanced dependency support using boolean expressions. @@ -16,8 +14,7 @@ In either mode, an instance can be linked to an arbitrary object via the django Try out the questionaire on the Unglue.it page for "Open Access Ebooks" https://unglue.it/work/82028/ -History -------- +## History 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. @@ -30,10 +27,9 @@ The old versions are tagged as follows: 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 +The "FEF-questionnaire" version 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 ------------------ +## About this Manual 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. @@ -44,8 +40,9 @@ What it does cover is the following: * **Migration** explains how a questionnaire defined with 1.0 can be used in 2.0. * **2.0 Postmortem** talks about some experiences made during the development of 2.0. -Integration ------------ +## Integration + +### Example Setup This part of the docs will take you through the steps needed to create a questionnaire app from scratch. It should also be quite handy for the task of integrating the questionnaire into an existing site. @@ -56,7 +53,7 @@ First, create a folder for your new site: Create a virtual environment so your python packages don't influence your system - virtualenv --no-site-packages -p python2.5 . + virtualenv --no-site-packages -p python2.7 . Activate your virtual environment @@ -64,7 +61,7 @@ Activate your virtual environment Install Django - pip install django + pip install django==1.8.18 Create your Django site @@ -80,7 +77,7 @@ Clone the questionnaire source git clone git://github.com/EbookFoundation/fef-questionnaire.git -You should now have a ed-questionnaire folder in your apps folder +You should now have a fef-questionnaire folder in your apps folder cd fef-questionnaire @@ -90,7 +87,50 @@ The next step is to install the questionnaire. If you are working with ed-questionnaire from your own fork you may want to use `python setup.py develop` instead, which will save you from running `python setup.py install` every time the questionnaire changes. -Now let's configure your basic questionnaire. +Now let's configure your basic questionnaire OR copy the settings.py and urls.py files from the "example" folder into `mysite/mysite`, then skip down to [initialize your database](#initialize-the-database). + + +Also add the locale and request cache middleware to MIDDLEWARE_CLASSES: + + 'questionnaire.request_cache.RequestCacheMiddleware' + +Add the questionnaire template directory as well as your own to TEMPLATES: + + 'DIRS': [os.path.join(BASE_DIR, 'mysite/templates/')], + +If you want to use multiple languages, add the i18n context processor to TEMPLATES + 'context_processors': ['django.template.context_processors.i18n',] + +And finally, add `transmeta`, `questionnaire` to your INSTALLED_APPS: + + 'transmeta', + 'questionnaire', + 'questionnaire.page', + +Next up we want to edit the `urls.py` file of your project to link the questionnaire views to your site's url configuration. See the example app to see how. + + +### Initialize the database + +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: + + cd ../.. + +The check that you are in the proper folder, type `ls`: if you can see `manage.py` in your list of files, you are good. Otherwise, find your way to the folder that contains that file. Then type: + + python manage.py syncdb + +You will be asked to create a superuser. + +The questionnaire expects a `base-questionnaire.html` template to be there, with certain stylesheets and blocks inside. Have a look at `./apps/fef-questionnaire/example/templates/base-questionnaire.html`. if you're adding the app to an existing project. + + mkdir templates + cd templates + cp ../apps/fef-questionnaire/questionnaire/templates/base-questionnaire.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. + +### Internationalizating the database First, you want to setup the languages used in your questionnaire. Open up your `mysite` folder in your favorite text editor. @@ -101,90 +141,30 @@ Open `mysite/mysite/settings.py` and add following lines, representing your lang ('de', 'Deutsch') ) -At the top of `settings.py` you should at this point add: +To run more than one language, set + python manage.py sync_transmeta_db - import os.path - -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/fef-questionnaire/questionnaire/static/'), - ) - -Also add the locale and request cache middleware to MIDDLEWARE_CLASSES: - - 'django.middleware.locale.LocaleMiddleware', - 'questionnaire.request_cache.RequestCacheMiddleware', - -If you are using Django 1.7 you will need to comment out the following line, like so: - # 'django.middleware.security.SecurityMiddleware', -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/fef-questionnaire/questionnaire/templates'), - os.path.abspath('./templates'), - -And finally, add `transmeta`, `questionnaire` to your INSTALLED_APPS: - - 'django.contrib.sites', - 'transmeta', - 'questionnaire', - 'questionnaire.page', - -To get the "sites" framework working you also need to add the following setting: - - SITE_ID = 1 - -Next up we want to edit the `urls.py` file of your project to link the questionnaire views to your site's url configuration. - -For an empty site with enabled admin interface you add: - - from django.conf.urls import patterns, include, url - - from django.contrib import admin - admin.autodiscover() - - urlpatterns = patterns('', - url(r'^admin/', include(admin.site.urls)), - - # questionnaire urls - url(r'q/', include('questionnaire.urls')), - ) - -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: - - cd ../.. - -The check that you are in the proper folder, type `ls`: if you can see `manage.py` in your list of files, you are good. Otherwise, find your way to the folder that contains that file. Then type: - - 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/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/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. +If you want to use multiple languages, add the i18n context processor to TEMPLATES + 'context_processors': ['django.template.context_processors.i18n',] + +and set up middleware as described in the [Django translation docs](https://docs.djangoproject.com/en/1.8/topics/i18n/translation/) 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/fef-questionnaire/example/fixtures/initial_data.yaml -You may then start your development server: + +### Start the server! + +Start your development server: python manage.py runserver And navigate to [localhost:8000](http://localhost:8000/). -Concepts --------- + + +## Concepts The ED Questionnaire has the following tables, described in detail below. @@ -299,23 +279,7 @@ In Poll mode, the landing url links a Questionnaire to an Object and a User to a Migration of 1.x to 2.0 ----------------------- -2.0 added new fields to the questionnaire, but it did so in a backwards compatible way. None of the new fields are mandatory and no changes should be necessary to your existing questionnaire. Since we do not have any relevant testing data however, you might find yourself on your own if it doesn't work. Please file an issue if you think we did something wrong, so we can fix it and help you. - -As Django per default does not provide a way to migrate database schemas, we pretty much make use of the bulldozer way of migrating, by exporting the data from one database and import it into a newly created one. - -From you existing 1.x site do: - - python manage.py dumpdata >> export.yaml - -Copy your file to your new site and in your new site, create your empty database: - - python manage.py syncdb - -You may then import your data from your old site, which should probably work :) - - python manage.py loaddata export.yaml - -This of course covers only the data migration. How to migrate your custom tailored site to use questionnaire 2.0 is unfortunately something we cannot really document. +Version 4.0 does not support migration of 1.X data files. 2.0 Postmortem -------------- @@ -354,7 +318,9 @@ Version 4.0 has not been tested for compatibility with previous versions. * 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 +* styling of required questions has been spiffed up. +* export of response data has been fixed. +* compatibility with Django 1.8. Compatibility with other versions of Django has not been tested. diff --git a/example/settings.py b/example/settings.py index 53121d8..3aaf2e3 100644 --- a/example/settings.py +++ b/example/settings.py @@ -1,129 +1,118 @@ -# Django settings for example project. -import os.path +""" +Django settings for mysite project. +Generated by 'django-admin startproject' using Django 1.8.18. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '3b@kp9-x$dxp@)aqct^$vf^*n95^@k%jd)&kx_%*(kj#0s+sty' + +# SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -TEMPLATE_DEBUG = DEBUG -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) - -MANAGERS = ADMINS - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': 'example.sqlite', # Or path to database file if using sqlite3. - 'USER': '', # Not used with sqlite3. - 'PASSWORD': '', # Not used with sqlite3. - 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. - } -} - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# If running in a Windows environment this must be set to the same as your -# system time zone. -TIME_ZONE = 'Europe/Berlin' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en' - -SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# Absolute path to the directory that holds media. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = '' - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = '/media/' - -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a -# trailing slash. -# Examples: "http://foo.com/media/", "/media/". -ADMIN_MEDIA_PREFIX = '/static/admin/' - -# Make this unique, and don't share it with anybody. -SECRET_KEY = 'j69g6-&t0l43f06iq=+u!ni)9n)g!ygy4dk-dgdbrbdx7%9l*6' - -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/home/media/media.lawrence.com/static/" -STATIC_ROOT = os.path.abspath('./static_root') - -# URL prefix for static files. -# Example: "http://media.lawrence.com/static/" -STATIC_URL = '/static/' - -# Additional locations of static files -STATICFILES_DIRS = ( - os.path.abspath('./static'), - os.path.abspath('../questionnaire/static/') -) - -# List of finder classes that know how to find static files in -# various locations. -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -# 'django.contrib.staticfiles.finders.DefaultStorageFinder', -) +ALLOWED_HOSTS = [] -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'questionnaire.request_cache.RequestCacheMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -) - -ROOT_URLCONF = 'example.urls' - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.abspath("../questionnaire/templates/"), - os.path.abspath("./templates/"), -) +# Application definition INSTALLED_APPS = ( + 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', + 'django.contrib.messages', 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.admin', - 'django.contrib.admindocs', 'django.contrib.staticfiles', 'transmeta', 'questionnaire', 'questionnaire.page', ) -LANGUAGES = ( - ('en', 'English'), - ('de', 'Deutsch'), +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'questionnaire.request_cache.RequestCacheMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', ) +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'mysite/templates/')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.template.context_processors.i18n', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = '/static/' + +# sets the available languages in the questionnaire +LANGUAGES = ( + ('en', 'English'), + ('de', 'Deutsch') + ) + # Defines the progressbar behavior in the questionnaire # the possible options are 'default', 'async' and 'none' # @@ -152,5 +141,4 @@ QUESTIONNAIRE_PROGRESS = 'async' # user goes through the steps of the question set. QUESTIONNAIRE_USE_SESSION = False -try: from local_settings import * -except: pass + diff --git a/questionnaire/page/views.py b/questionnaire/page/views.py index 3233114..8c9b03f 100644 --- a/questionnaire/page/views.py +++ b/questionnaire/page/views.py @@ -13,7 +13,7 @@ def page(request, page_to_render): return render(request, "pages/{}.html".format(page_to_render), { "request" : request,}, ) - + print request.session[translation.LANGUAGE_SESSION_KEY] return render(request, "page.html", { "request" : request, "page" : p, }, ) @@ -33,7 +33,7 @@ def set_language(request): lang_code = request.GET.get('language', None) if lang_code and translation.check_for_language(lang_code): if hasattr(request, 'session'): - request.session['django_language'] = lang_code + request.session[translation.LANGUAGE_SESSION_KEY] = lang_code else: response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code) return response diff --git a/questionnaire/templates/page.html b/questionnaire/templates/page.html index a299c3d..6425f35 100644 --- a/questionnaire/templates/page.html +++ b/questionnaire/templates/page.html @@ -1,11 +1,11 @@ {% extends "base-questionnaire.html" %} - +{% load i18n %} {% block title %} {{ block.super }} - {{ page.title }} {% endblock %} {% block questionnaire %} - {{ page.body }} + {{ page.body|safe }} {% if user.is_authenticated %} (edit) {% endif %} diff --git a/questionnaire/templates/questionnaire/choice-multiple-freeform.html b/questionnaire/templates/questionnaire/choice-multiple-freeform.html index 4f4c26e..3849375 100644 --- a/questionnaire/templates/questionnaire/choice-multiple-freeform.html +++ b/questionnaire/templates/questionnaire/choice-multiple-freeform.html @@ -32,9 +32,11 @@ {% else %} + {% if qdict.extras %}
  • + {% endif %} {% endif %} {% if qdict.extras %} {% for key, value in qdict.extras %} diff --git a/questionnaire/templatetags/landings.py b/questionnaire/templatetags/landings.py index 702f8fe..4beb4a1 100644 --- a/questionnaire/templatetags/landings.py +++ b/questionnaire/templatetags/landings.py @@ -5,7 +5,8 @@ register = django.template.Library() @register.simple_tag(takes_context=True) def render_with_landing(context, text): if not context.has_key('landing_object') and context.has_key('runinfo'): - context['landing_object'] = context['runinfo'].landing.content_object + landing = context['runinfo'].landing + context['landing_object'] = landing.content_object if landing else '' if text: template = Template(text) return template.render(context) diff --git a/questionnaire/views.py b/questionnaire/views.py index 6af14f5..639be45 100644 --- a/questionnaire/views.py +++ b/questionnaire/views.py @@ -347,6 +347,8 @@ 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. """ + print translation.get_language() + if use_session: session_runcode = request.session.get('runcode', None) if session_runcode is not None: @@ -390,7 +392,7 @@ def questionnaire(request, runcode=None, qs=None): # Only change the language to the subjects choice for the initial # questionnaire page (may be a direct link from an email) if hasattr(request, 'session'): - request.session['django_language'] = runinfo.subject.language + request.session[translation.LANGUAGE_SESSION_KEY] = runinfo.subject.language translation.activate(runinfo.subject.language) if 'lang' in request.GET: @@ -514,7 +516,7 @@ def questionnaire(request, runcode=None, qs=None): if next is None: # we are finished return finish_questionnaire(request, runinfo, questionnaire) - + commit() return redirect_to_qs(runinfo, request) @@ -796,7 +798,7 @@ def set_language(request, runinfo=None, next=None): lang_code = request.GET.get('lang', None) if lang_code and translation.check_for_language(lang_code): if hasattr(request, 'session'): - request.session['django_language'] = lang_code + request.session[translation.LANGUAGE_SESSION_KEY] = lang_code else: response.set_cookie(settings.LANGUAGE_COOKIE_NAME, lang_code) if runinfo: diff --git a/setup.py b/setup.py index fc8651f..91447dc 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,8 @@ setup( version="4.0.0", description="A Django application for creating online questionnaires/surveys.", long_description=read("README.md"), - author="Eldest Daughter, LLC.","Free Ebook Foundation" - author_email="gcaprio@eldestdaughter.com", "eric@hellman.net" + author="Eldest Daughter, LLC., Free Ebook Foundation", + author_email="gcaprio@eldestdaughter.com, eric@hellman.net", license="BSD", url="https://github.com/EbookFoundation/fef-questionnaire", packages=find_packages(exclude=["example"]), From fa1831037a5b6d082184bea8a481d7a84e5304cb Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 16 Jun 2017 16:21:07 -0400 Subject: [PATCH 2/6] cleanup files, initial data --- example/fixtures/initial_data.yaml | 118 ++++++++++-------- example/manage.py | 9 -- .../templates/base-questionnaire.html | 3 +- 3 files changed, 70 insertions(+), 60 deletions(-) delete mode 100755 example/manage.py diff --git a/example/fixtures/initial_data.yaml b/example/fixtures/initial_data.yaml index 33329b3..51a765b 100644 --- a/example/fixtures/initial_data.yaml +++ b/example/fixtures/initial_data.yaml @@ -1,107 +1,127 @@ -- fields: {domain: example.com, name: example.com} - model: sites.site - pk: 1 -- fields: {email: test@example.com, formtype: email, gender: unset, givenname: Test, - language: de, nextrun: 2011-05-16, state: active, surname: Test} - model: questionnaire.subject - pk: 1 -- fields: {email: null, formtype: email, gender: unset, givenname: Anonymous, language: en, - nextrun: null, state: inactive, surname: User} - model: questionnaire.subject - pk: 2 -- fields: {name: example, redirect_url: /} +- fields: {admin_access_only: false, html: '', name: example, parse_html: false, redirect_url: /} model: questionnaire.questionnaire pk: 1 -- fields: {checks: '', heading: Page 1, questionnaire: 1, sortid: 1, text_de: h1. Biervorlieben, - text_en: h1. Beer Preferences} +- fields: {checks: '', heading: Page 1, parse_html: true, questionnaire: 1, sortid: 1, + text_de:

    Biervorlieben

    , text_en:

    Beer Preferences

    } model: questionnaire.questionset pk: 1 -- fields: {checks: '', heading: Page 2, questionnaire: 1, sortid: 2, text_de: h1. Python - Web Frameworks, text_en: h1. Python Web Frameworks} +- fields: {checks: '', heading: Page 2, parse_html: false, questionnaire: 1, sortid: 2, + text_de: h1. Python Web Frameworks, text_en: h1. Python Web Frameworks} model: questionnaire.questionset pk: 2 -- fields: {checks: '', heading: Thankyou, questionnaire: 1, sortid: 99, text_de: "h1. Vielen Dank \n \n Wir hoffen, dass Sie uns in Zukunft wieder besuchen!", text_en: "h1. Thank you! \n \n We hope that you visit us again in the future!"} +- fields: {checks: '', heading: Finished!, parse_html: true, questionnaire: 1, sortid: 99, + text_de: "

    Vielen Dank

    \r\n \r\n Wir hoffen, dass Sie uns in Zukunft + wieder besuchen!", text_en: "

    Thank you!

    \r\n \r\n We hope that you + visit us again in the future!"} model: questionnaire.questionset pk: 3 -- fields: {checks: '', extra_de: '', extra_en: '', number: '1', questionset: 1, text_de: 'Trinken +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '1', parse_html: false, questionset: 1, sort_id: null, text_de: 'Trinken Sie Bier?', text_en: 'Do you drink beer?', type: choice-yesno} model: questionnaire.question pk: 1 -- fields: {checks: 'requiredif="1,yes"', extra_de: '', extra_en: '', number: '2', - questionset: 1, text_de: 'Was Art von Bier trinken Sie am meisten?', text_en: 'What - type of beer do you drink predominantly?', type: choice-freeform} +- fields: {checks: 'requiredif="1,yes"', extra_de: '', extra_en: '', footer_de: null, + footer_en: '', number: '2', parse_html: false, questionset: 1, sort_id: null, + text_de: 'Was Art von Bier trinken Sie am meisten?', text_en: 'What type of beer + do you drink predominantly?', type: choice-freeform} model: questionnaire.question pk: 2 -- fields: {checks: '', extra_de: '', extra_en: '', number: '3', questionset: 1, text_de: 'Welche +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '3', parse_html: false, questionset: 1, sort_id: null, text_de: 'Welche von dieser Bieren haben Sie probiert?', text_en: 'Which of these brands of beer have you tried?', type: choice-multiple} model: questionnaire.question pk: 3 -- fields: {checks: '', extra_de: '', extra_en: '', number: '4', questionset: 2, text_de: 'Which +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '4', parse_html: false, questionset: 2, sort_id: null, text_de: 'Which Python Web Frameworks have you tried?', text_en: 'Which Python Web Frameworks have you tried?', type: choice-multiple-freeform} model: questionnaire.question pk: 4 -- fields: {checks: '', extra_de: '', extra_en: '', number: '5', questionset: 2, text_de: "Welche\ - \ m\xF6gen Sie am liebsten?", text_en: 'Which do you like the most?', type: choice-freeform} +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '5', parse_html: false, questionset: 2, sort_id: null, text_de: "Welche + m\xF6gen Sie am liebsten?", text_en: 'Which do you like the most?', type: choice-freeform} model: questionnaire.question pk: 5 -- fields: {question: 2, sortid: 1, text_de: Pils, text_en: Pils/Bitter, value: pils} +- fields: {question: 2, sortid: 1, tags: '', text_de: Pils, text_en: Pils/Bitter, + value: pils} model: questionnaire.choice pk: 1 -- fields: {question: 2, sortid: 2, text_de: Helles, text_en: Lager, value: helles} +- fields: {question: 2, sortid: 2, tags: '', text_de: Helles, text_en: Lager, value: helles} model: questionnaire.choice pk: 2 -- fields: {question: 2, sortid: 3, text_de: Weizen, text_en: Wheat-beer, value: weizen} +- fields: {question: 2, sortid: 3, tags: '', text_de: Weizen, text_en: Wheat-beer, + value: weizen} model: questionnaire.choice pk: 3 -- fields: {question: 3, sortid: 1, text_de: Heineken, text_en: Heineken, value: heineken} +- fields: {question: 3, sortid: 1, tags: '', text_de: Heineken, text_en: Heineken, + value: heineken} model: questionnaire.choice pk: 4 -- fields: {question: 3, sortid: 2, text_de: Becks, text_en: Becks, value: becks} +- fields: {question: 3, sortid: 2, tags: '', text_de: Becks, text_en: Becks, value: becks} model: questionnaire.choice pk: 5 -- fields: {question: 3, sortid: 3, text_de: "L\xF6wenbr\xE4u", text_en: "L\xF6wenbr\xE4\ - u", value: lowenbrau} +- fields: {question: 3, sortid: 3, tags: '', text_de: "L\xF6wenbr\xE4u", text_en: "L\xF6wenbr\xE4u", + value: lowenbrau} model: questionnaire.choice pk: 6 -- fields: {question: 2, sortid: 4, text_de: "Altbier/D\xFCssel", text_en: Altbier, +- fields: {question: 2, sortid: 4, tags: '', text_de: "Altbier/D\xFCssel", text_en: Altbier, value: altbier} model: questionnaire.choice pk: 7 -- fields: {question: 2, sortid: 5, text_de: "K\xF6lsch", text_en: "K\xF6lsch", value: koelsch} +- fields: {question: 2, sortid: 5, tags: '', text_de: "K\xF6lsch", text_en: "K\xF6lsch", + value: koelsch} model: questionnaire.choice pk: 8 -- fields: {question: 4, sortid: 1, text_de: Django, text_en: Django, value: django} +- fields: {question: 4, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} model: questionnaire.choice pk: 9 -- fields: {question: 4, sortid: 2, text_de: Pylons, text_en: Pylons, value: pylons} +- fields: {question: 4, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} model: questionnaire.choice pk: 10 -- fields: {question: 4, sortid: 3, text_de: Turbogears, text_en: Turbogears, value: turbogears} +- fields: {question: 4, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, + value: turbogears} model: questionnaire.choice pk: 11 -- fields: {question: 4, sortid: 4, text_de: CherryPy, text_en: CherryPy, value: cherrypy} +- fields: {question: 4, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, + value: cherrypy} model: questionnaire.choice pk: 12 -- fields: {question: 5, sortid: 1, text_de: Django, text_en: Django, value: django} +- fields: {question: 5, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} model: questionnaire.choice pk: 13 -- fields: {question: 5, sortid: 2, text_de: Pylons, text_en: Pylons, value: pylons} +- fields: {question: 5, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} model: questionnaire.choice pk: 14 -- fields: {question: 5, sortid: 3, text_de: Turbogears, text_en: Turbogears, value: turbogears} +- fields: {question: 5, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, + value: turbogears} model: questionnaire.choice pk: 15 -- fields: {question: 5, sortid: 4, text_de: CherryPy, text_en: CherryPy, value: cherrypy} +- fields: {question: 5, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, + value: cherrypy} model: questionnaire.choice pk: 16 -- fields: {body_de: "Wilkommen zu der Fragebogenbeispielseite!\r\n\r\nWenn Sie einen\ - \ einfachen Fragebogen sehen wollen, \"tun Sie's!\":/take/1\r\n\r\nSie k\xF6\ - nnen das \"Admin Interface\":/admin/ benutzen, um den Fragebogen zu \xE4ndern.", - body_en: "Welcome to the example Questionnaire website!\r\n\r\nIf you wish to\ - \ take a sample questionnaire, \"go for it!\":/take/1\r\n\r\nUse the \"Admin\ - \ Interface\":/admin/, to change the questionnaire.", public: true, title_de: '', - title_en: Welcome} +- fields: {answer: '["yes"]', question: 1, run: 9, subject: 11} + model: questionnaire.answer + pk: 1 +- fields: {answer: '["weizen"]', question: 2, run: 9, subject: 11} + model: questionnaire.answer + pk: 2 +- fields: {answer: '["becks", "heineken"]', question: 3, run: 9, subject: 11} + model: questionnaire.answer + pk: 3 +- fields: {answer: '["django", "pylons", ["rails"]]', question: 4, run: 9, subject: 11} + model: questionnaire.answer + pk: 4 +- fields: {answer: '["django"]', question: 5, run: 9, subject: 11} + model: questionnaire.answer + pk: 5 +- fields: {body_de: "Wilkommen zu der Fragebogenbeispielseite!\r\n\r\nWenn Sie einen + einfachen Fragebogen sehen wollen, \"tun Sie's!\r\n\r\nSie + k\xF6nnen das Admin Interface benutzen, um den Fragebogen + zu \xE4ndern.", body_en: "Welcome to the example Questionnaire website!\r\n\r\nIf + you wish to take a sample questionnaire, go for it!\r\n\r\nUse + the Admin Interface to change the questionnaire.", public: true, + title_de: '', title_en: Welcome} model: page.page pk: index diff --git a/example/manage.py b/example/manage.py deleted file mode 100755 index 8fa8bc6..0000000 --- a/example/manage.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == '__main__': - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') - - from django.core.management import execute_from_command_line - execute_from_command_line(sys.argv) \ No newline at end of file diff --git a/questionnaire/templates/base-questionnaire.html b/questionnaire/templates/base-questionnaire.html index 19ac5cb..9aec0a5 100644 --- a/questionnaire/templates/base-questionnaire.html +++ b/questionnaire/templates/base-questionnaire.html @@ -35,9 +35,8 @@ {% endfor %} {% endblock language %} -
    From 195dcda657ad0cdb6193b26270f3c4906df96533 Mon Sep 17 00:00:00 2001 From: eric Date: Fri, 16 Jun 2017 18:39:43 -0400 Subject: [PATCH 3/6] use patched transmeta --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 91447dc..9f34372 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,9 @@ setup( author_email="gcaprio@eldestdaughter.com, eric@hellman.net", license="BSD", url="https://github.com/EbookFoundation/fef-questionnaire", + dependency_links=[ + 'https://github.com/eshellman/django-transmeta/archive/v0.7.3-eshellman.tar.gz' + ], packages=find_packages(exclude=["example"]), include_package_data=True, classifiers=[ @@ -29,7 +32,7 @@ setup( zip_safe=False, install_requires=[ 'django', - 'django-transmeta', + 'django-transmeta >= 0.7.4', 'django-compat', 'pyyaml', 'pyparsing' From 9454be4d91ae59c0c1178596ed522dc8a4b6061f Mon Sep 17 00:00:00 2001 From: eric Date: Mon, 19 Jun 2017 22:20:46 -0400 Subject: [PATCH 4/6] add other item code to app * refactor views * add example fixtures * consistent terminology for questionnaires --- README.md | 36 +- example/fixtures/books.yaml | 60 ++ example/fixtures/example.yaml | 337 +++++++++++ example/fixtures/initial_data.yaml | 127 ---- example/settings.py | 8 + example/templates/base.html | 55 -- example/urls.py | 10 +- questionnaire/fixtures/sampleSurvey.json | 2 +- questionnaire/forms.py | 12 + ...urvey_nonces.py => make_landing_nonces.py} | 2 +- questionnaire/migrations/0001_initial.py | 4 +- questionnaire/models.py | 12 +- questionnaire/page/views.py | 12 +- questionnaire/run.py | 258 ++++++++ .../templates/manage_questionnaire.html | 19 + questionnaire/templates/pages/complete.html | 2 +- questionnaire/templates/pages/generic.html | 2 +- questionnaire/templates/pages/summaries.html | 2 +- .../templates/questionnaire/complete.en.html | 2 +- .../templates/questionnaire/questionset.html | 2 +- questionnaire/templates/questionnaires.html | 52 ++ questionnaire/urls.py | 20 +- questionnaire/views.py | 566 +++++++----------- setup.py | 5 +- 24 files changed, 1020 insertions(+), 587 deletions(-) create mode 100644 example/fixtures/books.yaml create mode 100644 example/fixtures/example.yaml delete mode 100644 example/fixtures/initial_data.yaml delete mode 100644 example/templates/base.html create mode 100644 questionnaire/forms.py rename questionnaire/management/commands/{make_survey_nonces.py => make_landing_nonces.py} (87%) create mode 100644 questionnaire/run.py create mode 100644 questionnaire/templates/manage_questionnaire.html create mode 100644 questionnaire/templates/questionnaires.html diff --git a/README.md b/README.md index 125b659..58c3e80 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ The old versions are tagged as follows: The "ED-questionnaire" version was dubbed v3.0. It is not compatible with the v2.x branches. -The "FEF-questionnaire" version was created to add the ability to link the questionnaire to individual books in a book database. We'll call this v4.0 +The "FEF-questionnaire" version was created to add the ability to link the questionnaire to individual books in a book database. We'll call this v4.0. The app was extensively renovated and updated. This work was funded by the Mellon Foundation as part of the [Mapping the Free Ebook Supply Chain Project](https://www.publishing.umich.edu/projects/mapping-the-free-ebook/). ## About this Manual -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. +Questionnaire was 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 please help us improve it. What it does cover is the following: @@ -101,18 +101,25 @@ Add the questionnaire template directory as well as your own to TEMPLATES: If you want to use multiple languages, add the i18n context processor to TEMPLATES 'context_processors': ['django.template.context_processors.i18n',] -And finally, add `transmeta`, `questionnaire` to your INSTALLED_APPS: +Now add `transmeta`, `questionnaire` to your INSTALLED_APPS: 'transmeta', 'questionnaire', 'questionnaire.page', -Next up we want to edit the `urls.py` file of your project to link the questionnaire views to your site's url configuration. See the example app to see how. +And finally, add the fef-questionaire specific parameters. For our example, we'll use: + + QUESTIONNAIRE_PROGRESS = 'async' + QUESTIONNAIRE_USE_SESSION = False + QUESTIONNAIRE_ITEM_MODEL = 'mysite.Book' + QUESTIONNAIRE_SHOW_ITEM_RESULTS = True + +Next up we want to edit the `urls.py` file of your project to link the questionnaire views to your site's url configuration. The example app shows you how. ### Initialize the database -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: +Having done that we can initialize our database. (For this to work you must have set up your DATABASES in `settings.py`.). First, in your CLI navigate back to the `mysite` folder: cd ../.. @@ -141,17 +148,20 @@ Open `mysite/mysite/settings.py` and add following lines, representing your lang ('de', 'Deutsch') ) -To run more than one language, set - python manage.py sync_transmeta_db +If you've added a language, you'll need to + + python manage.py makemigrations + python manage.py migrate If you want to use multiple languages, add the i18n context processor to TEMPLATES 'context_processors': ['django.template.context_processors.i18n',] and set up middleware as described in the [Django translation docs](https://docs.djangoproject.com/en/1.8/topics/i18n/translation/) -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`): +To see example questionnaires 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/fef-questionnaire/example/fixtures/initial_data.yaml + python manage.py loaddata ./apps/fef-questionnaire/example/fixtures/example.yaml + python manage.py loaddata ./apps/fef-questionnaire/example/fixtures/books.yaml ### Start the server! @@ -274,7 +284,7 @@ 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. +In Poll mode, the landing url links a Questionnaire to an Object and a User to a Subject. This is useful if you have a database of things you want to ask questions about. Migration of 1.x to 2.0 ----------------------- @@ -288,7 +298,7 @@ Version 4.0 does not support migration of 1.X data files. Here's what we think we learned: -### ED.questionnaire is a Framework +### Questionnaire is a Framework More than anything else ed.questionnaire should be thought of as a framework. Your site has to provide and do certain things for the questionnaire to work. If your site is a customized questionnaire for a company with other needs on the same site you will end up integrating code which will call questionnaire to setup runs and you will probably work through the answer records to provide some sort of summary. @@ -321,6 +331,8 @@ Version 4.0 has not been tested for compatibility with previous versions. * styling of required questions has been spiffed up. * export of response data has been fixed. * compatibility with Django 1.8. Compatibility with other versions of Django has not been tested. - +* refactoring of views +* documentation has been updated to reflect Django 1.8. +* email and subject functionality has not been tested diff --git a/example/fixtures/books.yaml b/example/fixtures/books.yaml new file mode 100644 index 0000000..86199f5 --- /dev/null +++ b/example/fixtures/books.yaml @@ -0,0 +1,60 @@ +- fields: {title: "A Christmas Carol"} + model: mysite.book + pk: 4 +- fields: {title: "A Little Princess"} + model: mysite.book + pk: 5 +- fields: {title: "A Portrait of the Artist as a Young Man"} + model: mysite.book + pk: 6 +- fields: {title: "A Room with a View"} + model: mysite.book + pk: 7 +- fields: {title: "Agnes Grey"} + model: mysite.book + pk: 8 +- fields: {title: "Anne of Green Gables"} + model: mysite.book + pk: 9 +- fields: {title: "The Personal History, Adventures, Experience and Observation of David Copperfield the Younger of Blunderstone Rookery"} + model: mysite.book + pk: 10 +- fields: {title: "Far From the Madding Crowd"} + model: mysite.book + pk: 11 +- fields: {title: "Howards End"} + model: mysite.book + pk: 12 +- fields: {title: "Jacob\'s Room"} + model: mysite.book + pk: 13 +- fields: {title: "The Merry Adventures of Robin Hood"} + model: mysite.book + pk: 14 +- fields: {title: "Sense and Sensibility"} + model: mysite.book + pk: 15 +- fields: {title: "The Secret Garden"} + model: mysite.book + pk: 16 +- fields: {title: "The Adventures of Tom Sawyer"} + model: mysite.book + pk: 17 +- fields: {title: "The Invisible Man"} + model: mysite.book + pk: 18 +- fields: {title: "The Last of the Mohicans"} + model: mysite.book + pk: 19 +- fields: {title: "Oliver Twist, or the Parish Boy\'s Progress"} + model: mysite.book + pk: 20 +- fields: {title: "Peter Pan in Kensington Gardens"} + model: mysite.book + pk: 21 +- fields: {title: "Tales of the Jazz Age"} + model: mysite.book + pk: 22 +- fields: {title: "Tess of the D'Urbervilles: A Pure Woman Faithfully Presented"} + model: mysite.book + pk: 23 diff --git a/example/fixtures/example.yaml b/example/fixtures/example.yaml new file mode 100644 index 0000000..bda5e43 --- /dev/null +++ b/example/fixtures/example.yaml @@ -0,0 +1,337 @@ +- fields: {admin_access_only: false, html: '', name: 'Example Questionnaire', parse_html: false, redirect_url: /} + model: questionnaire.questionnaire + pk: 1 +- fields: {checks: '', heading: Page 1, parse_html: true, questionnaire: 1, sortid: 1, + text_de:

    Biervorlieben

    , text_en:

    Beer Preferences

    } + model: questionnaire.questionset + pk: 1 +- fields: {checks: '', heading: Page 2, parse_html: false, questionnaire: 1, sortid: 2, + text_de: h1. Python Web Frameworks, text_en: h1. Python Web Frameworks} + model: questionnaire.questionset + pk: 2 +- fields: {checks: '', heading: Finished!, parse_html: true, questionnaire: 1, sortid: 99, + text_de: "

    Vielen Dank

    \r\n \r\n Wir hoffen, dass Sie uns in Zukunft + wieder besuchen!", text_en: "

    Thank you!

    \r\n \r\n We hope that you + visit us again in the future!"} + model: questionnaire.questionset + pk: 3 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '1', parse_html: false, questionset: 1, sort_id: null, text_de: 'Trinken + Sie Bier?', text_en: 'Do you drink beer?', type: choice-yesno} + model: questionnaire.question + pk: 1 +- fields: {checks: 'requiredif="1,yes"', extra_de: '', extra_en: '', footer_de: null, + footer_en: '', number: '2', parse_html: false, questionset: 1, sort_id: null, + text_de: 'Was Art von Bier trinken Sie am meisten?', text_en: 'What type of beer + do you drink predominantly?', type: choice-freeform} + model: questionnaire.question + pk: 2 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '3', parse_html: false, questionset: 1, sort_id: null, text_de: 'Welche + von dieser Bieren haben Sie probiert?', text_en: 'Which of these brands of beer + have you tried?', type: choice-multiple} + model: questionnaire.question + pk: 3 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '4', parse_html: false, questionset: 2, sort_id: null, text_de: 'Which + Python Web Frameworks have you tried?', text_en: 'Which Python Web Frameworks + have you tried?', type: choice-multiple-freeform} + model: questionnaire.question + pk: 4 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', + number: '5', parse_html: false, questionset: 2, sort_id: null, text_de: "Welche + m\xF6gen Sie am liebsten?", text_en: 'Which do you like the most?', type: choice-freeform} + model: questionnaire.question + pk: 5 +- fields: {question: 2, sortid: 1, tags: '', text_de: Pils, text_en: Pils/Bitter, + value: pils} + model: questionnaire.choice + pk: 1 +- fields: {question: 2, sortid: 2, tags: '', text_de: Helles, text_en: Lager, value: helles} + model: questionnaire.choice + pk: 2 +- fields: {question: 2, sortid: 3, tags: '', text_de: Weizen, text_en: Wheat-beer, + value: weizen} + model: questionnaire.choice + pk: 3 +- fields: {question: 3, sortid: 1, tags: '', text_de: Heineken, text_en: Heineken, + value: heineken} + model: questionnaire.choice + pk: 4 +- fields: {question: 3, sortid: 2, tags: '', text_de: Becks, text_en: Becks, value: becks} + model: questionnaire.choice + pk: 5 +- fields: {question: 3, sortid: 3, tags: '', text_de: "L\xF6wenbr\xE4u", text_en: "L\xF6wenbr\xE4u", + value: lowenbrau} + model: questionnaire.choice + pk: 6 +- fields: {question: 2, sortid: 4, tags: '', text_de: "Altbier/D\xFCssel", text_en: Altbier, + value: altbier} + model: questionnaire.choice + pk: 7 +- fields: {question: 2, sortid: 5, tags: '', text_de: "K\xF6lsch", text_en: "K\xF6lsch", + value: koelsch} + model: questionnaire.choice + pk: 8 +- fields: {question: 4, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} + model: questionnaire.choice + pk: 9 +- fields: {question: 4, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} + model: questionnaire.choice + pk: 10 +- fields: {question: 4, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, + value: turbogears} + model: questionnaire.choice + pk: 11 +- fields: {question: 4, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, + value: cherrypy} + model: questionnaire.choice + pk: 12 +- fields: {question: 5, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} + model: questionnaire.choice + pk: 13 +- fields: {question: 5, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} + model: questionnaire.choice + pk: 14 +- fields: {question: 5, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, + value: turbogears} + model: questionnaire.choice + pk: 15 +- fields: {question: 5, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, + value: cherrypy} + model: questionnaire.choice + pk: 16 +- fields: {body_de: "Wilkommen zu der Fragebogenbeispielseite!\r\n\r\nWenn Sie einen + einfachen Fragebogen sehen wollen, \"tun Sie's!\r\n\r\nSie + k\xF6nnen das Admin Interface benutzen, um den Fragebogen + zu \xE4ndern.", body_en: "Welcome to the Example Questionnaire website!\r\n\r\nIf you wish to + take a sample questionnaire, go for it!. Or link + a questionnaire to an item\r\n\r\nUse the Admin Interface to + change the questionnaire.", public: true, + title_de: '', title_en: Welcome} + model: page.page + pk: index +- fields: {admin_access_only: false, html: questionnaire html here, name: MappingSurvey, + parse_html: false, redirect_url: ''} + model: questionnaire.questionnaire + pk: 3 +- fields: {checks: '', heading: Open Access Ebooks (Part 1), parse_html: true, questionnaire: 3, + sortid: 1, text_de: '', text_en: "

    Introduction

    \r\n

    \r\nWelcome, reader of + {{ landing_object.title }}! And thanks for visiting Unglue.it to complete + this survey, part of a research project to understand how open access ebooks + are discovered and how readers use them. For more information, please see the + project description.\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 10 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 spy on you 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. A report + of the results will be made available in July 2017 ]\r\n

    "} + model: questionnaire.questionset + pk: 5 +- fields: {checks: '', heading: Now About You..., parse_html: true, questionnaire: 3, + sortid: 2, text_de: '', text_en: '

    And now, four 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_de: '', extra_en: '', footer_de: '', footer_en: '', number: '1', parse_html: true, + questionset: 5, sort_id: 1, text_de: '', 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? - or in some other way?\r\n", type: open} + model: questionnaire.question + pk: 16 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '2', parse_html: false, + questionset: 5, sort_id: 2, text_de: '', 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? Your library?", type: open} + model: questionnaire.question + pk: 17 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '3', parse_html: true, + questionset: 5, sort_id: 3, text_de: '', text_en: 'Why are you interested in this book?', type: choice-multiple-freeform} + model: questionnaire.question + pk: 18 +- fields: {checks: '', extra_de: '', extra_en: 'If Yes - is there any particular reason why you + are using this version rather than one of the others?', footer_de: '', footer_en: '', number: '4', + parse_html: false, questionset: 5, sort_id: 4, text_de: '', text_en: 'Are you aware that this + title is available in multiple different digital and printed formats?', type: choice-yesnocomment-optional} + model: questionnaire.question + pk: 19 +- fields: {checks: '', extra_de: '', extra_en: 'Please tell us in more detail:', footer_de: '', 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_de: '', 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_de: '', extra_en: '', footer_de: '', footer_en: '', number: '1', parse_html: false, + questionset: 6, sort_id: null, text_de: '', text_en: 'Where do you live?', type: choice-freeform-optional} + model: questionnaire.question + pk: 21 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '2', parse_html: false, + questionset: 6, sort_id: null, text_de: '', text_en: 'What do you do for a living?', type: open} + model: questionnaire.question + pk: 22 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: "\r\n\r\n \r\n\r\n", number: '4', + parse_html: false, questionset: 6, sort_id: null, text_de: '', text_en: 'When did you finish + your formal education?', type: choice-freeform-optional} + model: questionnaire.question + pk: 23 +- fields: {checks: required-no, extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '5', parse_html: false, + questionset: 6, sort_id: null, text_de: '', text_en: ' Is there anything else you would like + to tell us or think we should know about how you found or are using the ebook? + or about yourself?', type: open-textfield} + model: questionnaire.question + pk: 24 +- fields: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '1', parse_html: false, + questionset: 7, sort_id: null, text_de: '', 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: {checks: '', extra_de: '', extra_en: '', footer_de: '', footer_en: '', number: '3', parse_html: false, + questionset: 6, sort_id: null, text_de: '', text_en: 'How old are you?', type: choice-optional} + model: questionnaire.question + pk: 26 +- fields: {question: 18, sortid: 1, tags: '', text_de: '', 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_de: '', 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_de: '', text_en: I need to read it for a course, + value: course} + model: questionnaire.choice + pk: 19 +- fields: {question: 20, sortid: 1, tags: '', text_de: '', 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_de: '', 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_de: '', 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_de: '', 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_de: '', 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_de: '', text_en: 'Share it with my friends ', + value: share} + model: questionnaire.choice + pk: 25 +- fields: {question: 20, sortid: 7, tags: '', text_de: '', text_en: Print it out, value: print} + model: questionnaire.choice + pk: 26 +- fields: {question: 20, sortid: 8, tags: '', text_de: '', text_en: "I\u2019m creating/collating + a (online) library", value: catalog} + model: questionnaire.choice + pk: 27 +- fields: {question: 20, sortid: 9, tags: '', text_de: '', text_en: "Something else entirely \u2026. ", + value: else} + model: questionnaire.choice + pk: 28 +- fields: {question: 21, sortid: 1, tags: '', text_de: '', text_en: Canada/USA, value: us} + model: questionnaire.choice + pk: 29 +- fields: {question: 21, sortid: 4, tags: '', text_de: '', text_en: Europe, value: eu} + model: questionnaire.choice + pk: 30 +- fields: {question: 21, sortid: 3, tags: '', text_de: '', text_en: South America, value: sa} + model: questionnaire.choice + pk: 31 +- fields: {question: 21, sortid: 2, tags: '', text_de: '', text_en: Central America/ Caribbean, + value: ca} + model: questionnaire.choice + pk: 32 +- fields: {question: 21, sortid: 9, tags: '', text_de: '', text_en: Other Asia, value: as} + model: questionnaire.choice + pk: 33 +- fields: {question: 21, sortid: 6, tags: '', text_de: '', text_en: Africa, value: af} + model: questionnaire.choice + pk: 34 +- fields: {question: 21, sortid: 5, tags: '', text_de: '', text_en: Middle East, value: me} + model: questionnaire.choice + pk: 35 +- fields: {question: 21, sortid: 11, tags: '', text_de: '', text_en: Another Planet, value: ap} + model: questionnaire.choice + pk: 36 +- fields: {question: 23, sortid: 1, tags: '', text_de: '', text_en: "I haven\u2019t - I\u2019m + still a student", value: x} + model: questionnaire.choice + pk: 37 +- fields: {question: 23, sortid: 2, tags: '', text_de: '', text_en: At primary/elementary school, + value: '8'} + model: questionnaire.choice + pk: 38 +- fields: {question: 23, sortid: 3, tags: '', text_de: '', text_en: At high school/secondary school, + value: h} + model: questionnaire.choice + pk: 39 +- fields: {question: 23, sortid: 4, tags: '', text_de: '', text_en: After trade qualifications, + value: t} + model: questionnaire.choice + pk: 40 +- fields: {question: 23, sortid: 5, tags: '', text_de: '', text_en: 'At College/Undergraduate Degree ', + value: c} + model: questionnaire.choice + pk: 41 +- fields: {question: 23, sortid: 6, tags: '', text_de: '', text_en: At Grad School/post-graduate + university, value: g} + model: questionnaire.choice + pk: 42 +- fields: {question: 18, sortid: 4, tags: '', text_de: '', text_en: 'If other, tell us more...', + value: other} + model: questionnaire.choice + pk: 43 +- fields: {question: 26, sortid: 1, tags: '', text_de: '', text_en: under 18, value: teen} + model: questionnaire.choice + pk: 46 +- fields: {question: 26, sortid: 2, tags: '', text_de: '', text_en: 18-30, value: young} + model: questionnaire.choice + pk: 47 +- fields: {question: 26, sortid: 3, tags: '', text_de: '', text_en: 31-60, value: mid} + model: questionnaire.choice + pk: 48 +- fields: {question: 26, sortid: 4, tags: '', text_de: '', text_en: over 60, value: old} + model: questionnaire.choice + pk: 49 +- fields: {question: 26, sortid: 5, tags: '', text_de: '', text_en: decline to say, value: x} + model: questionnaire.choice + pk: 50 +- fields: {question: 21, sortid: 10, tags: '', text_de: '', text_en: Oceania, value: oc} + model: questionnaire.choice + pk: 51 +- fields: {question: 21, sortid: 7, tags: '', text_de: '', text_en: India, value: in} + model: questionnaire.choice + pk: 52 +- fields: {question: 21, sortid: 8, tags: '', text_de: '', text_en: China, value: zh} + model: questionnaire.choice + pk: 53 diff --git a/example/fixtures/initial_data.yaml b/example/fixtures/initial_data.yaml deleted file mode 100644 index 51a765b..0000000 --- a/example/fixtures/initial_data.yaml +++ /dev/null @@ -1,127 +0,0 @@ -- fields: {admin_access_only: false, html: '', name: example, parse_html: false, redirect_url: /} - model: questionnaire.questionnaire - pk: 1 -- fields: {checks: '', heading: Page 1, parse_html: true, questionnaire: 1, sortid: 1, - text_de:

    Biervorlieben

    , text_en:

    Beer Preferences

    } - model: questionnaire.questionset - pk: 1 -- fields: {checks: '', heading: Page 2, parse_html: false, questionnaire: 1, sortid: 2, - text_de: h1. Python Web Frameworks, text_en: h1. Python Web Frameworks} - model: questionnaire.questionset - pk: 2 -- fields: {checks: '', heading: Finished!, parse_html: true, questionnaire: 1, sortid: 99, - text_de: "

    Vielen Dank

    \r\n \r\n Wir hoffen, dass Sie uns in Zukunft - wieder besuchen!", text_en: "

    Thank you!

    \r\n \r\n We hope that you - visit us again in the future!"} - model: questionnaire.questionset - pk: 3 -- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', - number: '1', parse_html: false, questionset: 1, sort_id: null, text_de: 'Trinken - Sie Bier?', text_en: 'Do you drink beer?', type: choice-yesno} - model: questionnaire.question - pk: 1 -- fields: {checks: 'requiredif="1,yes"', extra_de: '', extra_en: '', footer_de: null, - footer_en: '', number: '2', parse_html: false, questionset: 1, sort_id: null, - text_de: 'Was Art von Bier trinken Sie am meisten?', text_en: 'What type of beer - do you drink predominantly?', type: choice-freeform} - model: questionnaire.question - pk: 2 -- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', - number: '3', parse_html: false, questionset: 1, sort_id: null, text_de: 'Welche - von dieser Bieren haben Sie probiert?', text_en: 'Which of these brands of beer - have you tried?', type: choice-multiple} - model: questionnaire.question - pk: 3 -- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', - number: '4', parse_html: false, questionset: 2, sort_id: null, text_de: 'Which - Python Web Frameworks have you tried?', text_en: 'Which Python Web Frameworks - have you tried?', type: choice-multiple-freeform} - model: questionnaire.question - pk: 4 -- fields: {checks: '', extra_de: '', extra_en: '', footer_de: null, footer_en: '', - number: '5', parse_html: false, questionset: 2, sort_id: null, text_de: "Welche - m\xF6gen Sie am liebsten?", text_en: 'Which do you like the most?', type: choice-freeform} - model: questionnaire.question - pk: 5 -- fields: {question: 2, sortid: 1, tags: '', text_de: Pils, text_en: Pils/Bitter, - value: pils} - model: questionnaire.choice - pk: 1 -- fields: {question: 2, sortid: 2, tags: '', text_de: Helles, text_en: Lager, value: helles} - model: questionnaire.choice - pk: 2 -- fields: {question: 2, sortid: 3, tags: '', text_de: Weizen, text_en: Wheat-beer, - value: weizen} - model: questionnaire.choice - pk: 3 -- fields: {question: 3, sortid: 1, tags: '', text_de: Heineken, text_en: Heineken, - value: heineken} - model: questionnaire.choice - pk: 4 -- fields: {question: 3, sortid: 2, tags: '', text_de: Becks, text_en: Becks, value: becks} - model: questionnaire.choice - pk: 5 -- fields: {question: 3, sortid: 3, tags: '', text_de: "L\xF6wenbr\xE4u", text_en: "L\xF6wenbr\xE4u", - value: lowenbrau} - model: questionnaire.choice - pk: 6 -- fields: {question: 2, sortid: 4, tags: '', text_de: "Altbier/D\xFCssel", text_en: Altbier, - value: altbier} - model: questionnaire.choice - pk: 7 -- fields: {question: 2, sortid: 5, tags: '', text_de: "K\xF6lsch", text_en: "K\xF6lsch", - value: koelsch} - model: questionnaire.choice - pk: 8 -- fields: {question: 4, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} - model: questionnaire.choice - pk: 9 -- fields: {question: 4, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} - model: questionnaire.choice - pk: 10 -- fields: {question: 4, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, - value: turbogears} - model: questionnaire.choice - pk: 11 -- fields: {question: 4, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, - value: cherrypy} - model: questionnaire.choice - pk: 12 -- fields: {question: 5, sortid: 1, tags: '', text_de: Django, text_en: Django, value: django} - model: questionnaire.choice - pk: 13 -- fields: {question: 5, sortid: 2, tags: '', text_de: Pylons, text_en: Pylons, value: pylons} - model: questionnaire.choice - pk: 14 -- fields: {question: 5, sortid: 3, tags: '', text_de: Turbogears, text_en: Turbogears, - value: turbogears} - model: questionnaire.choice - pk: 15 -- fields: {question: 5, sortid: 4, tags: '', text_de: CherryPy, text_en: CherryPy, - value: cherrypy} - model: questionnaire.choice - pk: 16 -- fields: {answer: '["yes"]', question: 1, run: 9, subject: 11} - model: questionnaire.answer - pk: 1 -- fields: {answer: '["weizen"]', question: 2, run: 9, subject: 11} - model: questionnaire.answer - pk: 2 -- fields: {answer: '["becks", "heineken"]', question: 3, run: 9, subject: 11} - model: questionnaire.answer - pk: 3 -- fields: {answer: '["django", "pylons", ["rails"]]', question: 4, run: 9, subject: 11} - model: questionnaire.answer - pk: 4 -- fields: {answer: '["django"]', question: 5, run: 9, subject: 11} - model: questionnaire.answer - pk: 5 -- fields: {body_de: "Wilkommen zu der Fragebogenbeispielseite!\r\n\r\nWenn Sie einen - einfachen Fragebogen sehen wollen, \"tun Sie's!\r\n\r\nSie - k\xF6nnen das Admin Interface benutzen, um den Fragebogen - zu \xE4ndern.", body_en: "Welcome to the example Questionnaire website!\r\n\r\nIf - you wish to take a sample questionnaire, go for it!\r\n\r\nUse - the Admin Interface to change the questionnaire.", public: true, - title_de: '', title_en: Welcome} - model: page.page - pk: index diff --git a/example/settings.py b/example/settings.py index 3aaf2e3..f2a7f82 100644 --- a/example/settings.py +++ b/example/settings.py @@ -40,6 +40,7 @@ INSTALLED_APPS = ( 'transmeta', 'questionnaire', 'questionnaire.page', + 'mysite', ) MIDDLEWARE_CLASSES = ( @@ -132,6 +133,7 @@ LANGUAGES = ( # 'none' # Completely omits the progressbar. Good if you don't want one or if the # 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. @@ -141,4 +143,10 @@ QUESTIONNAIRE_PROGRESS = 'async' # user goes through the steps of the question set. QUESTIONNAIRE_USE_SESSION = False +# for item-linked questionnaires, defines the model used for the item-linked questionaires. +QUESTIONNAIRE_ITEM_MODEL = 'mysite.Book' + +# for item-linked questionnaires, show the results to any logged in user. If the results are meant to be private, this should be false, and you should wrap the corresponding views with access control appropriate to your application. + +QUESTIONNAIRE_SHOW_ITEM_RESULTS = True diff --git a/example/templates/base.html b/example/templates/base.html deleted file mode 100644 index 81ca6e8..0000000 --- a/example/templates/base.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - {% block title %}Questionnaire{% endblock title %} - - - - - - - {% block headextra %} - {% endblock %} - - - - - -
    -
    - -
    - {% block language %} - {% for lang in LANGUAGES %} - {% if not forloop.first %} | {% endif %} - {{ lang.1 }} - {% endfor %} - {% endblock language %} -
    - - - -
    -
     
    -
    - {% block content %} -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud execitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.

    - {% endblock %} -
    -
     
    -
    -
    -
    - - diff --git a/example/urls.py b/example/urls.py index f0ce120..81095ec 100644 --- a/example/urls.py +++ b/example/urls.py @@ -1,18 +1,16 @@ from django.conf.urls import patterns, include, url from django.contrib import admin +import questionnaire + admin.autodiscover() urlpatterns = patterns('', - 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'), + url(r'q/', include('questionnaire.urls')), + # admin url(r'^admin/doc/', include('django.contrib.admindocs.urls')), url(r'^admin/', include(admin.site.urls)), ) diff --git a/questionnaire/fixtures/sampleSurvey.json b/questionnaire/fixtures/sampleSurvey.json index b45f2d9..4c58914 100644 --- a/questionnaire/fixtures/sampleSurvey.json +++ b/questionnaire/fixtures/sampleSurvey.json @@ -18,7 +18,7 @@ { "fields": { "admin_access_only": false, - "html": "survey html here", + "html": "questionnaire html here", "name": "MappingSurvey", "parse_html": false, "redirect_url": "" diff --git a/questionnaire/forms.py b/questionnaire/forms.py new file mode 100644 index 0000000..1a3502d --- /dev/null +++ b/questionnaire/forms.py @@ -0,0 +1,12 @@ +from django import forms +from .models import Questionnaire + +class NewLandingForm(forms.Form): + label = forms.CharField(max_length=64, required=True) + questionnaire = forms.ModelChoiceField( + Questionnaire.objects.all(), + widget=forms.widgets.RadioSelect(), + empty_label=None, + required=True, + ) + diff --git a/questionnaire/management/commands/make_survey_nonces.py b/questionnaire/management/commands/make_landing_nonces.py similarity index 87% rename from questionnaire/management/commands/make_survey_nonces.py rename to questionnaire/management/commands/make_landing_nonces.py index 1086739..88fbbae 100644 --- a/questionnaire/management/commands/make_survey_nonces.py +++ b/questionnaire/management/commands/make_landing_nonces.py @@ -3,7 +3,7 @@ from ...models import Landing class Command(BaseCommand): - help = "make survey nonces with the specified label" + help = "make landing nonces with the specified label" args = "