Cleaned up and expanded commenting

This commit is contained in:
murraydr 2023-10-11 14:39:58 -04:00
parent fa27a9e403
commit 07ffe59aaf
8 changed files with 182 additions and 105 deletions

View File

@ -6,10 +6,9 @@ import helperFunctions as HF
import scheduledPayrollWindow as PWin
#TODO workflow with git
#TODO demonstrate workflow with git
#TODO search section/date (show curr and orig people assigned)
#TODO code comment
#TODO document how to setup for next semester (staff and section changes)
#TODO document how to get through the cert expiration every week
@ -49,7 +48,6 @@ stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
#remove leading/trailing whitespace
secD = secD.apply(lambda x: x.str.strip())
stfD = stfD.apply(lambda x: x.str.strip())
#stfD = stfD.apply(lambda x: x.str.lower())
#rewrite to file (each GUI function reads/writes on its own so that the state on disk matches what the user expects while the program is running)
secD.to_csv(sectionDatabaseFilename,index=False,index_label=False)
@ -81,9 +79,6 @@ df2 = pd.read_csv(sectionDatabaseFilename[:-4]+"_Orig.csv",dtype=str)
df1.fillna(value="",inplace=True)
df2.fillna(value="",inplace=True)
#diff=df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]
#diff=pd.concat([df1,df2]).drop_duplicates(keep=False)
diff = df1.merge(df2, indicator=True, how='outer')
diff=diff.loc[diff['_merge'] != 'both']
@ -91,8 +86,4 @@ print("\n"+"-"*200+"\nTo-Date difference from default shifts:")
print(diff)
print("-"*200)
#PWin.scheduledPayWin(sectionDatabaseFilename,RegHoursFilename,defaultFont)
#HF.sendEmail("murraydr","CSE102 Substitution","This is a test message")

View File

