polls : managing questionnaires and responses

This document describes the lino_xl.lib.polls plugin which adds database models and functionality for managing polls.

>>> import lino
>>> lino.startup('lino_book.projects.polly.settings.demo')
>>> from lino.api.doctest import *

Overview

A Poll is a series of Questions which we want to ask repeatedly to different people. Each Question has a question text and a ChoiceSet, i.e. a stored ordered set of possible choices.

A Response is when somebody answers to a Poll. A response has a user (the guiy who asked and/or entered the data) and the partner (the guy who answered).

The answers themselves are stored in the database as a set of AnswerChoices objects, each of which represents a given Choice selected by the questioned person for a given Question of the Poll. If the Question is multiple choice, then there may be more than one AnswerChoice per Question. A Response can also contain a set of AnswerRemarks, each of with represents a remark written by the responding person for a given question.

The detail view of a Response will usually contain two different slave tables showing the answers:

  • The AnswersByResponseEditor summary shows one row per question (also those that have not been answered) and one column for each response of the same poll for this partner. The answers for this response are editable unless the response is registered. The answers of other responses are never editable.

  • The AnswersByResponsePrint summary shows one row per answered question and the answer given in this response. It is designed to get printed.

Model reference

class lino_xl.lib.polls.Poll

A series of questions.

class lino_xl.lib.polls.Polls
class lino_xl.lib.polls.AllPolls

Show all polls of all users.

class lino_xl.lib.polls.Question

A question of a poll.

number

The number of this question within this poll.

poll
title
details
choiceset
multiple_choices
is_heading
seqno
class lino_xl.lib.polls.Questions
class lino_xl.lib.polls.QuestionsByPoll
class lino_xl.lib.polls.ChoiceSet
class lino_xl.lib.polls.ChoiceSets
class lino_xl.lib.polls.Choice
choiceset
class lino_xl.lib.polls.Choices
class lino_xl.lib.polls.ChoicesBySet
class lino_xl.lib.polls.Response
poll
date
state
remark
partner
toggle_choice

See ToggleChoice

class lino_xl.lib.polls.Responses
class lino_xl.lib.polls.AllResponses
class lino_xl.lib.polls.MyResponses
class lino_xl.lib.polls.ResponsesByPoll
class lino_xl.lib.polls.ResponsesByPartner

Show all responses for a given partner. Default view shows a summary of all responses for a that partner using a bullet list grouped by poll.

class lino_xl.lib.polls.AnswerChoice
response
question
choice
class lino_xl.lib.polls.AnswerRemark
response
question
remark

Answers by response

class lino_xl.lib.polls.AnswersByResponseBase
class lino_xl.lib.polls.AnswersByResponsePrint
class lino_xl.lib.polls.AnswersByResponseEditor
class lino_xl.lib.polls.AnswersByResponse

The table used for answering to a poll. This is a virtual table and its rows are volatile AnswersByResponseRow instances.

answer_buttons

A virtual field that displays the currently selected answer(s) for this question, eventually (if editing is permitted) together with buttons to modify the selection.

class lino_xl.lib.polls.AnswersByResponseRow

Volatile object to represent the one and only answer to a given question in a given response.

Used by AnswersByResponse whose rows are instances of this.

class lino_xl.lib.polls.AnswerRemarkField

An editable virtual field.

Answers by question

class lino_xl.lib.polls.AnswersByQuestion

The rows of this table are volatile AnswersByQuestionRow instances.

class lino_xl.lib.polls.AnswersByQuestionRow

Volatile object to represent a row of AnswersByQuestion.

class lino_xl.lib.polls.PollResult

Shows a summay of responses to this poll.

Roles

class lino_xl.lib.polls.PollsUser

A user who has access to polls functionality.

class lino_xl.lib.polls.PollsStaff

A user who manages configuration of polls functionality.

class lino_xl.lib.polls.PollsAdmin

Actions

class lino_xl.lib.polls.ToggleChoice

Toggle the given choice for the given question in this response.

Choicelists

class lino_xl.lib.polls.PollStates

The list of possible states of a Poll.

>>> rt.show(polls.PollStates)
======= ======== ======== =============
 value   name     text     Button text
------- -------- -------- -------------
 10      draft    Draft
 20      active   Active
 30      closed   Closed
======= ======== ======== =============
class lino_xl.lib.polls.ResponseStates

The list of possible states of a Response.

>>> rt.show(polls.ResponseStates)
======= ============ ============ =============
 value   name         text         Button text
------- ------------ ------------ -------------
 10      draft        Draft
 20      registered   Registered
======= ============ ============ =============

Miscellaneous tests

>>> print(settings.SETTINGS_MODULE)
lino_book.projects.polly.settings.demo
>>> pk = 2
>>> obj = polls.Response.objects.get(pk=pk)
>>> print(obj)
Robin Rood's response to Participant feedback
>>> rt.login(obj.user.username).show(polls.AnswersByResponseEditor, obj)
... 
Question 23/10/2014

1) There was enough to eat. **1** **2** **3** **4** **5** (**Remark**)

2) The stewards were nice and attentive. **1** **2** **3** **4** **5** (**Remark**)

3) The participation fee was worth the money. **1** **2** **3** **4** **5** (**Remark**)

4) Next time I will participate again. **1** **2** **3** **4** **5** (**Remark**)
>>> mt = contenttypes.ContentType.objects.get_for_model(obj.__class__).id
>>> url = '/api/polls/AnswersByResponseEditor?rp=ext-comp-1351&fmt=json&mt=%d&mk=%d' % (mt, pk)
>>> test_client.force_login(obj.user)
>>> res = test_client.get(url, REMOTE_USER=obj.user.username)
>>> print(res.status_code)
200
>>> d = json.loads(res.content.decode())
>>> len(d['rows'])
5
>>> print(d['rows'][0][0])
<span class="htmlText">1) There was enough to eat.</span>

The "My answer" column for the first row has 5 links:

>>> soup = BeautifulSoup(d['rows'][0][1], 'lxml')
>>> links = soup.find_all('a')
>>> len(links)
5

The first of them displays a "1":

>>> print(links[0].string)
... 
1

And clicking on it would run the following Javascript code:

>>> print(links[0].get('href'))
javascript:Lino.polls.Responses.toggle_choice("ext-comp-1351",false,2,{ "base_params": { "mk": 2, "mt": 10 }, "field_values": { "choice": "1", "choiceHidden": 17, "question": "1) There was enough to eat.", "questionHidden": 9 }, "fv": [ 9, 17 ], "param_values": { "state": null, "stateHidden": null, "user": null, "userHidden": null } })

The 2 is the id of the Response we are acting on:

>>> polls.Response.objects.get(pk=2)
Response #2 ("Robin Rood's response to Participant feedback")

"fv" stands for "field values". It refers to the two parameters of the lino.modlib.polls.models.ToggleChoice action. The 9 is the id of a polls.Question, the 17 is the id of a polls.Choice.

>>> a = polls.Responses.toggle_choice
>>> len(a.parameters)
2
>>> a.parameters['question']
<django.db.models.fields.related.ForeignKey: question>
>>> a.parameters['choice']
<django.db.models.fields.related.ForeignKey: choice>
>>> polls.Question.objects.get(pk=9)
Question #9 ('1) There was enough to eat.')
>>> polls.Choice.objects.get(pk=17)
Choice #17 ('1')