Ejemplo n.º 1
0
def handle_general_feedback(user: SurveyUser(), request):
    user_feedback = SurveyUserFeedback.objects.filter(
        user=user, question__isnull=True)[:1]

    if request.method == 'POST':
        general_feedback_form = GeneralFeedback(data=request.POST,
                                                lang=user.choosen_lang)

        if general_feedback_form.is_valid():
            if user_feedback:
                user_feedback = user_feedback[0]
            else:
                user_feedback = SurveyUserFeedback()
                user_feedback.user = user

            user_feedback.feedback = general_feedback_form.cleaned_data[
                'general_feedback']
            user_feedback.save()

            return general_feedback_form
    else:
        general_feedback_form = GeneralFeedback(lang=user.choosen_lang)

    if user_feedback:
        general_feedback_form.set_general_feedback(user_feedback[0].feedback)

    return general_feedback_form
Ejemplo n.º 2
0
def create_html_report(user: SurveyUser, lang: str,
                       request: HttpRequest) -> str:
    """Generate a HTML report."""
    translation.activate(lang)

    # Calculate the result
    (
        score,
        bonus_score,
        sections_data,
        sections_labels,
        categories_data,
        categories_labels,
    ) = calculateResult(user)

    # Generate the chart
    try:
        section_chart_png_base64 = generate_chart_png(user, sections_data,
                                                      sections_labels,
                                                      "base64")
        category_chart_png_base64 = generate_chart_png(user, categories_data,
                                                       categories_labels,
                                                       "base64")
    except AssertionError as e:
        logger.error(e)
        section_chart_png_base64 = None
        category_chart_png_base64 = None
    except Exception as e:
        logger.error(f"Error when generating the PNG chart: {e}.")
        raise e

    # Get the list of recommendations
    recommendations_list = getRecommendations(user, lang)

    # Get the list of questions
    question_list = get_questions_with_user_answers(user)

    # Render the HTML file
    output_from_parsed_template = render_to_string(
        "report/template.html",
        {
            "GLOBALS": globals,
            "CASES_LOGO": cases_logo,
            "SECIN_LOGO": secin_logo,
            "BASE_DIR": settings.BASE_DIR,
            "TOOL_LOGO": SITE_IMAGES_DIR + "/logo-" + lang + ".png",
            "DATE": datetime.today(),
            "SURVEY_USER": user,
            "CONTEXT": user.get_all_context_answers(),
            "SECTION_CHART": section_chart_png_base64,
            "CATEGORY_CHART": category_chart_png_base64,
            "SCORE": str(score),
            "BONUS_SCORE": bonus_score,
            "recommendations": recommendations_list,
            "questions": question_list,
        },
        request=request,
    )

    return output_from_parsed_template
Ejemplo n.º 3
0
def create_user_and_sequence(lang: str) -> SurveyUser:
    """Creates a new SurveyUser object.
    This function is called by the function handle_start_surveyonce, once the user has
    answered the first questions before the start of the survey."""
    user = SurveyUser()
    # defines the next question (exclude the context questions)
    survey_question = SurveyQuestion.objects.exclude(
        section__label__contains=CONTEXT_SECTION_LABEL
    ).order_by("qindex")[:1]
    user.current_question = survey_question[0]
    # Ensures the submitted languages is accepted
    langs, _ = zip(*LANGUAGES)
    if lang in langs:
        user.chosen_lang = lang
    else:
        user.chosen_lang = LOCAL_DEFAULT_LANG
    user.save()

    # We recreate the entire questions sequence in case if the map is not defined.
    if not does_answers_questions_map_exist():
        for question in (
            SurveyQuestion.objects.filter(is_active=True)
            .exclude(section__label=CONTEXT_SECTION_LABEL)
            .order_by("qindex")
        ):
            create_questions_sequence_object(user, question, question.qindex)
    else:
        create_questions_sequence_object(user, user.current_question, 1)

    return user
Ejemplo n.º 4
0
def create_user(lang: str, sector: str, company_size: str, country: str):
    user = SurveyUser()
    user.sector = sector
    user.e_count = company_size
    user.country_code = country
    survey_question = SurveyQuestion.objects.order_by('qindex')[:1]
    user.current_qindex = survey_question[0].qindex

    # prevent the use of custom languages
    langs = [x[0] for x in LANG_SELECT]
    if lang in langs:
        user.choosen_lang = lang
    else:
        user.choosen_lang = LANG_SELECT[0][0]

    user.save()

    return user
