Ejemplo n.º 1
0
def add_scoresheet_to_question(question, parseResult):
    cs = parseResult.calculated_score
    if cs:
        variables = Question.objects.filter(variable_name__in=cs.variables.asList())

        if len(variables) != len(cs.variables.asList()):
            raise ValidationError("Not all variables from scoresheet ({}) were matched.".format(cs.name))
        ss, _ = get_or_modify(ScoreSheet, {'name': cs.name},
            {'function': cs.function})
        ss.save()
        [ss.variables.add(i) for i in variables]
        ss.save()
        question.scoresheet = ss
        question.save()
Ejemplo n.º 2
0
def add_scoresheet_to_question(question, parseResult):
    cs = parseResult.calculated_score
    if cs:
        variables = Question.objects.filter(
            variable_name__in=cs.variables.asList())

        if len(variables) != len(cs.variables.asList()):
            raise ValidationError(
                "Not all variables from scoresheet ({}) were matched.".format(
                    cs.name))
        ss, _ = get_or_modify(ScoreSheet, {'name': cs.name},
                              {'function': cs.function})
        ss.save()
        [ss.variables.add(i) for i in variables]
        ss.save()
        question.scoresheet = ss
        question.save()
Ejemplo n.º 3
0
def make_question_dict(blockParseResult):
    """Make a dictionary from a pyparsing parseResult question.

    :param blockParseResult: A parseResult
    :rtype: dict

    """

    qytypeclasslist = [i for i in blockParseResult.classes if i in FIELD_NAMES]

    d = {
        'variable_name': blockParseResult.iden.strip(),
        'text': blockParseResult.code.strip(),
        'q_type': qytypeclasslist and qytypeclasslist[0] or "instruction",
    }

    if blockParseResult.choices:
        numbereddict = {k: v for k, v in enumerate(blockParseResult.choices.asList())}
        yamlstring = yaml.safe_dump(numbereddict)
        d.update(
            {
                'choiceset':
                get_or_modify(
                    ChoiceSet,
                    {'name': d['variable_name']},
                    {'yaml': yamlstring}
                )[0],
            }
        )
    else:
        d.update({'choiceset': None})

    keyvals = blockParseResult.keyvals or {}
    classlist = blockParseResult.classes and blockParseResult.classes.asList()
    d.update({'extra_attrs': {k: v for k, v in list(keyvals.items())}})
    d.update({'extra_attrs': {k: v for k, v in list(keyvals.items())}})

    d['extra_attrs'].update({'classes': {k: True for k in classlist}})
    d.update({'required': 'required' in classlist})

    return d
Ejemplo n.º 4
0
def make_question_dict(blockParseResult):
    """Make a dictionary from a pyparsing parseResult question.

    :param blockParseResult: A parseResult
    :rtype: dict

    """

    qytypeclasslist = [i for i in blockParseResult.classes if i in FIELD_NAMES]

    d = {
        'variable_name': blockParseResult.iden.strip(),
        'text': blockParseResult.code.strip(),
        'q_type': qytypeclasslist and qytypeclasslist[0] or "instruction",
    }

    if blockParseResult.choices:
        numbereddict = {
            k: v
            for k, v in enumerate(blockParseResult.choices.asList())
        }
        yamlstring = yaml.safe_dump(numbereddict)
        d.update({
            'choiceset':
            get_or_modify(ChoiceSet, {'name': d['variable_name']},
                          {'yaml': yamlstring})[0],
        })
    else:
        d.update({'choiceset': None})

    keyvals = blockParseResult.keyvals or {}
    classlist = blockParseResult.classes and blockParseResult.classes.asList()
    d.update({'extra_attrs': {k: v for k, v in list(keyvals.items())}})
    d.update({'extra_attrs': {k: v for k, v in list(keyvals.items())}})

    d['extra_attrs'].update({'classes': {k: True for k in classlist}})
    d.update({'required': 'required' in classlist})

    return d