@ -20,7 +20,8 @@ sectionDatabaseFilename="sectionsDatabase.csv"
staffDatabaseFilename="staffDatabase.csv"
#Converts full names to nedIDs and then back again based on the staff database
#Converts full name to nedID based on the staff database
#Uses a mildly 'fuzzy' search where the searched-for string must only occur *somewhere* in the actual full name
def nameToID(searchName,getAllMatches=False):
if searchName == "":
return ""
@ -34,6 +35,8 @@ def nameToID(searchName,getAllMatches=False):
return ret
else:
return ret[0]
#Converts netID to full name for GUI human-readability
def IDToName(searchID):
if searchID=="":
return ""
@ -43,6 +46,7 @@ def IDToName(searchID):
name+="*"#append asterisk to name
return name
#Tests if two requests overlap i.e. have at least one day/time combo in common
def isRequestOverlap(req1,req2):
print(req1)
print(req2)
@ -59,37 +63,49 @@ def isRequestOverlap(req1,req2):
overlap=True
return overlap
#Get the time that a section takes place during
def getTimeFromSection(section):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
return(secD.loc[secD['Section']==section]['Time'].iloc[0])
#Get the location that a section takes place in
def getLocationFromSection(section):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
return(secD.loc[secD['Section']==section]['Location'].iloc[0])
def isAssigned(netID,period,date):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
ret= False
filt = (secD.loc[(secD['Section']==period) & (secD['Date']==date)].apply(lambda r: r.astype('string').str.contains(netID).any(), axis=1))
if filt.any(): #if the requestor is not assigned to that section
ret=True
return ret
def reassign(date,period,oldID,newID):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
secD.loc[(secD['Section']==period) & (secD['Date']==date)]=secD.loc[(secD['Section']==period) & (secD['Date']==date)].replace(oldID,newID)
secD.to_csv(sectionDatabaseFilename,index=False,index_label=False)
def getSectionTitles():
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
sections=list(secD.drop_duplicates(subset=["Section"])["Section"].values)
return sections
#Get all dates that a section takes place in
def getDatesFromSection(sec):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
dates = list(secD.loc[secD["Section"]==sec]["Date"])
return dates
#Tests if a netID is currently assigned to a given section on a given date
def isAssigned(netID,period,date):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
ret= False
filt = (secD.loc[(secD['Section']==period) & (secD['Date']==date)].apply(lambda r: r.astype('string').str.contains(netID).any(), axis=1))
if filt.any(): #if the requestor is among the names in that section-date
ret=True
return ret
#Overwrite a netID in one row of the section database with another
def reassign(date,period,oldID,newID):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
secD.loc[(secD['Section']==period) & (secD['Date']==date)]=secD.loc[(secD['Section']==period) & (secD['Date']==date)].replace(oldID,newID)
secD.to_csv(sectionDatabaseFilename,index=False,index_label=False)
#Get the strings of all section titles currently in the database (dropping duplicates)
def getAllSectionTitles():
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
sections=list(secD.drop_duplicates(subset=["Section"])["Section"].values)
return sections
#Get the strings of all names currently in the staff database
def getAllNames():
stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
return list(stfD["Name"].values)
#Add or subtract from the count of a given person's sub request history in one category (e.g. rejections)
def incrementSubCount(netID,category,amount=1):
stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
if category==0 or category=="APP":
@ -108,8 +124,8 @@ def incrementSubCount(netID,category,amount=1):
stfD.loc[stfD['NetID']==netID,columnName]=str(float(stfD.loc[stfD['NetID']==netID,columnName])+amount)
stfD.to_csv(staffDatabaseFilename,index=False,index_label=False)
#Get an array of the counts of each category of sub history (Approved, Accepted, Rejected, Cancelled, Fulfilled) for a given person
def getSubCount(netID):
stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
APP = stfD.loc[(stfD["NetID"]==netID)]["Approved Substitutions"].values[0]
ACC = stfD.loc[(stfD["NetID"]==netID)]["Accepted Substitutions"].values[0]
@ -118,6 +134,9 @@ def getSubCount(netID):
FUL = stfD.loc[(stfD["NetID"]==netID)]["Fulfilled Substitutions"].values[0]
return (APP,ACC,REJ,CAN,FUL)
#Get a "high score" list or a "low score" list of names/scores based on a numeric string showing which categories to count
#0: Approved, 1: Accepted, 2: Rejected, 3: Cancelled, 4: Fulfilled
#E.g. "12" gives the ULAs with the most total accepted and rejected requests thusfar.
def getTopSubs(categoryStr,fewest=False,num=1):
if categoryStr=="":
return ""
@ -142,7 +161,7 @@ def getTopSubs(categoryStr,fewest=False,num=1):
stfD[header]+=(stfD[name].astype(float))
return stfD.sort_values(header,axis=0,ascending = fewest).head(num)[["Name",header]].to_string(index=False)
#Test if a given single shift exists, i.e. if that section meets on that date.
def shiftExists(period,date):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
try:
@ -152,6 +171,7 @@ def shiftExists(period,date):
return False
return True
#Get a list of all names currently assigned to a given section on a given date
def getAllNamesFromSection(periods,dates):
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
others=[]
@ -183,7 +203,7 @@ def dateBeforeOrEqual(date1, date2):
else:
return False
#create string files for easy pasting into google forms
#create string files for easy pasting into Google forms
def createStrings():
stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
secD = pd.read_csv(sectionDatabaseFilename,dtype=str,index_col=False)
@ -208,10 +228,12 @@ def createStrings():
seen=set()
f.write('\n'.join([x for x in vals if not (x in seen or seen.add(x))]))
#Create the content of form-letter emails to be sent based on the request details and the chosen approval status
def generateEmails(requestList):
emails=[]
for req in requestList:
#Unpack the request into named variables (for readability in the string formatting below)
timestamp = req[0]
requestor = IDToName(req[1]).strip("*")
section = req[2]
@ -221,28 +243,27 @@ def generateEmails(requestList):
status = req[6]
statusReason = req[7]
#Check if a replacement was specified at all
if replacement!="":
replaced=True
else:
replaced=False
#Unpack sections and dates to always be a list
if ";" in section:
sections = section.split(";")
else:
sections=[section]
if ";" in date:
dates = date.split(";")
else:
dates=[date]
times=[]
locations=[]
if status=="APP" or status=="ACC": #For now, don't tell the ULAs that their acceptance was begrudging. We can change this later if desired.
if status=="APP" or status=="ACC": #For now, don't tell the ULAs that their acceptance was begrudging so they don't try and game the system by lying about the real reason. We can change this later if desired.
status="approved"
changed=True
changed=True #Our database entries were changed
for s in sections:
times.append(getTimeFromSection(s))
locations.append(getLocationFromSection(s))
@ -251,7 +272,7 @@ def generateEmails(requestList):
status="rejected"
else:
status="cancelled"
changed=False
changed=False #Our database entries were changed
for s in sections:
try:
times.append(getTimeFromSection(s))
@ -329,6 +350,7 @@ def generateEmails(requestList):
emails.append([recipient,subject,message])
#Send emails (or print to terminal if debugging and actuallySend == False)
sendEmails(emails,actuallySend=False)
@ -336,7 +358,12 @@ def generateEmails(requestList):
#duumyParam is to make really sure that the author of the code that calls this functions really intends to actually send emails (by forcing them to use the keyword)
def sendEmails(emails,dummyParam=False,actuallySend=False):
def sendEmails(emails,dummyParam="DUMMY",actuallySend=False):
#Catch incorrect use of this method by someone who doesn't understand the args.
if dummyParam!="DUMMY":
print("DON'T USE THE helperFunctions.sendEmails() method unless you know what you're doing! This can send actual emails to people's inbox!")
return
if actuallySend:
#https://www.geeksforgeeks.org/send-mail-gmail-account-using-python/
@ -368,8 +395,13 @@ def sendEmails(emails,dummyParam=False,actuallySend=False):
print("\n"+"-"*80+"\nEmail would be sent to "+email[0]+":\nSubject: "+email[1]+"\nMessage:\n"+email[2]+"\n"+"-"*80)
#Get the raw data from the Google Forms API
#FYI: The first part of this code dealing with connection/authentication was not written by me and is largely a black box.
#This function only adds requests that are more recent than the last time it was run, so that the original data in the Google cloud never needs to be cleared
def getForms(subRequestsFilename):
#BLACKBOX AUTHENTICATION MAGIC
#--------------------------------------------------------------------------
#Requires some installation/setup https://developers.google.com/forms/api/quickstart/python
SCOPES = ["https://www.googleapis.com/auth/forms.responses.readonly"]
@ -401,21 +433,25 @@ def getForms(subRequestsFilename):
form_id = '1x-8fkuMAcQlTl36SdsbCG0tfClKAcvNshnV8L_Hl904'
result = service.forms().responses().list(formId=form_id).execute()
#END OF BLACKBOX AUTHENTICATION MAGIC
#--------------------------------------------------------------------------
subs = pd.read_csv(subRequestsFilename,dtype=str)
#Check when the last time the data was downloaded
with open("lastUpdatedToken.txt",'r') as f:
line = f.readline()
if line != "":
prevTime=datetime.strptime(line,"%Y-%m-%d %H:%M:%S.%f")
else:
prevTime=datetime.strptime("1975-01-01 01:01:01.000000","%Y-%m-%d %H:%M:%S.%f")
prevTime=datetime.strptime("1975-01-01 01:01:01.000000","%Y-%m-%d %H:%M:%S.%f")#If the file was blank, (such as by just being created) use an aribtrary very early date
data=result["responses"]
#Unpack the ugly data structure that Google Forms returns
for req in data:
try:
reason=req["answers"]["22a5ae9b"]["textAnswers"]["answers"][0]["value"]
except KeyError:
except KeyError:#No reason specified
reason = ""
requestor=req["answers"]["7bb6a9dd"]["textAnswers"]["answers"][0]["value"]
@ -435,11 +471,18 @@ def getForms(subRequestsFilename):
sections=sections[:-1]
timeStr = req["createTime"][:-1].replace("T"," ")+"000"
#The timestamp needs formatting adjustment and a time-zone shift to EST
timeStamp=datetime.strptime(timeStr, '%Y-%m-%d %H:%M:%S.%f')-timedelta(hours=4,minutes=0)
#If the request is more recent than our last download, then our database doesn't yet 'know' about it and it needs to be added.
if timeStamp>prevTime:
reqDict={"Timestamp": [timeStr], "Requestor": [requestor], "Section": [sections], "Dates": [dates], "Replacement": [replacement], "Reason": [reason]}
subs=pd.concat([subs,pd.DataFrame(reqDict)],ignore_index=True)
subs.sort_values(by=["Timestamp"],inplace=True)
#Write the updated request list to file
subs.to_csv(subRequestsFilename,index=False,index_label=False)
#Write the timestamp to the token for checking when this function was last run
with open("lastUpdatedToken.txt",'w') as f:
f.write(str(datetime.now()))
f.write(str(datetime.now()))

