4.
Implementation of Basic Functions
Student Management System – PFP191
The program is implemented in Python following a modular, object-oriented architecture. The
source code is organized into four main layers:
• models/ – defines the Student class with full encapsulation and validation
• services/ – contains SystemManager (core operations) and ScoreManager (score
handling)
• utils/ – validation functions ([Link]) and file I/O (file_handler.py)
• ui/ – interactive menus ([Link]) and the main entry point ([Link])
4.1 calculate_gpa() – GPA Calculation
File: models/[Link] | Method: Student.calculate_gpa()
This method computes the Grade Point Average of a student by averaging all numeric scores
stored in the private __scores dictionary. If the student has no enrolled subjects, the method
returns 0.0 to avoid a division-by-zero error. The result is rounded to two decimal places using
Python's built-in round() function.
Logic summary:
• If __scores is empty → return 0.0
• Otherwise → sum all values ÷ number of entries, rounded to 2 decimal places
Source code (models/[Link]):
def calculate_gpa(self):
if not self.__scores:
return 0.0
return round(sum(self.__scores.values()) / len(self.__scores), 2)
The GPA is automatically recalculated every time a score is added or updated via add_score() or
update_score(), ensuring the stored gpa value is always current.
4.2 add_student() – Add a New Student
File: services/system_manager.py | Method: SystemManager.add_student()
Before appending a new Student object to the internal __students list, this method verifies that the
student ID does not already exist in the system. Duplicate detection is handled by the private
helper __find_by_id(). If a duplicate is found, a ValueError is raised with a descriptive message.
This prevents data integrity issues caused by duplicate records.
Logic summary:
• Call __find_by_id(student.student_id)
• If a match is found → raise ValueError (ID already exists)
• If no match → append the Student object to __students
Source code (services/system_manager.py):
def add_student(self, student: Student):
if self.__find_by_id(student.student_id):
raise ValueError(f"Ma so {student.student_id} da ton tai!")
self.__students.append(student)
4.3 edit_student() – Edit Student Information
File: services/system_manager.py | Method: SystemManager.edit_student()
This method performs a partial update on an existing student record. Only fields passed with a
non-None value will be updated, allowing the caller to modify one or more attributes without
overwriting unchanged fields. If no student with the given ID is found, a KeyError is raised.
Parameters:
• student_id – the unique identifier of the student to update (required)
• new_name, new_day, new_month, new_year, new_major – optional update fields (default
None)
Logic summary:
• Find student using __find_by_id(); raise KeyError if not found
• For each optional parameter: if not None, update the corresponding setter on the Student
object
• Each setter internally calls its validator, ensuring data integrity on update
Source code (services/system_manager.py):
def edit_student(self, student_id, new_name=None, new_day=None,
new_month=None, new_year=None, new_major=None):
student = self.__find_by_id(student_id)
if not student:
raise KeyError(f"Khong tim thay sinh vien co ma {student_id}")
if new_name: [Link] = new_name
if new_day: [Link] = new_day
if new_month: [Link] = new_month
if new_year: [Link] = new_year
if new_major: [Link] = new_major
4.4 delete_student() – Delete a Student
File: services/system_manager.py | Method: SystemManager.delete_student()
This method removes a student from the system by locating the object in __students and calling
[Link]() on it. If no student with the specified ID exists, a KeyError is raised to inform the
caller.
Logic summary:
• Find student using __find_by_id(); raise KeyError if not found
• Call self.__students.remove(student) to delete from the list
Source code (services/system_manager.py):
def delete_student(self, student_id):
student = self.__find_by_id(student_id)
if not student:
raise KeyError(f"Khong tim thay sinh vien co ma {student_id}")
self.__students.remove(student)
4.5 search_by_name() and search_by_id() – Student Search
File: services/system_manager.py
The system provides two independent search functions that return a list of matching Student
objects.
search_by_name(keyword)
• Performs a case-insensitive partial-match search across all student names
• Converts both keyword and student name to lowercase before comparison
• Returns a list of all students whose names contain the keyword (empty list if none found)
search_by_id(student_id)
• Performs an exact-match lookup by student ID using the private __find_by_id() helper
• Returns a list with one element if the ID is found, or an empty list if not
Source code (services/system_manager.py):
def search_by_name(self, keyword):
keyword_lower = [Link]()
return [s for s in self.__students if keyword_lower in [Link]()]
def search_by_id(self, student_id):
found = self.__find_by_id(student_id)
return [found] if found else []
4.6 sort_by_gpa(), sort_by_name(), sort_by_birth_year() – Sorting
File: services/system_manager.py
Three sorting methods allow the student list to be reordered in-place using Python's built-in
[Link]() with lambda key functions. The table below summarises each method's behavior:
Method Sort Key Order Description
sort_by_gpa() [Link] High → Low Descending
sort_by_name() Last word of name A→Z Ascending
sort_by_birth_year() [Link] Earliest first Ascending
Note: sort_by_name() sorts by the last segment of the name string ([Link]()[-1]), which
corresponds to the Vietnamese family name convention. The comparison is case-insensitive.
Source code (services/system_manager.py):
def sort_by_gpa(self, reverse=True):
self.__students.sort(key=lambda s: [Link], reverse=reverse)
def sort_by_name(self):
self.__students.sort(
key=lambda s: [Link]()[-1].lower() if [Link]() else ""
)
def sort_by_birth_year(self):
self.__students.sort(key=lambda s: [Link])
4.7 Input & Validation Functions
File: utils/[Link]
All user input is validated before being accepted into the system. The validator module provides
eight standalone functions using Python's re module for regex-based checks and simple numeric
range comparisons. Each function returns True if valid, False otherwise.
Function Validation Rule Used In
Regex: ^[A-Za-z]{2}\d+$ (>= 2
is_valid_id(id) Student ID input, add_student
letters + digits)
Regex: ^[A-Za-z\u00C0-\u1EF9\
is_valid_name(name) s]+$ (letters + spaces, incl. Name input, [Link] setter
Vietnamese)
is_valid_score(score) 0 <= score <= 10 Score entry, add_score
is_valid_day(day) 1 <= day <= 31 Birth date input
is_valid_month(month) 1 <= month <= 12 Birth date input
is_valid_year(year) 1990 <= year <= 2010 Birth date input
Regex: ^[A-Za-z\s]+$ (letters
is_valid_major(major) Major input, [Link] setter
and spaces only)
Regex: ^[A-Za-z\u00C0-\u1EF9\
is_valid_subject(subj Subject name input,
ect) s]+$ (letters + spaces, incl.
ScoreManager
Vietnamese)
Source code (utils/[Link]):
import re
def is_valid_id(student_id):
return [Link](r"^[A-Za-z]{2}\d+$", student_id) is not None
def is_valid_name(name):
return [Link](r"^[A-Za-z\u00C0-\u1EF9\s]+$", name) is not None
def is_valid_score(score): return 0 <= score <= 10
def is_valid_day(day): return 1 <= day <= 31
def is_valid_month(month): return 1 <= month <= 12
def is_valid_year(year): return 1990 <= year <= 2010
def is_valid_major(major):
return [Link](r"^[A-Za-z\s]+$", major) is not None
def is_valid_subject(subject):
return [Link](r"^[A-Za-z\u00C0-\u1EF9\s]+$", subject) is not None
4.8 Control Menu – Program Entry Point
Files: ui/[Link] and [Link]
The program uses a while True loop in [Link] to continuously present a numbered main menu
(options 0–7) to the user. Each selection dispatches to either a sub-menu function in [Link] or a
direct operation on SystemManager. The loop terminates only when the user selects option 0
(Exit), at which point the program offers to save all data to file before quitting.
Main menu structure (ui/[Link]):
Option Menu Label Action
1 Student Management Add / Edit / Delete / View students
Enter / Edit scores / Edit subject name /
2 Score Management
View scores
Search by name (keyword) or by ID
3 Search Student
(exact)
4 Sort Students Sort by GPA / name / birth year
5 Class GPA Statistics Display average GPA for all students
6 Save Data to File Serialize all students to [Link]
7 Load Data from File Deserialize students from [Link]
0 Exit Prompt to save, then exit the program
Source code for the main loop ([Link]):
def main():
sm = SystemManager()
scm = ScoreManager(sm)
try:
sm.load_from_file() # auto-load on startup
except FileNotFoundError:
print("No data file found. Starting fresh.")
while True:
choice = display_main_menu()
if choice == "1": student_management_menu(sm)
elif choice == "2": score_management_menu(sm, scm)
elif choice == "3": search_menu(sm)
elif choice == "4": sort_menu(sm)
elif choice == "5": print(f"Class GPA: {sm.get_overall_gpa():.2f}")
elif choice == "6": sm.save_to_file()
elif choice == "7": sm.load_from_file()
elif choice == "0":
confirm = input("Save before exit? (y/n): ").lower()
if confirm == "y": sm.save_to_file()
print("Goodbye!"); break
End of Section 4 – Implementation of Basic Functions