def post(data): data = data.copy() if utils.get_DB(data.get("mat_no")): return "Student already exists" if not all([data.get(prop) for prop in required]) or (data.keys() - all_fields): # Empty value supplied or Invalid field supplied or Missing field present return "Invalid field supplied or missing a compulsory field" if ("grad_status" not in data) or data.get("session_grad") == 0: # Check exceptions to non required return "Invalid field supplied or missing a compulsory field" session_admitted = data['session_admitted'] master_schema = MasterSchema() database = "{}-{}.db".format(session_admitted, session_admitted + 1) master_model = master_schema.load({ 'mat_no': data['mat_no'], 'surname': data['surname'], 'database': database }) session = utils.load_session(session_admitted) personalinfo_schema = session.PersonalInfoSchema() data["is_symlink"] = data["mode_of_entry"] - 1 data["level"] = abs(data["level"]) * [1, -1][data.pop("grad_status")] student_model = personalinfo_schema.load(data) db.session.add(master_model) db.session.commit() db_session = personalinfo_schema.Meta.sqla_session if data["mode_of_entry"] != 1: # Point from Symlink table # TODO use util fn to predict class DB for DE students session_list = loads( Props.query.filter_by(key="SessionList").first().valuestr) class_session = session_list[session_list.index(session_admitted) - data["mode_of_entry"] + 1] student_model.database = "{}-{}.db".format(class_session, class_session + 1) symlink_session = utils.load_session(class_session) symlink_schema = symlink_session.SymLinkSchema() database = "{}-{}.db".format(session_admitted, session_admitted + 1) symlink_model = symlink_schema.load({ "mat_no": data["mat_no"], "database": database }) db_session_2 = symlink_schema.Meta.sqla_session db_session_2.add(symlink_model) db_session_2.commit() db_session.add(student_model) db_session.commit()
def get_students_details_by_category(level, entry_session, category=None, get_all=False): """ Gets the details for students in `level` level as required by the senate version for non-graduating students If `get_all` is supplied, `category` is ignored :param level: level of students :param entry_session: entry session of students :param category: (Optional) category to fetch :param get_all: (Optional) if true return the details of all categories of students :return: list of dicts of the details students with all categories if `get_all` is true else list of dicts of the details students with `category` category """ populate_course_list(level) students = get_students_by_category(level, entry_session, category=category, get_all=get_all) categories = get_categories() if get_all: students_details = {} for db_name in students.keys(): session = load_session(db_name) for category in categories: dets = list( map( lambda stud: get_student_details_for_cat( stud, level, session), students[db_name][category])) if students_details.get(category): students_details[category].extend(dets) else: students_details[category] = dets else: students_details = [] for db_name in students: session = load_session(db_name) dets = list( map( lambda stud: get_student_details_for_cat( stud, level, session), students[db_name])) students_details.extend(dets) return students_details
def patch(data, superuser=False): data = data.copy() session = utils.get_DB(data.get("mat_no")) if not session: return None, 404 if not all([data.get(prop) for prop in (required & data.keys())]) or (data.keys() - all_fields): # Empty value supplied or Invalid field supplied return "Invalid field supplied or missing a compulsory field", 400 if data.get("session_grad") == 0: return "Invalid field supplied", 400 session = utils.load_session(session) student = session.PersonalInfo.query.filter_by(mat_no=data["mat_no"]) if superuser: level = abs(data.get("level", 0)) or abs(student.first().level) if "grad_status" in data: data["level"] = level * [1, -1][data.pop("grad_status")] elif "level" in data: # Preserve grad status data["level"] = level * [1, -1][student.first().grad_status] else: for prop in ("session_admitted", "session_grad", "level", "mode_of_entry", "grad_status"): data.pop(prop, None) student.update(data) db_session = session.PersonalInfoSchema().Meta.sqla_session db_session.commit() return None, 200
def get(mat_no): db_name = utils.get_DB(mat_no) if not db_name: return None session = utils.load_session(db_name) student_data = session.PersonalInfo.query.filter_by(mat_no=mat_no).first() personal_info_obj = session.PersonalInfoSchema().dump(student_data) personal_info_obj['level'] = abs(personal_info_obj['level']) personal_info_obj.update({'grad_status': student_data.grad_status}) return personal_info_obj
def get_cls_limits(cls, db_name=None, session=None): """ Retrieves the lower and upper limit for `cls` graduating class designation :param cls: graduating class designation :param db_name: name of the database file of the graduating students :param session: session module of the graduating students :return: list of the lower and upper limits for the `cls` class designation """ if not session: session = load_session(db_name) limits = session.DegreeClass.query.filter_by( cls=class_mapping[cls]).first().limits return list(map(float, limits.split(',')))
def rule_2_3(): bad_mat_1 = set() bad_mat_2 = set() for session in range(start, stop): sess = utils.load_session(session) mats = [x.mat_no for x in sess.PersonalInfo.query.all()] for mat_no in mats: # print(mat_no) catg = result_statement.get(mat_no)["categories"] if catg.count("C") > 1: bad_mat_1.add(mat_no) d_idx = (catg + ["D"]).index("D") if (catg + ["D"])[d_idx + 1:]: bad_mat_2.add(mat_no) print("Rule 2 defaulters\n", bad_mat_1) print("Rule 3 defaulters\n", bad_mat_2)
def delete_course_reg_entry(mat_no, acad_session): course_reg = course_reg_utils.course_reg_for_session(mat_no, acad_session) if not course_reg: return 'Course Registration for session {}/{} does not exist'.format(acad_session, acad_session+1), 404 session = utils.load_session(utils.get_DB(mat_no)) courses_reg_schema = getattr(session, course_reg['table'] + 'Schema') # TODO reset optional course in personal info if set here courses_reg = courses_reg_schema.Meta.model.query.filter_by(mat_no=mat_no).first() db_session = courses_reg_schema.Meta.sqla_session db_session.delete(courses_reg) db_session.commit() db_session.close() return 'Record Deleted Successfully', 200
def update_gpa_credits(mat_no, grade, previous_grade, course_credit, course_level): gpa_credits = utils.gpa_credits_poll(mat_no)[:-1] index = utils.ltoi(course_level) level_gpa = gpa_credits[index][0] if gpa_credits[index][0] else 0 level_credits_passed = gpa_credits[index][1] if gpa_credits[index][1] else 0 if grade != previous_grade: creds = utils.get_credits(mat_no, lpad=True) level_credits = creds[index] grading_point_rule = utils.get_grading_point(utils.get_DB(mat_no)) grading_point = grading_point_rule[grade] grading_point_old = grading_point_rule[ previous_grade] if previous_grade else 0 diff = grading_point - grading_point_old level_gpa = level_gpa + ((course_credit * diff) / level_credits) sign_multiplier = diff // abs(diff) if diff != 0 else 0 level_credits_passed += course_credit * sign_multiplier gpa_credits[index] = (round(level_gpa, 4), level_credits_passed) cgpa = 0 mode_of_entry = personal_info.get(mat_no)['mode_of_entry'] weights = utils.get_level_weightings(mode_of_entry) for idx in range(len(weights)): cgpa += weights[idx] * gpa_credits[idx][0] if gpa_credits[ idx] and gpa_credits[idx][0] else 0 gpa_record = {'mat_no': mat_no, 'cgpa': round(cgpa, 4)} for key, idx in [('level{}00'.format(lev + 1), lev) for lev in range(5)]: gpa_record.update({ key: ','.join(list(map(str, gpa_credits[idx]))) if gpa_credits[idx][0] else None }) session = utils.load_session(utils.get_DB(mat_no)) gpa_record = session.GPACreditsSchema().load(gpa_record) db_session = session.GPACreditsSchema().Meta.sqla_session db_session.add(gpa_record) db_session.commit() db_session.close() return 'Success'
def filter_students_by_category(level, category, db_name, students): """ Strips and returns students with category `category` from a list of students :param level: level of the students :param category: category to match students against :param db_name: name of the database file of the students :param students: list of students :return: list of students with ctagory `category` """ session = load_session(db_name) res_obj = getattr(session, 'Result{}'.format(level)) studs = [] for mat_no in students: stud = res_obj.query.filter_by(mat_no=mat_no).first() if stud: if stud.category == category: studs.append(mat_no) return studs
def rule_1_4(): bad_mat_1 = set() bad_mat_2 = set() for session in range(start, stop): sess = utils.load_session(session) mats = [x.mat_no for x in sess.PersonalInfo.query.all()] for mat_no in mats: person = personal_info.get(mat_no) mode_of_entry = person["mode_of_entry"] results = result_statement.get(mat_no)["results"] for result in results: for record in result["first_sem"] + result["second_sem"]: rec_crs_lvl = crs_lvl[mode_of_entry][record[0]] if rec_crs_lvl // 100 < mode_of_entry: bad_mat_1.add(mat_no) if rec_crs_lvl > result["level"]: bad_mat_2.add(mat_no) print("Rule 1 defaulters\n", bad_mat_1) print("Rule 4 defaulters\n", bad_mat_2)
def get_students_by_category(level, entry_session, category=None, get_all=False): """ Gets all students within a category If `get_all` is supplied, `category` is ignored :param level: level of students :param entry_session: entry session of students :param category: (Optional) category to fetch :param get_all: (Optional) if True return all categories of students :return: list of students if not `get_all` else dictionary of all category mapping to list of students """ mat_no_dict = get_students_by_level(entry_session, level, retDB=True) acad_session = get_session_from_level(entry_session, level) if get_all: students = dict.fromkeys(mat_no_dict) categories = get_categories(final_year=(level == 500)) cat_dict = dict(zip(categories, [[]] * len(categories))) for db_name in mat_no_dict: students[db_name] = cat_dict.copy() session = load_session(db_name) for mat_no in mat_no_dict[db_name]: # level = level if level != 500 else get_level(mat_no) # Accounts for spillover students cat = get_category(mat_no, level, acad_session, session) if students[db_name].get(cat): students[db_name][cat].append(mat_no) else: students[db_name][cat] = [mat_no] else: students = dict.fromkeys(mat_no_dict) for db_name in mat_no_dict: filtered_studs = filter_students_by_category( level, category, db_name, mat_no_dict[db_name]) students[db_name] = filtered_studs return students
def filter_students_by_degree_class(degree_class, db_name, students): """ Strips and returns the details of students with `degree_class` graduating class designation from the list of students :param degree_class: graduating class designation :param db_name: name of the database file of the graduating students :param students: list of graduating students :return: list of dicts of graduating students """ session = load_session(db_name) limits = get_cls_limits(degree_class, session=session) cgpa_obj = getattr(session, 'GPA_Credits') studs = [] for stud in students: cgpa = cgpa_obj.query.filter_by(mat_no=stud).first().cgpa if limits[0] <= cgpa <= limits[1]: details = get_student_details_for_cls(stud, session) details['cgpa'] = float(cgpa) studs.append(details) students.remove(stud) return studs
def delete(mat_no): session = utils.get_DB(mat_no) if not session: return None, 404 session = utils.load_session(session) entries = [] db_session = session.PersonalInfoSchema().Meta.sqla_session entries.append(session.PersonalInfo.query.filter_by(mat_no=mat_no).first()) entries.append(session.GPA_Credits.query.filter_by(mat_no=mat_no).first()) for lvl in range(100, 900, 100): entries.append( eval("session.CourseReg{}".format(lvl)).query.filter_by( mat_no=mat_no).first()) entries.append( eval("session.Result{}".format(lvl)).query.filter_by( mat_no=mat_no).first()) for entry in entries: if entry: db_session.delete(entry) master = Master.query.filter_by(mat_no=mat_no).first() db.session.delete(master) db_session.commit() db.session.commit() return None, 200
def post_course_reg(data): level_options = course_reg_utils.get_optional_courses(data['course_reg_level']) level_options = utils.dictify(level_options[0] + level_options[1]) courses, person_options = [], [] tcr = [0, 0] for idx, sem in enumerate(['first_sem', 'second_sem']): for course_obj in data['courses'][sem]: courses.append(course_obj[0]) tcr[idx] += course_obj[2] if course_obj[0] in level_options: person_options.append('{} {}'.format(idx+1, course_obj[0])) courses = sorted(courses) mat_no = data['mat_no'] table_to_populate = data['table_to_populate'] course_reg_session = data['course_reg_session'] session = utils.load_session(utils.get_DB(mat_no)) course_reg_xxx_schema = getattr(session, table_to_populate + 'Schema')() table_columns = course_reg_xxx_schema.load_fields.keys() registration = {} for col in table_columns: if col in courses: registration[col] = '1' courses.remove(col) elif col not in ['carryovers', 'mat_no', 'tcr', 'level', 'session', 'probation', 'fees_status', 'others']: registration[col] = '0' registration.update({ 'carryovers': ','.join(courses), 'mat_no': mat_no, 'tcr': sum(tcr), 'level': data['course_reg_level'], 'session': course_reg_session, 'probation': data['probation_status'], 'fees_status': data['fees_status'], 'others': data['others'] }) course_registration = course_reg_xxx_schema.load(registration) db_session = course_reg_xxx_schema.Meta.sqla_session if person_options: person = session.PersonalInfo.query.filter_by(mat_no=mat_no).first() person.option = ','.join(person_options) db_session.add(person) db_session.add(course_registration) db_session.commit() db_session.close() success_text = 'course registration successful' # Here we check if there were any stray results waiting in unusual results for this session session_results = [x for x in utils.result_poll(mat_no) if x and (x['session'] == course_reg_session)] if session_results and ('unusual_results' in session_results[0]) and session_results[0]['unusual_results']: unusual_results = [utils.spc_fn(x) for x in utils.csv_fn(session_results[0]['carryovers'])] unusual_results = [[x[0], course_reg_session, mat_no, x[1]] for x in unusual_results] log = results.add_result_records(unusual_results) if log[0]: success_text += '; results for unregistered courses still remain in database' print('\n====>> ', success_text) return success_text, 200
def get_final_year_students_by_category(entry_session, category=None, get_all=False): """ Gets the details for students required by the senate version for graduating students If `get_all` is supplied, `category` is ignored :param entry_session: entry session of students :param category: (Optional) category to fetch :param get_all: (Optional) if true return the details of all categories of students :return: list of dicts of the details students with all categories if `get_all` is true else list of dicts of the details students with `category` category """ populate_course_list(500) students_categories = get_students_by_category(500, entry_session, category=category, get_all=get_all) all_students_by_category = group_students_by_category(students_categories) if get_all: all_students = {} # Successful students successful_students = all_students_by_category.pop( 'successful students') students = {} classes = class_mapping.keys() for degree_class in classes: studs = [] for db_name in successful_students: studs.extend( filter_students_by_degree_class( degree_class, db_name, successful_students[db_name])) students[degree_class] = studs all_students['successful students'] = students # Referred students referred_students = all_students_by_category.pop('carryover students') studs = [] for db_name in referred_students: session = load_session(db_name) studs.extend( list( map( lambda mat_no: get_details_for_ref_students( mat_no, session), referred_students[db_name]))) all_students['referred students'] = studs # Other students for group in all_students_by_category: studs, other_students = [], all_students_by_category[group] for db_name in other_students: session = load_session(db_name) studs.extend( list( map( lambda mat_no: get_other_students_details( mat_no, session, group), other_students[db_name]))) all_students[group] = studs return all_students
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
def get_students_by_level(entry_session, level, is_course_adviser=False, retDB=False): """ Gets all the mat numbers of students in a particular db This only includes the regular students that entered during the `entry_session` session, DE and probating students that somehow have ties to this db. :param entry_session: entry session :param level: level of students :param is_course_adviser: if true returns the students associated with the level course adviser :param retDB: if True, returns a dict :return: list of mat numbers if not `retDB` else a dictionary of db name being mapped to a list of mat numbers """ entry_session_db_name = '{}_{}'.format(entry_session, entry_session + 1) session = load_session(entry_session_db_name) # Regular students if level > 500 and is_course_adviser: students = [] else: students = session.PersonalInfo.query.filter_by( is_symlink=0).filter_by(mode_of_entry=1).all() if retDB: students = { entry_session_db_name: list(map(lambda stud: stud.mat_no, students)) } else: students = list(map(lambda stud: stud.mat_no, students)) level_num = level // 100 other_students = session.SymLink.query.filter(getattr(session.SymLink, f'database_{level_num}') != None)\ .order_by(f'DATABASE_{level_num}').all() # DE and probating students # Spillover students if level_num == 5 and not is_course_adviser: for num in range(6, 9): other_students.extend( session.SymLink.query.filter( getattr(session.SymLink, f'database_{num}') != None). order_by(f'DATABASE_{num}').all()) elif level_num > 5 and is_course_adviser: for num in range(7, 9): other_students.extend( session.SymLink.query.filter( getattr(session.SymLink, f'database_{num}') != None). order_by(f'DATABASE_{num}').all()) if retDB: # groups the students by their database name stud_db_map, num = {}, [5, 6][is_course_adviser] for stud in other_students: if level_num == [5, 6][is_course_adviser]: try: db = getattr(stud, f'database_{num}') assert db is not None db_name = db[:-3].replace('-', '_') except AssertionError: num += 1 db_name = getattr(stud, f'database_{num}')[:-3].replace( '-', '_') else: db_name = getattr(stud, f'database_{level_num}')[:-3].replace( '-', '_') try: stud_db_map[db_name].append(stud.mat_no) except KeyError: stud_db_map[db_name] = [stud.mat_no] if entry_session_db_name in stud_db_map: de_probating_students = stud_db_map.pop(entry_session_db_name) students[entry_session_db_name].extend(de_probating_students) students.update(stud_db_map) else: other_students = list(map(lambda stud: stud.mat_no, other_students)) students.extend(other_students) return students