Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ htmlcov/
data.json
*.csv
*.log

7 changes: 6 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -71,5 +74,7 @@ def main():
)
timer.start(interactive=True)

manager.dump_manager()

if __name__ == '__main__':
main()
50 changes: 49 additions & 1 deletion student_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand Down Expand Up @@ -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)
Expand All @@ -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
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in comment: aggrigate should be aggregate.

Suggested change
Protected method to aggrigate data
Protected method to aggregate data

Copilot uses AI. Check for mistakes.

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
49 changes: 45 additions & 4 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -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):
Expand Down