# import copy
# import random
# import string
#
# from django.db.models import Q
# from django.utils import timezone
# from django.utils.decorators import decorator_from_middleware_with_args, method_decorator
# from rest_framework import status, serializers
# from rest_framework.views import APIView
#
# from authenticate.middleware import AuthorizatorMiddleware
# from collaborators import defined_roles as dr
# from collaborators.middleware import CollaborationAccessMiddleware
# from exam import conf
# from exam.middleware import EntranceTokenValidationAndAuthorizationMiddleware, ExamPageStatusValidationMiddleware, \
#     PublicIdValidationAndAuthorizationMiddleware, ExamPurchaseHandlerMiddleware
# from exam.models import ALLOWED_ANSWER_TEXT_FORMAT
# from exam.models import ExamTemplate, Exam, EXAM_PAGE_STATE_FINISHED, EXAM_PAGE_STATE_SCORES_PUBLISHED, \
#     REPORT_CARD_STRATEGY_INSTANTLY, \
#     Corrections, ENTRANCE_TYPE_API_INVITE, ExamPage, EXAM_PAGE_STATE_NOT_ATTENDED, EXAM_PAGE_STATE_IN_PROGRESS, \
#     ENTRANCE_TYPE_PUBLIC, EXAM_PAGE_STATE_NOT_GENERATED, PublicExamTemplate, AnswerFile
# from exam.serializers import AddExamPageCorrectionInputSerializer, GenerateExamPageInputValidator, GetExamPageForExaminerInputSerializer, \
#     GetExamPageForExaminerSerializer, GetQuestionAnswerSheetFromScorePublishedInstituteStudentExamPageInputSerializer, \
#     GetQuestionAnswerSheetFromScorePublishedPublicExamPageInputSerializer, GetExamPageFullDataIncludingEntranceTokenSerializer, \
#     AttendedExamPageSerializer, IncreaseTimeExamPageInputSerializer, \
#     InstituteStudentVerifyExamPageInputSerializer, GetExamPageAfterScoresPublishedSerializer, \
#     GetExamPageBeforeScoresPublishedSerializer, \
#     GetExamPageOnAttendAllQuestionsTogetherSerializer, GetExamPageOnAttendNotReanswerableExamSerializer, \
#     InstituteStudentGetQuestionExamPageInputSerializer, InstituteStudentSetAnswerExamPageInputSerializer, AttendedExamsListInputSerializer, \
#     PublishExamPageCorrectionInputSerializer, \
#     remove_sensitive_data_from_question_for_students, GetExamSerializerForEnterPage, \
#     GetExamPageMinimumDataPlusFirstAvailableIndexesForStudentsSerializer, GetExamInputSerializer, \
#     ListPublicExamTemplateInputSerializer, \
#     PublicExamTemplateListSerializer, ExamTemplateGetSerializer, PublicExamTemplateGetSerializer, \
#     GetPublicExamTemplateInputSerializer, ExamGroupGetInputValidator, PublicGetQuestionExamPageInputSerializer, \
#     PublicGetGroupExamPageInputSerializer, \
#     ExamTemplateGroupGetInputValidator, InstituteStudentGetGroupExamPageInputSerializer, PublicSetAnswerExamPageInputSerializer, \
#     PublicAnswerFileExamPageInputSerializer, InstituteStudentAnswerFileExamPageInputSerializer, \
#     InstituteStudentAnswerFileDeleteExamPageInputSerializer, \
#     PublicAnswerFileDeleteExamPageInputSerializer, GetExamPageForStat, \
#     ExamGroupStatInputValidator, GetAnswerFileModelSerializer, PauseExamInputSerializer, \
#     securely_inject_user_inputs_with_attachments_to_question_instance
# from exam.serializers import GetExamSerializer, StartExamPageInputSerializer, ListExamValidatorInputSerializer, \
#     GetPublicExamSerializer, \
#     GetPublicExamPageInputSerializer, EditExamInputSerializer, CreateExamInputSerializer, ListExamSerializer, DeleteExamInputSerializer, \
#     CreateExamTemplateInputSerializer, \
#     ExamTemplateListSerializer, DeleteExamTemplateInputSerializer, GetExamTemplateInputSerializer, ListExamTemplateInputSerializer, \
#     EditExamTemplateInputSerializer, \
#     GetExamSerializerForCoverPage, GetCorrectionsForStudentSerializer
# from exam.utils import price_calculator_helper
# from exam.validators import QUERY_QUESTION_BY_ID, QUERY_QUESTION_LIST_BY_FILTERING
# from institute.models import Institute, InstituteStudent
# from payment.models import WikiazmaBalanceHistory, InstituteBalanceHistory, ExamPurchase, UserBalanceHistory
# from question.models import Question, QUESTION_TYPE_MULTIPLE_CHOICE, QUESTION_TYPE_DESCRIPTIVE
# from question.serializers import QuestionModelSerializer
# from question_bank.models import QuestionBank
# from text_book.models import AnswerSheet
# from utils.myresponse import MyResponse
# from utils.pushers import correction_email_content_builder, send_html_email, send_sms_correction
# from utils.utils import compress_image
# from wikiazma.middleware import SerializerValidationMiddleware
#
# serializer_validation_middleware = decorator_from_middleware_with_args(
#     SerializerValidationMiddleware)
# user_token_or_server_token_or_server_API_key_validation_and_authorization = decorator_from_middleware_with_args(
#     AuthorizatorMiddleware)
# check_access_permission_middleware = decorator_from_middleware_with_args(
#     CollaborationAccessMiddleware)
# entrance_token_validation_and_authorization = decorator_from_middleware_with_args(
#     EntranceTokenValidationAndAuthorizationMiddleware)
# public_id_validation_and_authorization = decorator_from_middleware_with_args(
#     PublicIdValidationAndAuthorizationMiddleware)
# exam_purchase_middleware = decorator_from_middleware_with_args(
#     ExamPurchaseHandlerMiddleware)
# exam_page_status_validation = decorator_from_middleware_with_args(
#     ExamPageStatusValidationMiddleware)
# exam_page_status = decorator_from_middleware_with_args(
#     PublicIdValidationAndAuthorizationMiddleware)
#
#
# def exam_purchase_wikiazma_balance_handler(exam, reason_meta_data, user, amount):
#     last_wikiazma_balance_history = WikiazmaBalanceHistory.get_last_wikiazma_balance()
#     new_wikiazma_balance_history = WikiazmaBalanceHistory(amount=amount,
#                                                           reason_meta_data=reason_meta_data, reason=ExamPurchase,
#                                                           exam=exam, user=user)
#     new_wikiazma_balance_history.remaining_balance = last_wikiazma_balance_history.remaining_balance + \
#         new_wikiazma_balance_history.amount
#     new_wikiazma_balance_history.save()
#
#
# def exam_purchase_institute_balance_handler(institute, exam, reason_meta_data, amount):
#     last_institute_balance_history = InstituteBalanceHistory.get_last_institute_balance(
#         institute)
#     new_balance_history = InstituteBalanceHistory(institute=institute, amount=amount,
#                                                   reason_meta_data=reason_meta_data, reason=ExamPurchase,
#                                                   exam=exam)
#     new_balance_history.remaining_balance = last_institute_balance_history.remaining_balance + \
#         new_balance_history.amount
#     new_balance_history.save()
#
#
# def exam_purchase_user_balance_handler(user, exam, reason_meta_data, amount):
#     last_user_balance_history = UserBalanceHistory.get_last_user_balance(user)
#     new_balance_history = UserBalanceHistory(user=user, amount=amount,
#                                              reason_meta_data=reason_meta_data, reason=ExamPurchase,
#                                              exam=exam)
#     new_balance_history.remaining_balance = last_user_balance_history.remaining_balance + \
#         new_balance_history.amount
#     new_balance_history.save()
#
#     if new_balance_history.remaining_balance < 0:
#         # TODO critical error
#         print(
#             f"amount={amount}, new_balance_history.remaining_balance={new_balance_history.remaining_balance}")
#         print("critical error # 94166237225")
#         pass
#
#
# def exam_purchase_handler(user, exam, exam_page):
#     total_cost, institute_share, wikiazma_share, wage, tax = price_calculator_helper(
#         user, exam)
#
#     if total_cost != 0:
#         exam_purchase_institute_balance_handler(
#             institute=exam.institute, amount=institute_share, exam=exam, reason_meta_data={'exam_page_id': str(exam_page.id)})
#         exam_purchase_wikiazma_balance_handler(
#             exam=exam, reason_meta_data={'exam_page_id': str(exam_page.id)}, user=user, amount=wikiazma_share)
#         exam_purchase_user_balance_handler(
#             user=user, exam=exam, reason_meta_data={'exam_page_id': str(exam_page.id)}, amount=-1 * total_cost)
#
#
# def helper_answer_file(exam_page, group_index, question_index, request, file):
#     if not exam_page.reanswer:
#         if group_index > exam_page.first_available_group_index or (
#                 group_index == exam_page.first_available_group_index and question_index > exam_page.first_available_question_index):
#             return MyResponse(request, {'status': 'index_error',
#                                         'message': 'You can\'t get this question',
#                                         'data': {
#                                             'first_available_question_index': exam_page.first_available_question_index,
#                                             'first_available_group_index': exam_page.first_available_group_index}},
#                               status=status.HTTP_409_CONFLICT)
#
#     content_type = file.content_type  # image/png
#     file_type = content_type.split('/')[0]  # image
#     groups = exam_page.generated_exam['groups']
#
#     try:
#         target_group = [
#             group for group in groups if group['index'] == group_index][0]
#         target_question = [question for question in target_group['questions']
#                            if question['index'] == question_index][0]
#
#     except:
#         return MyResponse(request, {'status': 'not_found', 'message': 'question or group was not found'},
#                           status=status.HTTP_404_NOT_FOUND)
#
#     if target_question['question_type'] != QUESTION_TYPE_DESCRIPTIVE:
#         return MyResponse(request, {'status': 'error', 'message': 'you can not attach file to this question'},
#                           status=status.HTTP_400_BAD_REQUEST)
#
#     # condition check
#
#     def _check(key):
#         return key in target_question['answer_rules'] and target_question['answer_rules'][key] == True
#
#     if _check('general_file_available') or (file_type == 'image' and _check('image_available')) or (
#             file_type == 'audio' and _check('audio_available')) or (file_type == 'video' and _check('video_available')):
#
#         answer_file = AnswerFile.objects.create(question_index=question_index, group_index=group_index, file=file,
#                                                 exam_page=request.middleware_exam_page, file_type=file_type)
#         answer_file_data = GetAnswerFileModelSerializer(answer_file).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'answer_file': answer_file_data}},
#                           status=status.HTTP_200_OK)
#     else:
#         return MyResponse(request, {'status': 'error', 'message': 'you can not upload this type'},
#                           status=status.HTTP_400_BAD_REQUEST)
#
#
# def auto_correct_helper(exam_page):
#     groups = exam_page.generated_exam['groups']
#     corrections_list = []
#     for group in groups:
#         questions = group['questions']
#
#         for question in questions:
#
#             user_inputs = [user_input for user_input in exam_page.user_inputs if
#                            user_input['group_index'] == group['index'] and user_input['question_index'] == question['index']]
#             user_input = user_inputs[0] if user_inputs else None
#
#             if question['question_type'] == QUESTION_TYPE_MULTIPLE_CHOICE:
#                 correction = correct_multiple_choice_question_helper(
#                     user_input, group, question, exam_page)
#                 corrections_list.append(correction)
#             elif question['question_type'] == QUESTION_TYPE_DESCRIPTIVE:
#                 if user_input == None:
#                     correction = Corrections.objects.create(
#                         corrector=None,
#                         question_index=question['index'],
#                         group_index=group['index'],
#                         exam_page=exam_page,
#                         question_id=question['question_id'],
#                         blank=True,
#                         correct_choices=None,
#                         wrong_choices=None,
#                         blank_choices=None,
#                         score=0,
#                     )
#                     corrections_list.append(correction)
#             else:
#                 # TODO critical error
#                 raise ValueError('Unknown question type')
#
#     return corrections_list
#
#
# def generate_report_card_helper(exam_page, corrections_list):
#     report_card = {}
#     total_score = 0
#
#     groups = exam_page.generated_exam['groups']
#
#     report_card_groups = []
#     for group in groups:
#         report_card_group_item = {
#             'name': group['name'],
#             'positive_scores': group['positive_scores'],
#             'negative_scores': group['negative_scores'],
#             'durations': group['durations'],
#             'questions_count': group['questions_count'],
#             'correct_choices': 0,
#             'wrong_choices': 0,
#             'blank_choices': 0,
#             'score': 0,
#
#         }
#         questions = group['questions']
#         for question in questions:
#             target_corrections = [item for item in corrections_list if
#                                   item.group_index == group['index'] and item.question_index == question['index']]
#             if not target_corrections:
#                 return None, None, None
#
#             target_corrections.sort(
#                 key=lambda item: item.modified_at, reverse=True)
#             selected_correction = target_corrections[0]
#             # modify total score
#             total_score += selected_correction.score
#             report_card_group_item['score'] += selected_correction.score
#
#             # blank_choices is different from blank_questions
#             # for example, a question may have max_selectable_choices=5 and user may have selected 2 of 5 choices
#             # selected_correction.blank will be False and selected_correction.blank_choices will be 3
#             # selected_correction.blank will track blank_questions
#             # selected_correction.blank_choices will track blank_choices
#             # if selected_correction.blank:
#             #     report_card_group_item['blank_choices'] += 1
#
#             if selected_correction.correct_choices is not None:
#                 report_card_group_item['correct_choices'] += selected_correction.correct_choices
#             if selected_correction.wrong_choices is not None:
#                 report_card_group_item['wrong_choices'] += selected_correction.wrong_choices
#             if selected_correction.blank_choices is not None:
#                 report_card_group_item['blank_choices'] += selected_correction.blank_choices
#
#         report_card_groups.append(report_card_group_item)
#
#     report_card['groups'] = report_card_groups
#
#     overall = {
#         "positive_scores": sum(group['positive_scores'] for group in report_card_groups),
#         "negative_scores": sum(group['negative_scores'] for group in report_card_groups),
#         "durations": sum(group['durations'] for group in report_card_groups),
#         "questions_count": sum(group['questions_count'] for group in report_card_groups),
#         "correct_choices": sum(group['correct_choices'] for group in report_card_groups),
#         "wrong_choices": sum(group['wrong_choices'] for group in report_card_groups),
#         "blank_choices": sum(group['blank_choices'] for group in report_card_groups),
#         "score": total_score
#     }
#
#     report_card['overall'] = overall
#     return report_card, total_score, overall['positive_scores']
#
#
# def finish_exam_page_helper(exam_page):
#     corrections_list = auto_correct_helper(exam_page)
#
#     exam_page.state_history.append(exam_page.state)
#     exam_page.state = EXAM_PAGE_STATE_FINISHED
#
#     max_positive_score = None
#
#     # TODO handle REPORT_CARD_STRATEGY_ON_LAST_STUDENT
#     if exam_page.exam.report_card_strategy == REPORT_CARD_STRATEGY_INSTANTLY:
#         report_card, total_score, max_positive_score = generate_report_card_helper(
#             exam_page, corrections_list)
#
#         if report_card is not None:
#             exam_page.report_card = report_card
#             exam_page.final_score = round(total_score, 2)
#
#             exam_page.state_history.append(exam_page.state)
#             exam_page.state = EXAM_PAGE_STATE_SCORES_PUBLISHED
#         else:
#             # TODO critical error
#             pass
#
#     exam_page.finished_at = timezone.now()
#
#     exam_page.save()
#
#     return exam_page, max_positive_score
#
#
# def finish_invalid_in_progress_exam_pages(pages):
#     for page in pages:
#         if page.invalid_in_progress_state:
#             page = finish_exam_page_helper(page)
#     return pages
#
#
# def finish_exam_page_request_handler(exam_page, request, **args):
#     exam_page, max_positive_score = finish_exam_page_helper(exam_page)
#
#     return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                 'data': {'final_score': exam_page.final_score, 'max_positive_score': max_positive_score,
#                                          'finish_message': exam_page.exam.finish_message, **args}},
#                       status=status.HTTP_200_OK)
#
#
# # def finish_exam_page_helper(exam_page, request, **args):
# #     corrections_list = auto_correct_helper(exam_page)
#
# #     exam_page.state_history.append(exam_page.state)
# #     exam_page.state = EXAM_PAGE_STATE_FINISHED
#
# #     max_positive_score = None
#
# #     # TODO handle REPORT_CARD_STRATEGY_ON_LAST_STUDENT
# #     if exam_page.exam.report_card_strategy == REPORT_CARD_STRATEGY_INSTANTLY:
# #         report_card, total_score, max_positive_score = generate_report_card_helper(exam_page, corrections_list)
#
# #         if report_card is not None:
# #             exam_page.report_card = report_card
# #             exam_page.final_score = round(total_score, 2)
#
# #             exam_page.state_history.append(exam_page.state)
# #             exam_page.state = EXAM_PAGE_STATE_SCORES_PUBLISHED
# #         else:
# #             # TODO critical error
# #             pass
#
# #     exam_page.finished_at = timezone.now()
#
# #     exam_page.save()
#
# #     return MyResponse(request, {'status': 'ok', 'message': 'Successful',
# #                                 'data': {'final_score': exam_page.final_score, 'max_positive_score': max_positive_score,
# #                                          'finish_message': exam_page.exam.finish_message, **args}},
# #                       status=status.HTTP_200_OK)
#
# def set_answer_helper(request, exam_page, user_inputs, answer_time, go_to_next_question):
#     for new_user_input in user_inputs:
#         new_user_input['answer_time'] = answer_time.isoformat()
#
#     if not exam_page.reanswer:
#         if len(user_inputs) != 1:
#             return MyResponse(request, {'status': 'error', 'message': f'You should send only one answer at a time'},
#                               status=status.HTTP_400_BAD_REQUEST)
#         new_user_input = user_inputs[0]
#
#         if new_user_input['question_index'] != exam_page.first_available_question_index or new_user_input[
#                 'group_index'] != exam_page.first_available_group_index:
#             return MyResponse(request, {'status': 'index_error',
#                                         'message': f'You can only answer the question with index={exam_page.first_available_question_index} in the group with index={exam_page.first_available_group_index}',
#                                         'data': {
#                                             'first_available_question_index': exam_page.first_available_question_index,
#                                             'first_available_group_index': exam_page.first_available_group_index}},
#                               status=status.HTTP_409_CONFLICT)
#
#     # TODO optimization
#     for new_user_input in user_inputs:
#         question, is_the_last_question_in_the_group = check_user_input(
#             exam_page, new_user_input)
#
#         for old_user_input in exam_page.user_inputs:
#             if old_user_input['group_index'] == new_user_input['group_index'] and old_user_input['question_index'] == new_user_input[
#                     'question_index']:
#                 exam_page.user_inputs.remove(old_user_input)
#                 break
#
#         exam_page.user_inputs.append(new_user_input)
#
#         if go_to_next_question and not exam_page.reanswer:
#             # TODO if reanswer is True, this will not make sense if user answers questions in a random order
#             # TODO test this
#             # TODO also, if we remove a question with the APIs we provide later, this approach won't work
#             if is_the_last_question_in_the_group:
#                 exam_page.first_available_group_index += 1
#                 exam_page.first_available_question_index = 0
#             else:
#                 exam_page.first_available_question_index += 1
#
#     exam_page.user_inputs_history.append(
#         {'date_time': timezone.now().strftime('%Y-%m-%dT%H:%M:%S'),
#          'device_token': request.middleware_device_token,
#          'user_inputs': user_inputs,
#          'go_to_next_question': go_to_next_question})
#     # TODO set first_available_question_index, first_available_group_index
#
#     exam_page.save()
#
#     # On set answer exam, we do not send generated_exam and we do not send user_inputs data
#     exam_page_data = GetExamPageMinimumDataPlusFirstAvailableIndexesForStudentsSerializer(
#         exam_page).data
#
#     # note #8174174 applied
#     return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                 'data': {'exam_page': exam_page_data, 'current_date_time': timezone.now()}},
#                       status=status.HTTP_200_OK)
#
#
# def helper_get_group(exam_page, group_index, request):
#     if not exam_page.reanswer:
#         # if group_index == exam_page.first_available_group_index, we still return error
#         if group_index >= exam_page.first_available_group_index:
#             return MyResponse(request, {'status': 'index_error',
#                                         'message': 'You can\'t get this group',
#                                         'data': {
#                                             'first_available_question_index': exam_page.first_available_question_index,
#                                             'first_available_group_index': exam_page.first_available_group_index}},
#                               status=status.HTTP_409_CONFLICT)
#
#     for group in exam_page.generated_exam['groups']:
#         if group['index'] != group_index:
#             continue
#         for question in group['questions']:
#             remove_sensitive_data_from_question_for_students(question)
#             # securely_add_user_inputs_to_question(question, exam_page.user_inputs, only_user_input_status=False)
#             securely_inject_user_inputs_with_attachments_to_question_instance(group_index=group_index, question_index=question['index'],
#                                                                               origin_question=question, exam_page=exam_page,
#                                                                               only_user_input_status=False)
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'group': group}})
#
#     return MyResponse(request, {'status': 'not_found', 'message': 'Group was not found'},
#                       status=status.HTTP_404_NOT_FOUND)
#
#
# def helper_get_question(group_index, question_index, request, exam_page):
#     if not exam_page.reanswer:
#         if group_index > exam_page.first_available_group_index or (
#                 group_index == exam_page.first_available_group_index and question_index > exam_page.first_available_question_index):
#             return MyResponse(request, {'status': 'index_error',
#                                         'message': 'You can\'t get this question',
#                                         'data': {
#                                             'first_available_question_index': exam_page.first_available_question_index,
#                                             'first_available_group_index': exam_page.first_available_group_index}},
#                               status=status.HTTP_409_CONFLICT)
#
#     for group in exam_page.generated_exam['groups']:
#         if group['index'] != group_index:
#             continue
#         for question in group['questions']:
#             if question['index'] != question_index:
#                 continue
#
#             remove_sensitive_data_from_question_for_students(question)
#             securely_inject_user_inputs_with_attachments_to_question_instance(group_index=group_index, question_index=question_index,
#                                                                               origin_question=question, exam_page=exam_page,
#                                                                               only_user_input_status=False)
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'question': question}})
#
#     return MyResponse(request, {'status': 'not_found', 'message': 'Question was not found'},
#                       status=status.HTTP_404_NOT_FOUND)
#
#
# # TODO check_question_ids_existence_in_rules vs exam_question_validator
# # exam_question_validator weak point: uses 3*N queries to perform accessibility check
# # check_question_ids_existence_in_rules weak point: frontend should give backend a hint
# #  about the type of accessibility for each question(although backend checks the truthfulness of frontend's statement. The
# #  hint is useful to reduce the number of queries to 3)
# def exam_question_validator(request, rules, context_institute):
#     questions_count = 0
#     for group in rules['groups']:
#         for question in group['questions']:
#             if question['type'] == QUERY_QUESTION_BY_ID:
#                 try:
#                     question_query = Question.objects.get(
#                         id=question['question_id'])
#
#                 except:
#                     return MyResponse(request,
#                                       {'status': 'error',
#                                        'message': f'Question "{question["question_id"]}" not found'},
#                                       status=status.HTTP_400_BAD_REQUEST)
#
#                 question_bank = question_query.question_bank
#                 if context_institute.can_read_question_bank(question_bank):
#                     questions_count += 1
#                 else:
#                     raise serializers.ValidationError(
#                         {'rules': f'Access denied. Question "{question["question_id"]}"'})
#
#             elif question['type'] == QUERY_QUESTION_LIST_BY_FILTERING:
#                 try:
#                     question_bank = QuestionBank.objects.get(
#                         id=question['question_bank_id'])
#                 except:
#                     return MyResponse(request,
#                                       {'status': 'error',
#                                        'message': f'question_bank "{question["question_bank_id"]}" not found'},
#                                       status=status.HTTP_400_BAD_REQUEST)
#
#                 if context_institute.can_read_question_bank(question_bank):
#                     questions_count += int(question['count'])
#                 else:
#                     raise serializers.ValidationError(
#                         {'rules': f'Access denied. Question "{question["question_id"]}"'})
#
#             else:
#                 # we should never reach this line because we have prevented wrong question types in our serializer
#                 return MyResponse(request,
#                                   {'status': 'error',
#                                    'message': f'Invalid Question Type "{question["type"]}"'},
#                                   status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#     return questions_count
#
#
# # TODO don't delete this function, refer to "check_question_ids_existence_in_rules vs exam_question_validator"
# # def check_question_ids_existence_in_rules(request, rules):
# #     questions_count = 0
# #     owned_question_ids = []
# #     public_question_ids = []
# #     subscribed_question_ids = []
# #     for group in rules['groups']:
# #         for question in group['questions']:
# #             if question['type'] == QUERY_QUESTION_BY_ID:
# #                 if question['access_type'] == ACCESS_TYPE_SUBSCRIBED:
# #                     subscribed_question_ids.append(question['question_id'])
# #                 elif question['access_type'] == ACCESS_TYPE_PUBLIC:
# #                     public_question_ids.append(question['question_id'])
# #                 else:  # ACCESS_TYPE_OWNED
# #                     owned_question_ids.append(question['question_id'])
#
# #                 questions_count += 1
# #             elif question['type'] == QUERY_QUESTION_LIST_BY_FILTERING:
# #                 # TODO check existence and permission of the question_bank
# #                 questions_count += int(question['count'])
# #                 return MyResponse(request,
# #                                   {'status': 'error', 'message': f'Invalid Question Type "{question["type"]}"'},
# #                                   status=status.HTTP_403)
# #             else:
# #                 # we should never reach this line because we have prevented wrong question types in our serializer
# #                 return MyResponse(request,
# #                                   {'status': 'error', 'message': f'Invalid Question Type "{question["type"]}"'},
# #                                   status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
# #     # by using map, I'm converting UUIDs to string ids
# #     returned_question_ids = list(map(lambda uuid: str(uuid),
# #                                      Question.objects.filter(id__in=owned_question_ids,
# #                                                              institute=request.middleware_institute).values_list('id',
# #                                                                                                                  flat=True)))
# #     if len(returned_question_ids) != len(set(owned_question_ids)):
#
# #         # return MyResponse(request, {'status': 'error', 'message': 'groups has some invalid question ids'}, status=status.HTTP_400_BAD_REQUEST)
# #         for question_id in set(owned_question_ids):
# #             if question_id not in returned_question_ids:
# #                 for group in rules['groups']:
# #                     for question in group['questions']:
# #                         if question_id == question['question_id']:
# #                             raise serializers.ValidationError(
# #                                 {
# #                                     'rules': f'Access denied. Question with id: "{question_id}" in group "{group["name"]}" is not in your institute.'})
#
# # returned_public_question_ids = Question.objects.filter(id__in=public_question_ids, question_bank__accessibility='public')
# # if len(returned_public_question_ids) != len(set(public_question_ids)):
# #     for question_id in set(public_question_ids):
# #         if question_id not in returned_public_question_ids:
# #             for group in rules['groups']:
# #                 for question in group['questions']:
# #                     if question_id == question['question_id']:
# #                         raise serializers.ValidationError(
# #                             {
# #                                 'rules': f'Access denied. Question with id: "{question_id}" in group "{group["name"]}" is not public.'})
#
# #     if len(subscribed_question_ids) != 0:
# #         # TODO handle later
# #         raise serializers.ValidationError(
# #             {'rules': f'Access denied for question with id: "{subscribed_question_ids[0]}"'})
#
# #     return questions_count
#
#
# def create_exam_template_from_exam(exam):
#     exam_template = ExamTemplate.objects.create(
#         name=exam.name,
#         institute=exam.institute,
#         rules=exam.rules,
#         one_question_at_a_time=exam.one_question_at_a_time,
#         reanswer=exam.reanswer,
#         report_card_strategy=exam.report_card_strategy,
#         shuffle_questions=exam.shuffle_questions,
#         shuffle_answers=exam.shuffle_answers,
#         apply_negative_scores=exam.apply_negative_scores,
#         examiner_info_visibility=exam.examiner_info_visibility,
#         override_institute_name=exam.override_institute_name,
#         override_institute_logo=exam.override_institute_logo,
#         theme=exam.theme,
#         description=exam.description,
#         duration=exam.duration,
#         questions_count=exam.questions_count,
#         delayable=exam.delayable,
#         finish_message=exam.finish_message,
#         poster=exam.poster
#     )
#     return exam_template
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(CreateExamInputSerializer), name='post')
# @method_decorator(
#     check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM__, dr.__PERMISSION_READ_QUESTION__, dr.__PERMISSION_READ_QUESTIONBANK__],
#                                        Institute, 'id', 'context_institute_id'),
#     name='post')
# class CreateExam(APIView):
#     """
#     This API class is responsible for handling incoming requests for creating an Exam
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         name = request.middleware_serializer_data.get('name')
#         rules = request.middleware_serializer_data.get('rules')
#         one_question_at_a_time = request.middleware_serializer_data.get(
#             'one_question_at_a_time')
#         public = request.middleware_serializer_data.get('public')
#         reanswer = request.middleware_serializer_data.get('reanswer')
#         report_card_strategy = request.middleware_serializer_data.get(
#             'report_card_strategy')
#         report_card_date = request.middleware_serializer_data.get(
#             'report_card_date')
#         shuffle_questions = request.middleware_serializer_data.get(
#             'shuffle_questions')
#         shuffle_answers = request.middleware_serializer_data.get(
#             'shuffle_answers')
#         apply_negative_scores = request.middleware_serializer_data.get(
#             'apply_negative_scores')
#         examiner_info_visibility = request.middleware_serializer_data.get(
#             'examiner_info_visibility')
#         override_institute_name = request.middleware_serializer_data.get(
#             'override_institute_name')
#         override_institute_logo = request.middleware_serializer_data.get(
#             'override_institute_logo')
#         poster = request.middleware_serializer_data.get('poster')
#         start_date = request.middleware_serializer_data.get('start_date')
#         end_date = request.middleware_serializer_data.get('end_date')
#         duration = request.middleware_serializer_data.get('duration')
#         description = request.middleware_serializer_data.get('description')
#         theme = request.middleware_serializer_data.get('theme')
#         create_exam_template = request.middleware_serializer_data.get(
#             'create_exam_template')
#         delayable = request.middleware_serializer_data.get('delayable')
#         max_delay = request.middleware_serializer_data.get('max_delay')
#         price = request.middleware_serializer_data.get('price')
#         finish_message = request.middleware_serializer_data.get(
#             'finish_message')
#
#         check_question_ids_response = exam_question_validator(
#             request, rules, context_institute=request.middleware_institute)
#         if type(check_question_ids_response) is MyResponse:
#             return check_question_ids_response
#         else:
#             questions_count = check_question_ids_response
#
#         if override_institute_logo:
#             override_institute_logo = compress_image(override_institute_logo,
#                                                      max_side_size=conf.maximum_side_size_compress_for_exam_override_institute_logo
#                                                      ) if override_institute_logo else None
#
#         public_id = None
#         if public:
#             public_id = ''.join(random.choices(
#                 string.ascii_letters + string.digits, k=60))
#             while Exam.objects.filter(public_id=public_id):
#                 public_id = ''.join(random.choices(
#                     string.ascii_letters + string.digits, k=60))
#
#         # will be saved later
#         exam = Exam(
#             institute=request.middleware_institute,
#             public=public,
#             public_id=public_id,
#             name=name,
#             one_question_at_a_time=True if not reanswer else one_question_at_a_time,
#             reanswer=reanswer,
#             report_card_strategy=report_card_strategy,
#             report_card_date=report_card_date,
#             shuffle_questions=shuffle_questions,
#             shuffle_answers=shuffle_answers,
#             apply_negative_scores=apply_negative_scores,
#             examiner_info_visibility=examiner_info_visibility,
#             override_institute_name=override_institute_name,
#             override_institute_logo=override_institute_logo,
#             start_date=start_date,
#             end_date=end_date,
#             duration=duration,
#             delayable=delayable,
#             max_delay=max_delay if delayable else None,
#             questions_count=questions_count,
#             description=description,
#             rules=rules,
#             theme=theme,
#             price=price,
#             finish_message=finish_message
#         )
#
#         if poster:
#             exam.poster = compress_image(
#                 poster, max_side_size=conf.maximum_side_size_compress_for_exam_poster)
#             exam.poster_thumbnail = compress_image(
#                 poster, max_side_size=conf.maximum_side_size_for_exam_poster_thumbnail)
#         if public:
#             generated_exam, total_questions_count = generate_exam_helper(exam)
#             exam.generated_exam = generated_exam
#             exam.questions_count = total_questions_count
#
#         exam.save()
#
#         if create_exam_template:
#             create_exam_template_from_exam(exam)
#
#         exam_data = GetExamSerializer(exam).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': exam_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(EditExamInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class EditExam(APIView):
#     def post(self, request):
#         # ########################## Block 1 ################################
#         # Receive data from serializer
#         name = request.middleware_serializer_data.get('name')
#         rules = request.middleware_serializer_data.get('rules')
#         one_question_at_a_time = request.middleware_serializer_data.get(
#             'one_question_at_a_time')
#         public = request.middleware_serializer_data.get('public')
#         reanswer = request.middleware_serializer_data.get('reanswer')
#         report_card_strategy = request.middleware_serializer_data.get(
#             'report_card_strategy')
#         report_card_date = request.middleware_serializer_data.get(
#             'report_card_date')
#         shuffle_questions = request.middleware_serializer_data.get(
#             'shuffle_questions')
#         shuffle_answers = request.middleware_serializer_data.get(
#             'shuffle_answers')
#         apply_negative_scores = request.middleware_serializer_data.get(
#             'apply_negative_scores')
#         examiner_info_visibility = request.middleware_serializer_data.get(
#             'examiner_info_visibility')
#         override_institute_name = request.middleware_serializer_data.get(
#             'override_institute_name')
#         override_institute_logo = request.middleware_serializer_data.get(
#             'override_institute_logo')
#         delete_override_institute_logo = request.middleware_serializer_data.get(
#             'delete_override_institute_logo')
#         poster = request.middleware_serializer_data.get('poster')
#         delete_poster = request.middleware_serializer_data.get('delete_poster')
#         start_date = request.middleware_serializer_data.get('start_date')
#         end_date = request.middleware_serializer_data.get('end_date')
#         duration = request.middleware_serializer_data.get('duration')
#         description = request.middleware_serializer_data.get('description')
#         theme = request.middleware_serializer_data.get('theme')
#         delayable = request.middleware_serializer_data.get('delayable')
#         max_delay = request.middleware_serializer_data.get('max_delay')
#         price = request.middleware_serializer_data.get('price')
#         finish_message = request.middleware_serializer_data.get(
#             'finish_message')
#         # ########################## Block 2 ################################
#         exam = request.middleware_model_record
#         if name:
#             exam.name = name
#
#         if rules:
#             check_question_ids_response = exam_question_validator(
#                 request, rules, context_institute=exam.institute)
#             if type(check_question_ids_response) is MyResponse:
#                 return check_question_ids_response
#             else:
#                 questions_count = check_question_ids_response
#             exam.rules = rules
#             exam.questions_count = questions_count
#
#         if reanswer != None:
#             exam.reanswer = reanswer
#         if one_question_at_a_time != None:
#             exam.one_question_at_a_time = one_question_at_a_time
#             if not reanswer:
#                 exam.one_question_at_a_time = True
#         if public != None:
#             exam.public = public
#             if public:
#                 if exam.public is False:
#                     public_id = ''.join(random.choices(
#                         string.ascii_letters + string.digits, k=60))
#                     while Exam.objects.filter(public_id=public_id):
#                         public_id = ''.join(random.choices(
#                             string.ascii_letters + string.digits, k=60))
#                     exam.public_id = public_id
#             else:
#                 exam.public_id = None
#
#         if report_card_strategy:
#             exam.report_card_strategy = report_card_strategy
#
#         if report_card_date:
#             exam.report_card_date = report_card_date
#
#         if shuffle_questions != None:
#             exam.shuffle_questions = shuffle_questions
#
#         if shuffle_answers != None:
#             exam.shuffle_answers = shuffle_answers
#
#         if apply_negative_scores != None:
#             exam.apply_negative_scores = apply_negative_scores
#
#         if delayable != None:
#             exam.delayable = delayable
#             if not delayable:
#                 exam.max_delay = None
#
#         if max_delay != None:
#             exam.max_delay = max_delay if exam.delayable else None
#
#         if price != None:
#             exam.price = price
#
#         if finish_message != None:
#             exam.finish_message = finish_message
#
#         if examiner_info_visibility != None:
#             exam.examiner_info_visibility = examiner_info_visibility
#         if override_institute_name or override_institute_name == '':
#             exam.override_institute_name = override_institute_name
#
#         if delete_override_institute_logo:
#             exam.override_institute_logo.delete()
#
#         if override_institute_logo:
#             exam.override_institute_logo = compress_image(override_institute_logo,
#                                                           max_side_size=conf.maximum_side_size_compress_for_exam_override_institute_logo
#                                                           ) if override_institute_logo else None
#
#         if delete_poster:
#             exam.poster.delete()
#             exam.poster_thumbnail.delete()
#
#         if poster:
#             exam.poster = compress_image(
#                 poster, max_side_size=conf.maximum_side_size_compress_for_exam_poster)
#             exam.poster_thumbnail = compress_image(
#                 poster, max_side_size=conf.maximum_side_size_for_exam_poster_thumbnail)
#         if description:
#             exam.description = description
#         if theme:
#             exam.theme = theme
#         if start_date:
#             exam.start_date = start_date
#         if end_date:
#             exam.end_date = end_date
#         if duration:
#             exam.duration = duration
#         exam.save()
#
#         exam_data = GetExamSerializer(exam).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': exam_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(GetExamInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM__], Exam, 'id', 'exam_id'), name='post')
# class GetExam(APIView):
#     def post(self, request):
#         exam = request.middleware_model_record
#
#         exam_data = GetExamSerializer(exam).data
#
#         # note #8174174 applied
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful',
#                            'data': {'exam': exam_data, 'current_date_time': timezone.now()}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(DeleteExamInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class DeleteExam(APIView):
#     """
#     This API class is responsible for handling incoming requests for creating an Exam
#     """
#
#     def post(self, request):
#         exam = request.middleware_model_record
#         exam.deleted = True
#         exam.save()
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful'}, status=status.HTTP_200_OK)
#
#
# def group_get_helper(request, rules, group_id):
#     for group in rules['groups']:
#         # TODO handling old groups, remove later
#         try:
#             if group['id'] != group_id:
#                 continue
#         except:
#             return MyResponse(request, {'status': 'not_found',
#                                         'message': f'Group with name="{group["id"]}" does not have an ID.'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#         question_ids = []
#         for question in group['questions']:
#             if question['type'] == QUERY_QUESTION_BY_ID:
#                 question_ids.append(question['question_id'])
#             # TODO elif question['type'] == QUERY_QUESTION_LIST_BY_FILTERING:
#
#         # we are not checking any permission here
#         # note #75827145 we trust that when rules was stored, permissions were checked
#         questions = Question.objects.filter(id__in=question_ids)
#         questions_data = QuestionModelSerializer(
#             questions, many=True).data
#
#         for question in group['questions']:
#             for question_object in questions_data:
#                 if question['question_id'] == question_object['id']:
#                     question['question_object'] = question_object
#                     questions_data.remove(question_object)
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'group': group}})
#
#     return MyResponse(request, {'status': 'not_found', 'message': 'Group was not found'},
#                       status=status.HTTP_404_NOT_FOUND)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(ExamGroupGetInputValidator), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class ExamGroupGet(APIView):
#     # TODO we do not check question permissions here. Refer to note #75827145
#     def post(self, request):
#         exam = request.middleware_model_record
#         group_id = request.middleware_serializer_data.get('group_id')
#         return group_get_helper(request, exam.rules, group_id)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(ExamGroupStatInputValidator), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class ExamStat(APIView):
#     def post(self, request):
#         exam = request.middleware_model_record
#         exam_pages = ExamPage.objects.filter(exam=exam)
#         exam_pages = finish_invalid_in_progress_exam_pages(exam_pages)
#         exam_pages_data = GetExamPageForStat(exam_pages, many=True).data
#
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful', 'data': {'exam_pages': exam_pages_data, 'exam_name': exam.name}})
#
#
# list_exam_sort_by_switcher = {
#     'name_asc': 'name',
#     'name_desc': '-name',
#     'created_at_asc': 'created_at',
#     'created_at_desc': '-created_at',
#     'modified_at_asc': 'modified_at',
#     'modified_at_desc': '-modified_at',
# }
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_api_key=True),
#     name='post')
# @method_decorator(serializer_validation_middleware(ListExamValidatorInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM__], Institute, 'id', 'institute_id'),
#                   name='post')
# class ListExam(APIView):
#     """
#     This API class is responsible for handling incoming requests for listing a ExamRules
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         q = request.middleware_serializer_data.get('q')
#         skip = request.middleware_serializer_data.get('skip')
#         take = request.middleware_serializer_data.get('take')
#         sort_by = request.middleware_serializer_data.get('sort_by')
#         sort_by_attr = list_exam_sort_by_switcher.get(sort_by, None)
#         active_filter = request.middleware_serializer_data.get('active_filter')
#         reanswer_filter = request.middleware_serializer_data.get(
#             'reanswer_filter')
#         shuffle_questions_filter = request.middleware_serializer_data.get(
#             'shuffle_questions_filter')
#         shuffle_answers_filter = request.middleware_serializer_data.get(
#             'shuffle_answers_filter')
#         one_question_at_a_time_filter = request.middleware_serializer_data.get(
#             'one_question_at_a_time_filter')
#         deleted_filter = request.middleware_serializer_data.get(
#             'deleted_filter')
#         # ########################## Block 2 ################################
#         # gets the exam_rules list based on two situations: search_words comes or not!
#         exams = Exam.objects.filter(institute=request.middleware_institute)
#         if q is not None:
#             exams = exams.filter(name__icontains=q)
#         if active_filter == 'present':
#             exams = exams.filter(
#                 Q(end_date__gt=timezone.now()) | Q(end_date__isnull=True))
#             exams = exams.filter(
#                 Q(start_date__lt=timezone.now()) | Q(start_date__isnull=True))
#         elif active_filter == 'past':
#             exams = exams.filter(end_date__lt=timezone.now())
#         elif active_filter == 'future':
#             exams = exams.filter(start_date__gt=timezone.now())
#
#         if reanswer_filter == 'include':
#             pass
#         elif reanswer_filter == 'exclude':
#             exams = exams.filter(reanswer=False)
#         elif reanswer_filter == 'only':
#             exams = exams.filter(reanswer=True)
#         if shuffle_questions_filter == 'include':
#             pass
#         elif shuffle_questions_filter == 'exclude':
#             exams = exams.filter(shuffle_questions=False)
#         elif shuffle_questions_filter == 'only':
#             exams = exams.filter(shuffle_questions=True)
#
#         if shuffle_answers_filter == 'include':
#             pass
#         elif shuffle_answers_filter == 'exclude':
#             exams = exams.filter(shuffle_answers=False)
#         elif shuffle_answers_filter == 'only':
#             exams = exams.filter(shuffle_answers=True)
#
#         if one_question_at_a_time_filter == 'include':
#             pass
#         elif one_question_at_a_time_filter == 'exclude':
#             exams = exams.filter(one_question_at_a_time=False)
#         elif one_question_at_a_time_filter == 'only':
#             exams = exams.filter(one_question_at_a_time=True)
#
#         if deleted_filter == 'include':
#             pass
#         elif deleted_filter == 'exclude':
#             exams = exams.filter(deleted=False)
#         elif deleted_filter == 'only':
#             exams = exams.filter(deleted=True)
#         # ########################## Block 3 ################################
#         # gets the count of institutes_list and makes an ordered_institutes_list based on sort_by_attr and skip & take
#         # then serializes it to json and passed it to MyResponse
#         total_count = exams.count()
#         exams = exams.order_by(sort_by_attr)[skip:skip + take]
#         exams_data = ListExamSerializer(exams, many=True).data
#
#         # note #8174174 applied
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                     'data': {'current_date_time': timezone.now(), 'total_count': total_count,
#                                              'exams': exams_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(CreateExamTemplateInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM_TEMPLATE__], Exam, 'id', 'exam_id'),
#                   name='post')
# class CreateExamTemplate(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam = request.middleware_model_record
#         exam_template = create_exam_template_from_exam(exam)
#         exam_template_data = ExamTemplateListSerializer(exam_template).data
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful', 'data': {
#                               'exam_template': exam_template_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(EditExamTemplateInputSerializer), name='post')
# @method_decorator(
#     check_access_permission_middleware(
#         [dr.__PERMISSION_WRITE_EXAM_TEMPLATE__], ExamTemplate, 'id', 'exam_template_id'),
#     name='post')
# class EditExamTemplate(APIView):
#
#     # TODO support poster for exam template
#     def post(self, request):
#         # ########################## Block 1 ################################
#         # Receive data from serializer
#         name = request.middleware_serializer_data.get('name')
#         rules = request.middleware_serializer_data.get('rules')
#         one_question_at_a_time = request.middleware_serializer_data.get(
#             'one_question_at_a_time')
#         reanswer = request.middleware_serializer_data.get('reanswer')
#         report_card_strategy = request.middleware_serializer_data.get(
#             'report_card_strategy')
#         shuffle_questions = request.middleware_serializer_data.get(
#             'shuffle_questions')
#         shuffle_answers = request.middleware_serializer_data.get(
#             'shuffle_answers')
#         apply_negative_scores = request.middleware_serializer_data.get(
#             'apply_negative_scores')
#         examiner_info_visibility = request.middleware_serializer_data.get(
#             'examiner_info_visibility')
#         override_institute_name = request.middleware_serializer_data.get(
#             'override_institute_name')
#         override_institute_logo = request.middleware_serializer_data.get(
#             'override_institute_logo')
#         theme = request.middleware_serializer_data.get('theme')
#         description = request.middleware_serializer_data.get('description')
#         duration = request.middleware_serializer_data.get('duration')
#         delayable = request.middleware_serializer_data.get('delayable')
#         max_delay = request.middleware_serializer_data.get('max_delay')
#         price = request.middleware_serializer_data.get('price')
#         finish_message = request.middleware_serializer_data.get(
#             'finish_message')
#         poster = request.middleware_serializer_data.get('poster')
#         delete_poster = request.middleware_serializer_data.get('delete_poster')
#         # ########################## Block 2 ################################
#         exam_template = request.middleware_model_record
#         if name:
#             exam_template.name = name
#
#         if rules:
#             check_question_ids_response = exam_question_validator(
#                 request, rules, context_institute=exam_template.institute)
#             if type(check_question_ids_response) is MyResponse:
#                 return check_question_ids_response
#             else:
#                 questions_count = check_question_ids_response
#             exam_template.rules = rules
#             exam_template.questions_count = questions_count
#
#         if reanswer != None:
#             exam_template.reanswer = reanswer
#         if one_question_at_a_time != None:
#             exam_template.one_question_at_a_time = one_question_at_a_time
#             if not reanswer:
#                 exam_template.one_question_at_a_time = True
#         if report_card_strategy:
#             exam_template.report_card_strategy = report_card_strategy
#         if shuffle_questions != None:
#             exam_template.shuffle_questions = shuffle_questions
#         if shuffle_answers != None:
#             exam_template.shuffle_answers = shuffle_answers
#
#         if apply_negative_scores != None:
#             exam_template.apply_negative_scores = apply_negative_scores
#
#         if delayable != None:
#             exam_template.delayable = delayable
#
#         if finish_message != None:
#             exam_template.finish_message = finish_message
#
#         if delete_poster:
#             exam_template.poster = None
#
#         elif poster != None:
#             exam_template.poster = poster
#
#         if price != None:
#             exam_template.price = price
#
#         if max_delay != None:
#             exam_template.max_delay = max_delay
#
#         if examiner_info_visibility != None:
#             exam_template.examiner_info_visibility = examiner_info_visibility
#         if override_institute_name or override_institute_name == '':
#             exam_template.override_institute_name = override_institute_name
#         if override_institute_logo:
#             exam_template.override_institute_logo = compress_image(override_institute_logo,
#                                                                    max_side_size=conf.maximum_side_size_compress_for_exam_override_institute_logo
#                                                                    ) if override_institute_logo else None
#         if theme:
#             exam_template.theme = theme
#         if description:
#             exam_template.description = description
#         if duration:
#             exam_template.duration = duration
#         exam_template.save()
#
#         exam_template_data = ExamTemplateListSerializer(exam_template).data
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful', 'data': {
#                               'exam_template': exam_template_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(GetExamTemplateInputSerializer), name='post')
# @method_decorator(
#     check_access_permission_middleware(
#         [dr.__PERMISSION_READ_EXAM_TEMPLATE__], ExamTemplate, 'id', 'exam_template_id'),
#     name='post')
# class GetExamTemplate(APIView):
#
#     def post(self, request):
#         exam_template = request.middleware_model_record
#         exam_template_data = ExamTemplateGetSerializer(exam_template).data
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful', 'data': {
#                               'exam_template': exam_template_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(GetPublicExamTemplateInputSerializer), name='post')
# class GetPublicExamTemplate(APIView):
#
#     def post(self, request):
#         public_exam_template_id = request.middleware_serializer_data.get(
#             'public_exam_template_id')
#
#         try:
#             public_exam_template = PublicExamTemplate.objects.get(
#                 id=public_exam_template_id)
#         except:
#             return MyResponse(request, {'status': 'not_found', 'message': 'Public Exam Template was not found.'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#         public_exam_template_data = PublicExamTemplateGetSerializer(
#             public_exam_template).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                     'data': {'public_exam_template': public_exam_template_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(ListExamTemplateInputSerializer), name='post')
# @method_decorator(
#     check_access_permission_middleware(
#         [dr.__PERMISSION_READ_EXAM_TEMPLATE__], Institute, 'id', 'institute_id'),
#     name='post')
# class ListExamTemplate(APIView):
#
#     def post(self, request):
#         # receiving user inputs from serializer
#         skip = request.middleware_serializer_data.get('skip')
#         take = request.middleware_serializer_data.get('take')
#         q = request.middleware_serializer_data.get('q')
#         reanswer_filter = request.middleware_serializer_data.get(
#             'reanswer_filter')
#         shuffle_questions_filter = request.middleware_serializer_data.get(
#             'shuffle_questions_filter')
#         shuffle_answers_filter = request.middleware_serializer_data.get(
#             'shuffle_answers_filter')
#         one_question_at_a_time_filter = request.middleware_serializer_data.get(
#             'one_question_at_a_time_filter')
#         deleted_filter = request.middleware_serializer_data.get(
#             'deleted_filter')
#         # ########################## Block 2 ################################
#         institute = request.middleware_model_record
#         exam_templates = ExamTemplate.objects.filter(institute=institute)
#         if q is not None:
#             exam_templates = exam_templates.filter(name__icontains=q)
#         if one_question_at_a_time_filter == 'include':
#             pass
#         elif one_question_at_a_time_filter == 'exclude':
#             exam_templates = exam_templates.filter(
#                 one_question_at_a_time=False)
#         elif one_question_at_a_time_filter == 'only':
#             exam_templates = exam_templates.filter(one_question_at_a_time=True)
#         if reanswer_filter == 'include':
#             pass
#         elif reanswer_filter == 'exclude':
#             exam_templates = exam_templates.filter(reanswer=False)
#         elif reanswer_filter == 'only':
#             exam_templates = exam_templates.filter(reanswer=True)
#         if shuffle_questions_filter == 'include':
#             pass
#         elif shuffle_questions_filter == 'exclude':
#             exam_templates = exam_templates.filter(shuffle_questions=False)
#         elif shuffle_questions_filter == 'only':
#             exam_templates = exam_templates.filter(shuffle_questions=True)
#
#         if shuffle_answers_filter == 'include':
#             pass
#
#         elif shuffle_answers_filter == 'exclude':
#             exam_templates = exam_templates.filter(shuffle_answers=False)
#
#         elif shuffle_answers_filter == 'only':
#             exam_templates = exam_templates.filter(shuffle_answers=True)
#
#         if deleted_filter == 'include':
#             pass
#         elif deleted_filter == 'exclude':
#             exam_templates = exam_templates.filter(deleted=False)
#
#         elif deleted_filter == 'only':
#             exam_templates = exam_templates.filter(deleted=True)
#         exam_templates = exam_templates[skip:skip + take]
#         exam_templates_data = ExamTemplateListSerializer(
#             exam_templates, many=True).data
#         return MyResponse(request,
#                           {'status': 'ok', 'message': 'Successful', 'data': {
#                               'exam_templates': exam_templates_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(ListPublicExamTemplateInputSerializer), name='post')
# class ListPublicExamTemplate(APIView):
#
#     def post(self, request):
#         # receiving user inputs from serializer
#         skip = request.middleware_serializer_data.get('skip')
#         take = request.middleware_serializer_data.get('take')
#         q = request.middleware_serializer_data.get('q')
#         one_question_at_a_time_filter = request.middleware_serializer_data.get(
#             'one_question_at_a_time_filter')
#         reanswer_filter = request.middleware_serializer_data.get(
#             'reanswer_filter')
#         shuffle_questions_filter = request.middleware_serializer_data.get(
#             'shuffle_questions_filter')
#         shuffle_answers_filter = request.middleware_serializer_data.get(
#             'shuffle_answers_filter')
#         deleted_filter = request.middleware_serializer_data.get(
#             'deleted_filter')
#         # ########################## Block 2 ################################
#
#         public_exam_templates = PublicExamTemplate.objects.filter()
#         if q is not None:
#             public_exam_templates = public_exam_templates.filter(
#                 name__icontains=q)
#         if one_question_at_a_time_filter == 'include':
#             pass
#         elif one_question_at_a_time_filter == 'exclude':
#             public_exam_templates = public_exam_templates.filter(
#                 one_question_at_a_time=False)
#         elif one_question_at_a_time_filter == 'only':
#             public_exam_templates = public_exam_templates.filter(
#                 one_question_at_a_time=True)
#         # TODO
#         if reanswer_filter == 'include':
#             pass
#         elif reanswer_filter == 'exclude':
#             public_exam_templates = public_exam_templates.filter(
#                 reanswer=False)
#         elif reanswer_filter == 'only':
#             public_exam_templates = public_exam_templates.filter(reanswer=True)
#         if shuffle_questions_filter == 'include':
#             pass
#         elif shuffle_questions_filter == 'exclude':
#             public_exam_templates = public_exam_templates.filter(
#                 shuffle_questions=False)
#         elif shuffle_questions_filter == 'only':
#             public_exam_templates = public_exam_templates.filter(
#                 shuffle_questions=True)
#
#         if shuffle_answers_filter == 'include':
#             pass
#
#         elif shuffle_answers_filter == 'exclude':
#             public_exam_templates = public_exam_templates.filter(
#                 shuffle_answers=False)
#
#         elif shuffle_answers_filter == 'only':
#             public_exam_templates = public_exam_templates.filter(
#                 shuffle_answers=True)
#         if deleted_filter == 'include':
#             pass
#         elif deleted_filter == 'exclude':
#             public_exam_templates = public_exam_templates.filter(deleted=False)
#
#         elif deleted_filter == 'only':
#             public_exam_templates = public_exam_templates.filter(deleted=True)
#         public_exam_templates = public_exam_templates.order_by('priority')[
#             skip:skip + take]
#         public_exam_templates_data = PublicExamTemplateListSerializer(
#             public_exam_templates, many=True).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                     'data': {'public_exam_templates': public_exam_templates_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(DeleteExamTemplateInputSerializer), name='post')
# @method_decorator(
#     check_access_permission_middleware(
#         [dr.__PERMISSION_WRITE_EXAM_TEMPLATE__], ExamTemplate, 'id', 'exam_template_id'),
#     name='post')
# class DeleteExamTemplate(APIView):
#
#     def post(self, request):
#         exam_template = request.middleware_model_record
#         exam_template.deleted = True
#         exam_template.save()
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful'}, status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(ExamTemplateGroupGetInputValidator), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM_TEMPLATE__], ExamTemplate, 'id', 'exam_template_id'),
#                   name='post')
# class ExamTemplateGroupGet(APIView):
#     # TODO we do not check question permissions here. Refer to note #75827145
#     def post(self, request):
#         exam_template = request.middleware_model_record
#         group_id = request.middleware_serializer_data.get('group_id')
#         return group_get_helper(request, exam_template.rules, group_id)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(StartExamPageInputSerializer), name='post')
# class GetExamPage(APIView):
#     def post(self, request):
#         # Receive data from serializer
#         exam_page_id = request.middleware_serializer_data.get('exam_page_id')
#         # TODO
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': 's'}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(StartExamPageInputSerializer), name='post')
# class EnterExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page_id = request.middleware_serializer_data.get('exam_page_id')
#         # TODO
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': 's'}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(StartExamPageInputSerializer), name='post')
# class SetAnswerExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page_id = request.middleware_serializer_data.get('exam_page_id')
#         # TODO
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': 's'}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(StartExamPageInputSerializer), name='post')
# class FinishExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page_id = request.middleware_serializer_data.get('exam_page_id')
#         # TODO
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': 's'}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(
#         accept_user_token=True, optional_authentication=True),
#     name='post')
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(serializer_validation_middleware(GetPublicExamPageInputSerializer), name='post')
# class GetPublicExamPageCover(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam = request.middleware_exam
#
#         exam_data = GetExamSerializerForCoverPage(exam).data
#
#         data = {}
#         # data['institute_id'] = exam_data['institute']['id']
#
#         # if not exam_data['examiner_info_visibility']:
#         #     data['institute_name'] = None
#         #     data['institute_logo'] = None
#         # else:
#
#         # if exam_data['override_institute_name']:
#         #     data['institute_name'] = exam_data['override_institute_name']
#         # else:
#         #     data['institute_name'] = exam_data['institute']['name']
#
#         # if exam_data['override_institute_logo']:
#         #     data['institute_logo'] = exam_data['override_institute_logo']
#         # else:
#         #     data['institute_logo'] = exam_data['institute']['logo']
#
#         data['name'] = exam_data['name']
#         data['description'] = exam_data['description']
#         data['poster'] = exam_data['poster']
#         data['poster_thumbnail'] = exam_data['poster_thumbnail']
#         data['report_card_strategy'] = exam_data['report_card_strategy']
#         data['report_card_date'] = exam_data['report_card_date']
#         data['created_at'] = exam_data['created_at']
#         data['price'] = exam_data['price']
#         data['institute'] = exam_data['institute']
#
#         exam_page = request.middleware_exam_page
#
#         if exam_page:
#             if exam_page.invalid_in_progress_state:
#                 exam_page, *_ = finish_exam_page_helper(exam_page)
#             if exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#                 exam_page_data = GetExamPageAfterScoresPublishedSerializer(
#                     exam_page).data
#                 corrections_data = GetCorrectionsForStudentSerializer(
#                     Corrections.objects.filter(exam_page=exam_page), many=True).data
#                 answer_file_data = GetAnswerFileModelSerializer(
#                     AnswerFile.objects.filter(exam_page=exam_page), many=True).data
#                 data['generated_exam'] = exam_page_data['generated_exam']
#                 data['user_inputs'] = exam_page_data['user_inputs']
#                 data['final_score'] = exam_page_data['final_score']
#                 data['report_card'] = exam_page_data['report_card']
#                 data['finished_at'] = exam_page.finished_at
#                 data['corrections'] = corrections_data
#                 data['answer_file'] = answer_file_data
#             else:
#                 exam_page_data = GetExamPageBeforeScoresPublishedSerializer(
#                     exam_page).data
#
#             data['state'] = exam_page_data['state']
#             data['entrance_date'] = exam_page_data['entrance_date']
#             data['start_date'] = exam_page_data['start_date']
#             data['end_date'] = exam_page_data['end_date']
#             data['duration'] = exam_page_data['duration']
#             data['apply_negative_scores'] = exam_page_data['apply_negative_scores']
#             data['questions_count'] = exam_page_data['questions_count']
#
#             data['reanswer'] = exam_page_data['reanswer']
#             data['one_question_at_a_time'] = exam_page_data['one_question_at_a_time']
#         else:
#             data['state'] = EXAM_PAGE_STATE_NOT_GENERATED
#             data['entrance_date'] = None
#             data['start_date'] = exam_data['start_date']
#             data['end_date'] = exam_data['end_date']
#             data['duration'] = exam_data['duration']
#             data['apply_negative_scores'] = exam_data['apply_negative_scores']
#             data['questions_count'] = exam_data['questions_count']
#
#             data['reanswer'] = exam_data['reanswer']
#             data['one_question_at_a_time'] = exam_data['one_question_at_a_time']
#
#         data['theme'] = exam_data['theme']
#         data['exam_id'] = exam_data['id']
#
#         # note #8174174
#         # in order to reduce the delay between [generating the timezone] and [receiving the timezone by user],
#         # we generate current_date_time at the last step of our APIs
#         data['current_date_time'] = timezone.now()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': data}, status=status.HTTP_200_OK)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(serializer_validation_middleware(GetPublicExamPageInputSerializer), name='post')
# class GetPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam = request.middleware_exam
#         exam_data = GetPublicExamSerializer(exam).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam': exam_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(PublicGetQuestionExamPageInputSerializer), name='post')
# class GetQuestionPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam_page = request.middleware_exam_page
#         # Receive data from serializer
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         group_index = request.middleware_serializer_data.get('group_index')
#
#         return helper_get_question(group_index, question_index, request, exam_page)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=False), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(PublicGetGroupExamPageInputSerializer), name='post')
# class GetGroupPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam_page = request.middleware_exam_page
#         # Receive data from serializer
#         group_index = request.middleware_serializer_data.get('group_index')
#
#         return helper_get_group(exam_page, group_index, request)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(
#         accept_user_token=True),
#     name='post')
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(exam_purchase_middleware(), name='post')
# @method_decorator(exam_page_status_validation(error_if_exam_page_is_none=False), name='post')
# class EnterPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam = request.middleware_exam
#         exam_page = request.middleware_exam_page
#
#         should_pay = False
#         if not exam_page:
#             should_pay = True
#             # only generate exam if the parent has not generated it yet
#             (generated_exam, total_questions_count) = generate_exam_helper(exam)
#
#             # ExamPage.objects.create() will also save it here
#             # we should not save this object yet, it does not have device_token, ...
#
#             exam_page = ExamPage(
#                 institute=exam.institute,
#                 entrance_type=ENTRANCE_TYPE_PUBLIC,
#                 exam=exam,
#                 apply_negative_scores=exam.apply_negative_scores,
#                 generated_exam=generated_exam,
#                 state=EXAM_PAGE_STATE_IN_PROGRESS,
#                 first_available_question_index=0,
#                 first_available_group_index=0,
#                 questions_count=total_questions_count,
#                 start_date=exam.start_date,
#                 end_date=exam.end_date,
#                 duration=exam.duration,
#                 reanswer=exam.reanswer,
#                 one_question_at_a_time=exam.one_question_at_a_time,
#                 entrance_date=timezone.now(),
#                 delayable=exam.delayable,
#                 user=request.middleware_user,
#             )
#         result = enter_exam_helper(request, exam_page)
#
#         if should_pay:
#             exam_purchase_handler(request.middleware_user, exam, exam_page)
#
#         return result
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=False), name='post')
# # @method_decorator(exam_purchase_middleware='post')
# # we so not double check student subscription after he entered an exam
# # a user might enter lots of exams and take them later even after his subscription is ended
# # there is nothing wrong if we double check but users may be interrupted in the middle of an exam for subscription expiration
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(PublicSetAnswerExamPageInputSerializer), name='post')
# class SetAnswerPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam_page = request.middleware_exam_page
#         user_inputs = request.middleware_serializer_data.get('user_inputs')
#         go_to_next_question = request.middleware_serializer_data.get(
#             'go_to_next_question')
#         answer_time = timezone.now()
#         return set_answer_helper(request, exam_page, user_inputs, answer_time, go_to_next_question)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=False), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# class FinishPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#
#         return finish_exam_page_request_handler(exam_page, request)
#
#
# def generate_question_from_question_object(exam, question_object, question_rule, question_index):
#     generated_question = {}
#     if question_rule['type'] == QUERY_QUESTION_BY_ID:
#         generated_question['positive_score'] = question_rule['positive_score']
#         generated_question['negative_score'] = question_rule['negative_score']
#         generated_question['duration'] = question_rule['duration']
#     if question_rule['type'] == QUERY_QUESTION_LIST_BY_FILTERING:
#         generated_question['positive_score'] = question_rule['positive_score_per_question']
#         generated_question['negative_score'] = question_rule['negative_score_per_question']
#         generated_question['duration'] = question_rule['duration_per_question']
#
#     generated_question['question_id'] = str(question_object.id)
#     generated_question['question_text'] = question_object.question_text
#     generated_question['question_text_format'] = question_object.question_text_format
#     generated_question['answer_rules'] = question_object.answer_rules
#
#     # temporary solution for adding anser_sheet to question before exam_question branch added
#     answer_sheet_queryset = AnswerSheet.objects.filter(
#         question=question_object)
#     if answer_sheet_queryset:
#         generated_question['solution'] = text_book_serializers.AnswerSheetModelSerializer(
#             answer_sheet_queryset[0]).data
#
#     if question_object.question_type == QUESTION_TYPE_MULTIPLE_CHOICE:
#         if exam.shuffle_answers:
#             random.shuffle(generated_question['answer_rules']['choices'])
#
#         answer_index = 0
#         for choice in generated_question['answer_rules']['choices']:
#             choice.pop('priority')
#             choice['index'] = answer_index
#             answer_index += 1
#
#     generated_question['question_type'] = question_object.question_type
#     generated_question['index'] = question_index
#
#     return generated_question
#
#
# def generate_exam_helper(exam):
#     generated_exam = copy.deepcopy(exam.rules)
#     group_index = 0
#     total_questions_count = 0
#     for group in generated_exam['groups']:
#         positive_scores = 0
#         negative_scores = 0
#         durations = 0
#         questions_count = 0
#         if exam.shuffle_questions:
#             random.shuffle(group['questions'])
#
#         generated_questions = []
#         question_index = 0
#         for question_rule in group['questions']:
#             # TODO validate uuid for question_id
#             # TODO optimize these queries with bulk get
#             if question_rule['type'] == QUERY_QUESTION_BY_ID:
#                 question_object = Question.objects.get(
#                     id=question_rule['question_id'])
#                 generated_question = generate_question_from_question_object(exam, question_object, question_rule,
#                                                                             question_index)
#                 question_index += 1
#                 generated_questions.append(generated_question)
#
#                 positive_scores += generated_question['positive_score']
#                 negative_scores += generated_question['negative_score']
#                 durations += generated_question['duration']
#                 questions_count += 1
#             elif question_rule['type'] == QUERY_QUESTION_LIST_BY_FILTERING:
#                 # TODO handle tag filters
#                 # TODO handle type filters
#                 new_question_objects = Question.objects.filter(question_bank_id=question_rule['question_bank_id'])[
#                     0:question_rule['count']]
#                 if len(new_question_objects) != question_rule['count']:
#                     # TODO
#                     pass
#                 for question_object in new_question_objects:
#                     generated_question = generate_question_from_question_object(exam, question_object, question_rule,
#                                                                                 question_index)
#                     question_index += 1
#                     generated_questions.append(generated_question)
#
#                     positive_scores += generated_question['positive_score']
#                     negative_scores += generated_question['negative_score']
#                     durations += generated_question['duration']
#                     questions_count += 1
#
#         group['questions'] = generated_questions
#         group['index'] = group_index
#         group_index += 1
#         group['positive_scores'] = round(positive_scores, 2)
#         group['negative_scores'] = round(negative_scores, 2)
#         group['durations'] = round(durations, 2)
#         group['questions_count'] = questions_count
#         total_questions_count += questions_count
#
#     return generated_exam, total_questions_count
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_server_api_key=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(GenerateExamPageInputValidator), name='post')
# class GenerateExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for listing InstituteStudents
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         wikiazma_institute_student_id = request.middleware_serializer_data.get(
#             'wikiazma_institute_student_id')
#         referer_institute_student_identity = request.middleware_serializer_data.get(
#             'referer_institute_student_identity')
#         wikiazma_exam_id = request.middleware_serializer_data.get(
#             'wikiazma_exam_id')
#         start_date = request.middleware_serializer_data.get('start_date')
#         end_date = request.middleware_serializer_data.get('end_date')
#         duration = request.middleware_serializer_data.get('duration')
#         delayable = request.middleware_serializer_data.get('delayable')
#
#         if wikiazma_institute_student_id:
#             try:
#                 institute_student = InstituteStudent.objects.get(
#                     institute=request.middleware_institute, id=wikiazma_institute_student_id)
#             except:
#                 return MyResponse(request, {'status': 'error', 'message': 'Institute Student not found'},
#                                   status=status.HTTP_404_NOT_FOUND)
#         elif referer_institute_student_identity:
#             try:
#                 institute_student = InstituteStudent.objects.get(institute=request.middleware_institute,
#                                                                  referer_identity=referer_institute_student_identity)
#             except:
#                 return MyResponse(request, {'status': 'error', 'message': 'Institute Student not found'},
#                                   status=status.HTTP_404_NOT_FOUND)
#         else:
#             return MyResponse(request,
#                               {'status': 'error',
#                                'message': 'either wikiazma_institute_student_id or referer_institute_student_identity should be provided'},
#                               status=status.HTTP_400_BAD_REQUEST)
#         try:
#
#             exam = Exam.objects.get(
#                 institute=request.middleware_institute, id=wikiazma_exam_id)
#         except:
#             return MyResponse(request, {'status': 'error', 'message': 'Exam not found'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#         (generated_exam, total_questions_count) = generate_exam_helper(exam)
#
#         entrance_token = ''.join(random.choices(
#             string.ascii_letters + string.digits, k=60))
#         while ExamPage.objects.filter(entrance_token=entrance_token):
#             entrance_token = ''.join(random.choices(
#                 string.ascii_letters + string.digits, k=60))
#
#         exam_page = ExamPage.objects.create(
#             entrance_token=entrance_token,
#             entrance_type=ENTRANCE_TYPE_API_INVITE,
#             institute=request.middleware_institute,
#             institute_student=institute_student,
#             exam=exam,
#             apply_negative_scores=exam.apply_negative_scores,
#             one_question_at_a_time=exam.one_question_at_a_time,
#             reanswer=exam.reanswer,
#             generated_exam=generated_exam,
#             state=EXAM_PAGE_STATE_NOT_ATTENDED,
#             first_available_question_index=0,
#             first_available_group_index=0,
#             start_date=start_date if start_date else exam.start_date,
#             end_date=end_date if end_date else exam.end_date,
#             duration=duration if duration else exam.duration,
#             questions_count=total_questions_count,
#             delayable=delayable
#         )
#
#         exam_page_data = GetExamPageFullDataIncludingEntranceTokenSerializer(
#             exam_page).data
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam_page': exam_page_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_server_api_key=True),
#                   name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentVerifyExamPageInputSerializer), name='post')
# class InstituteStudentVerifyExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page_id = request.middleware_serializer_data.get('exam_page_id')
#
#         try:
#             exam_page = ExamPage.objects.get(institute=request.middleware_institute, id=exam_page_id,
#                                              entrance_type=ENTRANCE_TYPE_API_INVITE)
#         except:
#             return MyResponse(request, {'status': 'error', 'message': 'Exam Page not found'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#         if exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             exam_page_data = GetExamPageAfterScoresPublishedSerializer(
#                 exam_page).data
#             data = {'exam_page': exam_page_data}
#             # corrections_data = GetCorrectionsForStudentSerializer(
#             #     Corrections.objects.filter(exam_page=exam_page), many=True).data
#             # data = {'exam_page': exam_page_data,
#             #         'corrections': corrections_data}
#         else:
#             exam_page_data = GetExamPageBeforeScoresPublishedSerializer(
#                 exam_page).data
#
#             data = {'exam_page': exam_page_data}
#
#         # note #8174174 applied
#         data['current_date_time'] = timezone.now()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': data}, status=status.HTTP_200_OK)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=False), name='post')
# class InstituteStudentGetExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#         exam = request.middleware_exam_page.exam
#
#         exam_data = GetExamSerializerForCoverPage(exam).data
#
#         data = {'exam': exam_data}
#
#         if exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             exam_page_data = GetExamPageAfterScoresPublishedSerializer(
#                 exam_page).data
#             corrections_data = GetCorrectionsForStudentSerializer(
#                 Corrections.objects.filter(exam_page=exam_page), many=True).data
#
#             data['exam_page'] = exam_page_data
#             data['corrections'] = corrections_data
#
#         else:
#             exam_page_data = GetExamPageBeforeScoresPublishedSerializer(
#                 exam_page).data
#             data['exam_page'] = exam_page_data
#
#         # note #8174174 applied
#         data['current_date_time'] = timezone.now()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': data}, status=status.HTTP_200_OK)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=False), name='post')
# class InstituteStudentGetExamPageCover(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#         exam = request.middleware_exam_page.exam
#
#         exam_data = GetExamSerializerForCoverPage(exam).data
#
#         data = {}
#         # data['institute_id'] = exam_data['institute']['id']
#
#         # # TODO add a test for this if
#         # if not exam_data['examiner_info_visibility']:
#         #     data['institute_name'] = None
#         #     data['institute_logo'] = None
#         # else:
#         #     if exam_data['override_institute_name']:
#         #         data['institute_name'] = exam_data['override_institute_name']
#         #     else:
#         #         data['institute_name'] = exam_data['institute']['name']
#
#         #     if exam_data['override_institute_logo']:
#         #         data['institute_logo'] = exam_data['override_institute_logo']
#         #     else:
#         #         data['institute_logo'] = exam_data['institute']['logo']
#
#         data['name'] = exam_data['name']
#         data['description'] = exam_data['description']
#         data['poster'] = exam_data['poster']
#         data['poster_thumbnail'] = exam_data['poster_thumbnail']
#         data['price'] = exam_data['price']
#         # TODO should we remove report_card_strategy? because it will give away some data about auto_correct-ability of questions
#         data['report_card_strategy'] = exam_data['report_card_strategy']
#         data['report_card_date'] = exam_data['report_card_date']
#         data['created_at'] = exam_data['created_at']
#
#         data['institute'] = exam_data['institute']
#
#         if exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             exam_page_data = GetExamPageAfterScoresPublishedSerializer(
#                 exam_page).data
#             corrections_data = GetCorrectionsForStudentSerializer(
#                 Corrections.objects.filter(exam_page=exam_page), many=True).data
#             answer_file_data = GetAnswerFileModelSerializer(
#                 AnswerFile.objects.filter(exam_page=exam_page), many=True).data
#             data['generated_exam'] = exam_page_data['generated_exam']
#             data['user_inputs'] = exam_page_data['user_inputs']
#             data['final_score'] = exam_page_data['final_score']
#             data['report_card'] = exam_page_data['report_card']
#             data['finished_at'] = exam_page.finished_at
#             data['corrections'] = corrections_data
#             data['answer_file'] = answer_file_data
#         else:
#             exam_page_data = GetExamPageBeforeScoresPublishedSerializer(
#                 exam_page).data
#
#         data['state'] = exam_page_data['state']
#         data['entrance_date'] = exam_page_data['entrance_date']
#         data['start_date'] = exam_page_data['start_date']
#         data['end_date'] = exam_page_data['end_date']
#         data['duration'] = exam_page_data['duration']
#
#         data['apply_negative_scores'] = exam_page_data['apply_negative_scores']
#         data['questions_count'] = exam_page_data['questions_count']
#
#         data['reanswer'] = exam_page_data['reanswer']
#         data['one_question_at_a_time'] = exam_page_data['one_question_at_a_time']
#         data['theme'] = exam_data['theme']
#         data['exam_id'] = exam_data['id']
#         # TODO
#         # data['exam_poster'] = exam_data['poster']
#
#         # note #8174174 applied
#         data['current_date_time'] = timezone.now()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': data}, status=status.HTTP_200_OK)
#
#
# def enter_exam_helper(request, exam_page):
#     if exam_page.state == EXAM_PAGE_STATE_NOT_ATTENDED:
#         exam_page.state_history.append(exam_page.state)
#         exam_page.state = EXAM_PAGE_STATE_IN_PROGRESS
#         exam_page.entrance_date = timezone.now()
#
#     # generate device token
#
#     device_token = ''.join(random.choices(
#         string.ascii_letters + string.digits, k=60))
#     while ExamPage.objects.filter(device_token=device_token):
#         device_token = ''.join(random.choices(
#             string.ascii_letters + string.digits, k=60))
#     exam_page.device_token = device_token
#     exam_page.device_history.append(
#         {'device_token': device_token,
#          'device_agent': request.META['HTTP_USER_AGENT'] if 'HTTP_USER_AGENT' in request.META else None})
#
#     # should be saved before using GetExamPageOnAttendAllQuestionsTogetherSerializer because this serializer changes the values for answer_rules
#     exam_page.save()
#
#     if exam_page.reanswer:
#         # the one_question_at_a_time can be true or false here
#         # On start exam, we send all generated_exam and all user_inputs data
#         exam_page_data = GetExamPageOnAttendAllQuestionsTogetherSerializer(
#             exam_page).data
#     else:
#         # the one_question_at_a_time should be true here
#         # On start exam, we send the first_available_question_index and all user_inputs data
#         exam_page_data = GetExamPageOnAttendNotReanswerableExamSerializer(
#             exam_page).data
#
#     exam = exam_page.exam
#     exam_data = GetExamSerializerForEnterPage(exam).data
#
#     # note #8174174 applied
#     return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                 'data': {'exam_page': exam_page_data, 'exam': exam_data, 'device_token': device_token,
#                                          'current_date_time': timezone.now()}}, status=status.HTTP_200_OK)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=False), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# class InstituteStudentEnterExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#
#         return enter_exam_helper(request, exam_page)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=False), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# class InstituteStudentTestResetExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam_page = request.middleware_exam_page
#
#         (generated_exam, total_questions_count) = generate_exam_helper(exam_page.exam)
#         exam_page.generated_exam = generated_exam
#         exam_page.questions_count = total_questions_count
#         exam_page.first_available_group_index = 0
#         exam_page.first_available_question_index = 0
#         exam_page.user_inputs = []
#         exam_page.state = EXAM_PAGE_STATE_NOT_ATTENDED
#         exam_page.entrance_date = None
#         exam_page.save()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Exam Page was reset'}, status=status.HTTP_200_OK)
#
#
# # TODO if first_available_indexes were wrong and no question with those indexes were found?
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentGetQuestionExamPageInputSerializer), name='post')
# class InstituteStudentGetQuestionExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         group_index = request.middleware_serializer_data.get('group_index')
#
#         return helper_get_question(group_index, question_index, request, exam_page)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentGetGroupExamPageInputSerializer), name='post')
# class InstituteStudentGetGroupExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         exam_page = request.middleware_exam_page
#         # Receive data from serializer
#         group_index = request.middleware_serializer_data.get('group_index')
#
#         return helper_get_group(exam_page, group_index, request)
#
#
# def validate_user_input_other_fields(question, new_user_input):
#     if question['question_type'] == QUESTION_TYPE_MULTIPLE_CHOICE:
#         if 'answer_indexes' not in new_user_input:
#             raise serializers.ValidationError({
#                 'user_inputs': f'answer_indexes is required for question with index={new_user_input["question_index"]} in the group with index={new_user_input["group_index"]}'})
#         if type(new_user_input['answer_indexes']) is not list:
#             raise serializers.ValidationError(
#                 {'user_inputs': 'answer_indexes must be a list'})
#
#         user_input_chosen_indexes = []
#
#         for chosen_index in new_user_input['answer_indexes']:
#             # check for duplicate chosen_index
#             if chosen_index in user_input_chosen_indexes:
#                 raise serializers.ValidationError({'user_inputs': "duplicate choice=" + str(
#                     chosen_index) + ' in the list of answer_indexes for question_index=' + str(question['index'])})
#             user_input_chosen_indexes.append(chosen_index)
#
#             chosen_index_found = False
#             for choice in question['answer_rules']['choices']:
#                 if choice['index'] == chosen_index:
#                     chosen_index_found = True
#                     break
#
#             if not chosen_index_found:
#                 raise serializers.ValidationError({'user_inputs': "answer_choice=" + str(
#                     chosen_index) + ' was not found in the list of choices for question_index=' + str(
#                     question['index'])})
#
#             if len(user_input_chosen_indexes) > question['answer_rules']['max_selectable_choices']:
#                 # TODO Message error
#                 raise serializers.ValidationError('sffd')
#
#     elif question['question_type'] == QUESTION_TYPE_DESCRIPTIVE:
#         if 'answer_text' not in new_user_input:
#             raise serializers.ValidationError({
#                 'user_inputs': f'answer_text is required for question with index={new_user_input["question_index"]} in the group with index={new_user_input["group_index"]}'})
#
#         if 'answer_text_format' not in new_user_input:
#             raise serializers.ValidationError({
#                 'user_inputs': f'answer_text_format is required for question with index={new_user_input["question_index"]} in the group with index={new_user_input["group_index"]}'})
#
#         if type(new_user_input['answer_text']) is not str:
#             raise serializers.ValidationError(
#                 {'user_inputs': 'answer_text  must be a string'})
#
#         if type(new_user_input['answer_text_format']) is not str:
#             raise serializers.ValidationError(
#                 {'user_inputs': 'answer_text_format  must be a string'})
#
#         if len(new_user_input['answer_text']) > question['answer_rules']['max_characters']:
#             raise serializers.ValidationError()
#
#         if new_user_input['answer_text_format'] not in ALLOWED_ANSWER_TEXT_FORMAT:
#             raise serializers.ValidationError(
#                 {'question_text_format': f'question_text_format should be one of {", ".join(ALLOWED_ANSWER_TEXT_FORMAT)}'})
#
#     else:
#         raise serializers.ValidationError(
#             {'user_inputs': "set_answer is not implemented for your descriptive questions yet"})
#
#
# def check_user_input(exam_page, new_user_input):
#     for group in exam_page.generated_exam['groups']:
#         if new_user_input['group_index'] == group['index']:
#             for question in group['questions']:
#                 if new_user_input['question_index'] == question['index']:
#                     validate_user_input_other_fields(question, new_user_input)
#                     is_the_last_question_in_the_group = (
#                         question == group['questions'][-1])
#                     return question, is_the_last_question_in_the_group
#
#     raise serializers.ValidationError(
#         {
#             'user_inputs': f'question_index="{new_user_input["question_index"]}" was not found in the list of questions with group_index={new_user_input["group_index"]}'})
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentSetAnswerExamPageInputSerializer), name='post')
# class InstituteStudentSetAnswerExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#         user_inputs = request.middleware_serializer_data.get('user_inputs')
#         go_to_next_question = request.middleware_serializer_data.get(
#             'go_to_next_question')
#         answer_time = timezone.now()
#         # This code is present inside user_inputs serializer
#         # if len(user_inputs) == 0:
#         #     return MyResponse(request, {'status': 'error', 'message': f'You should send At least one answer'},
#         #                       status=status.HTTP_400_BAD_REQUEST)
#
#         return set_answer_helper(request, exam_page, user_inputs, answer_time, go_to_next_question)
#
#
# def correct_multiple_choice_question_helper(user_input, group, question, exam_page, correction=None, force_score=None):
#     if correction is None:
#         correction = Corrections()
#
#     correction.corrector = None
#     correction.exam_page = exam_page
#     correction.question_id = question['question_id']
#     correction.question_index = question['index']
#     correction.group_index = group['index']
#
#     if user_input is None:
#         correction.blank = True
#
#         correct_choices = 0
#         wrong_choices = 0
#         blank_choices = question['answer_rules']['max_selectable_choices']
#         question_score = 0
#     else:
#         correction.blank = False
#         correct_choices = 0
#         wrong_choices = 0
#
#         for user_choice in user_input['answer_indexes']:
#             user_choice_found = False
#             for choice in question['answer_rules']['choices']:
#                 if choice['index'] == user_choice:
#                     user_choice_found = True
#                     if choice['is_correct_choice']:
#                         correct_choices += 1
#                     else:
#                         wrong_choices += 1
#                     break
#             if not user_choice_found:
#                 print("critical error #4817341")
#
#         blank_choices = question['answer_rules']['max_selectable_choices'] - \
#             (correct_choices + wrong_choices)
#
#         total_number_of_correct_choices = 0
#         total_number_of_wrong_choices = 0
#         for choice in question['answer_rules']['choices']:
#             if choice['is_correct_choice']:
#                 total_number_of_correct_choices += 1
#             else:
#                 total_number_of_wrong_choices += 1
#
#         positive_score = 0
#         if total_number_of_correct_choices != 0:
#             positive_score = correct_choices / \
#                 min(total_number_of_correct_choices,
#                     question['answer_rules']['max_selectable_choices']) * question['positive_score']
#
#         negative_score = 0
#         if exam_page.apply_negative_scores:
#             # TODO max_selectable_choices should be equal to the number of correct choices otherwise, user can
#             # TODO select all choices and still get the max positive score
#             if total_number_of_wrong_choices != 0:
#                 negative_score = wrong_choices / \
#                     min(total_number_of_wrong_choices,
#                         question['answer_rules']['max_selectable_choices']) * question['negative_score']
#
#         question_score = positive_score - negative_score
#
#     correction.score = question_score if force_score is None else force_score
#     correction.correct_choices = correct_choices
#     correction.wrong_choices = wrong_choices
#     correction.blank_choices = blank_choices
#     correction.save()
#
#     return correction
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(exam_page_status_validation(), name='post')
# class InstituteStudentFinishExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for starting an exam page
#     """
#
#     def post(self, request):
#         # Receive data from serializer
#         exam_page = request.middleware_exam_page
#
#         if exam_page.institute.on_exam_finished_redirect_url and exam_page.entrance_token:
#
#             if exam_page.institute.on_exam_finished_redirect_url.endswith('?'):
#                 redirect_url = exam_page.institute.on_exam_finished_redirect_url + \
#                     f'entrance_token={exam_page.entrance_token}'
#             elif '?' in exam_page.institute.on_exam_finished_redirect_url:
#                 redirect_url = exam_page.institute.on_exam_finished_redirect_url + \
#                     f'&entrance_token={exam_page.entrance_token}'
#             else:
#                 redirect_url = exam_page.institute.on_exam_finished_redirect_url + \
#                     f'?entrance_token={exam_page.entrance_token}'
#
#             return finish_exam_page_request_handler(exam_page, request, redirect_url=redirect_url)
#         else:
#             return finish_exam_page_request_handler(exam_page, request, )
#
#
# @method_decorator(user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True),
#                   name='post')
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(exam_page_status_validation(error_if_exam_page_is_none=False), name='post')
# class PublicGenerateInvoiceExamPage(APIView):
#
#     def post(self, request):
#         user = request.middleware_user
#         exam = request.middleware_exam
#
#         total_cost, institute_share, wikiazma_share, wage, tax = price_calculator_helper(
#             user, exam)
#
#         context = {'status': 'ok', 'message': 'Successful',
#                    "data": {'invoice': {"type": "exam", "price": request.middleware_exam.price, "wage": wage, "tax": tax,
#                                         "total_cost": total_cost, "meta_data": {'exam_id': exam.id}},
#                             'balance': UserBalanceHistory.get_last_user_balance(user).remaining_balance}}
#         return MyResponse(request, context, status=status.HTTP_200_OK)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=True), name='post')
# @method_decorator(serializer_validation_middleware(PublicAnswerFileExamPageInputSerializer), name='post')
# class PublicAnswerFileExamPage(APIView):
#     def post(self, request):
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         group_index = request.middleware_serializer_data.get('group_index')
#         file = request.middleware_serializer_data.get('file')
#         exam_page = request.middleware_exam_page
#
#         return helper_answer_file(exam_page, group_index, question_index, request, file)
#
#
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=False), name='post')
# @method_decorator(serializer_validation_middleware(PublicAnswerFileDeleteExamPageInputSerializer), name='post')
# class PublicAnswerFileDeleteExamPage(APIView):
#     def post(self, request):
#         file_id = request.middleware_serializer_data.get('file_id')
#         exam_page = request.middleware_exam_page
#         group_index = request.middleware_serializer_data.get('group_index')
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         try:
#             answer_file = AnswerFile.objects.get(
#                 id=file_id, exam_page=exam_page)
#             # TODO does it also delete the file?
#             if not exam_page.exam.reanswer:
#                 if group_index > exam_page.first_available_group_index or (
#                         group_index == exam_page.first_available_group_index and question_index > exam_page.first_available_question_index):
#                     return MyResponse(request, {'status': 'index_error',
#                                                 'message': 'You can\'t get this question',
#                                                 'data': {
#                                                     'first_available_question_index': exam_page.first_available_question_index,
#                                                     'first_available_group_index': exam_page.first_available_group_index}},
#                                       status=status.HTTP_409_CONFLICT)
#             answer_file.delete()
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful'},
#                               status=status.HTTP_200_OK)
#         except:
#             return MyResponse(request, {'status': 'not_found', 'message': 'File not found'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentAnswerFileDeleteExamPageInputSerializer), name='post')
# class InstituteStudentAnswerFileDeleteExamPage(APIView):
#     def post(self, request):
#         file_id = request.middleware_serializer_data.get('file_id')
#         exam_page = request.middleware_exam_page
#         group_index = request.middleware_serializer_data.get('group_index')
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         try:
#             answer_file = AnswerFile.objects.get(
#                 id=file_id, exam_page=exam_page)
#             # TODO does it also delete the file?
#             if not exam_page.exam.reanswer:
#                 if group_index > exam_page.first_available_group_index or (
#                         group_index == exam_page.first_available_group_index and question_index > exam_page.first_available_question_index):
#                     return MyResponse(request, {'status': 'index_error',
#                                                 'message': 'You can\'t get this question',
#                                                 'data': {
#                                                     'first_available_question_index': exam_page.first_available_question_index,
#                                                     'first_available_group_index': exam_page.first_available_group_index}},
#                                       status=status.HTTP_409_CONFLICT)
#             answer_file.delete()
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful'},
#                               status=status.HTTP_200_OK)
#         except:
#             return MyResponse(request, {'status': 'not_found', 'message': 'File not found'},
#                               status=status.HTTP_404_NOT_FOUND)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=True), name='post')
# @method_decorator(serializer_validation_middleware(InstituteStudentAnswerFileExamPageInputSerializer), name='post')
# class InstituteStudentAnswerFileExamPage(APIView):
#     def post(self, request):
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         group_index = request.middleware_serializer_data.get('group_index')
#         file = request.middleware_serializer_data.get('file')
#         exam_page = request.middleware_exam_page
#
#         return helper_answer_file(exam_page, group_index, question_index, request, file)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(PauseExamInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class PauseExam(APIView):
#     def post(self, request):
#         exam = request.middleware_model_record
#         pause = request.middleware_serializer_data.get('pause')
#
#         exam.paused = pause
#
#         exam.save()
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful'}, status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(IncreaseTimeExamPageInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM__], Exam, 'id', 'exam_id'),
#                   name='post')
# class ExtendExamPage(APIView):
#     def post(self, request):
#         start_date = request.middleware_serializer_data.get('start_date')
#         end_date = request.middleware_serializer_data.get('end_date')
#         duration = request.middleware_serializer_data.get('duration')
#         exam = request.middleware_model_record
#
#         exam.start_date = start_date
#         exam.end_date = end_date
#         exam.duration = duration
#
#         exam.save()
#
#         ExamPage.objects.filter(exam=exam).update(
#             duration=duration, start_date=start_date, end_date=end_date)
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful'}, status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True,
#                                                                               accept_server_token=False,
#                                                                               accept_server_api_key=False), name='post')
# @method_decorator(serializer_validation_middleware(AttendedExamsListInputSerializer), name='post')
# class AttendedExamPageList(APIView):
#     def post(self, request):
#         skip = request.middleware_serializer_data.get('skip')
#         take = request.middleware_serializer_data.get('take')
#         caller = request.middleware_user
#         q = request.middleware_serializer_data.get('q')
#
#         taken_exam_pages = ExamPage.objects.filter(Q(state=EXAM_PAGE_STATE_FINISHED) | Q(state=EXAM_PAGE_STATE_SCORES_PUBLISHED),
#                                                    user=caller)
#
#         if q is not None:
#             # TODO query performance should be considered
#             taken_exam_pages = taken_exam_pages.filter(
#                 Q(exam__name__icontains=q) | Q(institute__name__icontains=q))
#
#         taken_exam_pages = taken_exam_pages.order_by(
#             '-finished_at')[skip:skip + take]
#
#         taken_exam_page_data = AttendedExamPageSerializer(
#             taken_exam_pages, many=True).data
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'pages': taken_exam_page_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(
#         accept_user_token=True, optional_authentication=False),
#     name='post')
# @method_decorator(public_id_validation_and_authorization(device_token_is_optional=False), name='post')
# @method_decorator(serializer_validation_middleware(GetQuestionAnswerSheetFromScorePublishedPublicExamPageInputSerializer), name='post')
# class GetQuestionAnswerSheetFromScorePublishedPublicExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for showing answer sheet of question to examinees
#     """
#
#     def post(self, request):
#
#         exam_page = request.middleware_exam_page
#         group_index = request.middleware_serializer_data.get('group_index')
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         question_id = request.middleware_serializer_data.get('question_id')
#
#         if exam_page and exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             try:
#                 selected_group_record = [
#                     group for group in exam_page.generated_exam['groups'] if group['index'] == group_index][0]
#
#                 selected_question_record = [question for question in selected_group_record['questions'] if
#                                             question['index'] == question_index and question['question_id'] == question_id][0]
#
#             except:
#                 return MyResponse(request, {'status': 'error', 'message': 'Forbidden'}, status=status.HTTP_403_FORBIDDEN)
#
#             answer_sheet_query = AnswerSheet.objects.filter(
#                 question__pk=selected_question_record['question_id'])
#
#             if answer_sheet_query:
#                 answer_sheet_data = AnswerSheetModelSerializer(
#                     answer_sheet_query[0]).data
#             else:
#                 answer_sheet_data = None
#
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'answer_sheet': answer_sheet_data}},
#                               status=status.HTTP_200_OK)
#         else:
#             return MyResponse(request, {'status': 'error', 'message': 'Forbidden', }, status=status.HTTP_403_FORBIDDEN)
#
#
# @method_decorator(entrance_token_validation_and_authorization(device_token_is_required=False), name='post')
# @method_decorator(serializer_validation_middleware(GetQuestionAnswerSheetFromScorePublishedInstituteStudentExamPageInputSerializer),
#                   name='post')
# class GetQuestionAnswerSheetFromScorePublishedInstituteStudentExamPage(APIView):
#     """
#     This API class is responsible for handling incoming requests for showing answer sheet of question to examinees
#     """
#
#     def post(self, request):
#
#         exam_page = request.middleware_exam_page
#         group_index = request.middleware_serializer_data.get('group_index')
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         question_id = request.middleware_serializer_data.get('question_id')
#
#         if exam_page and exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             try:
#                 selected_group_record = [
#                     group for group in exam_page.generated_exam['groups'] if group['index'] == group_index][0]
#
#                 selected_question_record = [question for question in selected_group_record['questions'] if
#                                             question['index'] == question_index and question['question_id'] == question_id][0]
#
#             except:
#                 return MyResponse(request, {'status': 'error', 'message': 'Forbidden'}, status=status.HTTP_403_FORBIDDEN)
#
#             answer_sheet_query = AnswerSheet.objects.filter(
#                 question__pk=selected_question_record['question_id'])
#
#             if answer_sheet_query:
#                 answer_sheet_data = AnswerSheetModelSerializer(
#                     answer_sheet_query[0]).data
#             else:
#                 answer_sheet_data = None
#
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'answer_sheet': answer_sheet_data}},
#                               status=status.HTTP_200_OK)
#         else:
#             return MyResponse(request, {'status': 'error', 'message': 'Forbidden', }, status=status.HTTP_403_FORBIDDEN)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True), name='post')
# @method_decorator(serializer_validation_middleware(GetExamPageForExaminerInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_READ_EXAM_PAGE__], ExamPage, 'id', 'exam_page_id'),
#                   name='post')
# class GetExamPageForExaminer(APIView):
#     def post(self, request):
#         exam_page = request.middleware_model_record
#
#         exam_page_data = GetExamPageForExaminerSerializer(exam_page).data
#
#         return MyResponse(request, {'status': 'ok', 'message': 'Successful', 'data': {'exam_page': exam_page_data}},
#                           status=status.HTTP_200_OK)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True), name='post')
# @method_decorator(serializer_validation_middleware(AddExamPageCorrectionInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM_PAGE__], ExamPage, 'id', 'exam_page_id'),
#                   name='post')
# class ExamPageCorrectionAdd(APIView):
#     def post(self, request):
#         # ########################## Block 1 ################################
#         group_index = request.middleware_serializer_data.get('group_index')
#         question_index = request.middleware_serializer_data.get(
#             'question_index')
#         score = request.middleware_serializer_data.get('score')
#         comment_for_student = request.middleware_serializer_data.get(
#             'comment_for_student')
#         comment_for_other_correctors = request.middleware_serializer_data.get(
#             'comment_for_other_correctors')
#         exam_page = request.middleware_model_record
#         caller = request.middleware_user
#         # ########################## Block 2 ################################
#         if exam_page.state == EXAM_PAGE_STATE_FINISHED or exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#             # ########################## Block 3 ################################
#             corrections_query = Corrections.objects.filter(
#                 exam_page=exam_page, group_index=group_index, question_index=question_index)
#             if corrections_query:
#                 corrections_query.update(score=score, comment_for_student=comment_for_student,
#                                          comment_for_other_correctors=comment_for_other_correctors, corrector=caller)
#
#             else:
#
#                 # ########################## Block 4 ################################
#                 question = None
#                 try:
#                     selected_group_record = [
#                         group for group in exam_page.generated_exam['groups'] if group['index'] == group_index][0]
#                     selected_question_record = \
#                         [question for question in selected_group_record['questions']
#                          if question['index'] == question_index][0]
#                     question = Question.objects.get(
#                         id=selected_question_record['question_id'])
#                 except:
#                     return MyResponse(request, {'status': 'error', 'message': 'Forbidden', }, status=status.HTTP_403_FORBIDDEN)
#
#                 # ########################## Block 5 ################################
#
#                 try:
#                     user_input_records = [item for item in exam_page.user_inputs if
#                                           item['question_index'] == question_index and item['group_index'] == group_index][0]
#                     if user_input_records:
#                         blank = True
#                 except:
#                     blank = False
#                 # ########################## Block 6 ################################
#
#                 Corrections.objects.create(exam_page=exam_page, corrector=caller, group_index=group_index, question_index=question_index,
#                                            score=score, comment_for_student=comment_for_student,
#                                            comment_for_other_correctors=comment_for_other_correctors, question=question, blank=blank)
#             # ########################## Block 7 ################################
#             # TODO Doc
#             if exam_page.state == EXAM_PAGE_STATE_SCORES_PUBLISHED:
#                 exam_page.state = EXAM_PAGE_STATE_FINISHED
#                 exam_page.save()
#                 # ########################## Block 8 ################################
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful'},
#                               status=status.HTTP_200_OK)
#
#         else:
#             return MyResponse(request, {'status': 'error', 'message': 'Forbidden', }, status=status.HTTP_403_FORBIDDEN)
#
#
# @method_decorator(
#     user_token_or_server_token_or_server_API_key_validation_and_authorization(accept_user_token=True), name='post')
# @method_decorator(serializer_validation_middleware(PublishExamPageCorrectionInputSerializer), name='post')
# @method_decorator(check_access_permission_middleware([dr.__PERMISSION_WRITE_EXAM_PAGE__], ExamPage, 'id', 'exam_page_id'),
#                   name='post')
# class ExamPageCorrectionPublish(APIView):
#     def post(self, request):
#         # ########################## Block 1 ################################
#         exam_page = request.middleware_model_record
#
#         # Block 2 ################################ get all correction of this exam_page
#         corrections_list = Corrections.objects.filter(exam_page=exam_page)
#
#         report_card, total_score, max_positive_score = generate_report_card_helper(
#             exam_page, corrections_list)
#
#         # Block 3 ################################ update exam page
#
#         if report_card is not None and total_score is not None and max_positive_score is not None:
#
#             exam_page.state_history.append(exam_page.state)
#             exam_page.state = EXAM_PAGE_STATE_SCORES_PUBLISHED
#             exam_page.report_card = report_card
#             exam_page.final_score = round(total_score, 2)
#             exam_page.save()
#             # ########################## Block 4 ################################
#
#             full_name = "-"
#             phone = None
#             email = None
#
#             if exam_page.user:
#                 full_name = exam_page.user.full_name
#                 phone = exam_page.user.phone
#                 email = exam_page.user.email
#
#             elif exam_page.institute_student:
#                 full_name = exam_page.institute_student.full_name
#                 phone = exam_page.institute_student.phone
#                 email = exam_page.institute_student.email
#
#             # ########################## Block 4 ################################
#
#             redirect_url = "https://panel.wikiazma.com/exam/"
#             if exam_page.exam.public:
#                 redirect_url += f"p/{exam_page.exam.public_id}"
#             elif exam_page.entrance_token:
#                 redirect_url += exam_page.entrance_token
#             else:
#                 # TODO: HANDLE ASSIGNED EXAMS
#                 pass
#
#             # ########################## Block 4 ################################
#
#             if phone:
#                 send_sms_correction(to=phone, examinee_name=full_name, exam_name=exam_page.exam.name,
#                                     institute_name=exam_page.institute.name,
#                                     total_positive_score=total_score, max_positive_score=max_positive_score, report_card_url=redirect_url)
#
#             elif email:
#                 content = correction_email_content_builder(request, examinee_name=full_name, exam_name=exam_page.exam.name,
#                                                            institute_name=exam_page.institute.name,
#                                                            score=str(total_score), max_positive_score=str(max_positive_score),
#                                                            report_card_url=redirect_url)
#
#                 send_html_email(content, email)
#
#             # ########################## Block 5 ################################
#
#             return MyResponse(request, {'status': 'ok', 'message': 'Successful',
#                                         'data': {'final_score': total_score, 'max_positive_score': max_positive_score}},
#                               status=status.HTTP_200_OK)
#         else:
#             return MyResponse(request, {'status': 'error', 'message': 'BadRequest', }, status=status.HTTP_400_BAD_REQUEST)
