From 81eecfad9343768dd4f3050b977e391064eda700 Mon Sep 17 00:00:00 2001 From: darshan2006-op Date: Thu, 11 Dec 2025 16:50:05 +0530 Subject: [PATCH] added data persistence --- .gitignore | 1 + main.py | 7 ++++++- student_manager.py | 50 +++++++++++++++++++++++++++++++++++++++++++++- utils.py | 49 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 101 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 414bdd2..0d7ef7e 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ htmlcov/ data.json *.csv *.log + diff --git a/main.py b/main.py index b183d2f..f91f902 100644 --- a/main.py +++ b/main.py @@ -24,7 +24,10 @@ def main(): args = parser.parse_args() - manager = StudentManager() + manager, status = StudentManager.load_manager() + + if status == 2: + print("Error: Corrupted persistence file detected and removed. Starting with fresh data.") if args.command == 'add-assignment': if not args.value or not args.deadline: @@ -71,5 +74,7 @@ def main(): ) timer.start(interactive=True) + manager.dump_manager() + if __name__ == '__main__': main() \ No newline at end of file diff --git a/student_manager.py b/student_manager.py index 452ed31..45c3ffb 100644 --- a/student_manager.py +++ b/student_manager.py @@ -3,10 +3,11 @@ """ from datetime import datetime -from utils import calculate_days_remaining, validate_grade, get_priority_level +from utils import calculate_days_remaining, validate_grade, get_priority_level, save_to_json, load_from_json class StudentManager: + PERSISTENCE_FILE_NAME = "data.json" def __init__(self): self.assignments = [] self.grades = [] @@ -73,6 +74,28 @@ def get_upcoming_deadlines(self, days=7): return sorted(upcoming, key=lambda x: x['days_remaining']) + def dump_manager(self): + """Dumps data into json file for persistence""" + data = self._ser_object() + save_to_json(data, self.PERSISTENCE_FILE_NAME) + + @classmethod + def load_manager(cls): + """ + Load data into the program from persistence file + + Returns: + int: status of loading data from file + 0 - success + 1 - persistence file not found + 2 - corrupted persistence file + """ + res = load_from_json(cls.PERSISTENCE_FILE_NAME) + if res["status"] != 0: + status = res.get("status") + return cls(), status + return cls._deser_object(res.get("data")), 0 + def get_statistics(self): """Get student statistics""" total_assignments = len(self.assignments) @@ -84,3 +107,28 @@ def get_statistics(self): 'pending': total_assignments - completed, 'gpa': self.calculate_gpa() } + + def _ser_object(self): + """ + Protected method to aggrigate data + + Returns: + dict: final data + """ + return { + "Assignments": self.assignments, + "Grades": self.grades + } + + @classmethod + def _deser_object(cls, data): + """ + Protected method to load aggregate data + + Args: + data: dict - data to be deserialized + """ + manager = cls() + manager.assignments = data.get("Assignments", []) + manager.grades = data.get("Grades", []) + return manager \ No newline at end of file diff --git a/utils.py b/utils.py index 8197631..b8e8e53 100644 --- a/utils.py +++ b/utils.py @@ -4,7 +4,35 @@ from datetime import datetime import json +import os +class DateTimeEncoder(json.JSONEncoder): + def default(self, obj): + """Handles special case of date serialization""" + if isinstance(obj, datetime): + return obj.isoformat() + return super().default(obj) + +class DateTimeDecoder(json.JSONDecoder): + def __init__(self, *args, **kwargs): + super().__init__(object_hook=self.object_hook, *args, **kwargs) + + def object_hook(self, obj): + """Handles special case of date deserialization, including nested structures""" + return self._decode_dates(obj) + + def _decode_dates(self, value): + if isinstance(value, dict): + return {k: self._decode_dates(v) for k, v in value.items()} + elif isinstance(value, list): + return [self._decode_dates(item) for item in value] + elif isinstance(value, str): + try: + return datetime.fromisoformat(value) + except ValueError: + return value + else: + return value def format_date(date_string): """Convert date string to datetime object""" @@ -33,16 +61,29 @@ def validate_grade(grade): def save_to_json(data, filename): """Save data to JSON file""" with open(filename, 'w') as f: - json.dump(data, f, indent=4) - + # NOTE: Edit DateTimeEncoder accordingly if StudentManager contains other non serializable types + json.dump(data, f, indent=4, cls=DateTimeEncoder) def load_from_json(filename): """Load data from JSON file""" try: with open(filename, 'r') as f: - return json.load(f) + # NOTE: Edit DateTimeDecoder accordingly if StudentManager contains other non serializable types + return { + "data": json.load(f, cls=DateTimeDecoder), + "status": 0 + } except FileNotFoundError: - return {} + return { + "data" : None, + "status": 1 + } + except json.JSONDecodeError as e: + os.remove(filename) + return { + "data" : None, + "status": 2 + } def get_priority_level(days_remaining):