def test_results_table(): for student in Master.query.all(): mat_no = student.mat_no if not start <= int(student.database[:4]) <= stop: continue print(mat_no) # for debugging on failure session = load_session(student.database) table_blanks = (0, 0) # for tracking blanks within tables # TODO use new col on table to update to verify value is correct for level in range(100, 900, 100): result_lvl = eval("session.Result{}".format(level)) result = result_lvl.query.filter_by(mat_no=mat_no).first() if result: # Assert level for results not higher than present level assert personal_info.get(mat_no)["level"] >= min( 500, result.level) assert table_blanks != (1, 0 ) # assert no blank above this table table_blanks = (table_blanks[1], 1) expected_TCP = 0 # For verifying Total Credits Passed for prop in dir(result): if re.match("[A-Z][A-Z][A-Z][0-9][0-9][0-9]", prop) and result.__getattribute__(prop): score, grade = result.__getattribute__(prop).split(",") # Ensure score matches grade assert utils.compute_grade(int(score), student.database) == grade if grade not in ("F", "ABS"): expected_TCP += course_details.get( prop)["course_credit"] if result.carryovers: for course, score, grade in [ x.split() for x in result.carryovers.split(",") ]: # Assert course isn't from a higher level assert result.level >= course_details.get( course)["course_level"] # Ensure score matches grade assert utils.compute_grade(int(score), student.database) == grade if grade not in ("F", "ABS"): expected_TCP += course_details.get( course)["course_credit"] assert expected_TCP == result.tcp elif table_blanks[1]: table_blanks = (table_blanks[1], 0) # Met a blank
def enrich_course_list(course_list, fields=('code', 'title', 'credit', 'level')): """ Add details to courses supplied in course_list :param course_list: <list: str>: ['course_code_1', 'course_code_2', ...] or <list: list>: [['course_code_1', '', ...], ['course_code_2', '', ...]] :param fields: <list> (optional): the course details fields to include in the order required :return: the enriched list """ enriched_course_list = [] for crse in course_list: if isinstance(crse, list): crse_dets = course_details.get(crse[0]) [ crse.append(crse_dets[field]) for field in fields if field != 'code' ] elif isinstance(crse, str): crse_dets = course_details.get(crse) crse = [crse_dets[field] for field in fields] enriched_course_list.append(crse) return enriched_course_list
def sum_credits(course_objects, credits_index=None): """ :param course_objects: [('course_code_1', <some_other_detail>, <>,..), ('course_code_2', <some_other_detail>, <>,..), ...] :param credits_index: <int>: (optional) index of course object that contains the course_credits :return: <int: the sum of credits> """ tot = 0 for course_object in course_objects: if credits_index: tot += int(course_object[credits_index]) else: tot += int(course_details.get(course_object[0])['credit']) return tot
def test_course_reg_table(): for student in Master.query.all(): mat_no = student.mat_no if not start <= int(student.database[:4]) <= stop: continue print(mat_no) # for debugging on failure session = load_session(student.database) table_blanks = (0, 0) # for tracking blanks within tables # TODO use new col on table to update to verify value is correct for level in range(100, 900, 100): course_reg_lvl = eval("session.CourseReg{}".format(level)) course_reg = course_reg_lvl.query.filter_by(mat_no=mat_no).first() if course_reg: # Assert level for registration not higher than present level assert personal_info.get(mat_no)["level"] >= min( 500, course_reg.level) assert table_blanks != (1, 0 ) # assert no blank above this table table_blanks = (table_blanks[1], 1) expected_TCR = 0 # For verifying Total Credits Registered for prop in dir(course_reg): if re.match("[A-Z][A-Z][A-Z][0-9][0-9][0-9]", prop): if int(course_reg.__getattribute__(prop)): expected_TCR += course_details.get( prop)["course_credit"] for course in course_reg.carryovers.split(","): # TODO Remove re check after carryover100 set to NULL if re.match("[A-Z][A-Z][A-Z][0-9][0-9][0-9]", course): # Assert course isn't from a higher level assert course_reg.level >= course_details.get( course)["course_level"] expected_TCR += course_details.get( course)["course_credit"] assert expected_TCR == course_reg.tcr elif table_blanks[1]: table_blanks = (table_blanks[1], 0) # Met a blank
def get_carryovers(mat_no, level=None, next_level=False): """ Returns a dictionary of the carryover courses for a student for each semester :param level: Courses up to but not including this level which are not passed :param next_level: Courses up to and including level which are not passed """ level = level or get_level(mat_no) first_sem, second_sem = set(), set() for course in get_courses(mat_no)[:ltoi(level) + next_level]: first_sem |= set(course[0]) second_sem |= set(course[1]) person_options = csv_fn(personal_info.get(mat_no)["option"]) for pair in person_options: group, choice = spc_fn(pair, s_int) option = course_details.get_options(group) idx = option["semester"] - 1 [first_sem, second_sem][idx] -= set(option["members"]) [first_sem, second_sem][idx] |= {choice} res_stmt = result_statement.get(mat_no) results, categories = res_stmt["results"], res_stmt["categories"] res_first_sem, res_second_sem = [], [] if results: res_first_sem = reduce(add, [ result["first_sem"] for idx, result in enumerate(results) if not (categories[idx] in "CD" and 200 <= result["level"] <= 400) ]) res_second_sem = reduce(add, [ result["second_sem"] for idx, result in enumerate(results) if not (categories[idx] in "CD" and 200 <= result["level"] <= 400) ]) first_sem -= set([ record[0] for record in res_first_sem if record[4] not in ("F", "ABS") ]) second_sem -= set([ record[0] for record in res_second_sem if record[4] not in ("F", "ABS") ]) carryovers = {"first_sem": [], "second_sem": []} courses = [("first_sem", course) for course in first_sem] + [("second_sem", course) for course in second_sem] for sem, failed_course in courses: course = course_details.get(failed_course) carryovers[sem].append( [failed_course, course["credit"], course["level"]]) return carryovers
def format_results(results, tcp=(0, 0), tcf=(0, 0)): # TODO when making undumb, use TCP, TCF 1&2 from DB formatted_results, tcp, tcf, failed_courses = [[], []], [*tcp], [*tcf], [[], []] for code, score, grade in results: course = course_details.get(code) sem, credit, title = course["semester"], course["credit"], course[ "title"] formatted_results[sem - 1].append( (code, title, credit, int(score), grade)) if grade not in ("ABS", "F"): tcp[sem - 1] += credit else: tcf[sem - 1] += credit failed_courses[sem - 1].append(code) tcw = [tcp[0] + tcf[0], tcp[1] + tcf[1]] return [formatted_results] + [tcw] + [tcp] + [tcf] + [failed_courses]
def test_credits_of_course(): for year in range(start, stop + 1): session = load_session(year) for mode in (1, 2, 3): courses = session.Courses.query.filter_by( mode_of_entry=mode).first() credits = session.Credits.query.filter_by( mode_of_entry=mode).first() for lvl in range(mode * 100, 600, 100): courses_sems = eval("courses.level{}".format(lvl)).split(" ") courses_lvl = courses_sems[0].split( ",") + courses_sems[1].split(",") credits_lvl = eval("credits.level{}".format(lvl)) lvl_total = sum([ course_details.get(course)["course_credit"] for course in courses_lvl if course ]) # verify corresponding cells in courses and credits table have same credits total assert lvl_total == credits_lvl
def _get_single_results_stats(mat_no, level, acad_session): info = personal_info.get(mat_no) name = info['surname'] + ' ' + info['othernames'] reg_courses = course_reg_for_session(mat_no, acad_session) reg_course_codes = reg_courses.get('courses', []) tcr = reg_courses.get('tcr', 0) res, _ = res_poll_for_session(acad_session, mat_no=mat_no) tce, carryovers_dict, remark = 0, {}, '' if res: carryovers = res.pop('carryovers') carryovers_list = [] if not carryovers else carryovers.split(',') for course_dets in carryovers_list: code, _, _ = course_dets.split(' ') carryovers_dict[code] = True for course_code in reg_course_codes: if res.get(course_code) not in [ None, '-1,ABS' ] or carryovers_dict.get(course_code) not in [None, '-1,ABS']: tce += course_details.get(course_code)['credit'] return [mat_no, name, tcr, tce, remark], 200
def add_result_records(list_of_results, level=None): """ :param list_of_results: [ [course_code_1, session_written_1, mat_no_1, score_1], ...] :param level: level for the results being entered; to control course advisers' access :return: """ base_dir = os.path.dirname(__file__) result_errors_file = open( os.path.join(base_dir, '../../result_errors.txt'), 'a') error_log = [] # Initialize a dictionary of course details for all courses in list_of_results courses = list(set(list(zip(*list_of_results))[0])) try: course_details_dict = { course: course_details.get(course) for course in courses } except: # for any error, set this to an empty dict, individual calls will be made # and the error course would fail gracefully course_details_dict = {} for index, result_details in enumerate(list_of_results): idx = index + 1 # start index at 1 errors = add_single_result_record(idx, result_details, result_errors_file, course_details_dict, level) error_log.extend(errors) result_errors_file.close() print( Fore.CYAN, '====>> ', '{} result entries added with {} errors'.format( len(list_of_results), len(error_log)), Style.RESET_ALL) return error_log or ['Done'], 200
def test_get_course_details(): assert course_details.get_course_details(valid_course) == ([course_details.get(valid_course)], 200) assert course_details.get_course_details(inv_crs_val[0]) == (None, 404) assert course_details.get_course_details() == (course_details.get_all(), 200)
def test_get_one_course(): course_obj = course_details.get(valid_course) course_row = cur.execute(*sql_get_course(valid_course)).fetchone() for prop in set(course_keys): assert course_obj[prop] == course_row[prop]
def add_single_result_record(index, result_details, result_errors_file, course_details_dict, level=None): """ :param index: position of entry in the larger list --for tracking :param result_details: [course_code, session_written, mat_no, score] :param result_errors_file: file object in write or append mode for logging important errors :param course_details_dict: :param level: student's course-adviser assigned level """ error_log = [] try: course_code, session_taken, mat_no, score = result_details session_taken, score = map(int, [session_taken, score]) entry_session = utils.get_DB(mat_no) except: return handle_errors('Invalid inputs at index {}'.format(index), error_log, result_errors_file, result_details) grade = utils.compute_grade(score, entry_session) current_level = utils.get_level(mat_no) # Error check on level, grade and score error_text = '' if level: levels = [600, 700, 800] if level == 600 else [level] if current_level not in levels: error_text = "You are not allowed to enter results for {} at index {} whose current level is " \ "{}".format(mat_no, index, current_level) if not (-1 <= score <= 100) and not error_text: error_text = 'Unexpected score for {}, "{}", for {} at index {}; ' \ 'result not added'.format(course_code, score, mat_no, index) if not grade and not error_text: error_text = '{0} at index {1} was not found in the database'.format( mat_no, index) if error_text: return handle_errors(error_text, error_log, result_errors_file, result_details) # Get course details if course_code in course_details_dict and course_details_dict[course_code]: course_dets = course_details_dict[course_code] else: # if there was error in initializing course_details_dict, individual calls would be made course_dets = course_details.get(course_code) if not course_dets: # fail on non-existent course(s) error_text = '{} at index {} was not found in the database'.format( course_code, index) return handle_errors(error_text, error_log, result_errors_file, result_details) course_credit = course_dets['credit'] course_level = course_dets['level'] is_unusual = False # Get course reg course_registration = course_reg_for_session(mat_no, session_taken) or { 'level': current_level, 'courses': [] } courses_registered = course_registration['courses'] level_written = course_registration['level'] if not courses_registered: is_unusual = True error_log = handle_errors( 'No course registration found for {0} at index {1} for the {2}/{3} ' 'session'.format(mat_no, index, session_taken, session_taken + 1), error_log) elif course_code not in courses_registered: is_unusual = True error_log = handle_errors( '{0} at index {1} did not register {2} in the {3}/{4} session' ''.format(mat_no, index, course_code, session_taken, session_taken + 1), error_log) # Get the result table for the session res_poll = utils.result_poll(mat_no) result_record, table_to_populate = res_poll_for_session( session_taken, res_poll) session = utils.load_session(utils.get_DB(mat_no)) if not result_record: if is_unusual and grade == 'ABS': return handle_errors( 'Unregistered course {} with grade "ABS" cannot be added for ' '{}'.format(course_code, mat_no), error_log) table_to_populate = get_table_to_populate(course_registration, res_poll) result_xxx_schema = getattr(session, table_to_populate + 'Schema')() params = mat_no, session_taken, courses_registered, result_xxx_schema, level_written result_record = prepare_new_results_table(params) else: result_xxx_schema = getattr(session, table_to_populate + 'Schema')() # Check if a previous entry for the course exists in the current session and updates the value # of "previous_grade" while logging the changes to be made previous_grade = get_previous_grade_and_log_changes( result_details, result_record, is_unusual) # add score to result object if is_unusual or result_record['unusual_results']: unusual_results = result_record['unusual_results'].split(',') index = [ ind for ind, x in enumerate(unusual_results) if x.split(' ')[0] == course_code ] if index: unusual_results.pop(index[0]) if is_unusual and grade != "ABS": unusual_results.append('{} {} {}'.format(course_code, score, grade)) while '' in unusual_results: unusual_results.remove('') result_record['unusual_results'] = ','.join(unusual_results) if not is_unusual: if course_code in result_record: result_record[course_code] = '{},{}'.format(score, grade) else: carryovers = result_record['carryovers'].split( ',') if result_record['carryovers'] else [''] index = [ ind for ind, x in enumerate(carryovers) if x.split(' ')[0] == course_code ] if index: carryovers.pop(index[0]) carryovers.append('{} {} {}'.format(course_code, score, grade)) while '' in carryovers: carryovers.remove('') result_record['carryovers'] = ','.join(carryovers) # get the session category owed_courses_exist = check_owed_courses_exists( mat_no, level_written, grade, course_dets) if not is_unusual else True if not courses_registered: tcr, tcp = 0, 0 else: if grade not in ['F', 'ABS'] and previous_grade in [ 'F', 'ABS', '' ] and not is_unusual: result_record['tcp'] += course_credit elif grade in ['F', 'ABS'] and previous_grade not in [ 'F', 'ABS', '' ] and not is_unusual: result_record['tcp'] -= course_credit tcr, tcp = course_registration['tcr'], result_record['tcp'] res_record = result_xxx_schema.load(result_record) res_record.category = utils.compute_category(tcr, res_record, owed_courses_exist) db_session = result_xxx_schema.Meta.sqla_session db_session.add(res_record) if grade == 'ABS': delete_if_empty(res_record, result_xxx_schema) db_session.commit() db_session.close() # update GPA - Credits table if not is_unusual: update_gpa_credits(mat_no, grade, previous_grade, course_credit, course_level) return error_log