View File

@ -1 +1 @@
2022-10-04 15:15:51.231447
2023-10-11 14:08:24.903282

View File

@ -1714,4 +1714,4 @@ Sec 56,13,11/21,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,
Sec 56,14,11/28,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,,,,,,,,,
Sec 56,14,11/30,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,,,,,,,,,
Sec 56,15,12/05,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,,,,,,,,,
Sec 56,15,12/07,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,,,,,,,,,
Sec 56,15,12/07,12:40 PM-2:30 PM,403 Computer Center,john7531,imranmoh,,,,,,,,,,,,,,,,,,

1 Section Week Date Time Location Staff0 Staff1 Staff2 Staff3 Staff4 Staff5 Staff6 Staff7 Staff8 Staff9 Hours0 Hours1 Hours2 Hours3 Hours4 Hours5 Hours6 Hours7 Hours8 Hours9
1714 Sec 56 14 11/28 12:40 PM-2:30 PM 403 Computer Center john7531 imranmoh
1715 Sec 56 14 11/30 12:40 PM-2:30 PM 403 Computer Center john7531 imranmoh
1716 Sec 56 15 12/05 12:40 PM-2:30 PM 403 Computer Center john7531 imranmoh
1717 Sec 56 15 12/07 12:40 PM-2:30 PM 403 Computer Center john7531 imranmoh

