Coverage for lino/modlib/polls/models.py : 71%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: UTF-8 -*- # Copyright 2013-2016 Luc Saffre # License: BSD (see file COPYING for details)
.. rubric:: Models overview
A :class:`Poll` is a collection of :class:`Questions <Question>` which we want to ask repeatedly to different people. Each Question has a *question text* and a :class:`ChoiceSet`, i.e. a stored ordered set of possible choices. A :class:`Response` is when somebody answers to a `Poll`. A Response contains a set of :class:`AnswerChoices <AnswerChoice>`, 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` also contains a set of `AnswerRemarks`, each of with represents a remark written by the responding person for a given Question of the Poll.
See also :doc:`/tested/polly`.
"""
# om atelier import rstgen
name ChoicesBySet """
return self.choiceset.choices.order_by('seqno')
def select_by_response(self, ar): mi = ar.master_instance dd.logger.info("20140929 %s", mi) if isinstance(mi, Response): AnswerChoice(response=mi, choice=self).save()
"""A series of questions."""
'polls.ChoiceSet', null=True, blank=True, related_name='polls', verbose_name=_("Default Choiceset"))
_("Allow multiple choices"), default=False)
_("Questions to add"), help_text=_("Paste text for questions to add. " "Every non-empty line will create one question."), blank=True)
if self.questions_to_add: # print "20150203 self.questions_to_add", self, # self.questions_to_add q = None qkw = dict() number = 1 for ln in self.questions_to_add.splitlines(): ln = ln.strip() if ln: if ln.startswith('#'): q.details = ln[1:] q.save() continue elif ln.startswith('='): q = Question(poll=self, title=ln[1:], is_heading=True, **qkw) number = 1 else: q = Question(poll=self, title=ln, number=str(number), **qkw) number += 1 q.full_clean() q.save() qkw.update(seqno=q.seqno + 1) self.questions_to_add = '' self.save() # save again because we modified afterwards
super(Poll, self).after_ui_save(ar, cw)
def result(self, ar): return E.div(*tuple(get_poll_result(self)))
#~ yield E.h1(self.title) for cs in ChoiceSet.objects.all(): questions = self.questions.filter(choiceset=cs) if questions.count() > 0: yield E.h2(str(cs)) for question in questions: yield E.p(question.text)
ref title workflow_buttons details default_choiceset default_multiple_choices polls.QuestionsByPoll """, label=_("General"))
id user created modified state polls.ResponsesByPoll # result PollResult """, label=_("Results"))
ref title default_choiceset default_multiple_choices questions_to_add """, window_size=(60, 15))
"""A question of a poll.
.. attribute:: number
"""
_("Allow multiple choices"), blank=True, default=False)
#~ return self.text[:40].strip() + ' ...' return self.title
#~ return self.choiceset.choices.order_by('seqno') return self.poll.questions.order_by('seqno')
return None return self.choiceset
if self.multiple_choices is None: self.multiple_choices = self.poll.default_multiple_choices #~ if self.choiceset_id is None: #~ self.choiceset = self.poll.default_choiceset super(Question, self).full_clean()
poll number is_heading choiceset multiple_choices title details AnswersByQuestion """
"""Toggle the given choice for the given question in this response. """ question=dd.ForeignKey("polls.Question"), choice=dd.ForeignKey("polls.Choice"), )
response = ar.selected_rows[0] if response is None: return pv = ar.action_param_values qs = AnswerChoice.objects.filter(response=response, **pv) if qs.count() == 1: qs[0].delete() elif qs.count() == 0: if not pv.question.multiple_choices: # delete any other choice which might exist qs = AnswerChoice.objects.filter( response=response, question=pv.question) qs.delete() obj = AnswerChoice(response=response, **pv) obj.full_clean() obj.save() else: raise Exception( "Oops, %s returned %d rows." % (qs.query, qs.count())) ar.success(refresh=True) # dd.logger.info("20140930 %s", obj)
def poll_choices(cls): return Poll.objects.filter(state=PollStates.published)
user=self.user, poll=self.poll) return _("{poll} {partner} {date}").format( user=self.user.initials, date=dd.fds(self.date), partner=self.partner.get_full_name(salutation=False), poll=self.poll)
def get_registrable_fields(model, site): yield f
poll partner date workflow_buttons polls.AnswersByResponse """, label=_("General")) user state remark """, label=_("More"))
user date poll """
"""Show all responses for a given partner. Default view is :meth:`get_slave_summary`.
"""
def get_slave_summary(self, obj, ar): """Displays a summary of all responses for a given partner using a bullet list grouped by poll.
""" if obj is None: return
visible_polls = Poll.objects.filter(state__in=( PollStates.published, PollStates.closed)).order_by('ref')
qs = Response.objects.filter(partner=obj).order_by('date') polls_responses = {} for resp in qs: polls_responses.setdefault(resp.poll.pk, []).append(resp)
items = [] for poll in visible_polls: iar = self.insert_action.request_from( ar, obj, known_values=dict(poll=poll)) elems = [str(poll), ' : '] responses = polls_responses.get(poll.pk, []) elems += join_elems( [ar.obj2html(r, dd.fds(r.date)) for r in responses], sep=', ') if poll.state == PollStates.published: elems += [' ', iar.ar2button()] #elems += [' ', iar.insert_button()] items.append(E.li(*elems)) return E.div(E.ul(*items))
'polls.Choice', related_name='answers', verbose_name=_("My answer"), blank=True, null=True)
def choice_choices(cls, question): return question.get_choiceset().choices.all()
# return _("Remark for {0}").format(self.question) return str(self.question)
remark response question """, window_size=(60, 10))
"""Volatile object to represent the one and only answer to a given question in a given response.
Used by :class:`AnswersByResponse` whose rows are instances of this.
""" "full_clean after_ui_save disable_delete".split())
# Needed by AnswersByResponse.get_row_by_pk question=question, response=response) question=question, response=response)
question=question, response=response)
if self.choices.count() == 0: return str(_("N/A")) return ', '.join([str(ac.choice) for ac in self.choices])
""" An editable virtual field. """
#~ e = self.get_entry_from_answer(obj) obj.remark.remark = value obj.remark.save()
#~ logger.info("20120118 value_from_object() %s",dd.obj2str(obj)) #~ e = self.get_entry_from_answer(obj)
"""The table used for answering to a poll. The rows of this table are volatile :class:`AnswersByResponseRow` instances.
.. attribute:: 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.
""" # workflow_state_field = 'state'
def get_data_rows(self, ar): return
def get_slave_summary(self, response, ar): """Presents this response as a table with one row per question and one column for each response of the same poll. The answers for this response are editable if this response is not registered. The answers of other responses are never editable.
""" return return poll=response.poll).order_by('date') all_responses = all_responses.filter(partner=response.partner) else: headers.append(ar.obj2html(r, dd.fds(r.date))) # get_data_rows() needs it. # 20151211 # editable = Responses.update_action.request_from(ar).get_permission( # response)
ar, known_values=kv) # editable = insert.get_permission(response) self.answer_buttons.value_from_object(answer, ar)] items += [E.br(), answer.remark.remark] items += [ ' ', detail.ar2button( answer.remark, _("Remark"), icon_name=None)] # ar.obj2html(answer.remark, _("Remark"))] else: answer.remark, _("Remark"), icon_name=None) # sar = RemarksByAnswer.request_from(ar, answer) # btn = sar.insert_button(_("Remark"), icon_name=None)
else: other_answer = AnswersByResponseRow(r, answer.question) items = [str(other_answer)] if other_answer.remark.remark: items += [E.br(), answer.remark.remark]
def answer_buttons(self, obj, ar): # assert isinstance(obj, Answer) return ''
raise Exception("No toggle_choice on {0}?".format(ar.actor))
# print("20150203 answer_buttons({0})".format(sar))
# if the response is registered, just display the choice, no # toggle buttons since answer cannot be toggled: # 20151211 return str(obj)
response=obj.response, **pv) text = [E.b('[', text, ']')] else: raise Exception( "Oops: %s returned %d rows." % (qs.query, qs.count()))
def get_pk_field(self):
def get_row_by_pk(self, ar, pk): response = ar.master_instance #~ if response is None: return q = rt.modules.polls.Question.objects.get(pk=pk) return AnswersByResponseRow(response, q)
def disable_delete(self, obj, ar): return "Not deletable"
def question(self, obj, ar): obj.question.number, obj.question.title) else: txt = obj.question.title
attrs.update(title=obj.question.details) txt = E.b(txt, **attrs)
"""Volatile object to represent a row of :class:`AnswersByQuestion`.
""" "full_clean after_ui_save disable_delete".split())
self.response = response self.question = question # Needed by AnswersByQuestion.get_row_by_pk self.pk = self.id = response.pk try: self.remark = AnswerRemark.objects.get( question=question, response=response).remark except AnswerRemark.DoesNotExist: self.remark = ''
self.choices = AnswerChoice.objects.filter( question=question, response=response) for k in self.FORWARD_TO_RESPONSE: setattr(self, k, getattr(question, k))
if self.choices.count() == 0: return str(_("N/A")) return ', '.join([str(ac.choice) for ac in self.choices])
"""The rows of this table are volatile :class:`AnswersByQuestionRow` instances.
"""
def get_data_rows(self, ar): question = ar.master_instance if question is None: return for r in rt.modules.polls.Response.objects.filter(poll=question.poll): yield AnswersByQuestionRow(r, question)
def response(self, obj, ar): return ar.obj2html(obj.response)
def remark(self, obj, ar): return obj.remark
def answer(self, obj, ar): return str(obj)
"Shows a summay of responses to this poll."
# @classmethod # def get_data_rows(self, ar): # poll = ar.master_instance # if poll is None: # return # for obj in super(PollResult, self).get_request_queryset(ar): # yield obj
def question(self, obj, ar): return obj
def answers(self, obj, ar): #~ return ar.spawn(Answer.objects.filter(question=obj)) return AnswerChoices.request(known_values=dict(question=obj))
def a1(self, obj, ar): cs = obj.get_choiceset() if cs is not None: c = next(iter(cs.choices.all())) #~ return Answer.objects.filter(question=obj,choice=c) return AnswerChoices.request( known_values=dict(question=obj, choice=c))
|