Ejemplo n.º 5
0
def remove_questions_sequences(
    user: SurveyUser,
    question_answer: SurveyQuestionAnswer,
    question_index: int,
    current_sequence: SurveyUserQuestionSequence,
    posted_answers: List[int],
) -> None:
    sequences_to_remove = []
    """Validates all the sequences starting from the first index,
    because there is no reference on it."""
    sequences_to_validate = SurveyUserQuestionSequence.objects.filter(
        user=user, index__gt=1, is_active=True
    )
    for sequence_to_validate in sequences_to_validate:
        """Fetch the other answers to validate if they are referenced to the sequence,
        or currently selected answers as they are not saved yet."""
        other_user_answers = (
            SurveyUserAnswer.objects.filter(user=user, uvalue="1")
            .exclude(
                answer=question_answer,
                answer__question__section__label=CONTEXT_SECTION_LABEL,
            )
            .order_by("id")
        )
        if not is_question_referenced_to_user_answers(
            user,
            other_user_answers,
            current_sequence.question,
            sequence_to_validate.question,
            posted_answers,
            question_index,
        ):
            sequences_to_remove.append(sequence_to_validate)

    for num, sequence_to_remove in enumerate(sequences_to_remove):
        """The index is shifted, so we need to subtract the iteration number."""
        index_to_adjust_from = sequence_to_remove.index - num
        SurveyUserAnswer.objects.filter(
            user=user, answer__question=sequence_to_remove.question
        ).delete()
        should_user_question_be_reset = (
            user.current_question.id == sequence_to_remove.question.id
        )
        increment_questions_sequence_order_from(user, index_to_adjust_from, 0, 0)
        sequence_to_remove.delete()
        if should_user_question_be_reset:
            next_sequence = get_next_sequence_with_not_answered_question(
                user, question_index, current_sequence
            )
            if next_sequence is None:
                next_sequence = get_last_user_sequence(user)
            user.current_question = next_sequence.question
Ejemplo n.º 6
0
def save_question_sequence_and_validate_user_status(
    user: SurveyUser, question: SurveyQuestion, index: int, branch: int, level: int
) -> None:
    user_question_sequence = SurveyUserQuestionSequence.objects.filter(
        user=user, question=question, branch=branch
    )[:1]
    if not user_question_sequence:
        user_question_sequence = SurveyUserQuestionSequence()
        user_question_sequence.user = user
        user_question_sequence.question = question
        user_question_sequence.index = index
        user_question_sequence.branch = branch
        user_question_sequence.level = level
        user_question_sequence.save()
    else:
        user_question_sequence = user_question_sequence[0]
        user_question_sequence.is_active = True
        user_question_sequence.index = index
        user_question_sequence.save()
    if user.status == SURVEY_STATUS_UNDER_REVIEW:
        user.status = SURVEY_STATUS_IN_PROGRESS
        user.save()
Ejemplo n.º 7
0
def getRecommendations(user: SurveyUser, lang: str) -> Dict[str, List[str]]:
    employees_number_code = user.get_employees_number_code()

    user_answers = SurveyUserAnswer.objects.filter(user=user)

    final_report_recs: Dict[str, List[str]] = {}
    for user_answer in user_answers:
        recommendations = Recommendations.objects.filter(forAnswer=user_answer.answer)
        if not recommendations.exists():
            continue

        for rec in recommendations:
            if employees_number_code and (
                rec.min_e_count > employees_number_code
                or rec.max_e_count < employees_number_code
            ):
                continue

            if (user_answer.uvalue == "1" and rec.answerChosen) or (
                user_answer.uvalue == "0" and not rec.answerChosen
            ):
                # If categories are set, we use them otherwise question service category.
                category_name = ""
                if rec.categories:
                    rec_categories = rec.categories.all()
                    for index, rec_category in enumerate(rec_categories):
                        category_name += _(rec_category.label)
                        if len(rec_categories) > (index + 1):
                            category_name += " & "
                if category_name == "":
                    category_name = _(rec.forAnswer.question.service_category.label)

                translated_recommendation = _(rec.label)
                if is_recommendation_already_added(
                    translated_recommendation, final_report_recs
                ):
                    continue

                if category_name not in final_report_recs:
                    final_report_recs[category_name] = []
                final_report_recs[category_name].append(translated_recommendation)

    return {key: value for key, value in sorted(final_report_recs.items())}
