예제 #1
0
def do_submission_hooks(the_item: SurveyBase,
                        submission_data: dict,
                        actor: Party = None):
    assert not isinstance(the_item,
                          Question)  # no submission hooks for single questions

    hook_configuration = get_hook_configuration(the_item)
    if hook_configuration is None:
        return

    score = hook_configuration['scoring_strategy'].score_item(submission_data)

    actor = get_current_user_as_actor(
    ) if actor is None else get_party_as_actor(actor)
    verb = XApiVerb(XApiVerbs.Answered)
    activity = get_xapi_object(the_item)
    result = XApiScoredResult(score, hook_configuration['score_min'],
                              hook_configuration['score_max'], 1)
    context = XApiSurveyContext(the_item)

    statement = XApiStatement(actor, verb, activity, result, context)

    questionnaire = get_questionnaire(the_item)
    XApiPublisher().enqueue_deferred([statement], get_xapi_target(the_item),
                                     questionnaire.id)
예제 #2
0
def enqueue_answered_xapi_statements(sender: QuestionResponse):
    question = sender.question
    subject = next(filter(lambda s: isinstance(s, DataSubject), sender.owners))
    receiver = get_xapi_target(sender.question)
    statement = XApiAnswerSubmittedStatement(subject, question, sender.value)

    XApiPublisher().enqueue_deferred([statement], receiver, sender.id)
예제 #3
0
def publish_lti_launch_xapi_statement(sender: DataSubject, questionnaire=None):
    actor = get_party_as_actor(sender)
    verb = XApiVerb(XApiVerbs.Accessed)
    activity = get_xapi_object(questionnaire)
    context = XApiSt3k101Context()
    statement = XApiStatement(actor, verb, activity, xapi_context=context)
    XApiPublisher().enqueue([statement], g._config['XAPI_DEFAULT_ENDPOINT'])
예제 #4
0
def publish_questionnaire_retracted_xapi_statement(sender: Questionnaire):
    actor = get_current_user_as_actor()
    verb = XApiVerb(XApiVerbs.RetractedSurvey)
    activity = get_xapi_object(sender)
    context = XApiSt3k101Context()

    receiver = get_xapi_target(sender)
    statement = XApiStatement(actor, verb, activity, xapi_context=context)
    XApiPublisher().enqueue([statement], receiver)
예제 #5
0
def after_request(response: Response):
    """
    Called before response is sent to client.
    We may transform the response before sending it out.
    :param response: Reponse That is about to be sent back
    :return: Response the response that is sent back to the client
    """
    XApiPublisher().flush()

    # set the locale as cookie, keeps locale constant for a period of time
    if request.args.get('locale'):
        if request.args.get('locale_cookie', 1) == 1:
            response.set_cookie('locale', g._language.name)

    # we respond in the user's language
    response.headers['Content-Language'] = g._language.name
    return response
예제 #6
0
def publish_reference_id_updated_xapi_statement(sender: SurveyBase,
                                                previous_value: str = "Missing"
                                                ):
    actor = get_current_user_as_actor()
    verb = XApiVerb(XApiVerbs.ChangedReferenceId)
    activity = None
    if isinstance(sender, Question):
        activity = XApiActivities.Question
    elif isinstance(sender, Dimension):
        activity = XApiActivities.Dimension
    elif isinstance(sender, Questionnaire):
        activity = XApiActivities.Questionnaire
    assert activity is not None
    previous_value = "<{}>:{}".format(sender.owning_dataclient.email,
                                      previous_value)
    activity = XApiActivityObject(activity, previous_value,
                                  sender.name_translations)
    result = XApiResponseResult("<{}>:{}".format(
        sender.owning_dataclient.email, sender.reference_id))
    context = XApiSt3k101Context()

    receiver = get_xapi_target(sender)
    statement = XApiStatement(actor, verb, activity, result, context)
    XApiPublisher().enqueue([statement], receiver)
