I'm new to this, but after learning some of this in class I decided to try my hand at creating a program to get the info from a Zybooks signature which stores the run data for your project. it takes a string in the format (MM/DD..signature..MM/DD) (It does not like when the signature does not follow this syntax)
Signature ex:
11/8.. U - - - - - - - - |2 |2 |2 |6 ..11/8
10/28.. W - - - - - |1 |10 ..10/28
11/8.. U - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |4 |4 |4 |7 |0 |0 |0 - - - - - |4 |4 |4 |7 |4 |7 |5 |8 |9 |9 |9 |9 |9 |9 |9 |12 |12 |12 |14 - - - - - - - |14 |14 |14 - - - - - |16 - - - - - - - - - - - - - |16 |13 |16 |16 |16 |16 |13 - - |17 - - |17 - - |19 |19 - - - |19 |19 |17 |17 |19 - |19 |19 |19 |17 |19 - |19 |17 |17 |19 |19 |19 |17 |19 |18 - - - |18 - - - |20 |20 - - ..11/8
It gets the data from the signature and then gives you to option to print the stats or save the signature. Well, here's the code.
from datetime import datetime
import os
months_of_year = {"1": "January", "2": "February",
"3": "March", "4": "April",
"5": "May", "6": "June",
"7": "July", "8": "August",
"9": "September", "10": "October",
"11": "November", "12": "December"}
days_of_week = {"U": "Sunday", "M": "Monday",
"T": "Tuesday", "W": "Wednesday",
"R": "Thursday", "F": "Friday",
"S": "Saturday"}
def check_save_file():
"""
checks if save.txt exists, if not creates it
"""
if not os.path.isfile('save.txt'):
file = open('save.txt', 'w')
file.close()
def save_to_file(inp):
with open('save.txt', 'a') as file:
file.write("X"*20) # Header
file.write("\n")
cur_date = datetime.now() # Date info for save (printed onto same line?)
file.write(str(cur_date.month))
file.write("/")
file.write(str(cur_date.day))
file.write("/")
file.write(str(cur_date.year))
file.write("\n")
file.write(inp) # Actual signature data (should be 2 indices from header ex: 2 then 4)
file.write("\n")
file.write("Y"*20)
file.write("\n") # Footer
def read_from_file():
"""
:return:
returns tuple where (0 = list, lines)(1 = int, save_count)(2= dic, saves)(3 = list, date)
"""
save_count = 0
saves = {}
date = []
with open('save.txt', 'r') as file:
lines = file.readlines() # puts save into list w/ lines
if len(lines) == 0: # If there are no lines, we have no save data
return "No save data"
for index, line in enumerate(lines): # Want to get index and line for every line in lines
if line[0] == "X": # if the 0th index is 'X' it is a header
save_count += 1
saves[save_count] = index # Puts save_count as the key and the index save_count points to as a value
index += 1
date.append(lines[index])
return lines, save_count, saves, date
def read_save(save_num, lines, saves):
"""
:param save_num: int
:param lines: list
:param saves: dic
return tuple where 0 = date and 1 = stored signature
:return:
"""
return lines[saves[save_num] + 1], lines[saves[save_num] + 2]
def pull_header(usr_str):
"""
Pulls the header from parameter usr_str, and returns
a tuple with 0: the header, and 1:The length of the header
"""
c = 0
header = ""
while usr_str[c] != usr_str[c + 1] or usr_str[c] != ".": # Goes until ..
header += usr_str[c]
c += 1
header += ".."
return header, len(header) # returns the header abd the length of the header
def pull_footer(usr_str):
"""
Pulls the footer from the parameter usr_str, reverses it for input into get_date, and
returns a tuple with 0: The reversed footer, and 1:The negated length of it
"""
c = -1
footer = ""
while usr_str[c] != usr_str[c - 1] or usr_str[c] != ".": # Goes backwards until ..
footer += usr_str[c]
c -= 1
footer = footer[::-1] # reverses the footer for use in get_date
footer += ".."
return footer, -len(footer) # returns the header and the |distance form the end (DEPRECIATED)|
def get_date(header):
"""
This function returns a date given a string with the format "MM/DD.."
"""
c = 0
month = ""
day = ""
suffix = ""
while header[c] != "/": # every thing before the /
month += header[c]
c += 1
c += 1
while header[c] != ".": # Everything until the .
day += header[c]
c += 1
if int(day[-1]) == 1 and int(day) != 11: # suffixes based on the number
suffix = "st"
elif int(day[-1]) == 2 and int(day) != 12:
suffix = "nd"
elif int(day[-1]) == 3 and int(day) != 13:
suffix = "rd"
elif (int(day[-1]) == 0) or (int(day[-1]) > 3) or (11 <= int(day) <= 13):
suffix = "th"
month = months_of_year[month]
date = "%s the %s%s" % (month, day, suffix)
return date
def date_div(header, footer):
"""
This functions calculates how many days the assignment took, given the header and the footer of the signature
"""
d_s = ""
m_s = ""
start = header
end = footer
start = start[:-2] # removes the suffix ".."
end = end[:-2]
start = start.split("/") # splits the date into a list at the "/" M= 0 ,D = 1
end = end.split("/")
month_div = int(end[0]) - int(start[0]) # calculate the start and end differences
day_div = int(end[1]) - int(start[1])
if day_div > 1:
d_s = "s"
if month_div > 1:
m_s = "s"
if month_div == 0:
if day_div == 0:
return "You completed it on the same day!"
elif day_div != 0:
return "You completed it in %d day%s" % (day_div, d_s)
elif month_div != 0:
if day_div == 0:
return "You completed it in %d month%s" % (month_div, m_s)
elif day_div != 0:
return "You completed it in %d month%s and %d day%s" % (month_div, m_s, day_div, d_s)
def translate_sig(usr_str, index):
"""
This functions returns a string, and an integer representing the type of run, when given
a string(usr_str) and an integer index
"""
if usr_str[index].isalpha(): # If the character at the index is Alphabetical, stands for day of the week
return days_of_week[usr_str[index]], 0
elif usr_str[index] == "-": # Stands for Ran in develop mode
return 1, 1
elif usr_str[index] == "|": # This followed by an number represents ran in submit mode, followed by the score
score = ""
index += 1
while usr_str[index] != " ": # Since each entry is separated by a space, Just read until whitespace
score += usr_str[index]
index += 1
return score, 2
else:
pass
def print_stats(dev_count, sub_count, date_dif, average, hi_score):
"""
This function prints the statistics of your Lab, given dev_count, sub_count,
date_dif, average, and hi_score
"""
format_str = "{text:40}|{num:4}" # Using fields I just learned
print("Stats")
print("-" * 20)
print(format_str.format(text="Number of times ran in Dev Mode:", num=dev_count))
print()
print(format_str.format(text="Number of times ran in Submit Mode:", num=sub_count))
print()
print(format_str.format(text="Total runs:", num=dev_count + sub_count))
print()
print(format_str.format(text="Average score:", num=average))
print()
print(format_str.format(text="Highest score:", num=hi_score))
print()
print(date_dif)
def calc_avg_score(score_lst):
"""
Calculates the average score, given an list of scores
"""
length = len(score_lst)
sc_add = 0
for score in score_lst:
sc_add += int(score)
average = sc_add / length
return round(average, 2) # returns the average rounded by two places
def save_menu(sig):
"""
:param sig:
The signature (str)
:return:
returns a signature or 1, representing no need to cont.
"""
op = ""
while op != 'q':
print("Enter 1 for save, Enter 2 for load, q for Quit.")
op = input()
print()
if op == '1':
print("Starting Save")
save_to_file(sig)
print("Finished")
print()
return 1
elif op == '2':
lines, save_count, save, save_date = read_from_file() # get variables from the returned tuple
print("Current Saves")
for key in save.keys(): # for every save in the save.txt prints attached # and date
print("Save#: {}, Save Date: {}".format(key, save_date[key - 1]))
print()
print("Enter save # to load save")
save_num = int(input())
print()
date, sig = read_save(save_num, lines, save)
print("-"*30)
print("Date = {}".format(date))
print("Signature = {}".format(sig))
print()
print("Do want to translate this signature? (y/n)")
trans_save = None
cont = None
while trans_save is None: # continue until input is y or n
trans_save = input()
if trans_save.lower() == 'y':
cont = True
elif trans_save.lower() == 'n':
cont = False
else:
print("Invalid Operation")
if cont: # returns the signature minus newline
return sig[:-1]
else:
return 1
elif op == 'q':
pass
else:
print("Invalid Operation")
def main():
"""
Main block of the problem,
"""
check_save_file()
fin = None
done = None
nvalid = True
print("Copy and paste the signature below (MM/DD..SIG..MM/DD)")
sig = input().strip() # Get the signature and strip any trailing whitespace
while nvalid: # Try to see if sig is valid by checking if it has a proper header (Assumes sig is invalid)
try:
pull_header(sig)
except: # Should catch any errors
print("Invalid Signature")
sig = input().strip()
else:
nvalid = False
while not fin:
if not done:
header = pull_header(sig) # Tuple (Header , distance fromm start)
footer = pull_footer(sig) # Tuple (Footer , distance from end)
print("Date Started: %s." % (get_date(header[0])))
print()
index = header[1] # starting point is taken after the header finishes
c = 1
sub_count = 0
dev_count = 0
score_lst = []
while sig[index] != ".": # Until the program reaches "." indicating the beginning of the footer, continue translating
op = translate_sig(sig, index)
if op is not None: # This is none if the index is whitespace
if op[-1] == 0: # For new date
print("|%s| Day - %s" % (c, op[0]))
print()
elif op[-1] == 1: # Develop Mode
print("%s |Ran in develop mode, no score" % c)
dev_count += 1
c += 1
print()
elif op[-1] == 2: # Submit Mode
print("%s |Ran in submit mode, score = %s" % (c, op[0]))
sub_count += 1
c += 1
score_lst.append(int(op[0])) # adds score into score_lst
print()
index += 1
print("Date of last run: %s." % (get_date(footer[0])))
done = True
print()
stat = ""
get_stat = None
get_saves = None
cont = None # Should only be true if another translation is called
while not fin and not cont:
print("Do you want to see further statistics(1) or save/load(2) (1, 2, q to quit)")
while stat != 'q': # If 1 is entered show stats if 2 is entered call save_menu() if q is entered exit
stat = input()
if stat.lower() == '1':
get_stat = True
stat = 'q'
elif stat.lower() == '2':
get_saves = True
stat = 'q'
elif stat.lower() == 'q':
fin = True
else:
print("Not a valid option") # print this and return to top of loop if not == y or n
print("Enter a new Option")
print()
if get_stat:
print()
average = calc_avg_score(score_lst)
time_taken = date_div(header[0], footer[0])
max_score = max(score_lst)
print_stats(dev_count=dev_count, sub_count=sub_count, date_dif=time_taken, average=average,
hi_score=max_score) # prints stats
get_stat = False
if get_saves:
print()
do_op = save_menu(sig)
if do_op != 1: # if the opcode is anything but 1 treat is as a signature and restart the program
sig = do_op
done = False
fin = None
cont = True
get_saves = False
stat = ""
if __name__ == "__main__":
main()
Now, since I'm new to this I'm pretty sure that this isn't perfect, so I'd like to know how I would be able to improve it? (It's very long as well so would there be any way for me to shorten it?) This is my first question here so I'm also pretty sure I've broken some rule or convention to posting on stack Exchange (could you let me know if I have?)so sorry about that. Thank You!
str.split()
to split strings. Eg.[header, signature, footer] = usr_str.split('..')
\$\endgroup\$arr = sig.split(' ')
,arr[0]
has the first letter and then you can loop through the rest of the array and check for lenght or first element. \$\endgroup\$read_from_file
:index += 1; date.append(lines[index])
-->date.append(lines[index+1])
. (Is this even what you want to do?) Do not change your iterator variables. \$\endgroup\$