Ejemplo n.º 5
0
    def clean(self):
        cleaned_data = super(TextEditForm, self).clean()
        try:
            text = self.cleaned_data.get("text")
            asker_yaml = yaml.safe_load(yaml_header.searchString(text)[0][0])
            blocks = block.searchString(text)
        except Exception as e:
            raise forms.ValidationError(
                "There was a problem parsing the text:\n\n {}".format(e))

        try:
            asker, _, _ = get_or_modify(Asker, {"id": self.asker.id}, asker_yaml)
        except ValueError as e:
            raise forms.ValidationError("There was a problem with the form: {}".format(e))

        if not self.request.user.is_superuser and asker.reply_count():
            raise forms.ValidationError("This questionnaire has already been used, so you can't edit it.")

        # check variable names are valid and not used in other questionnaires
        variable_names = [i.iden for i in filter(isnotpage, blocks)]


        naughtyquestions = Question.objects.filter(variable_name__in=variable_names).exclude(page__asker=asker)
        if naughtyquestions.count():
            raise forms.ValidationError(
                "Some variable names are already in use by other questionnaires ({})".format(
                    ", ".join([i.variable_name for i in naughtyquestions])))


        # check all variables specified in scoresheets can be found
        scshts = [i for i in blocks if i.calculated_score]
        for i in scshts:
            varset = set(i.calculated_score.variables.asList())
            questions_set = set(variable_names)
            if not varset.issubset(questions_set):
                raise forms.ValidationError(
                    "Not all variables for scoresheet '{}' were matched: {}".format(
                        i.calculated_score.name,
                        ", ".join(varset.difference(questions_set))))

        # update the form with some parsed data
        self.cleaned_data.update({
            "asker": asker,
            "asker_yaml": asker_yaml,
            "blocks": blocks
        })

        # END OF CLEANING PART

        # START OF SAVING PART
        # This was part of the save method, but now do here wrapped in transaction
        # because we can't raise validation errors easily otherwise

        # group questions by page, using filter(bool) to get rid of empty pages
        questionsbypage = list(filter(bool,
                [list(filter(isnotpage, (i[1]))) for i in groupby(blocks, ispage)]))

        pages = list(filter(ispage, blocks))
        pages_d = [make_page_dict(i) for i in pages]

        # pad to make sure we have enough pages (e.g. if first questions are specified without a page)
        pages_d = padleft(pages_d, len(questionsbypage), {})

        [j.update({'asker': asker, 'order': i}) for i, j in enumerate(pages_d)]
        new_pages = [get_or_modify(AskPage, {'asker': asker, 'order': i['order']}, i)
            for i in pages_d]

        questionsbypage_dicts = [[make_question_dict(i) for i in j] for j in questionsbypage]

        # add askpages and ordering to questions
        [[q.update({'page': p, 'order': i}) for i, q in enumerate(plist)]
            for plist, p in zip(questionsbypage_dicts, list(zip(*new_pages))[0])]

        # make question objects in format [(question, created, modified), ...]
        questionsbypage_obs = [[get_or_modify(Question, {'variable_name': q['variable_name']}, q)
            for q in page] for page in questionsbypage_dicts]

        # save everything
        def trytosavequestion(q):
            errors = []
            try:
                q.full_clean()
            except Exception as e:
                errors.append(e)

            try:
                q.save()
            except Exception as e:
                errors.append(e)

            try:
                q.choiceset and q.choiceset.save()
            except Exception as e:
                errors.append(e)

            if any(errors):
                return [{'q': q, 'e': i} for i in errors]

        try:
            # wrap in transaction because is-valid shouldn't have has side effects in the DB
            with transaction.atomic():
                errors = list(itertools.chain(*list(filter(bool, list(itertools.chain(*[[trytosavequestion(i) for i in list(zip(*p))[0]] for p in questionsbypage_obs]))))))
                if any(errors):
                    raise SignalBoxMultipleErrorsException(errors)

        except SignalBoxMultipleErrorsException as e:
            [self.add_error(None,
                forms.ValidationError("Problem with: {}".format(i.get('q')), params=i)) for i in e.errors]

        # add scoresheets back in
        [[add_scoresheet_to_question(question, parseresult)
            for question, parseresult in zip(pageq, pagep)]
            for pageq, pagep in zip(list(zip(*questionsbypage_obs))[0], questionsbypage)]

        asker.save()

        # delete unused pages and questions which aren't in the created/updated sets
        def delete_question_if_unused(q, asker):
            try:
                q.delete()
            except DataProtectionException:
                # we don't delete question altogether, but do remove from pages
                [p.question_set.remove(q) for p in justpages if q in p.question_set.all()]
                pass

        justquestions = [i for i, c, m in itertools.chain(*questionsbypage_obs)]
        justpages = [i for i, c, m in new_pages]
        [p.delete() for p in set(asker.askpage_set.all()) - set(justpages)]
        [delete_question_if_unused(i, asker) for i in set(asker.questions()) - set(justquestions)]

        return asker