Ejemplo n.º 8
0
def calculateResult(user: SurveyUser) -> Tuple[int, int, List[int], List[str]]:
    user_bonus_points_percent = 0

    chart_exclude_sections = [CONTEXT_SECTION_LABEL]
    if "chart_exclude_sections" in CUSTOM.keys():
        chart_exclude_sections = (
            chart_exclude_sections + CUSTOM["chart_exclude_sections"]
        )

    """Only answered questions are used for the results calculation."""
    answered_questions_ids = [
        q.question.id
        for q in SurveyUserQuestionSequence.objects.filter(user=user, is_active=True)
    ]

    questions_by_section = (
        SurveyQuestion.objects.filter(id__in=answered_questions_ids)
        .exclude(section__label__in=chart_exclude_sections)
        .values_list("section_id")
        .order_by("section_id")
    )

    questions_by_category = questions_by_section.values_list(
        "service_category_id"
    ).order_by("service_category_id")

    max_evaluations_per_section = {
        q[0]: q[1] for q in questions_by_section.annotate(total=Sum("maxPoints"))
    }
    max_evaluations_per_category = {
        q[0]: q[1] for q in questions_by_category.annotate(total=Sum("maxPoints"))
    }
    total_questions_score = questions_by_section.aggregate(total=Sum("maxPoints"))[
        "total"
    ]

    sections = [
        _(section)
        for section in questions_by_section.values_list(
            "section__label", flat=True
        ).distinct()
    ]
    categories = [
        _(category)
        for category in questions_by_category.values_list(
            "service_category__label", flat=True
        ).distinct()
    ]

    # There are no exclude sections, because score or bonus points can be set to some answers.
    user_answers = SurveyUserAnswer.objects.filter(user=user).order_by(
        "answer__question__qindex", "answer__aindex"
    )

    total_bonus_points = user_answers.aggregate(total=Sum("answer__bonus_points"))[
        "total"
    ]
    user_given_bonus_points = user_answers.filter(uvalue=1).aggregate(
        total=Sum("answer__bonus_points")
    )["total"]
    total_user_score = user_answers.filter(uvalue=1).aggregate(
        total=Sum("answer__score")
    )["total"]

    user_evaluations_by_section = user.get_evaluations_by_section(
        max_evaluations_per_section
    )
    user_evaluations_by_category = user.get_evaluations_by_category(
        max_evaluations_per_category
    )

    # Get the score in percent, with then 100 being total_questions_score.
    if total_user_score > 0:
        total_user_score = round(total_user_score * 100 / total_questions_score)
    if total_bonus_points > 0:
        user_bonus_points_percent = round(
            user_given_bonus_points * 100 / total_bonus_points
        )

    return (
        total_user_score,
        user_bonus_points_percent,
        user_evaluations_by_section,
        sections,
        user_evaluations_by_category,
        categories,
    )
Ejemplo n.º 9
0
def handle_question_answers_request(request, user: SurveyUser,
                                    question_index: int):
    previous_question, current_question, next_question, total_questions_num = get_questions_slice(
        question_index)

    try:
        tuple_answers = get_answer_choices(current_question, user.choosen_lang)
    except Exception as e:
        raise e

    if request.method == 'POST':
        form = AnswerMChoice(tuple_answers,
                             data=request.POST,
                             lang=user.choosen_lang,
                             answers_field_type=current_question.qtype)

        if form.is_valid():
            answers = form.cleaned_data['answers']
            save_answers(tuple_answers, answers, user)

            feedback = form.cleaned_data['feedback']
            if feedback:
                user_feedback = SurveyUserFeedback.objects.filter(
                    user=user, question=current_question)[:1]
                if not user_feedback:
                    user_feedback = SurveyUserFeedback()
                    user_feedback.user = user
                    user_feedback.question = current_question
                else:
                    user_feedback = user_feedback[0]
                user_feedback.feedback = feedback
                user_feedback.save()

            if next_question != None:
                user.current_qindex = next_question.qindex
            else:
                user.status = SURVEY_STATUS_UNDER_REVIEW

            user.save()

            return None
    else:
        user_answers = SurveyUserAnswer.objects.filter(
            user=user, answer__question=current_question, uvalue__gt=0)
        selected_answers = []
        for user_answer in user_answers:
            selected_answers.append(user_answer.answer.id)

        user_feedback = SurveyUserFeedback.objects.filter(
            user=user, question=current_question)[:1]

        form = AnswerMChoice(tuple_answers,
                             lang=user.choosen_lang,
                             answers_field_type=current_question.qtype)
        form.set_answers(selected_answers)
        if user_feedback:
            form.set_feedback(user_feedback[0].feedback)

    uniqueAnswers = SurveyQuestionAnswer.objects.filter(
        question=current_question, uniqueAnswer=True)
    uniqueAnswers = ','.join(
        str(uniqueAnswer.id) for uniqueAnswer in uniqueAnswers)
    form.set_unique_answers(uniqueAnswers)

    return {
        'title':
        "Fit4Cybersecurity - " +
        TRANSLATION_UI['question']['question'][user.choosen_lang] + " " +
        str(current_question.qindex),
        'question':
        TranslationKey.objects.filter(key=current_question.titleKey,
                                      lang=user.choosen_lang)[0].text,
        'form':
        form,
        'action':
        '/survey/question/' + str(current_question.qindex),
        'user':
        user,
        'current_question_index':
        current_question.qindex,
        'previous_question_index':
        previous_question.qindex,
        'total_questions_num':
        total_questions_num,
        'available_langs': [lang[0] for lang in LANG_SELECT],
    }