예제 #7
0
    def post(self, questionnaire_id: int = None):
        schema = SubmissionSchema()
        data, errors = schema.load(request.json)
        questionnaire = Questionnaire.query.get_or_404(questionnaire_id)

        if errors:
            return {
                'message': 'Your request contained errors.',
                'errors': errors
            }, 400

        # survey lifecycle check
        if not questionnaire.published:
            abort(403)
        if not questionnaire.accepts_submissions:
            abort(403, message="Submissions are not accepted at this point")

        # captcha and challenges
        if not validate_captcha(data['captcha_token']):
            return {
                'message':
                'CAPTCHA confidence score too low. Please try again.'
            }, 403
        challenge_errors = validate_challenges(questionnaire, data)
        if challenge_errors:
            return {
                'message': 'Some challenges could not be completed.',
                'errors': challenge_errors
            }, 403

        verification_token = generate_verification_token()
        data_subject = DataSubject.get_or_create(data['data_subject']['email'])

        all_questions = {
            q.id
            for d in questionnaire.dimensions for q in d.questions
        }

        for dimension_data in data['dimensions']:
            dimension = next((d for d in questionnaire.dimensions
                              if d.id == dimension_data['id']), None)
            if not dimension:
                return {
                    'message':
                    'Questionnaire has no dimension with id {}'.format(
                        dimension_data['id'])
                }, 400

            for question_data in dimension_data['questions']:
                question = next((q for q in dimension.questions
                                 if q.id == question_data['id']),
                                None)  # type: Question
                if not question:
                    return {
                        'message':
                        'Questionnaire has no question with id {}'.format(
                            question_data['id'])
                    }, 400
                question.add_question_result(
                    question_data['value'],
                    data_subject,
                    verification_token=verification_token)

                all_questions.remove(question_data['id'])

            do_submission_hooks(dimension, dimension_data, data_subject)

        do_submission_hooks(questionnaire, data, data_subject)

        if all_questions:
            db.session.rollback()
            XApiPublisher().rollback()
            return {
                'message': 'Missing questions.',
                'missing': list(all_questions)
            }, 400

        try:
            send_mail(
                data_subject.email, _("Please verify your survey submission"),
                construct_verification_email(questionnaire,
                                             verification_token))
        except (SMTPRecipientsRefused, SMTPHeloError, SMTPSenderRefused,
                SMTPDataError, SMTPNotSupportedError) as e:
            db.session.rollback()
            XApiPublisher().rollback()
            return {
                'message': 'Error while sending verification email.',
                'errors': [str(e)]
            }, 500

        db.session.commit()
        return {
            'message': 'Submission successful. Please verify by email.'
        }, 200
예제 #8
0
    def post(self, questionnaire_id: int = None):
        schema = SubmissionSchema(exclude=["data_subject"])
        data, errors = schema.load(request.json)
        questionnaire = Questionnaire.query.get_or_404(questionnaire_id)

        if errors:
            return {
                'message': 'Your request contained errors.',
                'errors': errors
            }, 400

        # survey lifecycle check
        if not questionnaire.published:
            abort(403, message="Survey was not published yet.")
        if not questionnaire.allow_embedded:
            abort(403)
        if not questionnaire.accepts_submissions:
            abort(403, message="Submissions are not accepted at this point.")

        all_questions = {
            q.id
            for d in questionnaire.dimensions for q in d.questions
        }
        responses = []

        for dimension_data in data['dimensions']:
            dimension = next((d for d in questionnaire.dimensions
                              if d.id == dimension_data['id']), None)
            if not dimension:
                return {
                    'message':
                    'Questionnaire has no dimension with id {}'.format(
                        dimension_data['id'])
                }, 400
            for question_data in dimension_data['questions']:
                question = next((q for q in dimension.questions
                                 if q.id == question_data['id']),
                                None)  # type: Question
                if not question:
                    return {
                        'message':
                        'Questionnaire has no question with id {}'.format(
                            question_data['id'])
                    }, 400

                responses.append(
                    question.add_question_result(question_data['value'],
                                                 current_user(),
                                                 needs_verification=False))
                all_questions.remove(question_data['id'])
                question.statistic.update()

            do_submission_hooks(dimension, dimension_data)

        do_submission_hooks(questionnaire, data)

        if all_questions:
            db.session.rollback()
            XApiPublisher().rollback()
            return {
                'message': 'Missing questions.',
                'missing': list(all_questions)
            }, 400

        for response in responses:
            SIG_ANSWER_VERIFIED.send(response)
        db.session.commit()
        return {'message': 'Submission successful.'}, 200
예제 #9
0
def approve_pending_xapi_statements(sender: QuestionResponse):
    XApiPublisher().approve(sender.id)
예제 #10
0
def publish_logged_in_xapi_statement(sender: Party):
    statement = XApiLoggedInStatement(sender)
    XApiPublisher().enqueue([statement], g._config['XAPI_DEFAULT_ENDPOINT'])