Ejemplo n.º 6
0
    def clean(self):
        cleaned_data = super(TextEditForm, self).clean()
        try:
            text = self.cleaned_data.get("text")
            asker_yaml = yaml.safe_load(yaml_header.searchString(text)[0][0])
            blocks = block.searchString(text)
        except Exception as e:
            raise forms.ValidationError(
                "There was a problem parsing the text:\n\n {}".format(e))

        try:
            asker, _, _ = get_or_modify(Asker, {"id": self.asker.id},
                                        asker_yaml)
        except ValueError as e:
            raise forms.ValidationError(
                "There was a problem with the form: {}".format(e))

        if not self.request.user.is_superuser and asker.reply_count():
            raise forms.ValidationError(
                "This questionnaire has already been used, so you can't edit it."
            )

        # check variable names are valid and not used in other questionnaires
        variable_names = [i.iden for i in filter(isnotpage, blocks)]

        naughtyquestions = Question.objects.filter(
            variable_name__in=variable_names).exclude(page__asker=asker)
        if naughtyquestions.count():
            raise forms.ValidationError(
                "Some variable names are already in use by other questionnaires ({})"
                .format(", ".join([i.variable_name
                                   for i in naughtyquestions])))

        # check all variables specified in scoresheets can be found
        scshts = [i for i in blocks if i.calculated_score]
        for i in scshts:
            varset = set(i.calculated_score.variables.asList())
            questions_set = set(variable_names)
            if not varset.issubset(questions_set):
                raise forms.ValidationError(
                    "Not all variables for scoresheet '{}' were matched: {}".
                    format(i.calculated_score.name,
                           ", ".join(varset.difference(questions_set))))

        # update the form with some parsed data
        self.cleaned_data.update({
            "asker": asker,
            "asker_yaml": asker_yaml,
            "blocks": blocks
        })

        # END OF CLEANING PART

        # START OF SAVING PART
        # This was part of the save method, but now do here wrapped in transaction
        # because we can't raise validation errors easily otherwise

        # group questions by page, using filter(bool) to get rid of empty pages
        questionsbypage = list(
            filter(bool, [
                list(filter(isnotpage, (i[1])))
                for i in groupby(blocks, ispage)
            ]))

        pages = list(filter(ispage, blocks))
        pages_d = [make_page_dict(i) for i in pages]

        # pad to make sure we have enough pages (e.g. if first questions are specified without a page)
        pages_d = padleft(pages_d, len(questionsbypage), {})

        [j.update({'asker': asker, 'order': i}) for i, j in enumerate(pages_d)]
        new_pages = [
            get_or_modify(AskPage, {
                'asker': asker,
                'order': i['order']
            }, i) for i in pages_d
        ]

        questionsbypage_dicts = [[make_question_dict(i) for i in j]
                                 for j in questionsbypage]

        # add askpages and ordering to questions
        [[q.update({
            'page': p,
            'order': i
        }) for i, q in enumerate(plist)]
         for plist, p in zip(questionsbypage_dicts,
                             list(zip(*new_pages))[0])]

        # make question objects in format [(question, created, modified), ...]
        questionsbypage_obs = [[
            get_or_modify(Question, {'variable_name': q['variable_name']}, q)
            for q in page
        ] for page in questionsbypage_dicts]

        # save everything
        def trytosavequestion(q):
            errors = []
            try:
                q.full_clean()
            except Exception as e:
                errors.append(e)

            try:
                q.save()
            except Exception as e:
                errors.append(e)

            try:
                q.choiceset and q.choiceset.save()
            except Exception as e:
                errors.append(e)

            if any(errors):
                return [{'q': q, 'e': i} for i in errors]

        try:
            # wrap in transaction because is-valid shouldn't have has side effects in the DB
            with transaction.atomic():
                errors = list(
                    itertools.chain(*list(
                        filter(
                            bool,
                            list(
                                itertools.chain(*[[
                                    trytosavequestion(i)
                                    for i in list(zip(*p))[0]
                                ] for p in questionsbypage_obs]))))))
                if any(errors):
                    raise SignalBoxMultipleErrorsException(errors)

        except SignalBoxMultipleErrorsException as e:
            [
                self.add_error(
                    None,
                    forms.ValidationError("Problem with: {}".format(
                        i.get('q')),
                                          params=i)) for i in e.errors
            ]

        # add scoresheets back in
        [[
            add_scoresheet_to_question(question, parseresult)
            for question, parseresult in zip(pageq, pagep)
        ] for pageq, pagep in zip(
            list(zip(*questionsbypage_obs))[0], questionsbypage)]

        asker.save()

        # delete unused pages and questions which aren't in the created/updated sets
        def delete_question_if_unused(q, asker):
            try:
                q.delete()
            except DataProtectionException:
                # we don't delete question altogether, but do remove from pages
                [
                    p.question_set.remove(q) for p in justpages
                    if q in p.question_set.all()
                ]
                pass

        justquestions = [
            i for i, c, m in itertools.chain(*questionsbypage_obs)
        ]
        justpages = [i for i, c, m in new_pages]
        [p.delete() for p in set(asker.askpage_set.all()) - set(justpages)]
        [
            delete_question_if_unused(i, asker)
            for i in set(asker.questions()) - set(justquestions)
        ]

        return asker