def error_check(self, report_data): """ Check all input for errors. The called functions will throw exceptions themselves. We are not trying to catch them, but to trigger them before we try to commit everything to the DB. This should prevent inconsistent data in the DB. :param report_data: :return: """ section_api = SectionApi(autocommit=False) question_api = QuestionApi(autocommit=False) risk_factor_api = RiskFactorApi() # Database check if self.db_check(report_data) is not True: raise Exception(_('An unexpected error occurred.')) # Check for attributes if 'sections' not in report_data: raise RequiredAttributeMissing( _e['attr_missing'].format('sections')) cleaned_report = self.parse_input_data(report_data) # Check for duplicates (two sections with the same title) duplicate_sections = [] for section in report_data['sections']: if 'questions' not in section: raise RequiredAttributeMissing( _e['attr_missing'].format('questions')) # Check for attributes cleaned_section = section_api.parse_input_data(section) # Duplicate check if section['title'] not in duplicate_sections: duplicate_sections.append(section['title']) else: raise DatabaseItemAlreadyExists(_e['item_already_in'].format( 'Section', section['title'], 'Report', report_data['id'])) duplicate_questions = [] for question in section['questions']: # Check for attributes cleaned_question = question_api.parse_input_data(question) # Check for duplicates if question['question'] not in duplicate_questions: duplicate_questions.append(question['question']) else: raise DatabaseItemAlreadyExists( _e['item_already_in'].format('Question', question['question'], 'Section', section['id'])) # Check for answers # We must use the cleaned data, because 'answers' is not required, # and attempting to loop over None gets an error. for answer_id in cleaned_question['answers']: answer = question_api.get_answer(answer_id) # Check for risk_factor risk_factor = risk_factor_api.read( cleaned_question['risk_factor_id']) return True
def get_for_section_by_question_id(self, section_id, user_id, user_report_id): """ Get all questionAnswers for a specific section (section_id) in a dict where the key is the question_id, for a specific user in a specific UserReport :param section_id: :param user_id: :param user_report_id: :return: """ section_api = SectionApi() current_section = section_api.read(section_id) ordered_questions = {} for question in current_section.questions: if question.id in ordered_questions: raise DatabaseItemAlreadyExists( _('Error: two questions with the same id in one section!')) # Get the QuestionAnswers object try: question_answer = self.get_answer_by_question_id( question.id, user_id, user_report_id) except DatabaseItemDoesNotExist: continue ordered_questions[question.id] = question_answer return ordered_questions
def hook_add_total_score(output_data): if section_id is not None: new_output = output_data new_output['total_score'] = SectionApi().total_score(section_id) else: new_output = [] for item in output_data: item['total_score'] = SectionApi().total_score(item['id']) new_output.append(item) return new_output
def test_create(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) assert s in scoremodel.db.session assert s.order_in_report == 0 self.assertIsInstance(s, Section) self.assertRaises(DatabaseItemAlreadyExists, SectionApi().create, { 'title': 'Test', 'report_id': r.id })
def hook_add_multiplication_factor(output_data): if section_id is not None: new_output = output_data new_output['multiplication_factor'] = SectionApi( ).multiplication_factor(section_id) else: new_output = [] for item in output_data: item['multiplication_factor'] = SectionApi( ).multiplication_factor(item['id']) new_output.append(item) return new_output
def test_update(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) s_u = SectionApi().update(s.id, { 'title': 'Toto', 'report_id': r.id, 'order_in_report': 1 }) assert s_u == SectionApi().read(s.id) assert SectionApi().read(s.id).title == 'Toto' assert SectionApi().read(s.id).order_in_report == 1 self.assertIsInstance(s_u, Section)
def store_chain(self, report_data, created_report): """ For an already stored report (created_report), store all sections and attached questions in the DB. Will automatically distinguish between new sections/questions and updated sections/questions. :param report_data: :param created_report: :return: """ section_api = SectionApi() question_api = QuestionApi() # Check for sections that were originally part of created_report, but are not anymore # We delete those. original_sections = created_report.sections new_sections = [] for s in report_data['sections']: if 'id' in s: new_sections.append(s) #for original_section in original_sections: # if original_section.id not in new_sections: # section_api.delete(original_section.id) for section in report_data['sections']: section['report_id'] = created_report.id if 'id' in section and section['id'] > 0: # An update created_section = section_api.update(section['id'], section) else: # A new creation created_section = section_api.create(section) original_questions = created_section.questions new_questions = [] for q in section['questions']: if 'id' in q: new_questions.append(q) #for original_question in original_questions: # if original_question.id not in new_questions: # question_api.delete(original_question.id) for question in section['questions']: question['section_id'] = created_section.id if 'id' in question and question['id'] > 0: # An update created_question = question_api.update( question['id'], question) else: # A new creation created_question = question_api.create(question) return created_report
def test_delete(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) q = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id}) assert QuestionApi().delete(q.id) is True assert q not in scoremodel.db.session
def hook_add_multiplication_factor(output_data): if type(output_data) is not dict: return output_data question = QuestionApi().read(question_id) output_data['multiplication_factor'] = SectionApi( ).multiplication_factor(question.section_id) return output_data
def benchmarks_by_section(self, user_report_id): """ Get all benchmark questions (grouped per report) per section. :param user_report_id: :return: """ bm_by_section = {} benchmark_report_api = BenchmarkReportApi() existing_user_report = self.read(user_report_id) for benchmark_report in existing_user_report.template.benchmark_reports: benchmark_questions_by_section = benchmark_report_api.questions_by_section(benchmark_report.id) for section_id, benchmarks in benchmark_questions_by_section.items(): section_score = 0 multiplication_factor = SectionApi().multiplication_factor(section_id) for benchmark in benchmarks: section_score += benchmark.score * multiplication_factor if section_id in bm_by_section: if benchmark_report.id in bm_by_section[section_id]: raise Exception(_('Multiple values for the same benchmark_report in the same section.')) else: bm_by_section[section_id][benchmark_report.id] = { 'title': benchmark_report.title, 'section_score': section_score, 'benchmarks': {b.question_id: b for b in benchmark_questions_by_section[section_id]} } else: bm_by_section[section_id] = { benchmark_report.id: { 'title': benchmark_report.title, 'section_score': section_score, 'benchmarks': {b.question_id: b for b in benchmark_questions_by_section[section_id]} } } return bm_by_section
def v_user_report_section(user_id, user_report_id, section_id): section_api = SectionApi() if current_user.id != user_id: flash(_('You can only view your own reports.')) abort(403) return try: user_report = user_report_api.read(user_report_id) except DatabaseItemDoesNotExist as e: abort(404) return except Exception as e: flash(_('An unexpected error occurred.')) return redirect(url_for('site.v_index')) # Check whether current section is in this user_report if section_id not in [ section.id for section in user_report.template.sections ]: abort(404) return current_section = section_api.read(section_id) # Get all question_answers for this report and order them by question_id, so we can compare # question.answer.answer_id to question_answers['question_id'].answer_id question_answers = {} for question_answer in user_report.question_answers: question_answers[question_answer.question_id] = question_answer benchmarks_by_section = user_report_api.benchmarks_by_section( user_report_id) # Create a color-range for the risk_factors risk_factors = [r.risk_factor for r in RiskFactorApi().list()] colored_risk_factors = Color().range(risk_factors) return render_template('public/section.html', title=current_section.title, section=current_section, user_report_id=user_report_id, question_answers=question_answers, next_section=current_section.next_in_report, previous_section=current_section.previous_in_report, benchmarks_by_section=benchmarks_by_section, colored_risk_factors=colored_risk_factors)
def v_user_report_summary(user_id, user_report_id): if current_user.id != user_id: flash(_('You can only view your own reports.')) abort(403) user_report = user_report_api.read(user_report_id) question_answers = {} all_scores = {} for section in user_report.template.sections: all_scores[section.id] = 0 for question_answer in user_report.question_answers: question_answers[question_answer.question_id] = question_answer multiplication_factor = SectionApi().multiplication_factor( question_answer.question_template.section_id) all_scores[question_answer.question_template.section.id] += question_answer.score * \ multiplication_factor highest_unanswered = [] for question in ReportApi().questions_by_combined_weight( user_report.template.id): if question['question_id'] not in question_answers or question_answers[question['question_id']].score < \ question['max_score']: try: highest_unanswered.append(QuestionApi().read( question['question_id'])) except DatabaseItemDoesNotExist: pass if len(highest_unanswered) >= 5: visible_unanswered = highest_unanswered[:5] else: visible_unanswered = highest_unanswered benchmarks_by_question = {} for bm_r in user_report.template.benchmark_reports: for bm in bm_r.benchmarks: if bm.question_id in benchmarks_by_question: benchmarks_by_question[bm.question_id].append(bm) else: benchmarks_by_question[bm.question_id] = [bm] # Create a color-range for the risk_factors risk_factors = [r.risk_factor for r in RiskFactorApi().list()] colored_risk_factors = Color().range(risk_factors) return render_template( 'public/summary.html', report_template=user_report.template, user_report=user_report, user_report_creation_time='{:%Y-%m-%d %H:%M:%S}'.format( user_report.creation_time), highest_unanswered=visible_unanswered, benchmarks_by_question=benchmarks_by_question, question_answers_by_id=question_answers, colored_risk_factors=colored_risk_factors)
def test_update(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) q = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id}) q_d = QuestionApi().update(q.id, {'question': 'Foo', 'weight': 1, 'section_id': s.id}) assert q_d == QuestionApi().read(q.id) assert QuestionApi().read(q.id).question == 'Foo' self.assertIsInstance(q_d, Question)
def test_create(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) q = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id}) assert q in scoremodel.db.session # This only works in a context manager, and not with self.assertRaises() - I have no idea why. with self.assertRaises(DatabaseItemAlreadyExists): x = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id}) self.assertIsInstance(q, Question)
def test_complex(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) ri = RiskFactorApi().create({'risk_factor': 'Test', 'lang_id': en.id}) a = AnswerApi().create({'answer': 'Test', 'lang_id': en.id}) q = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id, 'answers': [a.id], 'risk_factor_id': ri.id}) assert q in scoremodel.db.session self.assertIsInstance(q, Question)
def db_check(self, report_data): """ For a report, check that reports, sections and questions do not already exist in the database when they are new. :param report_data: :return: """ section_api = SectionApi(autocommit=False) question_api = QuestionApi(autocommit=False) if 'sections' not in report_data: raise RequiredAttributeMissing( _e['attr_missing'].format('sections')) if 'id' in report_data and report_data['id'] > 0: # Check the DB for section in report_data['sections']: # If there is no title, this will be caught by the other error checking routines if 'title' in section: if 'id' not in section or section['id'] < 0: if section_api.db_exists(section['title'], report_data['id']): raise DatabaseItemAlreadyExists( _e['item_already_in'].format( 'Section', section['title'], 'Report', report_data['id'])) if 'id' in section and section['id'] > 0: # No sense in checking for questions in a section that doesn't exist if 'questions' not in section: raise RequiredAttributeMissing( _e['attr_missing'].format('questions')) for question in section['questions']: if 'question' in question: if 'id' not in question or question['id'] < 0: if question_api.db_exists( question['question'], section['id']): raise DatabaseItemAlreadyExists( _e['item_already_in'].format( 'Question', question['question'], 'Section', section['id'])) return True
def test_complex(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({ 'title': 'Test', 'report_id': r.id, 'order_in_report': 2, 'weight': 5 }) assert s in scoremodel.db.session assert s.order_in_report == 2 assert s.weight == 5 self.assertIsInstance(s, Section)
def v_user_report_check(user_id, user_report_id): if current_user.id != user_id: flash(_('You can only view your own reports.')) abort(403) user_report = user_report_api.read(user_report_id) # Get all question_answers for this report and order them by question_id, so we can compare # question.answer.answer_id to question_answers['question_id'].answer_id question_answers = {} all_scores = {} highest_answers = {} for section in user_report.template.sections: all_scores[section.id] = 0 for question_answer in user_report.question_answers: question = question_answer.question_template question_answers[question.id] = question_answer multiplication_factor = SectionApi().multiplication_factor( question.section_id) all_scores[question. section_id] += question_answer.score * multiplication_factor sorted_answers = sorted(question.answers, key=lambda a: a.value, reverse=True) if len(sorted_answers) > 0: highest_answers[question.id] = sorted_answers[0].value else: highest_answers[question.id] = 0 benchmarks_by_section = user_report_api.benchmarks_by_section( user_report_id) # Create a color-range for the risk_factors risk_factors = [r.risk_factor for r in RiskFactorApi().list()] colored_risk_factors = Color().range(risk_factors) return render_template( 'public/report.html', report_template=user_report.template, user_report=user_report, user_report_creation_time='{:%Y-%m-%d %H:%M:%S}'.format( user_report.creation_time), question_answers=question_answers, all_scores=all_scores, benchmarks_by_section=benchmarks_by_section, highest_answers=highest_answers, colored_risk_factors=colored_risk_factors)
def hook_add_multiplication_factor(output_data): if type(output_data) is not dict: return output_data new_question_answers_by_section = [] for section in output_data['question_answers_by_section']: question_answers = [] for qa in section['question_answers']: qa['multiplication_factor'] = SectionApi( ).multiplication_factor(section['section_id']) question_answers.append(qa) section['question_answers'] = question_answers new_question_answers_by_section.append(section) output_data[ 'question_answers_by_section'] = new_question_answers_by_section return output_data
def hook_add_by_section(output_data): for section_id, benchmarks in BenchmarkReportApi( ).questions_by_section(benchmark_report_id).items(): section_benchmarks = [] for b in benchmarks: b_out = b.output_obj() b_out['multiplication_factor'] = SectionApi( ).multiplication_factor(b.question.section_id) section_benchmarks.append(b_out) output_data['benchmarks_by_section'].append({ 'section_id': section_id, 'benchmarks': section_benchmarks }) return output_data
def test_read(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) assert s == SectionApi().read(s.id)
def test_delete(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) assert SectionApi().delete(s.id) is True assert s not in scoremodel.db.session
def test_read(self): en = LangApi().by_lang('en') r = ReportApi().create({'title': 'Test', 'lang_id': en.id}) s = SectionApi().create({'title': 'Test', 'report_id': r.id}) q = QuestionApi().create({'question': 'Test', 'weight': 1, 'section_id': s.id}) assert q == QuestionApi().read(q.id)