View File

@ -10,20 +10,16 @@ pd.set_option('display.min_rows', 20)
pd.set_option('display.expand_frame_repr', False)
pd.set_option('max_colwidth', 20)
#This is the main window for the substitution requests interface
def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequestsArchiveFilename,defaultFont=("Courier",11)):
#read in sub requests from the provided csv filepath
subRequests=[]
with open(subRequestsFilename) as f:
headerLine=f.readline()
reader = csv.reader(f)
for row in reader:
subRequests.append(row+[""])
subRequests.append(row+[""])
#format fields is Requester, Period, Date, Replacement, Reason, Times, CurrAssigned, Errors
columnWidths=[20,16,16,20,30,50,50,20]
@ -35,8 +31,9 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
row.append(sg.Text(headerVals[i],expand_x=True,expand_y=True,size=(columnWidths[i],1)))
layout=[row]
#track how many requests are for a specific timeslot
datetimeFreq={}
#--------------------------------------------------------------------------
#Basic error checking before opening display (determines which requests are in red)
datetimeFreq={} #track how many requests are for a specific timeslot
for i in range(len(subRequests)):
oldID=subRequests[i][1]
periods=subRequests[i][2].split(';')
@ -50,12 +47,16 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
else:
datetimeFreq[datetime]=1
if not HF.shiftExists(period,date):
subRequests[i][6]+="No such shift\n"
subRequests[i][6]+="No such shift\n" #Add to the list of errors with this request
else:
if not HF.isAssigned(oldID,period,date):
subRequests[i][6]+=oldID+" not assigned\n"
subRequests[i][6]+=oldID+" not assigned\n" #Add to the list of errors with this request
if newID != "" and HF.isAssigned(newID,period,date):
subRequests[i][6]+=newID+" already assigned\n"
subRequests[i][6]+=newID+" already assigned\n" #Add to the list of errors with this request
#--------------------------------------------------------------------------
#Formatting the text for the main window
for i in range(len(subRequests)):
request=subRequests[i]
netID=request[1]
@ -86,7 +87,6 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
others=str(others)[1:-1]
others=others.replace("'","")
#others=others.replace("],","]\n"+" "*(sum(columnWidths[:6])+16)+"|")
textVals=[HF.IDToName(netID),*request[2:4],newSubName,request[5],timeStr,others,request[6].strip("\n")]
@ -100,6 +100,7 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
layout.append(row)
layout.append([sg.Text('-'*(sum(columnWidths)+27))])
#Adding buttons
layout.append([sg.Button('Ok'), sg.Button('Cancel'), sg.Button('See Sub History'), sg.Button('Make Manual Changes')])
layout=[[sg.Column(layout,scrollable=True,expand_y=True,expand_x=True)]]
@ -112,6 +113,8 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
event=-1
while event != sg.WIN_CLOSED and completed==False:
event, values = window.read()
#values.values() is an array of the input from all window elements, we use strides to get the checkboxes as "columns" instead of "rows"
approveValues=list(values.values())[::5]
acceptValues=list(values.values())[1::5]
rejectValues=list(values.values())[2::5]
@ -122,22 +125,22 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
nAcc = sum([1 for d in acceptValues if True == d])
nRej = sum([1 for d in rejectValues if True == d])
nCan = sum([1 for d in cancelValues if True == d])
#event is the most recent "click" from the user
if event == "See Sub History":
SHW.subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont)
SHW.subHisWin(subRequestsArchiveFilename,defaultFont) #open history window
if event == "Make Manual Changes":
window['Ok'].Update(disabled=True) #This is to prevent undefined behavior where manual changes are not represented in the error-checking of the main window
SMW.subManualWin(staffDatabaseFilename,secDFilename,subRequestsArchiveFilename,defaultFont)
event = "Cancel"
if event == 'Ok':
window['Ok'].Update(disabled=True) #This is to prevent undefined behavior as a result of manual changes not being represented in the main window
SMW.subManualWin(subRequestsArchiveFilename,defaultFont) #open manual change window
event = "Cancel" #after we return from the manual change window, just close the whole program
if event == 'Ok':
valid=True
dialogShown = False #Do not show multiple error dialogues even if there are multiple problems.
#If two responses are checked in ANY single line, then it is not valid input
#If two responses are both checked in ANY single line, then it is not valid input
if True in [True for i, j, k, l in zip(approveValues, acceptValues, rejectValues, cancelValues) if ((i and j) or (i and k) or (j and k) or (i and l) or (j and l) or (k and l))]:
valid=False
if not dialogShown:
@ -154,6 +157,7 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
otherReqCache={}
for i in range(len(subRequests)):
if approveValues[i] or acceptValues[i]:
#If any of the app/acc lines have an error, then it is not valid input
if subRequests[i][6] != "":
valid=False
if not dialogShown:
@ -212,9 +216,10 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
else:
amount = 1
if approveValues[i] or acceptValues[i]: #If this request was checked "approved" or "accepted"
HF.reassign(date,period,oldID,newID)
HF.reassign(date,period,oldID,newID) #Actually make the changes in the database
#HF.incrementSubCount is for tracking how many subs this person requested and what their outcome was
if newID !="":
HF.incrementSubCount(newID,4,amount=amount)
HF.incrementSubCount(newID,4,amount=amount)#Also track how many sub requests were filled by each person
if approveValues[i]:
HF.incrementSubCount(oldID,0,amount=amount)
if acceptValues[i]:
@ -224,7 +229,7 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
if cancelValues[i]:
HF.incrementSubCount(oldID,3,amount=amount)
if (not approveValues[i] and not acceptValues[i] and not rejectValues[i] and not cancelValues[i]):
trimmedSubrequests.append(subRequests[i][:-1])
trimmedSubrequests.append(subRequests[i][:-1])#Trimmed requests is the list of requests that had no action taken and thus must be re-added to the list of pending requests
else:
if approveValues[i]:
result="APP"
@ -234,12 +239,13 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
result="REJ"
elif cancelValues[i]:
result="CAN"
resolvedSubrequests.append(subRequests[i][:-1]+[result]+[values["-INPUT_REASON_"+str(i)+"-"]])
resolvedSubrequests.append(subRequests[i][:-1]+[result]+[values["-INPUT_REASON_"+str(i)+"-"]])#Resolved requests is kept to be appended to the archived requests database
# If the program crashes during email sending (such as with invalid login) it will deliberately NOT reach the code where it writes to databases
#If the program crashes during email sending (such as with invalid login) it will deliberately NOT reach the code where it writes to databases
#KEEP THIS LINE BEFORE THE WRITING STEPS
HF.generateEmails(resolvedSubrequests)
#Write to the respective databases
with open(subRequestsFilename,'w', newline='') as f:
f.write(headerLine)
writer = csv.writer(f)
@ -253,4 +259,4 @@ def subAppWin(staffDatabaseFilename,secDFilename,subRequestsFilename,subRequests
elif event == 'Cancel':
completed=True
window.close()
window.close()

View File

@ -3,7 +3,7 @@ import csv
import pandas as pd
import helperFunctions as HF
def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
def subHisWin(subRequestsArchiveFilename,defaultFont):
history = pd.read_csv(subRequestsArchiveFilename,dtype=str)
@ -22,6 +22,7 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
while event != sg.WIN_CLOSED and event != "Close Window":
event, values = window.read()
#If event is the pressing of an incrementing button AND the textbox search returns exactly one name
if event in ["+APP","-APP","+ACC","-ACC","+REJ","-REJ","+CAN","-CAN","+FUL","-FUL"] and netID != -1 and len(netIDs)<=1:
if event[0]=="+":
change = 1
@ -33,16 +34,6 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
event = "Find History"
if event=="Find History":
window['+APP'].Update(disabled=False)
window['-APP'].Update(disabled=False)
window['+ACC'].Update(disabled=False)
window['-ACC'].Update(disabled=False)
window['+REJ'].Update(disabled=False)
window['-REJ'].Update(disabled=False)
window['+CAN'].Update(disabled=False)
window['-CAN'].Update(disabled=False)
window['+FUL'].Update(disabled=False)
window['-FUL'].Update(disabled=False)
name = values[0]
netIDs=HF.nameToID(name,getAllMatches=True)
if not hasattr(netIDs,'__len__') or len(netIDs)==0:
@ -58,10 +49,21 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
names = [HF.IDToName(i) for i in netIDs[:]]
window['OUTPUT'].update(value="Multiple matches found: ["+", ".join(names)+"]")
else:
#Enable the incrementing buttons once a single valid name is found
window['+APP'].Update(disabled=False)
window['-APP'].Update(disabled=False)
window['+ACC'].Update(disabled=False)
window['-ACC'].Update(disabled=False)
window['+REJ'].Update(disabled=False)
window['-REJ'].Update(disabled=False)
window['+CAN'].Update(disabled=False)
window['-CAN'].Update(disabled=False)
window['+FUL'].Update(disabled=False)
window['-FUL'].Update(disabled=False)
#Get the number of previous requests in each category for that ULA
APP,ACC,REJ,CAN,FUL=HF.getSubCount(netID)
#Printing options for Pandas
#Printing options for the Pandas dataframe
pd.set_option('display.min_rows', 20)
pd.set_option('display.expand_frame_repr', False)
pd.set_option('max_colwidth', 30)
@ -71,9 +73,11 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
output="No history"
else:
output=str(output.to_string(index=False))
#Change the textbox to include the relevant data
window['OUTPUT'].update(value=name+"; "+netID+"\n"+"APP:"+APP+" ACC:"+ACC+" REJ:"+REJ+" CAN:"+CAN+" FUL:"+FUL+"\n"+output)
if event=="Hall of Fame":
#Disable the incrementing buttons while in "Hall of Fame" mode since there is no single name attached.
window['+APP'].Update(disabled=True)
window['-APP'].Update(disabled=True)
window['+ACC'].Update(disabled=True)
@ -84,9 +88,12 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
window['-CAN'].Update(disabled=True)
window['+FUL'].Update(disabled=True)
window['-FUL'].Update(disabled=True)
#Printing options for Pandas
pd.set_option('display.expand_frame_repr', False)
pd.set_option('max_colwidth', 30)
#Display instructions while the inputStr is not a valid input for the Hall of Fame
output='To customize which categories to add (App,Acc,Rej,Can,Ful):\nuse 0,1,2,3,4 or any combination thereof in the text box then click "Hall of Fame" again\n(e.g. "12" yields Accepted plus Rejected)\n'
inputStr=values[0]
if inputStr=="" or not inputStr.isnumeric():
@ -97,4 +104,3 @@ def subHisWin(staffDatabaseFilename,subRequestsArchiveFilename,defaultFont):
window.close()

View File

@ -4,13 +4,13 @@ import pandas as pd
import helperFunctions as HF
from datetime import datetime, timedelta
def subManualWin(staffDatabaseFilename,secDFilename,subRequestsArchiveFilename,defaultFont):
def subManualWin(subRequestsArchiveFilename,defaultFont):
stfD = pd.read_csv(staffDatabaseFilename,dtype=str,index_col=False)
allnames=list(stfD["Name"].values)
sections=HF.getSectionTitles()
#Pull all names and section titles for the poopulation of the drop-down boxes
allnames=HF.getAllNames()
sections=HF.getAllSectionTitles()
#Define the window's layout
layout=[[sg.Text("Email?| Sec | Date | Replacee | Replacement")],
[sg.Checkbox('',size=(3,1),key='-EMAILCHECK-'),sg.Combo(sections,size=(6,1),key="-SECTION-",readonly=True, enable_events=True),sg.Combo([""],size=(6,1),key='-DATE-',readonly=True, enable_events=True),sg.Combo([""],size=(30,1),key="-REPLACEE-", enable_events=True,readonly=True),sg.Combo(allnames,size=(30,1),key="-REPLACEMENT-", enable_events=True,readonly=True)],
[sg.Text("Reason for manual change: "),sg.Input(key='-REASON-')],
@ -22,45 +22,50 @@ def subManualWin(staffDatabaseFilename,secDFilename,subRequestsArchiveFilename,d
#Event Loop to process "events" and get the "values" of the inputs
#Cease running if they close the window
event=-1
while event != sg.WIN_CLOSED and event != "Close Window":
event, values = window.read()
#If the 'section' box is interacted with and is not blank (i.e. an option was selected from the dropdown box)
if event=="-SECTION-" and values['-SECTION-']!="":
sec=values['-SECTION-']
dates=HF.getDatesFromSection(sec)
window["-DATE-"].update(values=dates)
window["-DATE-"].update(values=dates)#update the dropdown box of dates with the appropriate values for this section
window["-REPLACEE-"].update(values=[""])
#If the 'date' box is interacted with and is not blank (i.e. an option was selected from the dropdown box)
if event=="-DATE-" and values['-DATE-']!="":
date=values["-DATE-"]
names=list(HF.getAllNamesFromSection(sec,date))[0]
window["-REPLACEE-"].update(values=names)
window["-REPLACEE-"].update(values=names)#update the dropdown box of names with the appropriate values for this section/date combo
#Button was pressed to process the manual change
if event == "Make Change":
sec=values['-SECTION-']
date=values["-DATE-"]
replacee=HF.nameToID(values["-REPLACEE-"])
replacement=HF.nameToID(values["-REPLACEMENT-"])
timeStamp=str(datetime.now())
request=[timeStamp,replacee,sec,date,replacement,values["-REASON-"],"APP","MANUALLY CHANGED BY COURSE ADMINS"]
timeStamp=str(datetime.now())#Consider the submission time to be right now, when the button was pressed.
request=[timeStamp,replacee,sec,date,replacement,values["-REASON-"],"APP","MANUALLY CHANGED BY COURSE ADMINS"]#create a request in the style of the Google form, for archiving purposes
HF.reassign(date,sec,replacee,replacement)
HF.reassign(date,sec,replacee,replacement)#Actually perform the replacement
#Send the email confirmations out to the relevant parties
#NOTE: This is BEFORE the writing step so that if there's an error in emails (such as failed to authenticate) the code aborts BEFORE writing chnages to the database.
if values['-EMAILCHECK-']:
HF.generateEmails([request])
#write to the archive database
with open(subRequestsArchiveFilename,'a', newline='') as f:
writer = csv.writer(f)
writer.writerows([request])
#Reset the dropdowns to the original options
window["-SECTION-"].update(values=sections)
window["-DATE-"].update(values=[""])
window["-REPLACEE-"].update(values=[""])
window["-REPLACEMENT-"].update(values=allnames)
if values['-EMAILCHECK-']:
HF.generateEmails([request])
window.close()
window.close()

View File

@ -1 +1,27 @@
Timestamp,Requestor,Section,Dates,Replacement,Reason
2023-09-19 13:57:17.596000,sipahiog,Sec 46,09/19,haggart3,Sickness
2023-09-19 18:45:55.239000,sebalyma,Sec 40,10/10,ngvivian,will be in ohio for beginning of that week
2023-09-19 22:32:06.420000,mcgui186,Sec 6,09/21,schul769,Funeral
2023-09-20 17:12:04.654000,austi143,Sec 11,09/20,,"Grandpa had stroke, have to go home"
2023-09-20 17:12:30.949000,gautamya,Sec 21,09/20,,Sick
2023-09-20 17:29:31.529000,upadhy19,Sec 4,09/20,,Strong Stomachache
2023-09-21 13:22:29.001000,sipahiog,Sec 46,09/21,,Sickness
2023-09-21 18:25:24.758000,mcgui186,R_HR2,09/21,sipahiog,Funeral
2023-09-21 18:26:11.335000,mcgui186,R_HR1,09/21,haggart3,Funeral
2023-09-23 14:25:11.349000,borekmi1,Sec 1,09/25,sebalyma,Problem with the transportation to MSU
2023-09-25 17:05:16.919000,burgejae,Sec 46,09/26,aggarw75,Important meeting came up
2023-09-25 17:05:25.520000,anindhos,M_HR1;M_HR2,09/25,bhardw41,Mandatory project meeting
2023-09-25 21:37:26.276000,aggarw75,Sec 11,09/27,lnumehak,
2023-09-26 01:51:03.453000,john7531,S_HR1;S_HR2,10/02,bhardw41,Laptop died. Replacement may not come before my shift.
2023-09-26 14:52:30.307000,darshanv,W_HR1;W_HR2;Sec 20,09/27,micksoph,Visiting the SSN office for on campus registration and receiving SSN
2023-09-26 16:25:19.977000,sipahiog,T_HR1;T_HR2,09/26,haggart3,Something came up with a class
2023-09-28 14:58:15.450000,sonarsoh,Sec 17,10/02;10/04,tagaychr,broken arm
2023-10-03 19:14:01.917000,sipahiog,Sec 30,10/09,john7531,out of town
2023-10-04 19:47:39.642000,schne542,Sec 19,10/09,aggarw75,I have a doctors appointment on 10/9 back home.
2023-10-04 20:02:13.852000,mcgui186,R_HR1;R_HR2,10/05,haggart3,I need to go to helproom for a project of mine
2023-10-04 20:07:34.501000,sipahiog,Sec 46,10/10,haggart3,Out of town
2023-10-09 17:51:15.406000,austi143,Sec 11,10/09,bhardw41,Im sick
2023-10-11 01:06:38.157000,pittend2,Sec 25,10/11,kumararn,Sick
2023-10-11 16:49:39.541000,murraydr,M_HR1;M_HR2,10/09,,test1
2023-10-11 16:50:11.365000,murraydr,M_HR1,10/02;10/09,,test2
2023-10-11 17:24:59.666000,austi143,Sec 11,10/11,lnumehak,Im sick

1 Timestamp Requestor Section Dates Replacement Reason
2 2023-09-19 13:57:17.596000 sipahiog Sec 46 09/19 haggart3 Sickness
3 2023-09-19 18:45:55.239000 sebalyma Sec 40 10/10 ngvivian will be in ohio for beginning of that week
4 2023-09-19 22:32:06.420000 mcgui186 Sec 6 09/21 schul769 Funeral
5 2023-09-20 17:12:04.654000 austi143 Sec 11 09/20 Grandpa had stroke, have to go home
6 2023-09-20 17:12:30.949000 gautamya Sec 21 09/20 Sick
7 2023-09-20 17:29:31.529000 upadhy19 Sec 4 09/20 Strong Stomachache
8 2023-09-21 13:22:29.001000 sipahiog Sec 46 09/21 Sickness
9 2023-09-21 18:25:24.758000 mcgui186 R_HR2 09/21 sipahiog Funeral
10 2023-09-21 18:26:11.335000 mcgui186 R_HR1 09/21 haggart3 Funeral
11 2023-09-23 14:25:11.349000 borekmi1 Sec 1 09/25 sebalyma Problem with the transportation to MSU
12 2023-09-25 17:05:16.919000 burgejae Sec 46 09/26 aggarw75 Important meeting came up
13 2023-09-25 17:05:25.520000 anindhos M_HR1;M_HR2 09/25 bhardw41 Mandatory project meeting
14 2023-09-25 21:37:26.276000 aggarw75 Sec 11 09/27 lnumehak
15 2023-09-26 01:51:03.453000 john7531 S_HR1;S_HR2 10/02 bhardw41 Laptop died. Replacement may not come before my shift.
16 2023-09-26 14:52:30.307000 darshanv W_HR1;W_HR2;Sec 20 09/27 micksoph Visiting the SSN office for on campus registration and receiving SSN
17 2023-09-26 16:25:19.977000 sipahiog T_HR1;T_HR2 09/26 haggart3 Something came up with a class
18 2023-09-28 14:58:15.450000 sonarsoh Sec 17 10/02;10/04 tagaychr broken arm
19 2023-10-03 19:14:01.917000 sipahiog Sec 30 10/09 john7531 out of town
20 2023-10-04 19:47:39.642000 schne542 Sec 19 10/09 aggarw75 I have a doctors appointment on 10/9 back home.
21 2023-10-04 20:02:13.852000 mcgui186 R_HR1;R_HR2 10/05 haggart3 I need to go to helproom for a project of mine
22 2023-10-04 20:07:34.501000 sipahiog Sec 46 10/10 haggart3 Out of town
23 2023-10-09 17:51:15.406000 austi143 Sec 11 10/09 bhardw41 I’m sick
24 2023-10-11 01:06:38.157000 pittend2 Sec 25 10/11 kumararn Sick
25 2023-10-11 16:49:39.541000 murraydr M_HR1;M_HR2 10/09 test1
26 2023-10-11 16:50:11.365000 murraydr M_HR1 10/02;10/09 test2
27 2023-10-11 17:24:59.666000 austi143 Sec 11 10/11 lnumehak I’m sick