Spaces:
Sleeping
Sleeping
Duplicate from DavidD003/Wknd_Scheduler
Browse filesCo-authored-by: David De Sa <[email protected]>
- .gitattributes +33 -0
- README.md +14 -0
- SchedBuilderClasses2.py +0 -0
- SchedBuilderUtyModule.py +450 -0
- Tabs(Awaiting_Bug_Fix)_app.py +117 -0
- app.py +117 -0
- requirements.txt +2 -0
.gitattributes
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
25 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
26 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Wknd Scheduler
|
3 |
+
emoji: 📊
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: blue
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.7
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
license: mit
|
11 |
+
duplicated_from: DavidD003/Wknd_Scheduler
|
12 |
+
---
|
13 |
+
|
14 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
SchedBuilderClasses2.py
ADDED
The diff for this file is too large to render.
See raw diff
|
|
SchedBuilderUtyModule.py
ADDED
@@ -0,0 +1,450 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from SchedBuilderClasses2 import *
|
2 |
+
import openpyxl as pyxl
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
import sqlite3
|
6 |
+
import functools
|
7 |
+
from copy import deepcopy
|
8 |
+
|
9 |
+
|
10 |
+
def debug(func):
|
11 |
+
"""Print the function signature and return value"""
|
12 |
+
@functools.wraps(func)
|
13 |
+
def wrapper_debug(*args, **kwargs):
|
14 |
+
args_repr = [repr(a) for a in args] # 1
|
15 |
+
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] # 2
|
16 |
+
signature = ", ".join(args_repr + kwargs_repr) # 3
|
17 |
+
print(f"Calling {func.__name__}({signature})")
|
18 |
+
value = func(*args, **kwargs)
|
19 |
+
print(f"{func.__name__!r} returned {value!r}") # 4
|
20 |
+
return value
|
21 |
+
return wrapper_debug
|
22 |
+
|
23 |
+
|
24 |
+
|
25 |
+
def addTBL(tblName,fields="",dTypes=None,data=None,addOn=False):
|
26 |
+
"""Create table if not already existing, optionally with data, optionally clearing out old data if present. Fields as list of strings. Datatypes as list of strings, one must be provided for each field. See sqlite3 docs for mroe info"""
|
27 |
+
conn = sqlite3.connect('test17.db')
|
28 |
+
c = conn.cursor()
|
29 |
+
listedFields=''
|
30 |
+
if fields=="": #If none given, make alphabetical
|
31 |
+
fields=[chr(65+i) for i in range(len(data[0]))]
|
32 |
+
if dTypes==None: #Need not specify dtypes
|
33 |
+
for f in fields:
|
34 |
+
listedFields=listedFields+', '+ f
|
35 |
+
else: #define data types at inception of table
|
36 |
+
flds=list(zip(fields,dTypes))
|
37 |
+
for pair in flds:
|
38 |
+
listedFields=listedFields+', '+pair[0]+' '+pair[1]
|
39 |
+
listedFields='('+listedFields[2:]+''')''' #Add leading and closing bracket, remove naively added comma+space from leading field
|
40 |
+
c.execute('''CREATE TABLE IF NOT EXISTS '''+tblName+listedFields) # Create table.
|
41 |
+
if addOn==False: #Delete if not adding
|
42 |
+
c.execute('''DELETE FROM '''+tblName)
|
43 |
+
if (data is not None) and len(data)>0:
|
44 |
+
stmnt='INSERT INTO '+tblName+' VALUES ('
|
45 |
+
for i in range(len(fields)-1):
|
46 |
+
stmnt=stmnt+'?,'#Add '?,' equal to num columns less 1
|
47 |
+
stmnt=stmnt+'?)' #add closing ?), no final comma
|
48 |
+
for subEntry in data:
|
49 |
+
c.execute(stmnt, subEntry)
|
50 |
+
conn.commit()
|
51 |
+
|
52 |
+
def isNumeric(n):
|
53 |
+
try:
|
54 |
+
n=int(n)
|
55 |
+
return True
|
56 |
+
except ValueError:
|
57 |
+
try:
|
58 |
+
n=float(n)
|
59 |
+
return True
|
60 |
+
except:
|
61 |
+
return False
|
62 |
+
|
63 |
+
|
64 |
+
def viewTBL(tblName,fields=None,sortBy=None,filterOn=None,returnStatement=0):
|
65 |
+
"""return np array of table with optional select fields, filtered, sorted. Sort syntax=[(field1,asc/desc),(field2,asc/desc)...] Filter syntax=[(field1,value),(field2,value)...]"""
|
66 |
+
conn = sqlite3.connect('test17.db')
|
67 |
+
c = conn.cursor()
|
68 |
+
stmnt='SELECT '
|
69 |
+
if fields!=None:
|
70 |
+
flds=''
|
71 |
+
for f in fields:
|
72 |
+
flds=flds+', '+f
|
73 |
+
stmnt=stmnt+flds[2:]+ ' FROM ' +tblName+' '
|
74 |
+
else: stmnt=stmnt+'* FROM '+tblName+' ' #unspecified, select all
|
75 |
+
if filterOn!=None:
|
76 |
+
filt='WHERE '
|
77 |
+
for f in filterOn:
|
78 |
+
if isNumeric(f[1]): filt=filt+f[0]+' = '+ str(f[1])+' AND '
|
79 |
+
else: filt=filt+str(f[0])+' = "'+ str(f[1])+'" AND '
|
80 |
+
filt=filt[:-4] #Remove naively added final " and "
|
81 |
+
stmnt=stmnt+filt
|
82 |
+
if sortBy!=None:
|
83 |
+
srt='ORDER BY '
|
84 |
+
for s in sortBy:
|
85 |
+
srt=srt+s[0]+' '+s[1]+', '
|
86 |
+
srt=srt[:-2]
|
87 |
+
stmnt=stmnt+srt
|
88 |
+
stmnt=stmnt+';'
|
89 |
+
if returnStatement==True: # Add option to print out the sql statement for troubleshooting
|
90 |
+
return stmnt
|
91 |
+
else:
|
92 |
+
c.execute(stmnt)
|
93 |
+
return [list(x) for x in c.fetchall()] #sqlite3 returns list of tuples.. want sublists for being editable
|
94 |
+
|
95 |
+
def FTbtRow(ws):
|
96 |
+
"""Returns the excel row number for the bottom row with data in FT employee sheet"""
|
97 |
+
#1st find bottom row with data
|
98 |
+
for i in range(5,400):
|
99 |
+
ref="C"+str(i) #Referencing EEID column. Failure mode is EEID missing for someone. Thats a bigger problem than the code not working
|
100 |
+
if ws[ref].internal_value==None:
|
101 |
+
#Condition met when end of data found.
|
102 |
+
btmRow=i-1 #backtrack to last data
|
103 |
+
break
|
104 |
+
return btmRow
|
105 |
+
|
106 |
+
def getFTinfo(flNm):
|
107 |
+
"""Returns dataframe with FT employee info (seniority,crew,eeid,name,refusal hours to date, OT hrs worked this week given path to FT refusal sheet"""
|
108 |
+
myWb=pyxl.load_workbook(flNm)
|
109 |
+
ws=myWb['Hourly OT']
|
110 |
+
btmRow=FTbtRow(ws)
|
111 |
+
tab=[[x.internal_value for x in sublist] for sublist in ws['A5:I'+str(btmRow)]]
|
112 |
+
#for rec in tab: #numeric values getting cast to string on import. cast back
|
113 |
+
# for i in [0,2]:
|
114 |
+
# rec[i]=int(rec[i])
|
115 |
+
# for i in [5,6,7]:
|
116 |
+
# rec[i]=float(rec[i])
|
117 |
+
#Following to turn into dataframe
|
118 |
+
#df_FTinfo=pd.DataFrame(tab)
|
119 |
+
#df_FTinfo=df_FTinfo[[0,1,2,3,4,5,8]] #Pull out only required columns
|
120 |
+
#df_FTinfo.set_axis(['snrty', 'crew', 'eeid','last','first','yrRef','wkOT'], axis='columns', inplace=True)
|
121 |
+
return tab
|
122 |
+
|
123 |
+
|
124 |
+
def FTendCol(ws):
|
125 |
+
"""Returns column # for last column of skills matrix in excel from FT refusal sheet"""
|
126 |
+
for i in range(0,400):
|
127 |
+
if ws['A1'].offset(0,i).value=='Start-up':
|
128 |
+
#Condition met when end of data found.
|
129 |
+
endCol=i-1
|
130 |
+
break
|
131 |
+
return endCol
|
132 |
+
|
133 |
+
def getFTskills(flNm):
|
134 |
+
"""Returns a dataframe containing a table with 2 fields: eeid and job name, with one record for every job an ee is trained in"""
|
135 |
+
myWb=pyxl.load_workbook(flNm)
|
136 |
+
ws=myWb['Hourly OT']
|
137 |
+
endCol=FTendCol(ws) #Get right limit for iteration through skills
|
138 |
+
btmRow=FTbtRow(ws) #Get bottom limit for iteration through skills
|
139 |
+
skills=[] #Initialize empty skills list
|
140 |
+
for i in range(5,btmRow+1): #Data starts on row 5. +1 because range fn not inclusive
|
141 |
+
eeid=ws['C'+str(i)].value
|
142 |
+
for c in range(10,endCol+1):
|
143 |
+
if ws['C'+str(i)].offset(0,c-2).value==1: #subtract 2 from c because the endCol is counted from col A, and we are offsetting from col C, whihc is 2 offset from A
|
144 |
+
jobNm=ws['A2'].offset(0,c).value #if 1 indicates trained, pull job name from header row
|
145 |
+
skills.append([eeid,jobNm]) #Add new record to skills table
|
146 |
+
#idxs=np.array(skills)[:,0]
|
147 |
+
#skills=pd.DataFrame(skills,idxs) #Convert to dataframe
|
148 |
+
#skills.set_axis(['eeid','skill'], axis='columns', inplace=True)
|
149 |
+
return skills
|
150 |
+
|
151 |
+
def TempbtRow(ws):
|
152 |
+
"""Returns the excel row number for the bottom row with data in Temp employee sheet"""
|
153 |
+
#1st find bottom row with data
|
154 |
+
for i in range(4,400):
|
155 |
+
ref="C"+str(i) #Referencing EEID column. Failure mode is EEID missing for someone. Thats a bigger problem than the code not working
|
156 |
+
if ws[ref].internal_value==None:
|
157 |
+
#Condition met when end of data found.
|
158 |
+
btmRow=i-1 #backtrack to last data
|
159 |
+
break
|
160 |
+
return btmRow
|
161 |
+
|
162 |
+
def getTempinfo(flNm):
|
163 |
+
"""Returns dataframe with FT employee info (seniority,crew,eeid,name,refusal hours to date, OT hrs worked this week given path to FT refusal sheet"""
|
164 |
+
myWb=pyxl.load_workbook(flNm)
|
165 |
+
ws=myWb['Temp Refusal']
|
166 |
+
btmRow=TempbtRow(ws)
|
167 |
+
tab=[[x.internal_value for x in sublist] for sublist in ws['A4:I'+str(btmRow)]]
|
168 |
+
#df_Tempinfo=pd.DataFrame(tab)
|
169 |
+
#df_Tempinfo=df_Tempinfo[[0,1,2,3,4,5,8]] #Pull out only required columns
|
170 |
+
#df_Tempinfo.set_axis(['snrty', 'crew', 'eeid','last','first','yrRef','wkOT'], axis='columns', inplace=True)
|
171 |
+
return tab
|
172 |
+
|
173 |
+
def TempendCol(ws):
|
174 |
+
"""Returns column # for last column of skills matrix in excel from FT refusal sheet"""
|
175 |
+
for i in range(0,400):
|
176 |
+
if ws['A2'].offset(0,i).value=='Start Up':
|
177 |
+
#Condition met when end of data found.
|
178 |
+
endCol=i-1
|
179 |
+
break
|
180 |
+
return endCol
|
181 |
+
|
182 |
+
def getTempskills(flNm):
|
183 |
+
"""Returns a dataframe containing a table with 2 fields: eeid and job name, with one record for every job an ee is trained in"""
|
184 |
+
myWb=pyxl.load_workbook(flNm)
|
185 |
+
ws=myWb['Temp Refusal']
|
186 |
+
endCol=TempendCol(ws) #Get right limit for iteration through skills
|
187 |
+
btmRow=TempbtRow(ws) #Get bottom limit for iteration through skills
|
188 |
+
skills=[] #Initialize empty skills list
|
189 |
+
for i in range(4,btmRow+1): #Data starts on row 5. +1 because range fn not inclusive
|
190 |
+
eeid=ws['C'+str(i)].value
|
191 |
+
for c in range(11,endCol+1): #First skills column is 11 offset from col A
|
192 |
+
if ws['C'+str(i)].offset(0,c-2).value==1: #subtract 2 from c because the endCol is counted from col A, and we are offsetting from col C, which is 2 offset from A
|
193 |
+
jobNm=ws['A3'].offset(0,c).value #if 1 indicates trained, pull job name from header row
|
194 |
+
skills.append([eeid,jobNm]) #Add new record to skills table
|
195 |
+
#idxs=np.array(skills)[:,0]
|
196 |
+
#skills=pd.DataFrame(skills,idxs) #Convert to dataframe
|
197 |
+
#skills.set_axis(['eeid','skill'], axis='columns', inplace=True)
|
198 |
+
return skills
|
199 |
+
|
200 |
+
def imptXlTbl(XlFl,ShtNm,TblNm):
|
201 |
+
myWb=pyxl.load_workbook(XlFl)
|
202 |
+
ws=myWb[ShtNm]
|
203 |
+
tab=ws.tables[TblNm] #Pull out table
|
204 |
+
tab=[[x.value for x in sublist] for sublist in ws[tab.ref]] #Convert to list of lists (each sublist as row of excel table)
|
205 |
+
return tab[1:] #Convert nested lists to array, dropping first row which is table headings
|
206 |
+
|
207 |
+
def imptPolltbl(XlFl,ShtNm,TblNm,tp=None):
|
208 |
+
myWb=pyxl.load_workbook(XlFl)
|
209 |
+
ws=myWb[ShtNm]
|
210 |
+
tab=ws.tables[TblNm] #Pull out table
|
211 |
+
tab=[[x.value for x in sublist] for sublist in ws[tab.ref]] #Convert to list of lists (each sublist as row of excel table)
|
212 |
+
tab=tab[1:] #Remove header column
|
213 |
+
if tp=='FT': #Pull only FT's into FT table
|
214 |
+
tab=[rec for rec in tab if rec[3]!=None] #Remove rows without refusal hours
|
215 |
+
tab=[rec for rec in tab if rec[3]<10000] #Ft's id'd by less than 10k refusal hours
|
216 |
+
if tp=='P': #Pull only probationaries into table
|
217 |
+
tab=[rec for rec in tab if rec[3]!=None] #Remove rows without refusal hours
|
218 |
+
tab=[rec for rec in tab if rec[3]>=10000] #Probationaries ID'd by 10K or more refusal hours
|
219 |
+
return tab
|
220 |
+
|
221 |
+
def generateMasterPollTbl(pollDict):
|
222 |
+
"""Given a dictionary containing the polling tables for all crews, generates a master tbl in SQLlite for being able to filter on peoples availabilities, with '1' indicating interest, '0' no interest, and slot seq 1 starting at index 4"""
|
223 |
+
mPollTbl=[]
|
224 |
+
#the total list of all fields the table has is programmatically generated on these 3 lines
|
225 |
+
flds=["eeid",'lastNm','firstNm','ytdRefHrs']
|
226 |
+
flds.extend(['slot_'+str(i) for i in range(1,25)])#Note that there is one field for each slot seqID, 1 through 24, for filtering
|
227 |
+
flds.append('Comment')
|
228 |
+
for crewKey in pollDict:
|
229 |
+
tbl=pollDict[crewKey] #Pull the crew specific OT polling table from dictionary
|
230 |
+
for rec in tbl:
|
231 |
+
cmnt=rec[16]#retrieve comment to tag on later
|
232 |
+
slotwise_polling=list(rec[:4])
|
233 |
+
for i in range(4,16):
|
234 |
+
if rec[i] not in ('n','N',None) :
|
235 |
+
slotwise_polling.extend(['y','y']) #Add two entries because 1 entry in polling sheet applies to two slots
|
236 |
+
else:
|
237 |
+
slotwise_polling.extend(['n','n'])
|
238 |
+
slotwise_polling.append(cmnt)
|
239 |
+
mPollTbl.append(slotwise_polling)
|
240 |
+
addTBL('allPollData',fields=flds, data=mPollTbl,addOn=False)
|
241 |
+
|
242 |
+
|
243 |
+
def pullTbls(FtBook,TempBook,AssnBook,PollBook): #Need to make volunteer shift data puller
|
244 |
+
"""Take flNm, return ftInfoTbl, ftSkillsMtx, tempInfoTbl, tempSkillsMtx, AssignmentsTbl, slot_Legend, JobTrnCrossRef, pollDict, All_Slots, senList. Uses functions defined previously to return all required tables at once. Function of functions for final script"""
|
245 |
+
a=getFTinfo(FtBook) #to sqlite
|
246 |
+
b=getFTskills(FtBook) #to sqlite
|
247 |
+
b=[[int(d[0]),d[1]] for d in b] #Cast EEid to numeric value
|
248 |
+
c=getTempinfo(TempBook) #to sqlite
|
249 |
+
d=getTempskills(TempBook) #to sqlite
|
250 |
+
d=[[int(data[0]),data[1]] for data in d] #Cast EEid to numeric value
|
251 |
+
e=imptXlTbl(AssnBook,'Assignment_List','Assn_List')
|
252 |
+
f=imptXlTbl(AssnBook,'Slot_Legend','Slot_Legend')
|
253 |
+
g=imptXlTbl(AssnBook,'Job_Training_Crossref','TrainAssnMtx') #to sqlite
|
254 |
+
pollDict={} #Generate empty dictionary to store tables of people voluntary overtime
|
255 |
+
for crew in ['Blue','Bud','Rock']:
|
256 |
+
for eeType in ['FT','P','Temp']:
|
257 |
+
if eeType=='Temp': #If type= Temp, proceed to build table
|
258 |
+
keyNm='tbl_'+crew+eeType
|
259 |
+
tbl=imptXlTbl(PollBook,'Sheet1',keyNm)
|
260 |
+
else:
|
261 |
+
keyNm='tbl_'+crew+'FT' #If type= FT OR Probationary, will be referring to FT table in excel, so hard code the string
|
262 |
+
tbl=imptPolltbl(PollBook,'Sheet1',keyNm,tp=eeType)
|
263 |
+
if eeType=='P': #keyNm was made "FT" instead of 'P' so need to manually enter the key when generating dictionry entry
|
264 |
+
pollDict['tbl_'+crew+'P']=tbl
|
265 |
+
else:
|
266 |
+
pollDict[keyNm]=tbl
|
267 |
+
pollDict['tbl_wFT']=imptPolltbl(PollBook,'Sheet1','tbl_wFT',tp='FT') #No nice loop to initialize the WWF crew tables in poll sheet
|
268 |
+
pollDict['tbl_wP']=imptPolltbl(PollBook,'Sheet1','tbl_wFT',tp='P')
|
269 |
+
pollDict['tbl_wT']=imptXlTbl(PollBook,'Sheet1','tbl_wT')
|
270 |
+
h=imptXlTbl(AssnBook,'All_Slots','All_Slots')
|
271 |
+
#Generate tables in sqlite
|
272 |
+
addTBL("sklMtx",fields=["EEID","trnNm"],data=b,addOn=False) #Overwrite all training data and populate FT ops, then append temps for a master table
|
273 |
+
addTBL("sklMtx",fields=["EEID","trnNm"],data=d,addOn=True)
|
274 |
+
addTBL("xRef",fields=["dispNm","trnNm"],data=g,addOn=False) #Skill name cross ref table for fcn dispToTrn to work
|
275 |
+
addTBL("FTinfo",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=a,addOn=False)
|
276 |
+
addTBL("TempInfo",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=c,addOn=False)
|
277 |
+
#Generate a master seniority table.. following replaces hire date with integers for temps
|
278 |
+
senHiLoTemps=viewTBL('TempInfo',sortBy=[('sen','ASC')]) #First retrieve list of temps, most senior to least
|
279 |
+
i=100000 #Start new seniority number at arbitrarily high value not to interfere with full timer
|
280 |
+
for row in senHiLoTemps:
|
281 |
+
row[0]=i
|
282 |
+
i+=1
|
283 |
+
#Overwrite/make new master sen ref table. Then append the Temp data with integerized values
|
284 |
+
addTBL("senRef",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=a,addOn=False)
|
285 |
+
addTBL("senRef",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=senHiLoTemps,addOn=True)
|
286 |
+
senList=viewTBL('senRef',sortBy=[('sen','ASC')])
|
287 |
+
return a,b,c,d,e,f,g,pollDict,h,senList
|
288 |
+
|
289 |
+
def dispToTrn(dispNm):
|
290 |
+
"""Returns the trnNm associated with Display name for a given job. assumes popualted sqlite table 'xRef' with dispNm/trnNm pairs"""
|
291 |
+
q=viewTBL('xRef',fields=['dispNm','trnNm'],filterOn=[('dispNm',dispNm)])
|
292 |
+
if len(q)==0:
|
293 |
+
return "Custom func error 'dispToTrn' no entry found in xRef with dispNm="+str(dispNm)
|
294 |
+
return q[0][1]
|
295 |
+
|
296 |
+
def trnToDisp(trnNm):
|
297 |
+
"""Returns the trnNm associated with Display name for a given job. assumes popualted sqlite table 'xRef' with dispNm/trnNm pairs"""
|
298 |
+
q=viewTBL('xRef',fields=['dispNm','trnNm'],filterOn=[('trnNm',trnNm)])
|
299 |
+
if len(q)==0:
|
300 |
+
return "Custom func error 'trnToDisp' no entry found in xRef with trnNm="+str(trnNm)
|
301 |
+
return [e[0] for e in q] #If multiple DispNms for one train name (e.g. L4 Packer -> Packer, Candling) or Bottle Supply -> etc.
|
302 |
+
#Then return list of all dispNms
|
303 |
+
|
304 |
+
def sklChk(eeid,dispNm):
|
305 |
+
"""Returns True/False if eeid is trained on job with display name or not. Requires skills matrix named sklMtx in sqlite"""
|
306 |
+
trnNm=dispToTrn(dispNm)
|
307 |
+
if len(viewTBL('sklMtx',filterOn=[('EEID',eeid),('trnNm',trnNm)]))==0:
|
308 |
+
return False
|
309 |
+
else:
|
310 |
+
return True
|
311 |
+
|
312 |
+
|
313 |
+
def makeEEdict(ftInfoTbl,tempInfoTbl,wkHrs=40,tp='id'):
|
314 |
+
eeDict={}
|
315 |
+
for dtaTbl in [ftInfoTbl,tempInfoTbl]:
|
316 |
+
for row in dtaTbl:
|
317 |
+
# if row[1].lower().strip() in ['wwf','bud','blue','rock','silver','gold','student']: #Omit people not in packaging, or off, vacation etc
|
318 |
+
if row[2] not in list(eeDict.keys()): #Double check ee hasn't already been generated... why Cory would include an ee on temp table with crew reading 'fulltime' is beyond me but there you go
|
319 |
+
if tp=='id':
|
320 |
+
eeSkills=viewTBL('sklMtx',['trnNm'],filterOn=[('EEID',row[2])])
|
321 |
+
eeSkills=[trnToDisp(nm[0]) for nm in eeSkills] #Gather display names for skills trained on, reducing lists within list to spread elements
|
322 |
+
sk=[] #Create empty to accumulate all skills present within sublists of eeSkills
|
323 |
+
for s in eeSkills:
|
324 |
+
sk.extend(s)
|
325 |
+
sen=viewTBL('senRef',fields=['sen'],filterOn=[('id',str(row[2]))])[0][0]
|
326 |
+
anEE=ee(sen,row[1].lower().strip(),int(row[2]),row[3],row[4],row[5],row[8]+wkHrs,skills=sk) #Pull info from Refusals sheet
|
327 |
+
eeDict[anEE.eeID]=anEE
|
328 |
+
elif tp=='nm':
|
329 |
+
#Just need peoples names and EEID's linked from this.. enter dummy data for seniority, training, etc
|
330 |
+
if dtaTbl==tempInfoTbl:sen=100000
|
331 |
+
else: sen=1
|
332 |
+
anEE=ee(sen,row[1].lower().strip(),int(row[2]),row[3],row[4],row[5],row[8]+wkHrs,skills=[])
|
333 |
+
eeDict[anEE.dispNm().lower().replace(' ','-')]=anEE
|
334 |
+
return eeDict
|
335 |
+
|
336 |
+
def makeSlots(eeDict,AllSlots):
|
337 |
+
openSlots={} #Open here meaning unassigned.. Will be required when it comes time to force
|
338 |
+
for row in AllSlots:
|
339 |
+
if row[6]==1: #Check that the slot generation record is labelled as 'active'
|
340 |
+
for i in range(row[0],row[1]+1): #Generate a slot for each index over the range indicated... add 1 because python Range fn not inclusive of end point
|
341 |
+
sl=Slot(i, row[2],dispToTrn(row[2]))
|
342 |
+
#Determine how many eligible volunteers for this slot
|
343 |
+
elig=[] #To track how many people trained
|
344 |
+
for rec in viewTBL('allPollData',filterOn=[('slot_'+str(sl.seqID),'y')]): # iterate through results (employee info's) of query on who said yes to working at the time of this slot
|
345 |
+
if sl.dispNm in eeDict[rec[0]].skills: elig.append(rec[0]) #Append EEID to list 'elig' if the ee is trained on the job
|
346 |
+
sl.eligVol=elig # TTake len() to see number of eligible volunteers for the slot.
|
347 |
+
openSlots[str(sl.seqID)+'_'+str(sl.dispNm)]=sl #Enter it into the dictionary
|
348 |
+
return openSlots
|
349 |
+
|
350 |
+
def preProcessData(Acrew,wkHrs,FtBook,TempBook,AssnBook,PollBook,pNT=False,assnWWF=False,pVol=True,xtraDays=None,maxI=100):
|
351 |
+
"""A function to take input data and generate all necessary tables and objects in memory to carry out algorithm. Return Schedule object containing all workSlot objects, and dictioanry fo all employee objects"""
|
352 |
+
ftInfoTbl, ftSkillsMtx, tempInfoTbl, tempSkillsMtx, AssignmentsTbl, slot_Legend, JobTrnCrossRef,pollDict,AllSlots,senList=pullTbls(FtBook,TempBook,AssnBook,PollBook)
|
353 |
+
#GenerateMasterPollTbl to facilitate making the Slots... require having a table with all employee preferences.
|
354 |
+
generateMasterPollTbl(pollDict)
|
355 |
+
#Generate Worker Objects, and assign to dictionary keyed by eeID (numeric key, not string keys)
|
356 |
+
eeDict=makeEEdict(ftInfoTbl,tempInfoTbl,wkHrs)
|
357 |
+
#Generate Schedule Slot objects (all unassigned slots for weekend)
|
358 |
+
allSlots=makeSlots(eeDict,AllSlots)
|
359 |
+
return Schedule(Acrew,allSlots,eeDict,AssignmentsTbl,senList,pollDict,slot_Legend,pNT=pNT,assnWWF=assnWWF,pVol=pVol,xtraDays=xtraDays,maxI=maxI)
|
360 |
+
|
361 |
+
|
362 |
+
def getEEinfo(FtBook,TempBook): #Need to make volunteer shift data puller
|
363 |
+
"""Generate employee objects so as to be able to use their names to read pre generated schedule template."""
|
364 |
+
a=getFTinfo(FtBook) #to sqlite
|
365 |
+
b=getFTskills(FtBook) #to sqlite
|
366 |
+
b=[[int(d[0]),d[1]] for d in b] #Cast EEid to numeric value
|
367 |
+
c=getTempinfo(TempBook) #to sqlite
|
368 |
+
d=getTempskills(TempBook) #to sqlite
|
369 |
+
d=[[int(data[0]),data[1]] for data in d] #Cast EEid to numeric value
|
370 |
+
|
371 |
+
addTBL("FTinfo",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=a,addOn=False)
|
372 |
+
addTBL("TempInfo",fields=['sen','crew','id','last','first','ytd','totref','totchrg','wtdOT'],data=c,addOn=False)
|
373 |
+
|
374 |
+
return a,c
|
375 |
+
|
376 |
+
def addRecs(flNm,shNm,tblNm,data,otptNm='AutoPrimed_Template.xlsx'):
|
377 |
+
"""Adds data to existing excel table in new rows. Used to flesh out tables in visual template (blank tables)"""
|
378 |
+
wb=pyxl.load_workbook(flNm)
|
379 |
+
ws=wb[shNm]
|
380 |
+
t=ws.tables[tblNm]
|
381 |
+
ref=t.ref
|
382 |
+
row,col=ws[ref[ref.index(':')+1:]].row,ws[ref[:ref.index(':')]].column
|
383 |
+
records=data
|
384 |
+
for rec in records:
|
385 |
+
for i in range(len(rec)):
|
386 |
+
ws.cell(column=col+i,row=row+records.index(rec)+1).value=rec[i]
|
387 |
+
newT=pyxl.worksheet.table.Table(displayName=t.displayName,ref=t.ref[:-len(str(row))]+str(row+len(records)))
|
388 |
+
style = pyxl.worksheet.table.TableStyleInfo(name="TableStyleLight1",showRowStripes=True)
|
389 |
+
newT.tableStyleInfo = style
|
390 |
+
# del ws.tables[tblNm]
|
391 |
+
# ws.add_table(newT)
|
392 |
+
ws.tables[tblNm]=newT
|
393 |
+
wb.save(filename=otptNm)
|
394 |
+
|
395 |
+
|
396 |
+
def translate_Visual_Template(flNm,ftRef=None,tRef=None):
|
397 |
+
"""Takes in an assignment list file, and composes the All_Slots table and Assignment_List table by reading the Visual Template"""
|
398 |
+
ftInf,tInf=getEEinfo(ftRef,tRef)
|
399 |
+
eeDict=makeEEdict(ftInf,tInf,tp='nm')
|
400 |
+
# return eeDict
|
401 |
+
wb=pyxl.load_workbook(flNm)
|
402 |
+
ws=wb['Visual_Template']
|
403 |
+
#===========================================
|
404 |
+
#Print out All_Slots table. Simply observe which jobs are present
|
405 |
+
data=[]
|
406 |
+
jbNms={}
|
407 |
+
maxR=5
|
408 |
+
for i in range(5,100): #Based on template job names start at row 5
|
409 |
+
if ws['A'+str(i)].value!=None: #Typically just 25 jobs, should be extra, so skip blanks
|
410 |
+
data.append([1,24,ws['A'+str(i)].value,'','','',1])
|
411 |
+
jbNms[i]=ws['A'+str(i)].value #key job name by row id
|
412 |
+
if i>maxR: maxR=i #Track row of last job assigned
|
413 |
+
addRecs(flNm,'All_Slots','All_Slots',data)
|
414 |
+
#===========================================
|
415 |
+
#Print out Assignment_List
|
416 |
+
data=[]
|
417 |
+
got=[]
|
418 |
+
#First, identify all slots that are part of a merged cell, so we know to skip over those coordinates when we get to them when iterating over every single one
|
419 |
+
for mRng in ws.merged_cells.ranges:
|
420 |
+
rw,cl=next(mRng.cells) #Grab the first cell coordinates within merged range as this is where text is stored
|
421 |
+
if rw>4: #Do not capture title etc.
|
422 |
+
if ws.cell(row=rw,column=cl).value==None:
|
423 |
+
pass #if a blank cell was left merged, skip it. Therefore it will be assigned as normal
|
424 |
+
else:
|
425 |
+
got.extend(mRng.cells) #Gather up merged coords into got tracker
|
426 |
+
if ws.cell(row=rw,column=cl).fill==pyxl.styles.PatternFill(fill_type="solid",start_color='FFCC99FF',end_color='FFCC99FF'):
|
427 |
+
tp='F'
|
428 |
+
elif ws.cell(row=rw,column=cl).fill==pyxl.styles.PatternFill(fill_type="solid",start_color='FF00B0F0',end_color='FF00B0F0'):
|
429 |
+
tp='WWF'
|
430 |
+
elif 'N/A' in ws.cell(row=rw,column=cl).value:
|
431 |
+
tp='DNS'
|
432 |
+
else: tp='V'
|
433 |
+
#Active=1, assnType,start slot, end slot, eeid, job
|
434 |
+
#slots are -1 because col 1 in excel is job name. Unkn if 2 or 3 cells mrged so use first cell cooridnate already captured, and known that last cell is last entry in got list
|
435 |
+
if tp=='DNS':
|
436 |
+
data.append([1,tp,cl-1,got[-1][1]-1,"",jbNms[rw]])
|
437 |
+
else:
|
438 |
+
if ' ' in ws.cell(row=rw,column=cl).value: #Pull name to use as dictionary key as anything before appearance of a space
|
439 |
+
nm=ws.cell(row=rw,column=cl).value[:ws.cell(row=rw,column=cl).value.index(' ')].lower()
|
440 |
+
else: nm=ws.cell(row=rw,column=cl).value.lower() #No space, use name as appears
|
441 |
+
try:
|
442 |
+
data.append([1,tp,cl-1,got[-1][1]-1,eeDict[nm].eeID,jbNms[rw]])
|
443 |
+
except KeyError:
|
444 |
+
print('The name '+nm+' identified in the visual template could not be associated with EE data from the refusal sheets. Check the formatting')
|
445 |
+
#Note that, at this time, the output file already exists with previously added rows in first table. therefore refer to this file as the file to write to, in following command.
|
446 |
+
addRecs('AutoPrimed_Template.xlsx','Assignment_List','Assn_List',data)
|
447 |
+
# wb.save('wow.xlsx')
|
448 |
+
# new=deepcopy(wb)
|
449 |
+
# new.save('AutoPrimed_Template.xlsx')
|
450 |
+
return 'AutoPrimed_Template.xlsx'
|
Tabs(Awaiting_Bug_Fix)_app.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from datetime import datetime
|
3 |
+
import SchedBuilderUtyModule as tls
|
4 |
+
import SchedBuilderClasses2 as cls
|
5 |
+
#Just in case:
|
6 |
+
import openpyxl as pyxl
|
7 |
+
import pandas as pd
|
8 |
+
|
9 |
+
import numpy as np
|
10 |
+
from copy import deepcopy
|
11 |
+
|
12 |
+
#######################
|
13 |
+
#First define the functions
|
14 |
+
def PrimeVisualTemplate(vTmplFl,FTrefFl,TempRefFl):
|
15 |
+
primedTemplateName=tls.translate_Visual_Template(vTmplFl,FTrefFl,TempRefFl)
|
16 |
+
return primedTemplateName,primedTemplateName,datetime.now(),datetime.now(),FTrefFl.name,TempRefFl.name #Return twice so as to send file to two interface entities (file holders), plus time stampers, return the refusal sheets to send to B
|
17 |
+
|
18 |
+
def GenerateSchedule(schedWWF,xtraDays,wkHrs,DaysCrew,AssnFl,FTrefFl,TempRefFl,PollFl,stop1=0,stop2=0,template=False):
|
19 |
+
stop=(stop1,stop2)
|
20 |
+
assnWWF=schedWWF
|
21 |
+
if assnWWF=='Yes': assnWWF=True
|
22 |
+
if assnWWF=='No': assnWWF=True
|
23 |
+
xtraDays=xtraDays
|
24 |
+
Acrew=DaysCrew
|
25 |
+
wkHrs=wkHrs
|
26 |
+
mySched=tls.preProcessData(Acrew,wkHrs,FTrefFl,TempRefFl,AssnFl,PollFl,assnWWF=assnWWF,xtraDays=xtraDays)
|
27 |
+
mySched.evalAssnList()
|
28 |
+
mySched.proofEligVol()
|
29 |
+
mySched.proofEligVol()#i don't know man i found in testing sometimes it wasnt completing properly the first time around
|
30 |
+
if template==True: #Case of template viewer
|
31 |
+
flNm=mySched.printToExcel()
|
32 |
+
return flNm,datetime.now()
|
33 |
+
sch=mySched.fillOutSched_v3(stop=stop) #Stop is not None when submitted from tab C
|
34 |
+
flNm=sch.printToExcel()
|
35 |
+
return flNm,datetime.now()
|
36 |
+
|
37 |
+
def GenerateSchedule1(schedWWF,xtraDays,wkHrs,DaysCrew,AssnFl,FTrefFl,TempRefFl,PollFl,stop=None,template=True):
|
38 |
+
assnWWF=schedWWF
|
39 |
+
if assnWWF=='Yes': assnWWF=True
|
40 |
+
if assnWWF=='No': assnWWF=True
|
41 |
+
xtraDays=xtraDays
|
42 |
+
Acrew=DaysCrew
|
43 |
+
wkHrs=wkHrs
|
44 |
+
mySched=tls.preProcessData(Acrew,wkHrs,FTrefFl,TempRefFl,AssnFl,PollFl,assnWWF=assnWWF,xtraDays=xtraDays)
|
45 |
+
mySched.evalAssnList()
|
46 |
+
mySched.proofEligVol()
|
47 |
+
mySched.proofEligVol()#i don't know man i found in testing sometimes it wasnt completing properly the first time around
|
48 |
+
if template==True: #Case of template viewer
|
49 |
+
flNm=mySched.printToExcel()
|
50 |
+
return flNm,datetime.now()
|
51 |
+
sch=mySched.fillOutSched_v3(stop=stop) #Stop is not None when submitted from tab C
|
52 |
+
flNm=sch.printToExcel()
|
53 |
+
return flNm,datetime.now()
|
54 |
+
|
55 |
+
|
56 |
+
#######################
|
57 |
+
#Second Defining the Interface
|
58 |
+
with gr.Blocks() as demo:
|
59 |
+
with gr.Tab("A - Visual Template Builder"):
|
60 |
+
gr.Markdown("On this tab you can input a purely visual template (assignment tables empty) and have it primed for use in the scheduling algorithm. The info generated here will also be sent as inputs to tab B. See the documentation for more details on required formatting")
|
61 |
+
with gr.Row():
|
62 |
+
A_fl_FTref=gr.File(label="Full Time Refusals Sheet")
|
63 |
+
A_fl_Tref=gr.File(label="Temp Refusal Sheet")
|
64 |
+
with gr.Row():
|
65 |
+
A_fl_VT=gr.File(label="Visual Template File")
|
66 |
+
with gr.Column():
|
67 |
+
A_fl_PT=gr.File(label="Primed Template File")
|
68 |
+
A_tx_PTtimestamp=gr.Textbox(label="File Change Timestamp - Primed Template",placeholder="Waiting for file")
|
69 |
+
A_bt_PT = gr.Button("Prime Visual Template")
|
70 |
+
with gr.Tab("B - Scheduler"):
|
71 |
+
gr.Markdown("On this tab the inputs are used to generate a weekend schedule. The Template and refusal sheet files are carried over (with slightly garbled names) when generated using tab A. The schedule is considered 'primed' when the tables on the secondary sheets match what is indicated on the visual template. The other inputs on this sheet should be selected per the circumstances. For example. If Friday is part of the weekend to be scheduled, Select '32 hrs', 'Friday', and 'yes to WWF scheduling'. Normal non-long weekend will not assign OT to WWF, and be 40 hour weeks without Friday or Monday to be scheduled.")
|
72 |
+
with gr.Row():
|
73 |
+
B_fl_FTref=gr.File(label="Full Time Refusals Sheet")
|
74 |
+
|
75 |
+
B_fl_Tref=gr.File(label="Temp Refusal Sheet")
|
76 |
+
with gr.Row():
|
77 |
+
with gr.Column():
|
78 |
+
B_fl_PT=gr.File(label="Primed Template File")
|
79 |
+
B_tx_PTtimestamp=gr.Textbox(label="File Change Timestamp - Primed Template",placeholder="Waiting for file")
|
80 |
+
with gr.Column():
|
81 |
+
with gr.Row():
|
82 |
+
B_fl_Pl=gr.File(label="Polling File")
|
83 |
+
with gr.Tab("Non-File Inputs"):
|
84 |
+
with gr.Row():
|
85 |
+
B_wwfOT=gr.Radio(["Yes", "No"],label="Assign OT to WWF? (If 'yes', WWF will be considered for filling in slots beyond their prescribed shifts in the Assignment List)")
|
86 |
+
B_dayCrew=gr.Radio(["Bud","Blue"],label="Which crew is on A shift this week?")
|
87 |
+
with gr.Row():
|
88 |
+
B_wkHrs=gr.Radio([32, 40],label="Regular Work Hours This Week?")
|
89 |
+
B_xtraDay=gr.CheckboxGroup(["Friday", "Monday"], label="Check the boxes as appropriate if scheduling long weekend")
|
90 |
+
B_bt_MS = gr.Button("Generate Schedule")
|
91 |
+
B_fl_FS=gr.File(label="Generated Schedule")
|
92 |
+
B_tx_FTtimestamp=gr.Textbox(label="File Change Timestamp - Completed Schedule",placeholder="Waiting for first run")
|
93 |
+
with gr.Tab("C - Review"):
|
94 |
+
with gr.Tab("Inspect Template"):
|
95 |
+
gr.Markdown("On this tab you can have the program generate a schedule using only the template file so as to confirm the template was entered correctly for program interpretation. This process builds the template using all inputs present in tab B. Inputs are required for building the template.")
|
96 |
+
C_bt_MT = gr.Button("Generate Template")
|
97 |
+
C_fl_T=gr.File(label="Generated Template")
|
98 |
+
C_tx_Ttimestamp=gr.Textbox(label="File Change Timestamp - Generated Template",placeholder="Waiting for first run.")
|
99 |
+
with gr.Tab("Force Stop Mid-Scheduling"):
|
100 |
+
gr.Markdown("On this tab you can observe what a schedule looked like after making a specific number of assignments. Iteration number can be retrieved from the bottom of the verbose assignment list tab of a generated schedule. If the specified iteration and assignment number combination are not encountered, this function will simply re generate the entire schedule. This function only quits schedule building after the template. An assignment number within the template building portion will not be quit on, and it will proceed to build full schedule.")
|
101 |
+
with gr.Row():
|
102 |
+
with gr.Column():
|
103 |
+
with gr.Row():
|
104 |
+
C_nm_PS = gr.Number(label="Limit Number for Total Assignments")
|
105 |
+
C_nm_IN = gr.Number(label="Iteration Number To Stop On")
|
106 |
+
with gr.Column():
|
107 |
+
C_fl_PS=gr.File(label="Partially Complete Schedule")
|
108 |
+
C_tx_ts2 = gr.Textbox(label="Time Of Partial Schedule Generation",placeholder="N/A")
|
109 |
+
C_bt_PS = gr.Button("Partially Generate Schedule")
|
110 |
+
|
111 |
+
#######################
|
112 |
+
#Third Define the Interactions
|
113 |
+
A_bt_PT.click(PrimeVisualTemplate,[A_fl_VT,A_fl_FTref,A_fl_Tref],[A_fl_PT,B_fl_PT,A_tx_PTtimestamp,B_tx_PTtimestamp,B_fl_FTref,B_fl_Tref])
|
114 |
+
B_bt_MS.click(GenerateSchedule,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl],[B_fl_FS,B_tx_FTtimestamp])
|
115 |
+
C_bt_MT.click(GenerateSchedule1,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl],[C_fl_T,C_tx_Ttimestamp])
|
116 |
+
C_bt_PS.click(GenerateSchedule,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl,C_nm_IN,C_nm_PS],[C_fl_PS,C_tx_ts2])
|
117 |
+
demo.launch()
|
app.py
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from datetime import datetime
|
3 |
+
import SchedBuilderUtyModule as tls
|
4 |
+
import SchedBuilderClasses2 as cls
|
5 |
+
#Just in case:
|
6 |
+
import openpyxl as pyxl
|
7 |
+
import pandas as pd
|
8 |
+
|
9 |
+
import numpy as np
|
10 |
+
from copy import deepcopy
|
11 |
+
|
12 |
+
#######################
|
13 |
+
#First define the functions
|
14 |
+
def PrimeVisualTemplate(vTmplFl,FTrefFl,TempRefFl):
|
15 |
+
primedTemplateName=tls.translate_Visual_Template(vTmplFl,FTrefFl,TempRefFl)
|
16 |
+
return primedTemplateName,primedTemplateName,datetime.now(),datetime.now(),FTrefFl.name,TempRefFl.name #Return twice so as to send file to two interface entities (file holders), plus time stampers, return the refusal sheets to send to B
|
17 |
+
|
18 |
+
def GenerateSchedule(schedWWF,xtraDays,wkHrs,DaysCrew,AssnFl,FTrefFl,TempRefFl,PollFl,stop1=0,stop2=0,template=False):
|
19 |
+
stop=(stop1,stop2)
|
20 |
+
assnWWF=schedWWF
|
21 |
+
if assnWWF=='Yes': assnWWF=True
|
22 |
+
if assnWWF=='No': assnWWF=True
|
23 |
+
xtraDays=xtraDays
|
24 |
+
Acrew=DaysCrew
|
25 |
+
wkHrs=wkHrs
|
26 |
+
mySched=tls.preProcessData(Acrew,wkHrs,FTrefFl,TempRefFl,AssnFl,PollFl,assnWWF=assnWWF,xtraDays=xtraDays)
|
27 |
+
mySched.evalAssnList()
|
28 |
+
mySched.proofEligVol()
|
29 |
+
mySched.proofEligVol()#i don't know man i found in testing sometimes it wasnt completing properly the first time around
|
30 |
+
if template==True: #Case of template viewer
|
31 |
+
flNm=mySched.printToExcel()
|
32 |
+
return flNm,datetime.now()
|
33 |
+
sch=mySched.fillOutSched_v3(stop=stop) #Stop is not None when submitted from tab C
|
34 |
+
flNm=sch.printToExcel()
|
35 |
+
return flNm,datetime.now()
|
36 |
+
|
37 |
+
def GenerateSchedule1(schedWWF,xtraDays,wkHrs,DaysCrew,AssnFl,FTrefFl,TempRefFl,PollFl,stop=None,template=True):
|
38 |
+
assnWWF=schedWWF
|
39 |
+
if assnWWF=='Yes': assnWWF=True
|
40 |
+
if assnWWF=='No': assnWWF=True
|
41 |
+
xtraDays=xtraDays
|
42 |
+
Acrew=DaysCrew
|
43 |
+
wkHrs=wkHrs
|
44 |
+
mySched=tls.preProcessData(Acrew,wkHrs,FTrefFl,TempRefFl,AssnFl,PollFl,assnWWF=assnWWF,xtraDays=xtraDays)
|
45 |
+
mySched.evalAssnList()
|
46 |
+
mySched.proofEligVol()
|
47 |
+
mySched.proofEligVol()#i don't know man i found in testing sometimes it wasnt completing properly the first time around
|
48 |
+
if template==True: #Case of template viewer
|
49 |
+
flNm=mySched.printToExcel()
|
50 |
+
return flNm,datetime.now()
|
51 |
+
sch=mySched.fillOutSched_v3(stop=stop) #Stop is not None when submitted from tab C
|
52 |
+
flNm=sch.printToExcel()
|
53 |
+
return flNm,datetime.now()
|
54 |
+
|
55 |
+
|
56 |
+
#######################
|
57 |
+
#Second Defining the Interface
|
58 |
+
with gr.Blocks() as demo:
|
59 |
+
# with gr.Tab("A - Visual Template Builder"):
|
60 |
+
gr.Markdown("In this section you can input a purely visual template (assignment tables empty) and have it primed for use in the scheduling algorithm. The info generated here will also be sent as inputs to tab B. See the documentation for more details on required formatting")
|
61 |
+
with gr.Row():
|
62 |
+
A_fl_FTref=gr.File(label="Full Time Refusals Sheet")
|
63 |
+
A_fl_Tref=gr.File(label="Temp Refusal Sheet")
|
64 |
+
with gr.Row():
|
65 |
+
A_fl_VT=gr.File(label="Visual Template File")
|
66 |
+
with gr.Column():
|
67 |
+
A_fl_PT=gr.File(label="Primed Template File")
|
68 |
+
A_tx_PTtimestamp=gr.Textbox(label="File Change Timestamp - Primed Template",placeholder="Waiting for file")
|
69 |
+
A_bt_PT = gr.Button("Prime Visual Template")
|
70 |
+
# with gr.Tab("B - Scheduler"):
|
71 |
+
gr.Markdown("In this tab the inputs are used to generate a weekend schedule. The Template and refusal sheet files are carried over (with slightly garbled names) when generated using tab A. The schedule is considered 'primed' when the tables on the secondary sheets match what is indicated on the visual template. The other inputs on this sheet should be selected per the circumstances. For example. If Friday is part of the weekend to be scheduled, Select '32 hrs', 'Friday', and 'yes to WWF scheduling'. Normal non-long weekend will not assign OT to WWF, and be 40 hour weeks without Friday or Monday to be scheduled.")
|
72 |
+
with gr.Row():
|
73 |
+
B_fl_FTref=gr.File(label="Full Time Refusals Sheet")
|
74 |
+
|
75 |
+
B_fl_Tref=gr.File(label="Temp Refusal Sheet")
|
76 |
+
with gr.Row():
|
77 |
+
with gr.Column():
|
78 |
+
B_fl_PT=gr.File(label="Primed Template File")
|
79 |
+
B_tx_PTtimestamp=gr.Textbox(label="File Change Timestamp - Primed Template",placeholder="Waiting for file")
|
80 |
+
with gr.Column():
|
81 |
+
with gr.Row():
|
82 |
+
B_fl_Pl=gr.File(label="Polling File")
|
83 |
+
with gr.Tab("Non-File Inputs"):
|
84 |
+
with gr.Row():
|
85 |
+
B_wwfOT=gr.Radio(["Yes", "No"],label="Assign OT to WWF? (If 'yes', WWF will be considered for filling in slots beyond their prescribed shifts in the Assignment List)")
|
86 |
+
B_dayCrew=gr.Radio(["Bud","Blue"],label="Which crew is on A shift this week?")
|
87 |
+
with gr.Row():
|
88 |
+
B_wkHrs=gr.Radio([32, 40],label="Regular Work Hours This Week?")
|
89 |
+
B_xtraDay=gr.CheckboxGroup(["Friday", "Monday"], label="Check the boxes as appropriate if scheduling long weekend")
|
90 |
+
B_bt_MS = gr.Button("Generate Schedule")
|
91 |
+
B_fl_FS=gr.File(label="Generated Schedule")
|
92 |
+
B_tx_FTtimestamp=gr.Textbox(label="File Change Timestamp - Completed Schedule",placeholder="Waiting for first run")
|
93 |
+
# with gr.Tab("C - Review"):
|
94 |
+
# with gr.Tab("Inspect Template"):
|
95 |
+
gr.Markdown("In this section you can have the program generate a schedule using only the template file so as to confirm the template was entered correctly for program interpretation. This process builds the template using all inputs present in tab B. Inputs are required for building the template.")
|
96 |
+
C_bt_MT = gr.Button("Generate Template")
|
97 |
+
C_fl_T=gr.File(label="Generated Template")
|
98 |
+
C_tx_Ttimestamp=gr.Textbox(label="File Change Timestamp - Generated Template",placeholder="Waiting for first run.")
|
99 |
+
# with gr.Tab("Force Stop Mid-Scheduling"):
|
100 |
+
gr.Markdown("In this section you can observe what a schedule looked like after making a specific number of assignments. Iteration number can be retrieved from the bottom of the verbose assignment list tab of a generated schedule. If the specified iteration and assignment number combination are not encountered, this function will simply re generate the entire schedule. This function only quits schedule building after the template. An assignment number within the template building portion will not be quit on, and it will proceed to build full schedule.")
|
101 |
+
with gr.Row():
|
102 |
+
with gr.Column():
|
103 |
+
with gr.Row():
|
104 |
+
C_nm_PS = gr.Number(label="Limit Number for Total Assignments")
|
105 |
+
C_nm_IN = gr.Number(label="Iteration Number To Stop On")
|
106 |
+
with gr.Column():
|
107 |
+
C_fl_PS=gr.File(label="Partially Complete Schedule")
|
108 |
+
C_tx_ts2 = gr.Textbox(label="Time Of Partial Schedule Generation",placeholder="N/A")
|
109 |
+
C_bt_PS = gr.Button("Partially Generate Schedule")
|
110 |
+
|
111 |
+
#######################
|
112 |
+
#Third Define the Interactions
|
113 |
+
A_bt_PT.click(PrimeVisualTemplate,[A_fl_VT,A_fl_FTref,A_fl_Tref],[A_fl_PT,B_fl_PT,A_tx_PTtimestamp,B_tx_PTtimestamp,B_fl_FTref,B_fl_Tref])
|
114 |
+
B_bt_MS.click(GenerateSchedule,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl],[B_fl_FS,B_tx_FTtimestamp])
|
115 |
+
C_bt_MT.click(GenerateSchedule1,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl],[C_fl_T,C_tx_Ttimestamp])
|
116 |
+
C_bt_PS.click(GenerateSchedule,[B_wwfOT,B_xtraDay,B_wkHrs,B_dayCrew,B_fl_PT,B_fl_FTref,B_fl_Tref,B_fl_Pl,C_nm_IN,C_nm_PS],[C_fl_PS,C_tx_ts2])
|
117 |
+
demo.launch()
|
requirements.txt
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
openpyxl
|
2 |
+
datetime
|