class Services: def __init__(self): self.students = Repository(Repository.TYPE_STUDENTS) self.disciplines = Repository(Repository.TYPE_DISCIPLINES) self.grades = Repository(Repository.TYPE_GRADES) self.tracker = ActionTracker(self) def get_students_count(self): return self.students.get_size() def get_disciplines_count(self): return self.disciplines.get_size() def get_grades_count(self): return self.grades.get_size() def has_students(self): return self.students.is_not_empty() def has_disciplines(self): return self.disciplines.is_not_empty() def has_grades(self): return self.grades.is_not_empty() def is_empty(self): return self.students.is_empty() and self.disciplines.is_empty( ) and self.grades.is_empty() def clear(self): self.students.clear() self.disciplines.clear() self.grades.clear() def add_all(self, other, track=True): self.students.add_all(other.students) self.disciplines.add_all(other.disciplines) self.grades.add_all(other.grades) if track: self.tracker.add_action(ActionTracker.ADD_MULTIPLE, other) def is_student_id(self, sid): for s in self.students: if s.id == sid: return True return False def is_discipline_id(self, did): for d in self.disciplines: if d.id == did: return True return False def add_student(self, sid, name, track=True): student = Student(sid, name) self.students.add(student) if track: self.tracker.add_action(ActionTracker.ADD, student, ActionTracker.STUDENT) def add_discipline(self, did, name, track=True): discipline = Discipline(did, name) self.disciplines.add(discipline) if track: self.tracker.add_action(ActionTracker.ADD, discipline, ActionTracker.DISCIPLINE) def get_discipline(self, did): for d in self.disciplines: if d.id == did: return d raise Exception("Invalid discipline ID, no discipline found with ID " + str(did)) def get_student(self, sid): for s in self.students: if s.id == sid: return s raise Exception("Invalid student ID, no student found with ID " + str(sid)) def add_grade(self, discipline, student, value, track=True): if is_int(discipline): discipline = self.get_discipline(discipline) if is_int(student): student = self.get_student(student) grade = Grade(discipline, student, value) self.grades.add(grade) if track: self.tracker.add_action(ActionTracker.ADD, grade, ActionTracker.GRADE) def remove_student(self, sid, track=True): deleted = Group() student = self.get_student(sid) grades_copy = list(self.grades) for g in grades_copy: if g.student == student: deleted.grades.append(g) self.grades.remove(g) deleted.students.append(student) self.students.remove(student) if track: self.tracker.add_action(ActionTracker.REMOVE_MULTIPLE, deleted) def remove_discipline(self, did, track=True): deleted = Group() discipline = self.get_discipline(did) grades_copy = list(self.grades) for g in grades_copy: if g.discipline == discipline: deleted.grades.append(g) self.grades.remove(g) deleted.disciplines.append(discipline) self.disciplines.remove(discipline) if track: self.tracker.add_action(ActionTracker.REMOVE_MULTIPLE, deleted) def update_student(self, sid, name, track=True): student = self.get_student(sid) if track: self.tracker.add_action(ActionTracker.EDIT, copy.copy(student), ActionTracker.STUDENT) student.name = name def update_discipline(self, did, name, track=True): discipline = self.get_discipline(did) if track: self.tracker.add_action(ActionTracker.EDIT, copy.copy(discipline), ActionTracker.DISCIPLINE) discipline.name = name def search(self, keyword): search_group = Group() keyword = keyword.upper() for s in self.students: if keyword in str(s.id).upper() or keyword in s.name.upper(): search_group.students.append(s) for d in self.disciplines: if keyword in str(d.id).upper() or keyword in d.name.upper(): search_group.disciplines.append(d) return search_group def get_failing_students(self): students_with_grades = [] for s in self.students: for d in self.disciplines: count = 0 average = 0 for g in self.grades: if g.student == s and g.discipline == d: count += 1 average += g.value if count == 0: continue average /= count if average < 5: students_with_grades.append( Average(average, s.name, d.name)) return students_with_grades def get_best_students(self): students_with_grades = [] for s in self.students: total_count = 0 total_average = 0 for d in self.disciplines: count = 0 average = 0 for g in self.grades: if g.student == s and g.discipline == d: count += 1 average += g.value if count == 0: continue total_count += 1 total_average += average / count if total_count == 0: continue total_average /= total_count students_with_grades.append( Average(total_average, student_name=s.name)) return students_with_grades def get_disciplines_with_grades(self): disciplines_with_grades = [] for d in self.disciplines: count = 0 average = 0 for g in self.grades: if g.discipline == d: count += 1 average += g.value if count == 0: continue average /= count disciplines_with_grades.append( Average(average, discipline_name=d.name)) return disciplines_with_grades def undo(self): self.tracker.undo() def redo(self): self.tracker.redo()
class Services: def __init__(self): self.repository = Repository() def preload_list(self): """ Adds to the repository a list of predefined values that are randomised Input: - Output: - """ randomiser = Randomiser() repository_to_add = Repository() for i in range(10): random_name = randomiser.get_random_first_name( ) + " " + randomiser.get_random_last_name() repository_to_add.add_student(i, random_name) repository_to_add.add_discipline( i, randomiser.get_random_discipline_name()) for i in range(10): repository_to_add.add_grade( randomiser.get_random(repository_to_add.disciplines), randomiser.get_random(repository_to_add.students), randomiser.get_random_grade()) self.repository.add_all(repository_to_add) def add(self, params): """ Add a student or discipline to the repository Input: list - list of params (type, id, name) Output: - """ validate_param_length(params, 3) atype = params[0] aid = params[1] name = params[2] validate_type(atype) if atype == STUDENT: validate_new_student_id(self.repository, aid) if atype == DISCIPLINE: validate_new_discipline_id(self.repository, aid) validate_name(name) aid = int(aid) if atype == STUDENT: self.repository.add_student(aid, name) if atype == DISCIPLINE: self.repository.add_discipline(aid, name) def remove(self, params): """ Remove a student or discipline from the repository Input: list - list of params (type, id) Output: - """ validate_param_length(params, 2) atype = params[0] aid = params[1] validate_type(atype) if atype == STUDENT: validate_existing_student_id(self.repository, aid) if atype == DISCIPLINE: validate_existing_discipline_id(self.repository, aid) aid = int(aid) if atype == STUDENT: self.repository.remove_student(aid) if atype == DISCIPLINE: self.repository.remove_discipline(aid) def update(self, params): """ Update a student or discipline from the repository Input: list - list of params (type, id, new_name) Output: - """ validate_param_length(params, 3) atype = params[0] aid = params[1] name = params[2] validate_type(atype) if atype == STUDENT: validate_existing_student_id(self.repository, aid) if atype == DISCIPLINE: validate_existing_discipline_id(self.repository, aid) validate_name(name) aid = int(aid) if atype == STUDENT: self.repository.update_student(aid, name) if atype == DISCIPLINE: self.repository.update_discipline(aid, name) def build_list(self, repository, header="All students and diciplines"): """ Parses a repository's data into a string to be printed Input: Repository - repository to parse string - header to show at the beginning of the generated string Output: string - the parsed repository """ if repository.is_empty(): raise NoDataError("No data to show") data_string = "\n" data_string += header if repository.has_students() and repository.has_disciplines(): data_string += "\n" + generator.generate_chars( '-', 121) + "\n|" + generator.generate_chars( ' ', 4) + "Students" + generator.generate_chars( ' ', 47) + "|" + generator.generate_chars( ' ', 4) + "Disciplines" + generator.generate_chars( ' ', 44) + "|\n" + generator.generate_chars( '-', 121) students_length = len(repository.students) disciplines_length = len(repository.disciplines) max_length = max(students_length, disciplines_length) for i in range(max_length): if i < students_length: student_string = str(repository.students[i]) data_string += "\n| " + student_string + generator.generate_chars( ' ', 57 - len(student_string)) + " | " else: data_string += "\n|" + generator.generate_chars(' ', 59) + "| " if i < disciplines_length: discipline_string = str(repository.disciplines[i]) data_string += discipline_string + generator.generate_chars( ' ', 57 - len(discipline_string)) + " |" else: data_string += generator.generate_chars(' ', 58) + "|" data_string += "\n" + generator.generate_chars('-', 121) return data_string if repository.has_students(): data_string += "\n" + generator.generate_chars( '-', 60) + "\nStudents\n" + generator.generate_chars('-', 60) for student in repository.students: data_string += "\n" + str(student) return data_string if repository.has_disciplines(): data_string += "\n" + generator.generate_chars( '-', 60) + "\nDisciplines\n" + generator.generate_chars( '-', 60) for discipline in repository.disciplines: data_string += "\n" + str(discipline) return data_string def list_all(self): """ Builds are returns a list as string representing the repository to be printed Input: - Output: string - string representing the repository """ return self.build_list(self.repository) def grade(self, params): """ Gives a grade to a student at a given discipline Input: params - list of params (student_id, discipline_id, grade) Output: - """ validate_param_length(params, 3) sid = params[0] did = params[1] grade = params[2] validate_existing_student_id(self.repository, sid) validate_existing_discipline_id(self.repository, did) validate_grade(grade) self.repository.add_grade(int(did), int(sid), float(grade)) def search(self, params): """ Searches by a keyword and returns a string with the results Input: params - list of params (keyword) Output: string - results of search """ validate_param_length(params, 1) keyword = params[0] validate_keyword(keyword) repo_with_search = self.repository.search(keyword) return self.build_list(repo_with_search, "Results for the keyword '" + keyword + "'") def see_failing_students(self): """ Returns a string representing the failing students Input: - Output: string - all failing students """ faillings = self.repository.get_failing_students() if len(faillings) < 1: raise NoDataError("No students failing at one or more disciplines") students_to_show = "\nStudents failing:" count = 1 for f in faillings: students_to_show += "\n" + str( count ) + ". " + f.student_name + " at " + f.discipline_name + " with the average of " + f.get_formated_average( ) count += 1 return students_to_show def see_best_students(self): """ Returns a string representing all students ordered by their average grades Input: - Output: string - all students ordered by their average grades """ best_students = self.repository.get_best_students() if len(best_students) < 1: raise NoDataError("No students with grades to show") best_students = sorted(best_students, key=lambda b: b.value, reverse=True) students_to_show = "\nBest students in descending order of average grades:" count = 1 for b in best_students: students_to_show += "\n" + str( count ) + ". " + b.student_name + " with the average of " + b.get_formated_average( ) count += 1 return students_to_show def see_grades(self): """ Returns a string representing all disciplines ordered by their average grades Input: - Output: string - all disciplines ordered by their average grades """ disciplines_with_grades = self.repository.get_disciplines_with_grades() if len(disciplines_with_grades) < 1: raise NoDataError("No disciplines with grades to show") disciplines_with_grades = sorted(disciplines_with_grades, key=lambda d: d.value, reverse=True) disciplines_to_show = "\nDisciplines with grades in descending order of average grades:" count = 1 for d in disciplines_with_grades: disciplines_to_show += "\n" + str( count ) + ". " + d.discipline_name + " with the average of " + d.get_formated_average( ) count += 1 return disciplines_to_show def undo(self): """ Undos the last operation that modified the repository Input: - Output: - """ self.repository.undo() def redo(self): """ Redos the last operation that modified the repository Input: - Output: - """ self.repository.redo()