def put(self, *args, **kwargs): parser = reqparse.RequestParser() parser.add_argument('action', type=str, required=True, choices=['approve', 'reject']) args = parser.parse_args() if args['action'] == 'reject': sub = SolutionSubmissionModel.query.get(kwargs['id']) if sub is not None: sub.status = 'rejected' Question.reset_solution(sub.question_id, sub.solution_type) db.session.commit() if args['action'] == 'approve': sub = SolutionSubmissionModel.query.get(kwargs['id']) if sub is not None: sub.status = 'accepted' # create approval entry SolutionApproval.create(sub.id, kwargs['user_type'].id, kwargs['user'].id) Question.approve_solution(sub.question_id, sub.solution_type) db.session.commit() return {'error': False}
def get_analysis(attempted_mock_test_id): attempted_mock_test = AttemptedMockTestModel.query.get( attempted_mock_test_id) if attempted_mock_test is None: raise InvalidAttemptedMockTestId mock_test = MockTest.query.get(attempted_mock_test.mock_test_id) if mock_test is None: raise InvalidMockTestId if attempted_mock_test.answers is None: return {'error': True, 'message': 'No answers yet'} attempted_mock_test.analysis = json.loads(attempted_mock_test.analysis) attempted_mock_test.analysis[ 'percentile'] = AttemptedMockTest.get_percentile( attempted_mock_test) attempted_mock_test.analysis['cutoff'] = mock_test.cutoff rank_colleges = AttemptedMockTest.get_rank_and_college( attempted_mock_test, mock_test) if rank_colleges is not None: attempted_mock_test.analysis[ 'expected_rank'], attempted_mock_test.analysis[ 'expected_colleges'] = rank_colleges attempted_mock_test.analysis = json.dumps(attempted_mock_test.analysis) question_data = json.loads(mock_test.question_ids) question_ids = [] for subject_id, data in question_data.items(): data['subject_id'] = subject_id question_ids.extend(data['q_ids']) questions = Question.get_filtertered_list( include_question_ids=question_ids)['questions'] return { 'attempted_mock_test': attempted_mock_test, 'mock_test': mock_test, 'questions': questions }
def get(self, *args, **kwargs): mock_test = MockTest.query.get(kwargs['id']) if mock_test is None: raise InvalidMockTestId pushed_batch_ids = [ p.batch_id for p in PushedMockTest.query.filter( PushedMockTest.mock_test_id == mock_test.id).all() ] batches = Batch.get_filtered(include_ids=pushed_batch_ids, institute_id=kwargs['user'].id) mock_test.batches_pushed_to = [{ 'id': b.id, 'name': b.name, 'class': b.clazz } for b in batches] question_ids = [] for sid, data in json.loads(mock_test.question_ids).items(): question_ids.extend(data['q_ids']) questions = Question.get_filtertered_list( include_question_ids=question_ids)['questions'] return {'mock_test': mock_test, 'questions': questions}
def get(self, *args, **kwargs): parser = reqparse.RequestParser() parser.add_argument('mock_test_id', type=int, required=True) args = parser.parse_args() mock_test_id = args['mock_test_id'] mock_test = MockTest.query.get(mock_test_id) if mock_test is None: raise InvalidMockTestId question_data = json.loads(mock_test.question_ids) question_ids = [] for subject_id, data in question_data.items(): data['subject_id'] = subject_id question_ids.extend(data['q_ids']) sorted_question_data = sorted(question_data.values(), key=lambda d: d['order']) questions = Question.get_filtertered_list( include_question_ids=question_ids)['questions'] """ for question in questions: print '--options--' print question.correct_options print '---$$$---' question.correct_options = None question.option_reasons = None question.text_solution = None question.video_solution_url = None question.similar_question_ids = None question.average_time = None """ questions = {q.id: q for q in questions} for subject in sorted_question_data: subject['questions'] = map(lambda q_id: questions[q_id], subject['q_ids']) return {'mock_test': mock_test, 'subjects': sorted_question_data}
def get(self, *args, **kwargs): """ Get questions questions not similar to the given question id :param args: :param kwargs: :return: """ parser = reqparse.RequestParser() parser.add_argument('nature', type=str, choices=app.config['QUESTION_NATURE'].keys()) parser.add_argument('type', type=str, choices=app.config['QUESTION_TYPE'].keys()) parser.add_argument('difficulty', type=str, choices=app.config['QUESTION_DIFFICULTY_LEVEL']) parser.add_argument('average_time', type=int, choices=map(int, app.config['QUESTION_AVERAGE_TIME'])) # this contains comma separated ontology node ids parser.add_argument('ontology', type=comma_separated_ints_type) parser.add_argument('question_id', type=int) parser.add_argument('offset', type=int, default=0) args = parser.parse_args() if args['question_id'] is None: exprs = [ Question.is_similarity_marked == False, Question.status['categorized'] == '1' ] if args['nature'] is not None: exprs.append(Question.nature == args['nature']) if args['type'] is not None: exprs.append(Question.type == args['type']) if args['difficulty'] is not None: exprs.append(Question.difficulty == args['difficulty']) if args['average_time'] is not None: exprs.append(Question.average_time == args['average_time']) if args['ontology'] is not None: exprs.append(Question.ontology == args['ontology']) question = Question.query.filter(*exprs).offset( args['offset']).first() if question is None: return {'questions': [], 'total': 0} else: other_questions = Question.get_filtertered_list( nature=args['nature'], type=args['type'], difficulty=args['difficulty'], average_time=args['average_time'], ontology=args['ontology'], categorized='1', exclude_question_ids=[ question.id, ]) if other_questions['total'] == 0: skip = args['offset'] + 1 while question is not None: question = Question.query.filter( *exprs).offset(skip).first() if question is None: return {'questions': [], 'total': 0} other_questions = Question.get_filtertered_list( nature=args['nature'], type=args['type'], difficulty=args['difficulty'], average_time=args['average_time'], ontology=args['ontology'], categorized='1', exclude_question_ids=[ question.id, ]) if other_questions['total'] > 0: break skip += 1 return { 'questions': [question] + other_questions['questions'], 'total': other_questions['total'] + 1 } else: question_id = args['question_id'] question = Question.get(question_id) other_questions = Question.get_filtertered_list( ontology=question.ontology, categorized='1', exclude_question_ids=[ question.id, ]) return { 'questions': [question] + other_questions['questions'], 'total': other_questions['total'] + 1 }
def add_questions_to_db_and_mock_test(paper_questions, comprehensions, mock_test_id): """This method adds the given questions (result of `parse_question`) to the DB and also adds them to the questions_ids attribute of the mock test row. """ ## Get the mock test mock_test = MockTest.query.get(mock_test_id) ## Get the S3 buckets here (otherwise will have to make calls for every content block) conn = S3Connection(config.S3_ACCESS_KEY, config.S3_SECRET) question_files_final_bucket = conn.get_bucket( app.config['S3_QUESTION_FILES_FINAL_BUCKET']) ## Upload the questions to the DB if there are no errors added_questions = [] comprehension_ids = {} print 'Number of Questions: {0}'.format(len(paper_questions)) for question in paper_questions: status = dict(config.QUESTION_STATUS.items()) status['categorized'] = '1' status['text_solution_added'] = '1' # status['proof_read_categorization'] = '1' # make a list of the correct options correct_options = [] for i in range(len(question['options']['values'])): if question['options']['values'][i]['correct']: correct_options.append(i) # move the images to s3 and change the markup accordingly """ question['body']['value'] = move_images_to_final_bucket(question['body']['value'], question_files_final_bucket) question['text_solution']['value'] = move_images_to_final_bucket(question['text_solution']['value'], question_files_final_bucket) for i in range(len(question['options']['values'])): question['options']['values'][i]['value'] = move_images_to_final_bucket(question['options']['values'][i]['value'], question_files_final_bucket) for i in range(len(comprehensions)): comprehensions[i]['value'] = move_images_to_final_bucket(comprehensions[i]['value'], question_files_final_bucket) """ # create a comprehension if needed or just pick up a comprehension ID comprehension_id = None if question['comprehension']: if comprehension_ids.get(question['comprehension_index']): comprehension_id = comprehension_ids[ question['comprehension_index']] else: comp_ = comprehensions[question['comprehension_index']] comprehension = Comprehension.create(comp_['value']) db.session.add(comprehension) db.session.commit() comprehension_id = comprehension.id comprehension_ids[ question['comprehension_index']] = comprehension.id # create the question in the DB question_data = { 'content': question['body']['value'], 'status': status, 'all_options': [option['value'] for option in question['options']['values']], 'correct_options': correct_options, 'ontology_id': question['ontology']['value'][-1], 'average_time': int(question['attributes']['average_time']['value']), 'nature': question['attributes']['nature']['value'], 'difficulty': question['attributes']['difficulty']['value'], 'type': question['attributes']['type']['value'], 'text_solution': question['text_solution']['value'], 'text_solution_by_type': 1, 'text_solution_by_id': app.config['AUTO_UPLOAD_DTP_ID'], 'comprehension_id': comprehension_id } question_ = Question.create(**question_data) added_questions.append([question_, question['ontology']['value']]) # create the attached text solution submission row in the db too solution_submission_params = { 'submitted_by_type': 3, 'submitted_by_id': app.config['AUTO_UPLOAD_TEACHER_ID'], 'question_id': question_.id, 'solution_type': 'text', 'solution': question['text_solution']['value'], } SolutionSubmission.create(**solution_submission_params) # create the attached category submission row in the db too last_ontology_obj = Ontology.query.get(question_data['ontology_id']) category_submission_params = { 'submitted_by_type': 3, 'submitted_by_id': app.config['AUTO_UPLOAD_TEACHER_ID'], 'question_id': question_.id, 'ontology': last_ontology_obj.absolute_path, 'nature': question_data['nature'], 'type': question_data['type'], 'difficulty': question_data['difficulty'], 'average_time': question_data['average_time'] } CategorySubmission.create(**category_submission_params) ## Add the questions to the mock Test mock_test_questions = {} order = -1 for question, ontology in added_questions: subject_id = ontology[0] if str(subject_id) not in mock_test_questions: print str(subject_id) order = order + 1 mock_test_questions.update( {str(subject_id): { 'order': order, 'q_ids': [question.id] }}) continue if str(subject_id) in mock_test_questions: mock_test_questions[str(subject_id)]['q_ids'].append(question.id) continue print mock_test_questions.keys() ## Add the `mock_test_questions` to the mock test mock_test.question_ids = json.dumps(mock_test_questions) db.session.add(mock_test) db.session.commit() return True
def post(self, *args, **kwargs): parser = reqparse.RequestParser() parser.add_argument('mock_test_id', type=int, required=True) parser.add_argument('pushed_mock_test_id', type=int) parser.add_argument('answers', type=self.__class__.answers_json_type, required=True) args = parser.parse_args() mock_test_id = args['mock_test_id'] pushed_mock_test_id = args['pushed_mock_test_id'] mock_test = MockTest.query.get(mock_test_id) if mock_test is None: raise InvalidMockTestId # get attempted mock tests by the student which have same pushed id as in this request or a null pushed id and # same mock test id as this request. If such a mock test is found error is returned. This prevents reattempting # the mock test probably from a different browser/ browser tab amt = AttemptedMockTest.query.filter( AttemptedMockTest.student_id == kwargs['user'].id, or_( and_( AttemptedMockTest.pushed_mock_test_id != None, AttemptedMockTest.pushed_mock_test_id == pushed_mock_test_id), and_(AttemptedMockTest.pushed_mock_test_id == None, AttemptedMockTest.mock_test_id == mock_test_id))).all() if len(amt) > 0: raise MockTestTestAlreadyAttempted # get attempted mock tests of the same type and check if number of permitted mock tests as per payment plan is # exceeded or not attempted_mock_test_ids = [ amt.mock_test_id for amt in AttemptedMockTest.query.filter( AttemptedMockTest.student_id == kwargs['user'].id).all() ] attempted_mock_tests = MockTest.query.filter( MockTest.id.in_(attempted_mock_test_ids)).all() attempted_mock_tests_of_type = filter( lambda m: m.type == mock_test.type, attempted_mock_tests) if len(attempted_mock_tests_of_type) >= app.config['PAYMENT_PLAN'][ mock_test.type]: raise PaymentPlanLimitReached # create attempted test entry attempted_mock_test = AttemptedMockTest( pushed_mock_test_id=pushed_mock_test_id, mock_test_id=mock_test_id, student_id=kwargs['user'].id, attempted_at=datetime.datetime.utcnow()) answers = args['answers'] question_ids = answers.keys() questions = { q.id: q for q in Question.get_filtertered_list( include_question_ids=question_ids)['questions'] } if len(question_ids) != len(questions): raise InvalidQuestionId marking_scheme = app.config['MARKING_SCHEME'] target_exam = mock_test.target_exam maximum_marks = 0 total_marks = 0 subject_wise = {} topic_wise = {} question_overtime = app.config['QUESTION_OVER_TIME'] perfect_attempts = [] wasted_attempts = [] overtime_attempts = [] completely_wasted_attempts = [] ontology = {node.id: node for node in Ontology.get_all_nodes_of_tree()} # dictionary with string value of duration as key and value as question id durations_dict = {} # list with durations of questions durations_list = [] for question_id, value in answers.items(): question_id = int(question_id) question = questions[question_id] subject_id = question.ontology[0] topic_id = None for node_id in question.ontology: if node_id in ontology: if ontology[node_id].type == '3': topic_id = node_id break # subject seen first time if subject_id not in subject_wise: subject_wise[subject_id] = { 'name': ontology[subject_id].name, 'topic_ids': [], 'correct': [], 'incorrect': [], 'not_attempted': [], 'marks': 0, 'time': 0, 'maximum_marks': 0, 'perfect_attempts': [], 'wasted_attempts': [], 'overtime_attempts': [], 'completely_wasted_attempts': [], } # topic seen first time if topic_id is not None and topic_id not in topic_wise: topic_wise[topic_id] = { 'name': ontology[topic_id].name, 'correct': [], 'incorrect': [], 'not_attempted': [], 'marks': 0, 'time': 0, 'maximum_marks': 0, 'perfect_attempts': [], 'wasted_attempts': [], 'overtime_attempts': [], 'completely_wasted_attempts': [], } subject_wise[subject_id]['topic_ids'].append(topic_id) if subject_id not in marking_scheme[target_exam]: # subject id not added in marking scheme config, indicates config errors print 'subject id %s not added in marking scheme config, indicates config errors' % str( subject_id) continue if question.type not in marking_scheme[target_exam][subject_id]: # question type not added for subject in marking scheme config, indicates config errors print 'question type %s not added for subject in marking scheme config, indicates config errors' % str( question.type) continue print '-----------------------------------' print question.correct_options print '-----------------------------------' # if not attempted if len(value['options']) == 0: marks = marking_scheme[target_exam][subject_id][ question.type]['not_attempted'] value['marks'] = marks value['is_correct'] = False subject_wise[subject_id]['not_attempted'].append(question.id) if topic_id is not None: topic_wise[topic_id]['not_attempted'].append(question.id) # if correct elif set(question.correct_options) == (set(value['options'])): marks = marking_scheme[target_exam][subject_id][ question.type]['correct'] value['marks'] = marks value['is_correct'] = True subject_wise[subject_id]['correct'].append(question.id) if topic_id is not None: topic_wise[topic_id]['correct'].append(question.id) if value['time'] < question.average_time + question_overtime: subject_wise[subject_id]['perfect_attempts'].append( question_id) if topic_id is not None: topic_wise[topic_id]['perfect_attempts'].append( question_id) perfect_attempts.append(question.id) else: subject_wise[subject_id]['overtime_attempts'].append( question_id) if topic_id is not None: topic_wise[topic_id]['overtime_attempts'].append( question_id) overtime_attempts.append(question.id) # if incorrect else: marks = marking_scheme[target_exam][subject_id][ question.type]['incorrect'] value['marks'] = marks value['is_correct'] = False subject_wise[subject_id]['incorrect'].append(question.id) if topic_id is not None: topic_wise[topic_id]['incorrect'].append(question.id) if value['time'] <= question.average_time: subject_wise[subject_id]['wasted_attempts'].append( question_id) if topic_id is not None: topic_wise[topic_id]['wasted_attempts'].append( question_id) wasted_attempts.append(question.id) else: subject_wise[subject_id][ 'completely_wasted_attempts'].append(question_id) if topic_id is not None: topic_wise[topic_id][ 'completely_wasted_attempts'].append(question_id) completely_wasted_attempts.append(question.id) for duration in value['durations']: duration_key = self.get_duration_key(duration) if duration_key is not None: durations_dict[duration_key] = question.id durations_list.append(duration) correct_answer_marks = marking_scheme[target_exam][subject_id][ question.type]['correct'] subject_wise[subject_id]['time'] += value['time'] subject_wise[subject_id]['marks'] += marks subject_wise[subject_id]['maximum_marks'] += correct_answer_marks if topic_id is not None: topic_wise[topic_id]['time'] += value['time'] topic_wise[topic_id]['marks'] += marks topic_wise[topic_id]['maximum_marks'] += correct_answer_marks total_marks += marks maximum_marks += correct_answer_marks total_time = 0 total_correct = 0 total_incorrect = 0 total_not_attempted = 0 overall_correct_q_ids = [] overall_incorrect_q_ids = [] total_ideal_time = 0 total_taken_time = 0 for sub in subject_wise.values(): overall_correct_q_ids.extend(sub['correct']) overall_incorrect_q_ids.extend(sub['incorrect']) sub['accuracy'] = round( len(sub['correct']) * 100.0 / (len(sub['correct']) + len(sub['incorrect'])), 2) if (len(sub['correct']) + len(sub['incorrect'])) > 0 else 0.0 total_time += sub['time'] total_correct += len(sub['correct']) total_incorrect += len(sub['incorrect']) total_not_attempted += len(sub['not_attempted']) overall_attempted_count = len(overall_correct_q_ids) + len( overall_incorrect_q_ids) overall_accuracy = round( len(overall_correct_q_ids) * 100.0 / overall_attempted_count, 2) if overall_attempted_count > 0 else 0.0 for q_id in overall_correct_q_ids + overall_incorrect_q_ids: q = questions[int(q_id)] total_ideal_time += q.average_time total_taken_time += answers[str(q_id)]['time'] overall_speed = total_ideal_time - total_taken_time num_subjects = len(subject_wise.keys()) attempt_order_time_window_length = total_time / (num_subjects * 10) sorted_durations_list = sorted(durations_list, key=lambda d: d[0]) subjects_attempt_order = [] if int(attempt_order_time_window_length) > 0: for current_time_window_start in xrange( 0, int(math.ceil(total_time)), int(attempt_order_time_window_length)): current_time_window_end = current_time_window_start + attempt_order_time_window_length i = -1 j = -1 for index, duration in enumerate(sorted_durations_list): if len(duration) != 2: continue # if current_time_window_start lies in the current duration if duration[0] <= current_time_window_start < duration[1]: i = index # if current_time_window_end lies in the current duration if duration[0] < current_time_window_end <= duration[1]: j = index break # if time window start and end lie inside test duration if i != -1 and j != -1: sub = [] for d in sorted_durations_list[i:j + 1]: question_id = durations_dict[self.get_duration_key(d)] question = questions[question_id] sub.append(question.ontology[0]) c = Counter(sub) subjects_attempt_order.append(c.most_common(1)[0][0]) # if time window start lies inside test duration but time window end does not elif i != -1 and j == -1: sub = [] for d in sorted_durations_list[i:]: question_id = durations_dict[self.get_duration_key(d)] question = questions[question_id] sub.append(question.ontology[0]) c = Counter(sub) subjects_attempt_order.append(c.most_common(1)[0][0]) attempted_mock_test.answers = json.dumps(answers) attempted_mock_test.score = total_marks attempted_mock_test.analysis = json.dumps({ 'subjects': subject_wise, 'topics': topic_wise, 'perfect': perfect_attempts, 'overtime': overtime_attempts, 'wasted': wasted_attempts, 'completely_wasted': completely_wasted_attempts, 'total_marks': total_marks, 'maximum_marks': maximum_marks, 'percentage_marks': round((total_marks * 100.0 / maximum_marks), 2) if maximum_marks > 0 else 0.0, 'total_time': total_time, 'total_correct': total_correct, 'total_incorrect': total_incorrect, 'total_not_attempted': total_not_attempted, 'attempt_order_time_window_length': attempt_order_time_window_length, 'subjects_attempt_order': subjects_attempt_order, 'accuracy': overall_accuracy, 'speed': overall_speed }) db.session.add(attempted_mock_test) db.session.commit() upload_report_and_send_email.delay(attempted_mock_test.id) return {'attempted_mock_test': attempted_mock_test}
def get_cumulative_analysis(student_id, institute_id=None): # get mock tests by student which have completed if institute_id is not None: batches = Batch.get_filtered(institute_id=institute_id) pushed_mock_tests = PushedMockTest.query.filter( PushedMockTest.batch_id.in_([b.id for b in batches])) attempted_mock_tests = AttemptedMockTest.query.filter( AttemptedMockTest.student_id == student_id, AttemptedMockTest.score != None, AttemptedMockTest.pushed_mock_test_id.in_( [p.id for p in pushed_mock_tests])).all() else: attempted_mock_tests = AttemptedMockTest.query.filter( AttemptedMockTest.student_id == student_id, AttemptedMockTest.score != None).all() mock_test_ids = [amt.mock_test_id for amt in attempted_mock_tests] mock_tests = MockTest.query.filter( MockTest.id.in_(mock_test_ids)).all() question_ids = set() overall_correct_q_ids = set() overall_incorrect_q_ids = set() overall_not_attempted_q_ids = set() total_ideal_time = 0 total_taken_time = 0 for amt in attempted_mock_tests: analysis = json.loads(amt.analysis) subjects = analysis['subjects'] for sid in subjects: overall_correct_q_ids.update(set(subjects[sid]['correct'])) overall_incorrect_q_ids.update(set(subjects[sid]['incorrect'])) overall_not_attempted_q_ids.update( set(subjects[sid]['not_attempted'])) question_ids.update(overall_correct_q_ids) question_ids.update(overall_incorrect_q_ids) question_ids.update(overall_not_attempted_q_ids) questions = { q.id: q for q in Question.get_filtertered_list( include_question_ids=list(question_ids))['questions'] } overall_attempted_count = len(overall_incorrect_q_ids) + len( overall_correct_q_ids) accuracy = round( len(overall_correct_q_ids) * 100.0 / overall_attempted_count, 2) if overall_attempted_count > 0 else 0.0 for amt in attempted_mock_tests: answers = json.loads(amt.answers) for q_id, answer in answers.items(): q_id = int(q_id) # if attempted question if len(answer['options']) != 0 and q_id in questions: total_ideal_time += questions[q_id].average_time total_taken_time += answer['time'] overall_speed = total_ideal_time - total_taken_time return { 'attempted_mock_tests': attempted_mock_tests, 'mock_tests': mock_tests, 'questions': questions.values(), 'accuracy': accuracy, 'speed': overall_speed }
def get(self, *args, **kwargs): attempted_mock_test_id = kwargs['id'] attempted_mock_test = AttemptedMockTest.query.filter( AttemptedMockTest.id == attempted_mock_test_id, AttemptedMockTest.pdf_report_url == None).first() if attempted_mock_test is None: return '404 Not Found', 404 mock_test = MockTest.query.get(attempted_mock_test.mock_test_id) if mock_test is None: return '404 Not Found', 404 if attempted_mock_test.answers is None: return '404 Not Found', 404 page = request.args.get('page') ontology = {node.id: node for node in Ontology.get_all_nodes_of_tree()} analysis = json.loads(attempted_mock_test.analysis) for sid, data in analysis['subjects'].items(): data['name'] = ontology[int(sid)].name analysis['cutoff'] = mock_test.cutoff analysis['percentile'] = AttemptedMockTestResource.get_percentile( attempted_mock_test) rank_colleges = AttemptedMockTestResource.get_rank_and_college( attempted_mock_test, mock_test) if rank_colleges is not None: analysis['expected_rank'], analysis[ 'expected_colleges'] = rank_colleges common_page_vars = { 'page': page, 'analysis': analysis, 'mock_test_name': mock_test.name, 'target_exam_name': app.config['TARGET_EXAMS'][mock_test.target_exam] } if page == 'page1': return render_template('pdf_report.html', **common_page_vars) if page in (None, 'page2'): MAXIMUM_TIME_WIDTH = 1000.0 # px MAXIMUM_TIME = mock_test.duration # seconds ATTEMPT_TIME_DISPLAY_UNIT_SECONDS = 150 # seconds #ATTEMPT_TIME_DISPLAY_UNIT_WIDTH = (MAXIMUM_TIME_WIDTH/MAXIMUM_TIME)*ATTEMPT_TIME_DISPLAY_UNIT_SECONDS # px subject_attempt_order = [] time_window_chunk_length = int( math.floor(ATTEMPT_TIME_DISPLAY_UNIT_SECONDS / analysis['attempt_order_time_window_length'])) temp = [] for i, sid in enumerate(analysis['subjects_attempt_order']): temp.append(sid) if (i + 1) % time_window_chunk_length == 0: temp.append(sid) c = Counter(temp) subject_attempt_order.append(c.most_common(1)[0][0]) temp = [] color_classes = ['info', 'primary', 'success', 'warning', 'danger'] unique_subject_ids = list(set(subject_attempt_order)) subject_colors = { sid: color_classes[unique_subject_ids.index(sid)] for sid in unique_subject_ids } total_time_bar_width = round(analysis['total_time'] * (MAXIMUM_TIME_WIDTH / MAXIMUM_TIME)) subject_attempt_order = [{ 'name': ontology[sid].name, 'id': sid, 'color_class': subject_colors[sid], 'width': 100.0 / len(subject_attempt_order) } for sid in subject_attempt_order] combined_subjects = [] last_subject = None for i, s in enumerate(subject_attempt_order): if last_subject is not None: if last_subject == s['id']: combined_subjects[-1]['width'] += s['width'] else: combined_subjects.append(s) last_subject = s['id'] else: combined_subjects.append(s) last_subject = s['id'] subject_attempt_legend = [{ 'name': ontology[sid].name, 'color': subject_colors[sid] } for sid in unique_subject_ids] mtqi = json.loads(mock_test.question_ids) sorted_subjects = OrderedDict( sorted(analysis['subjects'].items(), key=lambda t: mtqi[t[0]]['order'])) if page == 'page2': return render_template( 'pdf_report.html', subject_attempt_order=combined_subjects, total_time_bar_width=total_time_bar_width, max_time_width=MAXIMUM_TIME_WIDTH, duration=MAXIMUM_TIME, subject_attempt_legend=subject_attempt_legend, sorted_subjects=sorted_subjects, **common_page_vars) if page in (None, 'page3', 'page4'): answers = json.loads(attempted_mock_test.answers) question_ids = answers.keys() questions = { q.id: q for q in Question.get_filtertered_list( include_question_ids=question_ids)['questions'] } # dictionary with string value of duration as key and value as question id durations_dict = {} # list with durations of questions durations_list = [] for question_id, value in answers.items(): for duration in value['durations']: duration_key = self.get_duration_key(duration) if duration_key is not None: durations_dict[duration_key] = int(question_id) durations_list.append(duration) sorted_durations_list = sorted(durations_list, key=lambda d: d[0]) if mock_test.duration <= 7200: UNIT_TIME_DURATION = 600 # seconds else: UNIT_TIME_DURATION = 900 # seconds UNIT_TIME_DURATION = 600 # ordered list of list of questions attempted every `UNIT_TIME_DURATION` question_attempt_order = [] for current_time_window_start in xrange( 0, int(math.ceil(analysis['total_time'])), UNIT_TIME_DURATION): current_time_window_end = current_time_window_start + UNIT_TIME_DURATION i = -1 j = -1 for index, duration in enumerate(sorted_durations_list): # if current_time_window_start lies in the current duration if duration[0] <= current_time_window_start < duration[1]: i = index # if current_time_window_end lies in the current duration if duration[0] < current_time_window_end <= duration[1]: j = index break # if time window start and end lie inside test duration if i != -1 and j != -1: qs = [] for d in sorted_durations_list[i:j + 1]: question_id = durations_dict[self.get_duration_key(d)] qs.append(question_id) question_attempt_order.append(qs) # if time window start lies inside test duration but time window end does not elif i != -1 and j == -1: qs = [] for d in sorted_durations_list[i:]: question_id = durations_dict[self.get_duration_key(d)] qs.append(question_id) question_attempt_order.append(qs) test_duration_minutes = mock_test.duration / 60.0 if page in (None, 'page3'): difficulty_map = { '1': 'Easy', '2': 'Easy', '3': 'Medium', '4': 'Medium', '5': 'Hard' } # ordered list of list of difficulty counts every `UNIT_TIME_DURATION` difficulty_attempt_order = [] for question_list in question_attempt_order: easy = {'count': 0, 'marks': 0} medium = {'count': 0, 'marks': 0} hard = {'count': 0, 'marks': 0} for qid in question_list: dif = difficulty_map[questions[qid].difficulty] if dif == 'Easy': easy['count'] += 1 easy['marks'] += answers[str(qid)]['marks'] if dif == 'Medium': medium['count'] += 1 medium['marks'] += answers[str(qid)]['marks'] if dif == 'Hard': hard['count'] += 1 hard['marks'] += answers[str(qid)]['marks'] difficulty_attempt_order.append({ 'minutes': (len(difficulty_attempt_order) + 1) * (UNIT_TIME_DURATION / 60), 'easy': easy, 'medium': medium, 'hard': hard }) while len(difficulty_attempt_order ) < 210 * 60 / UNIT_TIME_DURATION: difficulty_attempt_order.append({ 'minutes': (len(difficulty_attempt_order) + 1) * (UNIT_TIME_DURATION / 60), 'easy': { 'count': 0, 'marks': 0 }, 'medium': { 'count': 0, 'marks': 0 }, 'hard': { 'count': 0, 'marks': 0 } }) if page == 'page3': return render_template( 'pdf_report.html', difficulty_attempt_order=difficulty_attempt_order, test_duration_minutes=test_duration_minutes, **common_page_vars) if page in (None, 'page4'): # ordered list of list of attempt quality counts every `UNIT_TIME_DURATION` aq_attempt_order = [] for question_list in question_attempt_order: perfect = {'count': 0, 'marks': 0} overtime = {'count': 0, 'marks': 0} wasted = {'count': 0, 'marks': 0} completely_wasted = {'count': 0, 'marks': 0} for qid in question_list: if qid in analysis['perfect']: perfect['count'] += 1 perfect['marks'] += answers[str(qid)]['marks'] if qid in analysis['overtime']: overtime['count'] += 1 overtime['marks'] += answers[str(qid)]['marks'] if qid in analysis['wasted']: wasted['count'] += 1 wasted['marks'] += answers[str(qid)]['marks'] if qid in analysis['completely_wasted']: completely_wasted['count'] += 1 completely_wasted['marks'] += answers[str( qid)]['marks'] aq_attempt_order.append({ 'minutes': (len(aq_attempt_order) + 1) * (UNIT_TIME_DURATION / 60), 'perfect': perfect, 'overtime': overtime, 'wasted': wasted, 'completely_wasted': completely_wasted }) while len(aq_attempt_order) < 210 * 60 / UNIT_TIME_DURATION: aq_attempt_order.append({ 'minutes': (len(aq_attempt_order) + 1) * (UNIT_TIME_DURATION / 60), 'perfect': { 'count': 0, 'marks': 0 }, 'overtime': { 'count': 0, 'marks': 0 }, 'wasted': { 'count': 0, 'marks': 0 }, 'completely_wasted': { 'count': 0, 'marks': 0 } }) if page == 'page4': return render_template( 'pdf_report.html', aq_attempt_order=aq_attempt_order, test_duration_minutes=test_duration_minutes, **common_page_vars) if page in (None, 'page5'): optimum_accuracy = 40 # percent spent_time = {} answers = json.loads(attempted_mock_test.answers) for subject_id in analysis['subjects']: if subject_id not in spent_time: spent_time[subject_id] = { 'name': ontology[int(subject_id)].name, 'correct': 0, 'incorrect': 0, 'not_attempted': 0, 'total_time': analysis['subjects'][subject_id]['time'] } for q_id in analysis['subjects'][subject_id]['correct']: spent_time[subject_id]['correct'] += answers[str( q_id)]['time'] for q_id in analysis['subjects'][subject_id]['incorrect']: spent_time[subject_id]['incorrect'] += answers[str( q_id)]['time'] for q_id in analysis['subjects'][subject_id]['not_attempted']: spent_time[subject_id]['not_attempted'] += answers[str( q_id)]['time'] if page == 'page5': return render_template('pdf_report.html', optimum_accuracy=optimum_accuracy, spent_time=spent_time, **common_page_vars) return render_template( 'pdf_report.html', subject_attempt_order=combined_subjects, total_time_bar_width=total_time_bar_width, max_time_width=MAXIMUM_TIME_WIDTH, duration=MAXIMUM_TIME, subject_attempt_legend=subject_attempt_legend, sorted_subjects=sorted_subjects, test_duration_minutes=test_duration_minutes, spent_time=spent_time, difficulty_attempt_order=difficulty_attempt_order, aq_attempt_order=aq_attempt_order, optimum_accuracy=optimum_accuracy, **common_page_vars)
def put(self, *args, **kwargs): parser = reqparse.RequestParser() parser.add_argument('name', type=str) parser.add_argument('is_locked', type=int, choices=[0, 1]) parser.add_argument('question_ids', type=MockTestList.question_ids_json_type) args = parser.parse_args() mock_test = MockTestModel.query.get(kwargs['id']) if mock_test is None: raise InvalidMockTestId if args['name'] is not None: mock_test.name = args['name'] if args['question_ids'] is not None: args['question_ids'] = json.loads(args['question_ids']) for subject_id, data in args['question_ids'].items(): seen_comprehensions = OrderedDict() comp_ques_ids = [] q_ids = data['q_ids'] question_data = Question.get_filtertered_list( include_question_ids=q_ids) questions = question_data['questions'] total = question_data['total'] if total != len(q_ids): raise InvalidQuestionId # sort questions in the order in which they appear in `q_ids` questions = sorted(questions, key=lambda q: q_ids.index(q.id)) for question in questions: if question.comprehension_id is not None: # if comprehension not encountered before if question.comprehension_id not in seen_comprehensions: comp_ques_ids.append(question.id) # comprehension questions in order of their ids, i.e order of their addition comprehension_ques_ids = [ q.id for q in sorted( question.comprehension.questions.all(), key=lambda q: q.id) ] seen_comprehensions[ question.comprehension_id] = sorted( comprehension_ques_ids) i = 0 for comp_id, ques_ids in seen_comprehensions.items(): ques_id_set = set(ques_ids) ques_id_set.remove(comp_ques_ids[i]) # questions ids other than the first encountered question of this comprehension other_comp_ques_ids = ques_id_set # remove qny question ids from `other_comp_ques_ids` if present in `q_ids` for id in other_comp_ques_ids: try: q_ids.remove(id) except: continue # index of first encountered question of this comprehension comp_ques_index = q_ids.index(comp_ques_ids[i]) # add all questions of this comprehension to `q_ids` starting from `comp_ques_index` for index, id in enumerate(ques_ids): q_ids.insert(comp_ques_index + index, id) # finally remove the first encountered question of this comprehension from `q_ids` q_ids.remove(q_ids[comp_ques_index + index + 1]) i += 1 mock_test.question_ids = json.dumps(args['question_ids']) if args['is_locked'] is not None: mock_test.is_locked = args['is_locked'] == 1 db.session.commit() return {'mock_test': mock_test}