-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAssignment07.py
More file actions
328 lines (253 loc) · 11 KB
/
Assignment07.py
File metadata and controls
328 lines (253 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# ------------------------------------------------------------------------------------------ #
# Title: Assignment07
# Desc: This assignment demonstrates using data classes
# with structured error handling
# Change Log: (Who, When, What)
# RRoot,1/1/2030,Created Script
# <Kiem Hollis>,<3/12/2025>,<Created Script>
# ------------------------------------------------------------------------------------------ #
import json
# Define the Data Constants
MENU: str = '''
---- Course Registration Program ----
Select from the following menu:
1. Register a Student for a Course.
2. Show current data.
3. Save data to a file.
4. Exit the program.
-----------------------------------------
'''
FILE_NAME: str = "Enrollments.json"
# Define the Data Variables
students: list = [] # a table of student data
menu_choice: str # Hold the choice made by the user.
# TODO Create a Person Class
class Person:
# TODO Add first_name properties to the constructor
def __init__(self, first_name: str = "", last_name: str = ""):
self.first_name = first_name
self.last_name = last_name
@property
def first_name(self):
return self.__first_name.title()
# TODO Create a getter and setter for the first_name property
@first_name.setter
def first_name(self, value: str):
if value.isalpha() or value == "": # Adding validation code
self.__first_name = value
else:
raise ValueError ("The first name should only contain alphabets.")
@property
# TODO Add last_name properties to the constructor
def last_name(self):
return self.__last_name.title()
# TODO Create a getter and setter for the last_name property
@last_name.setter
def last_name(self, value: str):
if value.isalpha() or value == "": # Adding validation code
self.__last_name = value
else:
raise ValueError("The last name should only contain alphabets.")
# TODO Create a Student class the inherits from the Person class
class Student(Person):
"""
A class representing student data.
Properties:
- first_name (str): The student's first name.
- last_name (str): The student's last name.
- gpa (float): The gpa of the student.
ChangeLog:
- RRoot, 1.1.2030: Created the class.
"""
def __init__(self, first_name: str = '', last_name: str = '', course_name: str = ''):
Person.__init__(self, first_name, last_name)
self.course_name = course_name
# TODO Override the __str__() method to return Person data
def __str__(self):
return f'{self.first_name},{self.last_name},{self.course_name}'
@property
# TODO add the getter for course_name
def course_name(self):
return self.__course_name.title()
# TODO add the setter for course_name
@course_name.setter
def course_name(self, value: str):
self.__course_name = value
# Processing --------------------------------------- #
class FileProcessor:
"""
A collection of processing layer functions that work with Json files
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created Class
"""
@staticmethod
def read_data_from_file(file_name: str):
""" This function reads data from a json file and loads it into a list of dictionary rows
then returns the list filled with student data.
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:param file_name: string data with name of file to read from
:return: list
"""
student_objects = []
try:
# Get a list of dictionary rows from the data file
file = open(file_name, "r")
json_students = json.load(file)
# Convert the list of dictionary rows into a list of Student objects
# TODO replace this line of code to convert dictionary data to Student data
for json_data in json_students:
first_name = json_data["FirstName"]
last_name = json_data["LastName"]
course_name = json_data["CourseName"]
student = Student(first_name, last_name, course_name)
student_objects.append(student)
file.close()
except Exception as e:
IO.output_error_messages(message="Error: There was a problem with reading the file.", error=e)
finally:
if file.closed == False:
file.close()
return student_objects
@staticmethod
def write_data_to_file(file_name: str, student_data: list):
""" This function writes data to a json file with data from a list of dictionary rows
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:param file_name: string data with name of file to write to
:param student_data: list of dictionary rows to be writen to the file
:return: None
"""
try:
# TODO Add code to convert Student objects into dictionaries (Done)
student_list = list()
for student in student_data:
data = {"FirstName":student.first_name,
"LastName":student.last_name,
"CourseName":student.course_name}
student_list.append(data)
file = open(file_name, "w")
json.dump(student_list, file)
file.close()
IO.output_student_and_course_names(student_data=student_data)
except Exception as e:
message = "Error: There was a problem with writing to the file.\n"
message += "Please check that the file is not open by another program."
IO.output_error_messages(message=message,error=e)
finally:
if file.closed == False:
file.close()
# Presentation --------------------------------------- #
class IO:
"""
A collection of presentation layer functions that manage user input and output
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created Class
RRoot,1.2.2030,Added menu output and input functions
RRoot,1.3.2030,Added a function to display the data
RRoot,1.4.2030,Added a function to display custom error messages
"""
@staticmethod
def output_error_messages(message: str, error: Exception = None):
""" This function displays the a custom error messages to the user
ChangeLog: (Who, When, What)
RRoot,1.3.2030,Created function
:param message: string with message data to display
:param error: Exception object with technical message to display
:return: None
"""
print(message, end="\n\n")
if error is not None:
print("-- Technical Error Message -- ")
print(error, error.__doc__, type(error), sep='\n')
@staticmethod
def output_menu(menu: str):
""" This function displays the menu of choices to the user
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:return: None
"""
print() # Adding extra space to make it look nicer.
print(menu)
print() # Adding extra space to make it look nicer.
@staticmethod
def input_menu_choice():
""" This function gets a menu choice from the user
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:return: string with the users choice
"""
choice = "0"
try:
choice = input("Enter your menu choice number: ")
if choice not in ("1","2","3","4"): # Note these are strings
raise Exception("Please, choose only 1, 2, 3, or 4")
except Exception as e:
IO.output_error_messages(e.__str__()) # Not passing e to avoid the technical message
return choice
@staticmethod
def output_student_and_course_names(student_data: list):
""" This function displays the student and course names to the user
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:param student_data: list of dictionary rows to be displayed
:return: None
"""
print("-" * 50)
for student in student_data:
# TODO Add code to access Student object data instead of dictionary data
print(student)
print("-" * 50)
@staticmethod
def input_student_data(student_data: list):
""" This function gets the student's first name and last name, with a course name from the user
ChangeLog: (Who, When, What)
RRoot,1.1.2030,Created function
:param student_data: list of dictionary rows to be filled with input data
:return: list
"""
try:
student_first_name = input("Enter the student's first name: ")
if not student_first_name.isalpha():
raise ValueError("The last name should not contain numbers.")
student_last_name = input("Enter the student's last name: ")
if not student_last_name.isalpha():
raise ValueError("The last name should not contain numbers.")
course_name = input("Please enter the name of the course: ")
# TODO Replace this code to use a Student objects instead of a dictionary objects
student = Student(student_first_name, student_last_name, course_name)
student_data.append(student)
print()
print(f"You have registered {student_first_name} {student_last_name} for {course_name}.")
except ValueError as e:
IO.output_error_messages(message="One of the values was the correct type of data!", error=e)
except Exception as e:
IO.output_error_messages(message="Error: There was a problem with your entered data.", error=e)
return student_data
# Start of main body
# When the program starts, read the file data into a list of lists (table)
# Extract the data from the file
students = FileProcessor.read_data_from_file(file_name=FILE_NAME)
# Present and Process the data
while (True):
# Present the menu of choices
IO.output_menu(menu=MENU)
menu_choice = IO.input_menu_choice()
# Input user data
if menu_choice == "1": # This will not work if it is an integer!
students = IO.input_student_data(student_data=students)
continue
# Present the current data
elif menu_choice == "2":
IO.output_student_and_course_names(students)
continue
# Save the data to a file
elif menu_choice == "3":
FileProcessor.write_data_to_file(file_name=FILE_NAME, student_data=students)
continue
# Stop the loop
elif menu_choice == "4":
break # out of the loop
else:
print("Please only choose option 1, 2, or 3")
print("Program Ended")