· 4 years ago · Aug 15, 2021, 04:58 AM
1# ------------------------------------------#
2# Title: CDInventory.py
3# Desc: Working with classes and functions.
4# Change Log: (Who, When, What)
5# Charles Hodges(hodges11@uw.edu), 2021-Aug-15, Created File
6# ------------------------------------------#
7
8
9# Variables
10dic_row = {} # list of data row
11lst_input_options = ['l', 'a', 'i', 'd', 's', 'x']
12lst_tbl = [] # list of lists to hold data
13obj_file = None # file object
14
15# Strings
16str_cancelling_reload = (
17 'Canceling... Inventory data NOT reloaded. '
18 'Press [ENTER] to continue to the menu. \n'
19 )
20str_cd_removed = 'The CD was removed.'
21str_choice = '' # User input
22str_confirm_reload = (
23 'Type \'yes\' to continue and reload from the file. '
24 'Otherwise, the reload will be canceled. --> '
25 )
26str_file_name = 'CDInventory.txt' # The data storage file
27str_footer = '======================================'
28str_general_error = '!General Error!'
29str_header = '\n======= The Current Inventory: ======='
30str_inventory_not_saved = (
31 'The inventory was NOT saved to file.'
32 'Press [ENTER] to return to the menu.'
33 )
34str_menu = (
35 '\n'
36 'MENU\n\n'
37 '[l] load Inventory from file\n'
38 '[a] Add CD\n'
39 '[i] Display Current Inventory\n'
40 '[d] Delete CD from Inventory\n'
41 '[s] Save Inventory to file\n'
42 '[x] Exit\n'
43 )
44str_not_find_cd = 'Could not find this CD!'
45str_reloading = 'reloading...'
46str_save_inventory = 'Save this inventory to file? [y/n] '
47str_sub_header = 'ID\tCD Title \t(by: Artist)\n'
48str_what_artist = 'What is the Artist\'s name? '
49str_what_id = 'Enter ID: '
50str_what_title = 'What is the CD\'s title? '
51str_which_delete = 'Which CD would you like to delete? Please use ID: '
52str_which_operation = (
53 'Which operation would you like to perform?'
54 '[l, a, i, d, s or x]: '
55 )
56str_warning = (
57 'WARNING: If you continue, all unsaved data will be lost and the '
58 'Inventory will be re-loaded from the file.'
59 )
60
61
62# -- PROCESSING -- #
63class DataProcessor:
64 """Processing the data in the table, before file interaction"""
65
66 @staticmethod
67 def add_cd(int_id_input, str_title_input, str_artist_input):
68 """Function to manage data ingestion from User input of CD info.
69
70 Accepts the User input of new CD information, and creates a dictionary
71 object, which is appended to the list table which makes up the
72 Inventory.
73
74 Args:
75 str_id_input (int):
76 str_title_input (string):
77 str_artist_input (string):
78
79 Returns:
80 None.
81 """
82 dic_row = {
83 'ID': int_id_input,
84 'Title': str_title_input.title(),
85 'Artist': str_artist_input.title()
86 }
87 lst_tbl.append(dic_row)
88 IO.show_inventory()
89
90 @staticmethod
91 def delete_cd():
92 """Function to identify and then delete a CD from the Inventory.
93
94 When the User selects a CD to delet, by ID, that CD is deleted from the
95 Inventory.
96
97 Args:
98 None.
99
100 Returns:
101 None.
102 """
103 # Display Inventory to user
104 IO.show_inventory()
105 # Ask user which ID to remove
106 int_id_del = int(input(str_which_delete).strip())
107 # Search thru table and delete CD
108 int_row_nr = -1
109 bln_cd_removed = False
110 for row in lst_tbl:
111 int_row_nr += 1
112 if row['ID'] == int_id_del:
113 del lst_tbl[int_row_nr]
114 bln_cd_removed = True
115 break
116 if bln_cd_removed:
117 print(str_cd_removed)
118 else:
119 print(str_not_find_cd)
120 # Display Inventory to user again
121 IO.show_inventory()
122
123
124class FileProcessor:
125 """Processing the data to and from text file"""
126
127 @staticmethod
128 def read_file():
129 """Function to manage data ingestion from file to a list of
130 dictionaries.
131
132 Reads the data from file identified by file_name into a 2D table
133 (list of dicts) table one line in the file represents one dictionary
134 row in table.
135
136 Args:
137 None.
138
139 Returns:
140 None.
141 """
142 # This code clears existing data, and loads data from file
143 lst_tbl.clear()
144 with open(str_file_name, 'r') as obj_file:
145 for line in obj_file:
146 data = line.strip().split(',')
147 dic_row = {
148 'ID': int(data[0]),
149 'Title': data[1],
150 'Artist': data[2]
151 }
152 lst_tbl.append(dic_row)
153
154 @staticmethod
155 def load_file():
156 """Function to manage data ingestion from file to a list of
157 dictionaries, when initiated by the User, from the menu.
158
159 Reads the data from file identified by file_name into a 2D table
160 (list of dicts) table one line in the file represents one dictionary
161 row in table.
162
163 Args:
164 None.
165
166 Returns:
167 None.
168 """
169 print(str_warning)
170 str_yes_no = input(str_confirm_reload)
171 if str_yes_no.lower() == 'yes':
172 print(str_reloading)
173 FileProcessor.read_file()
174 IO.show_inventory()
175 else:
176 input(str_cancelling_reload)
177
178 @staticmethod
179 def save_file():
180 """Function to save a file.
181
182 When the User decides to write the current Inventory to a file, after
183 any edits, this function is used.
184
185 Args:
186 None.
187
188 Returns:
189 None.
190 """
191 # Display current inventory and ask user for confirmation to save
192 IO.show_inventory()
193 str_yes_no = input(str_save_inventory).strip().lower()
194 # Process choice
195 if str_yes_no == 'y':
196 # Save data
197 obj_file = open(str_file_name, 'w')
198 for row in lst_tbl:
199 lst_values = list(row.values())
200 lst_values[0] = str(lst_values[0])
201 obj_file.write(','.join(lst_values) + '\n')
202 obj_file.close()
203 else:
204 input(str_inventory_not_saved)
205
206 @staticmethod
207 def create_file():
208 """Function to create a file if there is none, already.
209
210 Since Write or Append are the only two ways to open/create a file
211 if it has not yet been created, we use Append, as it will not
212 overwrite any data, if it has already been created. This function
213 creates and closes, or merely opens and closes the text file.
214
215 Args:
216 None.
217
218 Returns:
219 None.
220 """
221 obj_file = open(str_file_name, 'a')
222 obj_file.close()
223
224
225# -- PRESENTATION (Input/Output) -- #
226
227class IO:
228 """Handling Input / Output"""
229
230 @staticmethod
231 def print_menu():
232 """Displays a menu of choices to the user
233
234 Args:
235 None.
236
237 Returns:
238 None.
239 """
240 print(str_menu)
241
242 @staticmethod
243 def menu_choice():
244 """Gets user input for menu selection
245
246 Args:
247 None.
248
249 Returns:
250 choice (string): a lower case string of the users input out of the
251 choices: l, a, i, d, s or x
252
253 """
254 choice = ' '
255 while choice not in lst_input_options:
256 choice = input(str_which_operation).lower().strip()
257 print() # Add extra space for layout
258 return choice
259
260 @staticmethod
261 def show_inventory():
262 """Displays current inventory table
263
264 Args:
265 table (list of dict): 2D data structure (list of dicts) that holds
266 the data during runtime.
267
268 Returns:
269 None.
270
271 """
272 print(str_header)
273 print(str_sub_header)
274 for row in lst_tbl:
275 print('{}\t{} \t\t(by:{})'.format(*row.values()))
276 print(str_footer)
277
278 @staticmethod
279 def input_cd_info():
280 """Requests and receives CD information from the User.
281
282 Args:
283 None.
284
285 Returns:
286 int_id_input(int): ID Number
287 str_title_input(string): CD Title
288 str_artist_input(string): Artist Name
289 """
290 int_id_input = int(input(str_what_id).strip())
291 str_title_input = input(str_what_title).strip()
292 str_artist_input = input(str_what_artist).strip()
293 return int_id_input, str_title_input, str_artist_input
294
295
296# When the program starts, read in the currently saved Inventory, if it exists.
297# Otherwise, create the inventory file.
298try:
299 FileProcessor.read_file()
300except FileNotFoundError:
301 FileProcessor.create_file()
302
303
304# Start main loop
305while True:
306 # Display Menu to user, and get choice
307 IO.print_menu()
308 str_choice = IO.menu_choice()
309
310 # Exit
311 if str_choice == 'x':
312 break
313
314 # Load Inventory.
315 if str_choice == 'l':
316 FileProcessor.load_file()
317 continue # start loop back at top.
318
319 # Add a CD.
320 elif str_choice == 'a':
321 # Ask user for new ID, CD Title and Artist,
322 int_id_input, str_title_input, str_artist_input = IO.input_cd_info()
323 # Add CD information to the Inventory
324 DataProcessor.add_cd(int_id_input, str_title_input, str_artist_input)
325 continue # start loop back at top.
326
327 # Display current inventory.
328 elif str_choice == 'i':
329 IO.show_inventory()
330 continue # start loop back at top.
331
332 # Delete a CD.
333 elif str_choice == 'd':
334 DataProcessor.delete_cd()
335 continue # start loop back at top.
336
337 # Save inventory to file.
338 elif str_choice == 's':
339 FileProcessor.save_file()
340 continue # start loop back at top.
341
342 # A catch-all, which should not be possible, as user choice gets
343 # vetted in IO, but to be safe.
344 else:
345 print(str_general_error)
346