Ejemplo n.º 10
0
def handle_question_answers_request(
    request: HttpRequest, user: SurveyUser, question_index: int
) -> Union[Dict, SurveyUser]:
    current_sequence = get_sequence_by_user_and_index(user, question_index)
    current_question = current_sequence.question
    does_map_exist = does_answers_questions_map_exist()

    try:
        question_answers = current_question.surveyquestionanswer_set.filter(
            is_active=True
        )
        tuple_answers = get_answer_choices(current_question, user.chosen_lang)
    except Exception as e:
        logger.error(e)
        raise e

    free_text_answer_id = 0
    for question_answer in question_answers:
        if question_answer.atype == "T":
            free_text_answer_id = question_answer.id

    translation.activate(user.chosen_lang)

    if request.method == "POST":
        form = AnswerMChoice(
            tuple_answers,
            data=request.POST,
            lang=user.chosen_lang,
            answers_field_type=current_question.qtype,
            question_answers=question_answers,
        )
        if form.is_valid():
            user = SurveyUser.objects.select_for_update(nowait=True).filter(id=user.id)[
                0
            ]
            answers = form.cleaned_data["answers"]
            answer_content = ""
            if "answer_content" in form.cleaned_data:
                answer_content = form.cleaned_data["answer_content"]

            save_answers(
                user,
                current_question,
                current_sequence,
                question_index,
                answers,
                answer_content,
            )

            feedback = form.cleaned_data["feedback"]
            if feedback:
                save_feedback(user, current_question, feedback)

            was_sequence_question_answered = current_sequence.has_been_answered
            mark_sequence_as_answered(current_sequence)
            next_sequence = get_next_sequence_with_not_answered_question(
                user, question_index, current_sequence
            )

            if next_sequence is not None:
                if (
                    does_map_exist
                    and not was_sequence_question_answered
                    and next_sequence.index != question_index + 1
                ):
                    increment_questions_sequence_order_from(user, question_index, 0, 1)

                user.current_question = next_sequence.question
            else:
                user.status = SURVEY_STATUS_UNDER_REVIEW

            user.save()

            return user
    else:
        form = AnswerMChoice(
            tuple_answers,
            lang=user.chosen_lang,
            answers_field_type=current_question.qtype,
            question_answers=question_answers,
        )

        user_answers = SurveyUserAnswer.objects.filter(
            user=user, answer__question=current_question
        )
        selected_answers = []
        for user_answer in user_answers:
            if user_answer.uvalue == "1":
                selected_answers.append(user_answer.answer.id)
            if user_answer.content:
                form.set_answer_content(user_answer.content)

        user_feedback = SurveyUserFeedback.objects.filter(
            user=user, question=current_question
        )[:1]

        form.set_answers(selected_answers)
        if user_feedback:
            form.set_feedback(user_feedback[0].feedback)

    uniqueAnswers = SurveyQuestionAnswer.objects.filter(
        question=current_question, uniqueAnswer=True
    )
    form.set_unique_answers(
        ",".join(str(uniqueAnswer.id) for uniqueAnswer in uniqueAnswers)
    )
    form.set_free_text_answer_id(free_text_answer_id)

    return {
        "title": CUSTOM["tool_name"]
        + " - "
        + _("Question")
        + " "
        + str(question_index),
        "question": _(current_question.label),
        "question_tooltip": _(current_question.tooltip),
        "form": form,
        "action": "/survey/question/" + str(question_index),
        "user": user,
        "current_question_index": question_index,
        "previous_question_index": question_index - 1 if question_index > 1 else 1,
        "total_questions_num": get_total_questions_number(user, question_index),
        "show_warning_dialog": current_sequence.has_been_answered and does_map_exist,
    }