Skip to content

Instantly share code, notes, and snippets.

@Lanse505
Created January 26, 2022 13:57
Show Gist options
  • Save Lanse505/3af9900d70cc8c27e87b61bc4b5a8853 to your computer and use it in GitHub Desktop.
Save Lanse505/3af9900d70cc8c27e87b61bc4b5a8853 to your computer and use it in GitHub Desktop.
import os.path
import json
import util
# TODO: Code Review with Joakim
if __name__ == '__main__': # Checks if the code is being invoked directly and not as an import
# Main Menu as a printable multi-line string
menu = """
#####################
# 1 - Add Person #
# 2 - Remove Person #
# 3 - Save File #
# 4 - View Person #
# 5 - Exit #
#####################
"""
field_names = ['username', 'first_name', 'last_name', 'email'] # Default Field Names
field_names_sv = ['användarnamn', 'förnamn', 'efternamn', 'epost'] # Default Swedish Field Names
# Get if the user wants to load from the target CSV or the cached JSON
get_from_csv = input("Do you want to load from a CSV or from the cached JSON? [CSV, JSON]: ")
csv_dict = {} # Setups an empty dict to use
if get_from_csv.upper() == "CSV": # Checks if the user wanted to load from the CSV
util.populate_from_csv(csv_dict, field_names) # Populates the csv_dict from the csv
if get_from_csv.upper() == "JSON" and os.path.exists('./temp.json'): # Checks if the JSON file already exists
with open('./temp.json', "r+", encoding='utf-8-sig') as temp: # Opens the existing temp.json
csv_dict = json.load(temp) # Parses the JSON into the csv_dict as a dict object
with open('./temp.json', "w+", encoding='utf-8-sig') as temp: # Opens a new file
json.dump(csv_dict, temp, indent=True, ensure_ascii=False, cls=util.PersonEncoder) # Dumps the dict to Json
is_first_run = True # Sets the "first run" value so it only prints the json to log once
while True: # Continuous Loop that's broken out of later
option = -1 # Checks if the current target is the menu or a sub-program
if is_first_run: # If it is the first run
with open('./temp.json', "r+", encoding='utf-8-sig') as temp: # Opens up the temp.json
print(temp.read()) # Prints the entire file contents to log
print() # Print empty line
is_first_run = False # Set that this is no longer the first run of the main menu
while option == -1: # If the option value is -1 then run the main menu cycle
print(menu) # Prints the Main Menu multi-line string
valueIn = input("Select your preferred option: ") # Asks for the preferred program option
# Checks if the input is a parsable integer and is within 1-5
while not util.is_integer(valueIn) or not util.is_within(int(valueIn), 1, 5):
print("Error: Invalid Input, Please enter a number between 1-4") # Prints error message if not
valueIn = input("Select your preferred option: ") # Asks for new input and loops
option = int(valueIn) # Parses the validated input to the option value
if option == 1: # option 1: Add Person
option = util.addPerson(csv_dict) # Calls addPerson in util
input("Press Any Key to Return to Menu") # Input to not instantly clear the message line
elif option == 2: # option 2: Remove Person
option = util.removePerson(False, csv_dict) # Calls removePerson with a default "False" for while-loop
input("Press Any Key to Return to Menu") # Input to not instantly clear the message line
elif option == 3: # option 3: Flush JSON to CSV
print("Flushing JSON to CSV") # Info Message
option = util.flush_from_json_to_csv(field_names_sv) # Calls flush_to_csv in util
input("Press Any Key to Return to Menu") # Input to not instantly clear the message line
elif option == 4: # option 4: runs the get_user_data function in util
option = util.get_user_data(csv_dict) # Calls the get_user_data function in util
input("Press Any Key to Return to Menu") # Input to not instantly clear the message line
elif option == 5: # option 5: exit
break # Breaks out of the while loop
print() # Prints an empty line
import csv
import json
import re
from json import JSONEncoder
class Person:
def __init__(self, info: dict):
self.info = info
def get_value(self, identifier: str):
return self.info[identifier]
def get_info(self):
return self.info
class PersonEncoder(JSONEncoder):
def default(self, person):
return person.info
# Opens up the csv as a file and returns both the file itself and the csv-reader for the file
def open_csv(path: str, mode: str):
file = open(path, mode, encoding='utf-8-sig') # Opens up the file using utf-8-sig encoding to support all chars
reader = csv.reader(file, delimiter=';') # Opens up the CSV using a csv reader using ';' as a delimiter
return reader, file # Returns a tuple of the csv reader and the original file
# TODO: Code Review with Joakim
# TODO: Rewrite to support any input location
# Populates the csv_dict object using the clean csv file
def populate_from_csv(csv_dict, field_names):
csv_file, file = open_csv("./clean.csv", "r+") # Opens the csv
for i, row in enumerate(csv_file): # Iterate over the rows in the csv
if i == 0: # If the row is the header row then continue
continue # Skip if it's the head column
person_dict: dict = {} # Create an empty dictionary for the user
for j, name in enumerate(field_names): # Loops over the field_names
row_value = row[j] # Get the value from the CSV
if row_value is not None: # If the value isn't empty then add it and its value to the new dict
person_dict[name] = row[j] # Set the fields of the "person_dict"
continue # Continue as to not add double values to the dict
person_dict[name] = None # If the value was empty then add an empty field value to the dict
person: Person = Person(person_dict) # Creates a new Person object holding the person_dict as information
csv_dict[row[0]] = person # Set the "person" to the "csv_dict" with the username as key.
file.close() # Closes the file reference
# TODO: Code Review with Joakim
# Flushes the code from the stored object map to JSON and the csv
def flush_from_json_to_csv(field_names_sv):
with open('./temp.json', "r+", encoding='utf-8-sig') as saved: # Opens the file again in read/write mode
json_dict = json.load(saved) # Loads the Json into csv_dict overriding it with the contents of the JSON
with open('./labb2_personer_vt22.csv', 'w', newline='') as file: # Opens the csv file itself
writer = csv.writer(file, delimiter=';') # Grabs the writer for the file
writer.writerow(field_names_sv) # Writes the header using swedish names
for (key, value) in enumerate(json_dict.items()): # Loops over the top-level keys and values
nk, nv = value # Grabs the value which is a key/value store
# Writes a row consisting of the internal values of 'username', 'first_name', 'last_name' and 'email'
writer.writerow([nv['username'], nv['first_name'],
nv['last_name'], nv['email']])
return -1 # Returns -1 to reset the menu to the Main Menu
# TODO: Code Review with Joakim
# Flushes the csv_dict contents to JSON
def flush_to_json(csv_dict):
with open('./temp.json', "w", encoding='utf-8-sig'): # Clear the json file
pass # Just close the file, so that we clear it and then close it
with open('./temp.json', "w", encoding='utf-8-sig') as flush: # Opens the file again but in read/write mode
json.dump(csv_dict, flush, indent=True, ensure_ascii=False, cls=PersonEncoder) # Dumps the csv_dict contents to the json
# TODO: Code Review with Joakim
# Adds a new person to the csv dictionary, also applies regex input validation.
def addPerson(csv_dict):
username = input("Please enter a username for the person you want to add: ") # Grab the username input
# Match it against our regex and check so it doesn't exist already
while not is_regex_compliant(username, r"[hv]\d{2}[a-z]{5}") and username not in csv_dict:
print("Invalid input: Please provide a valid username matching the following regex format:") # Print Error
print(r"[[hv]\d{2}[a-z]{5}] - [Example: h00andan]") # Print Example
username = input("Please enter a username for the person you want to add: ") # Ask for new Input
first_name = input("Please enter the first name of the user you want to add: ") # Ask for First Name
while first_name is None or first_name == "": # Validate that there was a valid input and the input wasn't empty
print("Error: Invalid Input - 'First Name' was either of type None or Empty") # Print Error
first_name = input("Please enter the first name of the user you want to add: ") # Ask for new input
last_name = input("Please enter the last name of the user you want to add: ") # Ask for Last Name
while last_name is None or last_name == "": # Validate that there was a valid input and the input wasn't empty
print("Error: Invalid Input - 'First Name' was either of type None or Empty") # Print Error
last_name = input("Please enter the last name of the user you want to add: ") # Ask for new
# Generate the user-dict based on the input information
user_dict = {'username': username, 'first_name': first_name,
'last_name': last_name, 'email': username + "@du.se"
}
csv_dict[username] = user_dict # Add the user-dict to the internal csv-dict
flush_to_json(csv_dict) # Flush the newly added content to JSON
return -1 # Return -1 to return to main menu
# TODO: Code Review with Joakim
# Removes a person from the list, validates the input username by checking if the dict contains the name.
def removePerson(removed, csv_dict):
while not removed: # Check so an removal hasn't been done
# Get the input username
username = input("Please enter the username of the User you want to delete [exit = return to main menu]: ")
if username == "exit": # Check if it's 'exit' in which case return to main menu
return -1 # Return to main menu
if username in csv_dict: # Check so the username key exists in the internal csv_dict
csv_dict.pop(username) # Remove the user from the internal dict
flush_to_json(csv_dict) # Flush the updated dict to JSON
print(f"Removed user: {username}") # Print a message stating they got removed
removed = True # Set removed to True
else:
print(f"Error: User with Username: {username} , Didn't exist!") # If the user didn't exist, print error
# TODO: Code Review with Joakim
# Gets the userdata for the provided username, uses both regex validation and existence validation to validate the input
def get_user_data(csv_data):
username = input("Please enter the username of the user you want to view: ") # Gather username input
# Check so the input exists and is regex compliant
while not is_regex_compliant(username, r"[hv]\d{2}[a-z]{5}") or username not in csv_data:
print(r"Invalid Error: Please input a regex compliant username [[hv]\d{2}[a-z]{5}]: ") # Print Error
username = input("Please enter the username of the user you want to view: ") # Ask for new input
data = csv_data[username] # Get the user-data dict from the internal csv-dict by key
first_name = data['first_name'] # Get the first_name
last_name = data['last_name'] # Get the last_name
email = data['email'] # Get the Email
print(f"Printing data for user: {username}") # Information Print
print(f"Username: {username}") # Print Username
print(f"First Name: {first_name}") # Print First Name
print(f"Last Name: {last_name}") # print Last Name
print(f"Email: {email}") # print Email
return -1 # Return -1 to return to main menu
# Checks if the provided input can be converted to an integer
def is_integer(num):
try: # Attempt to execute
int(num) # Attempt parse to int
return True # If success True
except ValueError:
return False # If failure False
# Checks if the provided string is valid against the provided regex pattern
def is_regex_compliant(testable: str, pattern: str):
if re.match(pattern, testable): # Attempt to match string against provide regex pattern
return True # If it passes Regex then return True
return False # If it doesn't pass Regex then return False
# Checks if the provided value is less than the maximum specified value but greater than the minimum specified value.
def is_within(value, minimum_inclusive, maximum_inclusive):
# Check so that the value is:
# Larger than minimum_inclusive
# Smaller than maximum_inclusive
if minimum_inclusive <= value <= maximum_inclusive:
return True # Returns true if it's between min_inclusive and max_inclusive
return False # Returns false if it's outside the bounds of min_inclusive and max_inclusive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment