From 0a89c6ffd3a8a0682511be8d42600cd1e37e5d63 Mon Sep 17 00:00:00 2001 From: Yola M Date: Tue, 29 Oct 2024 19:06:32 +0100 Subject: [PATCH 1/5] feat: managing + editing student's attendance added function to manage attendance + function to edit student's attendance status --- main.py | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/main.py b/main.py index 061cf50..4cd30c0 100644 --- a/main.py +++ b/main.py @@ -107,3 +107,63 @@ def mark_attenfance(students): student['present'] = False # Mark as absent else: print("Invalid input, please enter 'yes' or 'no'.") + +#MANAGING STUDENT'S DATA: +def student_data(): + student_firstName = input('Enter students first name: ') + student_lastName = input('Enter students last name: ') + return f"{student_firstName} {student_lastName}" + +def presence_function(): + presence = input("Was the student present? (yes/no): ") + if (presence.lower() == 'yes'): + return 'PRESENT' + elif (presence.lower() == 'no'): + return 'ABSENT' + else: + print("Invalid output. Please try again. ") + return presence_function() + +# CREATING 'ATTENDANCE_DICTIONARY' AND CREATING 'STUDENTS' LIST - a list where every element is a dictionary +def manage_attendance(): + attendance_dictionary = {} + student_id = 1 + while True: + student = student_data() + attendance_status = presence_function() + attendance_dictionary[student_id] = [student, attendance_status] + student_id += 1 + + add_student = input("Want to add another student? (yes/no):") + print() + if add_student.lower() != 'yes': + break + + #EXPORT to .csv file (integration with export_attendance()) + students = [ + { + "first_name": s.split(' ')[0], + "last_name": s.split(' ')[1], + "present": a == "PRESENT" + } + for s_id, (s, a) in attendance_dictionary.items() + ] + + export_attendance(students) + + #printing students' list: + print("ATTENDANCE LIST:") + for s_id, (student, status) in attendance_dictionary.items(): + print(f"STUDENT ID: {s_id}, NAME: {student}, ATTENDANCE STATUS: {status}") + print() + return attendance_dictionary + +# EDITING STUDENT'S ATTENDANCE STATUS +def edit_attendance(attendance_dictionary, student_id): + if student_id in attendance_dictionary: + new_attendance_status = presence_function() + attendance_dictionary[student_id][1] = new_attendance_status + else: + print("Student not found") + return attendance_dictionary + From 498d732ed6b88a36d8ba35dbd095149d818c0760 Mon Sep 17 00:00:00 2001 From: Yola M Date: Tue, 29 Oct 2024 19:35:43 +0100 Subject: [PATCH 2/5] feat: added menu for easier testing + more user friendly --- main.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/main.py b/main.py index 4cd30c0..d1d30a5 100644 --- a/main.py +++ b/main.py @@ -167,3 +167,33 @@ def edit_attendance(attendance_dictionary, student_id): print("Student not found") return attendance_dictionary +#MENU +if __name__ == "__main__": + attendance = {} + students = [] + while True: + print("MENU:") + print("1. Manage attendance") + print("2. Edit attendance") + print("3. Import students") + print("4. Export attendance") + print("5. Exit") + choice = input("Choose your option: ") + + + if choice == "1": + attendance = manage_attendance() + elif choice == "2": + student_id = int(input("Enter student ID: ")) + edit_attendance(attendance, student_id) + print("Updated attendance: ", attendance) + elif choice == "3": + students = import_from_file() + print("Imported students: ", students) + elif choice == "4": + export_attendance(students) + elif choice == "5": + print("Exiting the program.") + break + else: + print("Invalid choice. Please try again.") \ No newline at end of file From df05ff22bb588416b64220559b015b4c274ee85a Mon Sep 17 00:00:00 2001 From: Yola Date: Mon, 25 Nov 2024 23:17:30 +0100 Subject: [PATCH 3/5] feat: added unit tests --- test_main.py | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 test_main.py diff --git a/test_main.py b/test_main.py new file mode 100644 index 0000000..b7c40e8 --- /dev/null +++ b/test_main.py @@ -0,0 +1,125 @@ +import os +import pytest +from typing import List, Dict +from main import import_from_file, export_attendance, add_student, edit_student, manage_attendance + +class MockCSVStorage: + def __init__(self): + self.data = [] + + def write(self, rows: List[Dict[str, str]]): + self.data = rows + + def read(self) -> List[Dict[str, str]]: + return self.data + + def append(self, row: Dict[str, str]): + self.data.append(row) + + +class TestAttendanceSystem: + #test: saving to file + def test_save_to_file(self): + #Given + students = [ + {"first_name": "John", "last_name": "Doe", "present": True}, + {"first_name": "Jane", "last_name": "Smith", "present": False}, + ] + mock_storage = MockCSVStorage() + + #When + export_attendance(students, mock_storage) + + #Then + expected_data = [ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + {"first_name": "Jane", "last_name": "Smith", "present": "no"}, + ] + assert mock_storage.data == expected_data + + #test: loading from file + def test_load_from_file(self): + #Given + mock_storage = MockCSVStorage() + mock_storage.write([ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + {"first_name": "Jane", "last_name": "Smith", "present": "no"}, + ]) + + #When + students = import_from_file(mock_storage) + + #Then + expected_students = [ + {"first_name": "John", "last_name": "Doe", "present": True}, + {"first_name": "Jane", "last_name": "Smith", "present": False}, + ] + assert students == expected_students + + #test: adding students + def test_add_students(self): + #Given + mock_storage = MockCSVStorage() + mock_storage.write([ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + ]) + + #When + add_student("Jane", "Smith", mock_storage) + + #Then + expected_data = [ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + {"first_name": "Jane", "last_name": "Smith", "present": "no"}, + ] + assert mock_storage.data == expected_data + + #test: editing students' data + def test_edit_student(self): + #Given + mock_storage = MockCSVStorage() + mock_storage.write([ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + {"first_name": "Jane", "last_name": "Smith", "present": "no"}, + ]) + + #When + edit_student("Jane", "Smith", "Janet", "Smith", mock_storage) + + #Then + expected_data = [ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + {"first_name": "Janet", "last_name": "Smith", "present": "no"}, + + ] + assert mock_storage.data == expected_data + + #test: attendance + def test_attendance_management(self): + # Given + mock_storage = MockCSVStorage() + mock_storage.write([ + {"first_name": "John", "last_name": "Doe", "present": "no"}, + ]) + + #replacement function to simulate user input + def mock_input(p): + if "Is John Doe present" in p: + return "yes" + return "no" + + #When + students = mock_storage.read() + for student in students: + if mock_input(f"Is {student['first_name']} {student['last_name']} present? (yes/no): ").lower() == "yes": + student["present"] = "yes" + else: + student["present"] = "no" + + mock_storage.write(students) + + #Then + expected_data = [ + {"first_name": "John", "last_name": "Doe", "present": "yes"}, + ] + assert mock_storage.read() == expected_data \ No newline at end of file From 89dcff0467728fd8f5f50948b9ff278d0dd78c17 Mon Sep 17 00:00:00 2001 From: Yola Date: Tue, 3 Dec 2024 23:59:49 +0100 Subject: [PATCH 4/5] fix: fixed add_students and edit_students function in main so that it works for files and mockCSVstorage feat: added exceptions' handling --- main.py | 79 ++++++++++++++++++++++++++++++++++++------------- mock_storage.py | 17 +++++++++++ test_main.py | 54 ++++++++++++++++++--------------- 3 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 mock_storage.py diff --git a/main.py b/main.py index d1d30a5..0d0ba0d 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,16 @@ import os import csv +from mock_storage import MockCSVStorage #IMPORT def import_from_file(filename="students.csv"): students = [] try: with open(filename, 'r', newline='') as file: #'r' read file, newline='' to manage newline characters correctly in CSV files - for line in file: + reader = csv.DictReader(file) + lines = file.readlines() #read all the rows at once + + for line in lines[1:]: #skip the 1st row (headlines) cut_parts = line.strip().split(',') #delete whitespaces and create a seperator as , #in the file: first name, last name if len(cut_parts) == 2: #no attendance provided @@ -23,13 +27,18 @@ def import_from_file(filename="students.csv"): }) else: print('Something went wrong when importing the file.') return students + except FileNotFoundError: print(f"The file '{filename}' was not found.") #added if filenotfound exception #EXPORT def export_attendance(students, filename="students.csv"): + fieldnames = ["first_name", "last_name", "present"] + with open(filename, 'w', newline='') as file: #'w' write file + writer = csv.DictWriter(file, fieldnames) + writer.writeheader() for student in students: present = 'yes' if student['present'] else 'no' file.write(f"{student['first_name']},{student['last_name']},{present}\n") @@ -38,39 +47,67 @@ def export_attendance(students, filename="students.csv"): #ADDING NEW STUDENTS AND UPDATING DATABASE # function that adds new student to the list and saves it to the file -def add_student(first_name, last_name, filename="students.csv"): - file_exists = os.path.isfile(filename) +def add_student(first_name, last_name, storage): + if isinstance(storage, str): #if storage is a path to file + file_exists = os.path.isfile(storage) - with open(filename, 'a', newline='') as file: + with open(storage, 'a', newline='') as file: writer = csv.writer(file) if not file_exists: - writer.writerow(["first_name", "last_name", "present"]) + writer.writerow(["first_name", "last_name", "present"]) + + writer.writerow(["first_name", "last_name", "no"]) #present is "no" by default + + elif isinstance(storage, MockCSVStorage): #if storage is mockcsvstorage + students = storage.read() + + if any(student["first_name"] == first_name and student["last_name"] == last_name for student in students): + raise Exception("Student already exists in the database") - # "present" is false by default - writer.writerow([first_name, last_name, False]) - print(f"Student {first_name} {last_name} was added to {filename}.") + storage.append({"first_name": first_name, "last_name": last_name, "present": "no"}) + print(f"Student {first_name} {last_name} was added to {storage}.") # function that edits student list -def edit_student(old_first_name, old_last_name, new_first_name, new_last_name, filename="students.csv"): - students = [] +def edit_student(old_first_name, old_last_name, new_first_name, new_last_name, storage): updated = False - with open(filename, 'r') as file: - reader = csv.DictReader(file) - for row in reader: + if isinstance(storage, str): #if storage is a path to file + students = [] + with open(storage, 'r') as file: + reader = csv.DictReader(file) + for row in reader: if row["first_name"] == old_first_name and row["last_name"] == old_last_name: - row["first_name"] = new_first_name - row["last_name"] = new_last_name - updated = True + row["first_name"] = new_first_name + row["last_name"] = new_last_name + updated = True students.append(row) - with open(filename, 'w', newline='') as file: - writer = csv.DictWriter(file, fieldnames=["first_name", "last_name", "present"]) - writer.writeheader() - writer.writerows(students) - + with open(storage, 'w', newline='') as file: + writer = csv.DictWriter(file, fieldnames=["first_name", "last_name", "present"]) + writer.writeheader() + writer.writerows(students) + + elif isinstance(storage, MockCSVStorage): #if storage is mockcsvstorage + students = storage.read() + print(f"Before edit: {students}") + student_found = False + + for student in students: + if student["first_name"] == old_first_name and student["last_name"] == old_last_name: + student["first_name"] = new_first_name + student["last_name"] = new_last_name + student_found = True + updated = True + break #found + updated so no need to continue + + if not student_found: + raise Exception("Student does not exist in the database") + + storage.write(students) + print(f"After edit: {students}") + if updated: print(f"Student: {old_first_name} {old_last_name} has been updated to {new_first_name} {new_last_name}.") else: diff --git a/mock_storage.py b/mock_storage.py new file mode 100644 index 0000000..f7d1304 --- /dev/null +++ b/mock_storage.py @@ -0,0 +1,17 @@ +from typing import List, Dict + +class MockCSVStorage: + def __init__(self): + self.data = [] + + def write(self, rows: List[Dict[str, str]]): + self.data = rows + + def read(self) -> List[Dict[str, str]]: + return self.data + + def append(self, row: Dict[str, str]): + self.data.append(row) + +#added cause i cant import sth from main.py to test_main.py +#and then from test_main.py to main.py 'cause they're mutually dependent \ No newline at end of file diff --git a/test_main.py b/test_main.py index b7c40e8..5e2fb76 100644 --- a/test_main.py +++ b/test_main.py @@ -1,63 +1,69 @@ import os import pytest +import csv from typing import List, Dict +from mock_storage import MockCSVStorage from main import import_from_file, export_attendance, add_student, edit_student, manage_attendance -class MockCSVStorage: - def __init__(self): - self.data = [] - - def write(self, rows: List[Dict[str, str]]): - self.data = rows - - def read(self) -> List[Dict[str, str]]: - return self.data - - def append(self, row: Dict[str, str]): - self.data.append(row) +@pytest.fixture +def csv_file(): + file = "students_test.csv" + open(file, 'w').close() + yield file + os.remove(file) class TestAttendanceSystem: #test: saving to file - def test_save_to_file(self): + def test_save_to_file(self, csv_file): #Given students = [ {"first_name": "John", "last_name": "Doe", "present": True}, {"first_name": "Jane", "last_name": "Smith", "present": False}, ] - mock_storage = MockCSVStorage() #When - export_attendance(students, mock_storage) + export_attendance(students, csv_file) #Then expected_data = [ {"first_name": "John", "last_name": "Doe", "present": "yes"}, {"first_name": "Jane", "last_name": "Smith", "present": "no"}, ] - assert mock_storage.data == expected_data + + with open(csv_file, 'r') as file: + reader = csv.DictReader(file) + actual_data = [row for row in reader] + + assert actual_data == expected_data #test: loading from file - def test_load_from_file(self): + def test_load_from_file(self, csv_file): #Given - mock_storage = MockCSVStorage() - mock_storage.write([ + students = [ {"first_name": "John", "last_name": "Doe", "present": "yes"}, {"first_name": "Jane", "last_name": "Smith", "present": "no"}, - ]) + ] + + with open(csv_file, 'w', newline='') as file: + fieldnames = ["first_name", "last_name", "present"] + writer = csv.DictWriter(file, fieldnames) + writer.writeheader() + writer.writerows(students) #When - students = import_from_file(mock_storage) + students_from_file = import_from_file(csv_file) #Then expected_students = [ {"first_name": "John", "last_name": "Doe", "present": True}, {"first_name": "Jane", "last_name": "Smith", "present": False}, ] - assert students == expected_students + + assert students_from_file == expected_students #test: adding students - def test_add_students(self): + def test_add_students(self): #warunki i handling ex jesli student jest juz wiec go nie dodajemy #Given mock_storage = MockCSVStorage() mock_storage.write([ @@ -75,7 +81,7 @@ def test_add_students(self): assert mock_storage.data == expected_data #test: editing students' data - def test_edit_student(self): + def test_edit_student(self): #warunki i handling ex jesli studenta nie ma to nie mozna zedytowac #Given mock_storage = MockCSVStorage() mock_storage.write([ From 1507aca205f310e16d691efa9a955fb7acbce04b Mon Sep 17 00:00:00 2001 From: Yola Date: Wed, 4 Dec 2024 00:06:12 +0100 Subject: [PATCH 5/5] fix: removed pl comments o.o --- test_main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_main.py b/test_main.py index 5e2fb76..7cfc039 100644 --- a/test_main.py +++ b/test_main.py @@ -63,7 +63,7 @@ def test_load_from_file(self, csv_file): assert students_from_file == expected_students #test: adding students - def test_add_students(self): #warunki i handling ex jesli student jest juz wiec go nie dodajemy + def test_add_students(self): #Given mock_storage = MockCSVStorage() mock_storage.write([ @@ -81,7 +81,7 @@ def test_add_students(self): #warunki i handling ex jesli student jest juz wiec assert mock_storage.data == expected_data #test: editing students' data - def test_edit_student(self): #warunki i handling ex jesli studenta nie ma to nie mozna zedytowac + def test_edit_student(self): #Given mock_storage = MockCSVStorage() mock_storage.write([