def execute_the_todo_list(study=None, user=None): """Create list of Observations due and do() them""" from signalbox.models.observation_timing_functions import observations_due_in_window todo = observations_due_in_window() if study: todo = [x for x in todo if supergetattr(x, 'dyad.study', None) == study] if user: todo = [x for x in todo if supergetattr(x, 'dyad.user', None) == user] return [(i, i.do()) for i in todo]
def execute_the_todo_list(study=None, user=None): """Create list of Observations due and do() them""" from signalbox.models.observation_timing_functions import observations_due_in_window todo = observations_due_in_window() if study: todo = [ x for x in todo if supergetattr(x, 'dyad.study', None) == study ] if user: todo = [x for x in todo if supergetattr(x, 'dyad.user', None) == user] return [(i, i.do()) for i in todo]
def display_text(self, reply=None, request=None): """ :type reply: is_reply|None :type request: c|None :return: The html formatted string displaying the question :rtype: string """ templ_header = r"""{% load humanize %}""" # include these templatetags in render context templ = Template(templ_header + self.text) context = { 'reply': reply, 'user': supergetattr(reply, 'observation.dyad.user', default=None), 'page': self.page, 'scores': {}, 'answers': {}, 'answers_label': {} } fc = self.field_class() if reply and reply.asker and fc.compute_scores: context['scores'] = self.page.summary_scores(reply) # put actual values into main namespace too context.update({k: v.get('score', None) for k, v in context['scores'].items()}) if fc.allow_showing_answers and reply: context['answers'] = {i.variable_name(): int_or_string(i.answer) for i in reply.answer_set.all()} for i in self.questionasset_set.all(): context[i.slug] = unicode(i) return markdown.markdown(templ.render(Context(context)))
def clean(self): super(ShowIf, self).clean() if self.values and supergetattr(self, 'previous_question.choices', None): possibles = set( self.previous_question.choiceset.allowed_responses()) vals = set( filter(bool, list(map(valid.is_int, self.values.split(","))))) if not vals.issubset(possibles): raise ValidationError( """Valid choices for the selected question are: %s""" % ("; ".join(map(str, possibles)), )) if self.previous_question and self.summary_score: raise ValidationError( """You can hide/show this item based on a previous question value or a summary score, but not both.""") if self.values and (self.less_than or self.more_than): raise ValidationError( """You can either specify a range using the more_than and less_than fields, or exact values, but not both.""") if not self.values and not (self.less_than or self.more_than): raise ValidationError( """Please specify a list of values to match, or a range.""")
def dict_for_yaml(self): d = { "text": self.text, "q_type": self.q_type, "choiceset": supergetattr(self, "choiceset.name", None), "required": self.required, } return {self.variable_name: {k: v for k, v in list(d.items()) if v}}
def dict_for_yaml(self): d = { "text": self.text, "q_type": self.q_type, "choiceset": supergetattr(self, "choiceset.name", None), "required": self.required, } return {self.variable_name: {k: v for k, v in d.items() if v}}
def get_value_for_export(self): """Pre-process the answer in preparation for exporting as csv etc. The processing which occurs will depend on the field type and the methods described in ask.fields. """ class_name = fields.class_name(supergetattr(self, 'question.q_type', "")) processor = getattr(getattr(fields, class_name), 'export_processor') return processor(self.answer)
def display_text(self, reply=None, request=None): """ :type reply: is_reply|None :type request: c|None :return: The html formatted string displaying the question :rtype: string """ templ_header = r"""{% load humanize %}{% load mathfilters %}""" # include these templatetags # in render context templ = Template(templ_header + self.text) context = { 'reply': reply, 'condition': supergetattr(reply, "observation.dyad.condition"), 'user': supergetattr(reply, 'observation.dyad.user', default=None), 'page': self.page, 'scores': {}, 'answers': defaultdict(None), 'answers_label': {} } fc = self.field_class() if reply and reply.asker and fc.compute_scores: context['scores'] = self.page.summary_scores(reply) # put actual values into main namespace too context.update({ k: v.get('score', None) for k, v in list(context['scores'].items()) }) if fc.allow_showing_answers and reply: context['answers'] = { i.variable_name(): int_or_string(i.answer) for i in reply.answer_set.all() } for i in self.questionasset_set.all(): context[i.slug] = str(i) from django.template.base import VariableDoesNotExist try: return markdown.markdown(templ.render(Context(context))) except VariableDoesNotExist: return markdown.markdown(self.text)
def get_value_for_export(self): """Pre-process the answer in preparation for exporting as csv etc. The processing which occurs will depend on the field type and the methods described in ask.fields. """ class_name = fields.class_name( supergetattr(self, 'question.q_type', "")) processor = getattr(getattr(fields, class_name), 'export_processor') return processor(self.answer)
def finish(self, request): """Questionnaire is complete, so finish up.""" mess = supergetattr(self, "asker.success_message", None) mess and messages.add_message(request, messages.SUCCESS, mess) if self.observation: self.observation.status = 1 self.observation.save() self.complete = True self.save()
def mapping_of_answers_and_scores(self): """ Create a mapping of all answers and summary scores in this Reply :rtype: dict """ mapping = { supergetattr(i, 'question.variable_name', i.other_variable_name): i.answer for i in self.answer_set.all() } mapping.update({k: v.get('score', None) for k, v in self.asker.summary_scores(self).items()}) return mapping
def finish(self, request): """Questionnaire is complete, so finish up.""" mess = supergetattr(self, "asker.success_message", None) mess and messages.add_message(request, messages.SUCCESS, mess) if self.observation: self.observation.status = 1 self.observation.save() self.complete = True self.save() sbox_anonymous_reply_complete.send(sender=self.__class__, reply=self, request=request, dispatch_uid="reply_finished")
def mapping_of_answers_and_scores(self): """ Create a mapping of all answers and summary scores in this Reply :rtype: dict """ mapping = { supergetattr(i, 'question.variable_name', i.other_variable_name): i.answer for i in self.answer_set.all() } mapping.update({ k: v.get('score', None) for k, v in list(self.asker.summary_scores(self).items()) }) return mapping
def json_export(self): """Export Asker and related objects as json. Export everything needed to recreate this questionnaire on another signalbox instance (or 3rd party system) with the exception of question assets.""" _asdict = lambda object, fields: { i: str(supergetattr(object, i)) for i in fields } pages = self.askpage_set.all() questionlists = [i.get_questions() for i in pages] questions = list(itertools.chain(*questionlists)) choicesets = set([i.choiceset for i in questions if i.choiceset]) choices = Choice.objects.filter(choiceset__in=choicesets) QUESTION_FIELDS = ('natural_key', 'variable_name', 'order', 'required', 'help_text', 'text', 'choiceset.pk', 'q_type', 'showif') CHOICESET_FIELDS = ('natural_key', 'name') CHOICE_FIELDS = ('choiceset.natural_key', 'is_default_value', 'order', 'label', 'score') qdicts = [_asdict(i, QUESTION_FIELDS) for i in questions] # give a consistent ordering to everything [d.update({'order': i}) for i, d in enumerate(qdicts)] output = { 'meta': { 'name': self.slug, 'show_progress': self.show_progress, 'generated': str(datetime.now()), }, 'questions': qdicts, 'choicesets': [_asdict(i, CHOICESET_FIELDS) for i in choicesets], 'choices': [_asdict(i, CHOICE_FIELDS) for i in choices], 'scoresheets': [i.as_simplified_dict() for i in self.scoresheets()] } jsonstring = json.dumps(output, indent=4) return "{}".format(jsonstring)
def finish(self, request): """Questionnaire is complete, so finish up.""" mess = supergetattr(self, "asker.success_message", None) mess and messages.add_message(request, messages.SUCCESS, mess) if self.observation: self.observation.status = 1 self.observation.save() self.complete = True self.save() sbox_anonymous_reply_complete.send( sender=self.__class__, reply=self, request=request, dispatch_uid="reply_finished")
def clean(self): if self.asker and self.external_asker_url: raise ValidationError("""You can't use both an internal Questionnaire and one hosted on an external site.""") if self.asker and supergetattr(self, "script_type.require_study_ivr_number", False): last_q = self.asker.questions()[-1] if last_q.q_type != "hangup": raise ValidationError("The last question of a telephone call needs to be a 'hangup' type (currently {})".format(last_q.q_type)) try: if len(self.script_body) > 160 and self.script_type.name == "TwilioSMS": raise ValidationError( "Messages must be < 160 characters long.") except AttributeError: raise
def clean(self): super(ShowIf, self).clean() if self.values and supergetattr(self, 'previous_question.choices', None): possibles = set(self.previous_question.choiceset.allowed_responses()) vals = set(filter(bool, map(valid.is_int, self.values.split(",")))) if not vals.issubset(possibles): raise ValidationError("""Valid choices for the selected question are: %s""" % ( "; ".join(map(str, possibles)), )) if self.previous_question and self.summary_score: raise ValidationError("""You can hide/show this item based on a previous question value or a summary score, but not both.""") if self.values and (self.less_than or self.more_than): raise ValidationError("""You can either specify a range using the more_than and less_than fields, or exact values, but not both.""") if not self.values and not (self.less_than or self.more_than): raise ValidationError( """Please specify a list of values to match, or a range.""")
def clean(self): if self.asker and self.external_asker_url: raise ValidationError("""You can't use both an internal Questionnaire and one hosted on an external site.""" ) if self.asker and supergetattr( self, "script_type.require_study_ivr_number", False): last_q = self.asker.questions()[-1] if last_q.q_type != "hangup": raise ValidationError( "The last question of a telephone call needs to be a 'hangup' type (currently {})" .format(last_q.q_type)) try: if len(self.script_body ) > 160 and self.script_type.name == "TwilioSMS": raise ValidationError( "Messages must be < 160 characters long.") except AttributeError: raise
def json_export(self): """Export Asker and related objects as json. Export everything needed to recreate this questionnaire on another signalbox instance (or 3rd party system) with the exception of question assets.""" _asdict = lambda object, fields: {i: str(supergetattr(object, i)) for i in fields} pages = self.askpage_set.all() questionlists = [i.get_questions() for i in pages] questions = list(itertools.chain(*questionlists)) choicesets = set([i.choiceset for i in questions if i.choiceset]) choices = Choice.objects.filter(choiceset__in=choicesets) QUESTION_FIELDS = ('natural_key', 'variable_name', 'order', 'required', 'help_text', 'text', 'choiceset.pk', 'q_type', 'showif') CHOICESET_FIELDS = ('natural_key', 'name') CHOICE_FIELDS = ('choiceset.natural_key', 'is_default_value', 'order', 'label', 'score') qdicts = [_asdict(i, QUESTION_FIELDS) for i in questions] # give a consistent ordering to everything [d.update({'order': i}) for i, d in enumerate(qdicts)] output = { 'meta': {'name': self.slug, 'show_progress': self.show_progress, 'generated': str(datetime.now()), }, 'questions': qdicts, 'choicesets': [_asdict(i, CHOICESET_FIELDS) for i in choicesets], 'choices': [_asdict(i, CHOICE_FIELDS) for i in choices], 'scoresheets': [i.as_simplified_dict() for i in self.scoresheets()] } jsonstring = json.dumps(output, indent=4) return "{}".format(jsonstring)
def __unicode__(self): return smart_text("{} (page {}): {}".format( self.variable_name(), supergetattr(self, "page.id", None), smart_text(self.answer)[:80]) )
def __unicode__(self): return smart_text("{} (page {}): {}".format( self.variable_name(), supergetattr(self, "page.id", None), smart_text(self.answer)[:80]))
def mapped_score(self): """Return mapped score; leave answer unchanged if no map found.""" possiblechoices = supergetattr(self, 'question.choiceset.get_choices', ()) score_maptos = {i.score: i.mapped_score for i in possiblechoices} return score_maptos.get(self.answer, self.answer)
def redirect_url(self): return self.redirect_to or \ supergetattr(self, "created_by_script.asker.redirect_url", None) or reverse('user_homepage')
def participant(self): """Return the user to whom the answer relates (maybe not the user who entered it).""" return supergetattr(self, "reply.observation.dyad.user", None)
def study(self): """Return the study this Answer was made in response to.""" return supergetattr(self, "reply.observation.dyad.study", None)