# added comments for testing # Constants kLogfile = "../TMCrepo/trunk/webapps/dynamicdata/LCSlogfile.txt" kSimtimefile = "sim_elapsedtime.json" #"../webapps/dynamicdata/sim_elapsed_time.json" hwys = ['','1','5', '22', '55', '57', '73', '74', '91', '133', '142', '241', '261', '405', '605'] hwyDirections = ['','NB', 'SB', 'NB/SB','EB','WB','EB/WB'] # Names to appear in the username dropdown box users = [] users.append('') # Show the login page def index(): userfile = open('student_names.txt','r') # read the file into a buffer buffer = [line.strip() for line in userfile.readlines()] # Append each name in the buffer to the users list, skipping blank lines for item in buffer: if len(item) > 0: users.append(item) # build the form form = FORM(LABEL('User:',_for='username', _class="label username-label"), #INPUT(_name='username', _size='15', _style="font-size: 18px;"), BR(), SELECT(users,_name='username',requires=IS_LENGTH(minsize=1,error_message='Must select a user from the list.')),BR(), INPUT(_type='submit',_value="Log in", _class=" btn btn-primary", _style="margin-top: 3%;")) if form.process().accepted: # Put the username entry into the session variable session.username = reverseNameFields(form.vars.username) redirect(URL('home')) return dict(form=form) def home(): return dict(name=session.username) def help(): return dict() # List all the current records in the database - remove in final application def list(): highways = db().select(db.closures.ALL, orderby=db.closures.closureid) return dict(highways = highways) # Show details of a single record - remove in final application def show(): # Retrieve the requested log entry from the database # Assumes the requested entry exists in the db (no error handling yet) hwy = db(db.closures.lognum == request.args(0)).select().first() return dict(hwy=hwy) # Display a search form def search(): form = FORM(LABEL('ClosureID/Log:',_for='closureid', _class="label"), INPUT(_name='closureid',_size='7', _style="margin-right: 5px;"), INPUT(_name='lognum',_size='3'), XML('   '), LABEL('Route: ',_for='route', _class="label"), SELECT(hwys,_name='route'), XML('   '), LABEL('Direction: ',_for='direction', _class="label"), SELECT(hwyDirections,_name='direction'), BR(),BR(), LABEL('Dates:',_for='startdate', _class="label"), INPUT(_name='startdate',_class='date'), XML('   '), LABEL(' to:',_for='enddate', _class="label"), INPUT(_name='enddate',_class='date'),BR(), INPUT(_value="Search", _type='submit', _class="btn btn-primary btn-default", _style="margin: 7% 45% 2% 40%;")) if form.process(onvalidation=special_validation).accepted: # Put the form fields into the session variables session.closureid = form.vars.closureid.strip().upper() session.lognum = form.vars.lognum.strip() session.startdate = form.vars.startdate.strip() session.enddate = form.vars.enddate.strip() session.route = form.vars.route session.direction = form.vars.direction redirect(URL('results')) return dict(form=form) # Search Form: Special validation check to reject lognum without closureID def special_validation(form): # Error if a lognum was given and no closure id if (len(form.vars.lognum) > 0 and len(form.vars.closureid) == 0): form.errors.lognum = 'Must provide a closureID when specifying a log number' # Show the item that was found in the search def results(): # query object is equivalent to the where clause in query query = True msg = "" if (len(session.closureid) != 0): query = (db.closures.closureid == session.closureid) msg += " Closure ID = " + session.closureid if (len(session.lognum) != 0): query = query & (db.closures.lognum == session.lognum) msg += " Log number = " + session.lognum if (len(session.route) != 0): query = query & (db.closures.route == session.route) msg += " Route = " + session.route if (len(session.direction) != 0): query = query & (db.closures.direction == session.direction) msg += " Route = " + session.route if (len(session.startdate) != 0): query = query & (db.closures.startdate >= session.startdate) msg += "Start date = " + session.startdate if (len(session.enddate) != 0): query = query & (db.closures.enddate <= session.enddate) msg += "End date = " + session.enddate # if no restrictions entered then get all entries if query == True : hwy = db().select(db.closures.ALL) msg = "ALL" else: # get entries with the matching requirements hwy = db(query).select() count = len(hwy) # Show the results in table format. Get the radio call number from supervisor name lookup header = THEAD(TR(TH(''), TH('DTM',BR(),'Area'), TH('Closure ID/',BR(),'Log No.'),TH('Route & Dir/',BR(),'Type of Closure'),TH('Start Date/',BR(),'End Date/',BR(),'Est. Delay'),TH('Facility/Lanes'),TH('Limits'),TH('Work'), TH('TMP:',BR(),'Cozeep/',BR(),'Detour'),TH('Requestor/',BR(),'Radio Call No.'))) multiform = [] # Iterates over all search results for row in hwy: statusfields = row.closureid +','+ row.lognum + ',1097,' + str(row.s1097user) +','+ str(row.startdate) + ',' + formatTime(row.starttime) +','+str(row.s1097date)+','+ formatTime(row.s1097time) + ',1098,' + str(row.s1098user) +','+ str(row.s1098date)+','+ formatTime(row.s1098time)+ ',1022,' + str(row.s1022user) +','+ str(row.s1022date)+','+ formatTime(row.s1022time) # Each row contains a form with two buttons and columns with fields from database multiform.append(TR(TD( XML(""),BR(), FORM( INPUT(_type='submit',_name='btn2',_value='Show Status Form',_class="submit-button" ), INPUT(_type='hidden',_name='row',_value=row.closureid))), TD(row.closureid[0]),TD(row.closureid,HR(),row.lognum), TD(row.route,' ',row.direction,HR(),row.closuretype), TD(row.startdate,' ',formatTime(row.starttime),HR(),row.enddate,' ',formatTime(row.endtime),HR(),row.estdelay), TD(row.facility,HR(),row.closedlanes), TD(row.startlocation,HR(),row.endlocation), TD(row.worktype), TD(row.tmpcozeep,BR(),row.tmpdetour), TD(row.supervisor,HR(),db(db.supervisors.name == row.supervisor).select().first().radiocallnum) )) session.chosenid = request.vars.row #Pass the hidden field containing the closure ID if request.vars.btn2: redirect(URL('statuslist')) return dict(msg=msg, count=count, highways=hwy, table=header, multiform=multiform) # Show a selected closure with a status update form def statuslist(): closedItems = [] if (session.chosenid): if (type(session.chosenid) is str): retrieved = db(db.closures.closureid == session.chosenid).select().first() closedItems.append(retrieved) else: # This logic is available to show multiple results, for possible future use. for item in session.chosenid: retrieved = db(db.closures.closureid == item).select().first() closedItems.append(retrieved) # Build the table rows tblrows = TR() for row in closedItems: # Construct the status radio buttons; disable if date already in database statusflags = "disabled" if row.s1097date == "" else "" if row.s1097date == "": btn = LABEL('1097'), INPUT(_type='radio', _name='statustype', _value='1097'+row.closureid) else: btn = LABEL('1097 ○',_class='labelgray'), btngroup = btn if row.s1098date == "": btn = LABEL('1098'), INPUT(_type='radio', _name='statustype', _value='1098'+row.closureid) else: btn = LABEL('1098 ○',_class='labelgray'), btngroup += btn if row.s1022date == "": btn = LABEL('1022'), INPUT(_type='radio', _name='statustype', _value='1022'+row.closureid) else: btn = LABEL('1022 ○',_class='labelgray'), btngroup += btn if row.s1097date != "" and row.s1098date != "" and row.s1022date != "": btngroup += BR(),LABEL('Statuser:', _class='labelgray') else: btngroup += BR(),LABEL('Statuser:'),INPUT(_name='statuser',_size='9'),BR(),BR(),INPUT(_type='submit',_value="submit status update",_class="submit-button") # LABEL('1097 ○',_class='colorgray'), INPUT(_type='radio', _name='statustype', _value='1097'+row.closureid),LABEL('1098'), INPUT(_type='radio', _name='statustype', _value='1098'+row.closureid),LABEL('1022'), INPUT(_type='radio', _name='statustype', _value='1022'+row.closureid),BR(), LABEL('Statuser:'),INPUT(_name='statuser',_size='9') tblrows += TR(TD(row.closureid,HR(),row.lognum),TD(row.route,' ',row.direction,HR(),row.closuretype),TD(row.startdate,HR(),row.enddate,HR(),row.estdelay),TD(row.supervisor,BR(),db(db.supervisors.name == row.supervisor).select().first().radiocallnum),TD(btngroup)) form = FORM(BR(), TABLE(THEAD(TR(TH('Closure ID/',BR(),'Log No.'), TH('Route & Dir',BR(),'Type of Closure'),TH('Start Date/',BR(),'End Date/',BR(),'Est. Delay'),TH('Requestor/',BR(),'Radio Call No.'),TH('Status'))), tblrows, _border='1', _cellpadding='5', _width="70%")) else: msg = "No items were selected. Use the checkbox in the lefthand column." form = "" return dict(msg=msg,form=form) if form.process().accepted: #session.flash = 'Status submit acknowledgement appears here.' session.statustype = form.vars.statustype session.statuser = form.vars.statuser redirect(URL('statusAck')) return dict(form=form) # show status update acknowledgement - and update database def statusAck(): if (session.statustype): if (type(session.statustype) is str): msg = "You submitted a status update for " + session.statustype[4:] + ": " + session.statustype[0:4] # Perform the update on the database # Construct the name of the field to update fieldname = "s"+session.statustype[0:4]+"user" db(db.closures.closureid == session.statustype[4:]).update(**{fieldname:session.statuser}) import datetime now = datetime.datetime.today() fieldname = "s"+session.statustype[0:4]+"date" db(db.closures.closureid == session.statustype[4:]).update(**{fieldname:now.strftime("%Y-%m-%d")}) fieldname = "s"+session.statustype[0:4]+"time" db(db.closures.closureid == session.statustype[4:]).update(**{fieldname:now.strftime("%H%M")}) # Log the update to external file logmessage = getSimTime() + ", LCS status update, " + session.username + ", " + session.statustype[4:] + " / " + session.statustype[0:4] + " / " + session.statuser + "\n" text_file = open(kLogfile, "a") text_file.write(logmessage) text_file.close() else: msg = "error because only checking one box is allowed." else: msg = "Error no statustype checkbox was checked" return dict(msg=msg) # Utility functions for formatting def formatTime(msg): if (msg): return msg[0:2]+':'+msg[2:4] else: return "" # Fetch simulation time and format it into a timestamp def getSimTime(): import json,datetime try: jsontime = json.load(open(kSimtimefile,'r')) currentSimTime = jsontime["elapsedtime"] timestamp = str(datetime.timedelta(seconds = int(currentSimTime))) return '0'+timestamp # assume simtime is < 10 and prefix a zero except: # Fallback if missing file, use current time now = datetime.datetime.today() return now.strftime("%H:%M:%S") # Create a new record def submit(): # Don't name this function 'request' because it creates a name conflict with http.request hournames = ['','00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'] closuretypes = ['', 'Lane', 'Full', 'Moving', 'One-Way Traffic', 'Alternating Lanes', 'Traffic Break'] facilities = ['', 'Connector', 'Conventional_Hwy', 'Mainline', 'Off Ramp', 'On Ramp', 'Rest Area', 'Surface Street'] worktypes = ['','AC Paving', 'Accident Investigation', 'Attenuator Repair', 'Blasting', 'Bridge Inspection', 'Bridge Work', 'Brush Fire', 'Chip Seal Operation', 'Concrete Pour', 'Core Drilling', 'Crack Seal Operation', 'Curb/Gutter/Sidewalk Work', 'Drainage Cleaning', 'Drainage Inspection', 'Drainage Work', 'Electrical Work', 'Emergency Work', 'Falsework Installation', 'Falsework Removal', 'Fence Work', 'Filming Activity', 'Fog Seal Operation', 'Graffiti Removal', 'Grinding and Paving', 'Grinding Operation', 'Guardrail Repair', 'Guardrail Work', 'Highway Construction', 'K-rail Installation', 'K-rail Removal', 'Landscape Work', 'Litter Removal', 'Maintenance Operation', 'Median Barrier Work', 'Miscellaneous Work', 'Pavement Marker Replacement', 'Pavement Repair', 'Pavement Work', 'Paving Operation', 'Pile Driving', 'Police Investigation', 'Roadway Excavation', 'Roadway Flooding', 'Sewer Work', 'Shoulder Work', 'Sign Work', 'Slab Replacement', 'Slide Removal', 'Slope Clearing', 'Soundwall Work', 'Special Event', 'Spray Operation', 'Striping Operation', 'Survey Work', 'Sweeping Operation', 'Traffic Signal Work', 'Tree Work', 'Utility Work', 'Vegetation Spraying'] supervisors = [''] # List of names for the dropdown box # Obtain all the supervisor names from the database for row in db().select(db.supervisors.ALL): supervisors.append(row.radiocallnum + ' ' + row.name) crew = [''] # List of names for the dropdown box crewlookup = [] # Obtain all the crew names from the database for row in db().select(db.crew.ALL): crew.append(row.radiocallnum + ' ' + row.name) crewlookup.append(row.radiocallnum + ' ' + row.name) # Build the list of street locations and a hidden cross street lookup table streets = [''] streetlookup = [] for row in db().select(db.streets.ALL, orderby=db.streets.street): streets.append(row.street) streetlookup.append(row.route + ',' + row.street) # Build the list of existing closures existingclosures = [] existingclosures.append("") for row in db().select(db.closures.ALL, orderby=db.closures.closureid): # Omit duplicate ID's (with different lognumbers) if row.closureid not in existingclosures: existingclosures.append(row.closureid) form = FORM( LABEL('*Route',_for='route'), SELECT(hwys,_name='route', _id='routecombo', _onchange='routechanged()', requires=IS_LENGTH(minsize=1,error_message='route cannot be empty')), XML('   '), LABEL('*Direction',_for='direction'), SELECT(hwyDirections,_name='direction', requires=IS_LENGTH(minsize=1,error_message='direction cannot be empty')), XML('   '), LABEL('*Facility',_for='facility'), SELECT(facilities,_name='facility', _id='facilitycombo', requires=IS_LENGTH(minsize=1,error_message='facility cannot be empty')), BR(),BR(), TABLE(TR(TD(),TD(LABEL('*County')),TD(LABEL('*Location'))), TR(TD(LABEL('BEGIN=')),TD(SELECT('ORA',_name='startcounty')), TD(SELECT(streets,_name='startlocation',_id='startlocation')), TR(TD(LABEL('END=')),TD(SELECT('ORA',_name='endcounty')),TD(SELECT(streets,_name='endlocation',_id='endlocation'))))),BR(), LABEL('Date Range:'),BR(), LABEL('From',_for='startdate'),INPUT(_name='startdate',_size='8',_class='date'), XML('   '), LABEL('to:',_for='enddate'), INPUT(_name='enddate',_size='8',_class='date'),XML('      '), LABEL('Times',_for='starttime'),SELECT(hournames,_name='starttime'), LABEL(':',_for='starttimemin'),SELECT('','00','15','30','45','59',_name='starttimemin'), XML('  '), LABEL('to:',_for='endtime'),SELECT(hournames,_name='endtime'), LABEL(':',_for='endtimemin'),SELECT('','00','15','30','45','59',_name='endtimemin'), BR(),BR(), TABLE(TR(TD(LABEL('*Type of Closure') ), TD(LABEL('*Type of Work')), TD(LABEL('Estimated Delay')), TD(LABEL('TMP Details'))), TR(TD(SELECT(closuretypes,_name='closuretype',_id='closuretype', _onchange='closuretypechanged()',requires=IS_LENGTH(minsize=1,error_message='type of closure cannot be empty'))), TD(SELECT(worktypes,_name='worktype', requires=IS_LENGTH(minsize=1,error_message='type of work cannot be empty'))), TD(INPUT(_name='estdelay',_size='4'),'minutes'), TD(INPUT(_type='checkbox',_name='cozeep'),'CoZeep MaZeep/CHP',BR(), INPUT(_type='checkbox', _name='detour'),'Detour Available')), TR(TD(DIV(LABEL("Lanes closed"),DIV(INPUT(_type='checkbox', _name='lanes', _id='lanes', _value=' '), _id='boxes'),_id='lanechooser',_style='display:none')), TD(INPUT(_type='hidden', _name='lanecount', _id='lanecount', _value='4')), TD(), TD()), _width='100%' ), TABLE(TR(TD(LABEL('*Supervisor')), TD(LABEL('Field Rep')) ), TR(TD(SELECT(supervisors,_name='supervisor', _id='supervisorcombo', _onchange='supervisorchanged()', requires=IS_LENGTH(minsize=1,error_message='supervisor cannot be empty'))), TD(SELECT(crew,_name='fieldrep',_id='fieldrep')), TD(XML("    ")), TD('Is this an existing incident?', INPUT(_type='radio',_name='existing',_value='No',_onclick='radioclicked()'), 'No', INPUT(_type='radio',_name='existing',_value='Yes',value='Yes',_onclick='radioclicked()'), 'Yes' ) ), TR(TD(),TD(),TD(), TD(' Select closure ID:',SELECT(existingclosures,_name='existingid'),_id='closureselect'))), TABLE(TR(TD( LABEL('Meeting Place/CHP Contact')), TD(LABEL('Reason for Closure')), TD(LABEL('Additional Remarks / Detour '))), TR(TD(INPUT(_name='meeting', _size='25')),TD(INPUT(_name='reason',_size='25')),TD(INPUT(_name='remarks',_size='25'))) ), BR(), INPUT(_type='submit',_value='Submit Closure', _class="btn btn-primary btn-default", _style="margin: 2% 45% 2% 40%;"), XML('\n'),SELECT(streetlookup,_name='stlookup', _id='stlookup', _class='hideme'), SELECT(crewlookup,_name='crewlookup', _id='crewlookup', _class='hideme')) if form.process(onvalidation=validate_existing_id).accepted: newLognum = calcNextLogNum(form.vars.existingid) if newLognum == '1': newID = calcNextClosureID(form.vars.route) else: newID = form.vars.existingid supervisor_name = form.vars.supervisor[3:] fieldrep_name = form.vars.fieldrep[5:] selectedlanes = buildLanesClosedString(form.vars.lanes,form.vars.lanecount) # Insert the record into the database newrec = db.closures.insert(closureid=newID, lognum=newLognum, route=form.vars.route, direction=form.vars.direction, facility=form.vars.facility, startcounty=form.vars.startcounty, endcounty=form.vars.endcounty, startlocation=form.vars.startlocation, endlocation=form.vars.endlocation, startdate=form.vars.startdate, enddate=form.vars.enddate, starttime=form.vars.starttime+form.vars.starttimemin, endtime=form.vars.endtime+form.vars.endtimemin, closuretype=form.vars.closuretype, closedlanes=selectedlanes, worktype=form.vars.worktype, estdelay=form.vars.estdelay, tmpcozeep=getCheckbox(form.vars.cozeep), tmpdetour=getCheckbox(form.vars.detour), supervisor=supervisor_name, fieldrep=fieldrep_name, s1097date='', s1098date='', s1022date='' ) session.flash = 'New lane closure added: ' + newID + ' ' + newLognum + ': ' + selectedlanes # Log the new closure to external file. Username, closureID, route, dir, type of closure, type of work logmessage = getSimTime() + ", LCS new closure, " + session.username + ", " + newID + '.' + newLognum + ' / ' + form.vars.route + form.vars.direction + ' / ' + form.vars.closuretype + ' / ' + form.vars.worktype + "\n" text_file = open(kLogfile, "a") text_file.write(logmessage) text_file.close() redirect(URL('search.html')) return dict(form=form) # An unlinked page to allow admin to reset the database to simulation start state def resetdb(): form = FORM("Press this button to reset the closure database to its original state at the start of a simulation.",BR(), "Warning: this will delete all the current closures.",BR(), "Import filename is 'db_closures_start.csv'.",BR(), INPUT(_value="Reset Database", _type='submit', _class="btn btn-primary btn-default")) if form.process().accepted: db(db.closures.id > 0).delete() # remove all current records with open('db_closures_start.csv', 'rb') as dumpfile: db.closures.import_from_csv_file(dumpfile) # import from starting state session.flash = 'Closure database has been reset' redirect(URL('search.html')) return dict(form=form) # Validation for existing closure button # If user selected Yes (it's existing incident) then they must select a closure ID def validate_existing_id(form): if form.vars.existing == 'Yes' and form.vars.existingid == '': form.errors.existingid = "Existing incidents require selecting an existing closure ID" # Calculate the closure id to assign to the new closure def calcNextClosureID(routeNum): # Retrieve the last existing closure on this route #item = db(db.closures.closureid.startswith('T'+routeNum)).select().last() # defective item = db(db.closures.route == routeNum).select().last() if (item != None): currID = item.closureid lastchar = currID[-1:] # Get last character of ID lastchar = chr(ord(lastchar) + 1) # increment it to next character (need bounds check) newID = currID[:-1] + lastchar # append char to ID return newID else: return 'T'+routeNum+'AA' # For a non-existing route # If existing incident closure ID is provided, increment the log number by 1 def calcNextLogNum(existingid): if existingid != '': item = db(db.closures.closureid.startswith(existingid)).select().last() prevLog = int(item.lognum) nextLog = prevLog + 1 return str(nextLog) else: return '1' # Convert checkbox value to YES/NO def getCheckbox(ckBox): if (ckBox == "on"): return "YES" else: return "NO" # Swap the fields in a name, changing lastname first to firstname first. def reverseNameFields(formattedname): # Divide the name into last and first names x = formattedname.split(',') # If there isn't a comma, return the entire name if len(x) == 1: return x[0].strip() else: # return the swapped fields return x[1].strip() + ' ' + x[0] # Convert the lanes closed checkboxes into a human readable string # E.g. #1 #3 of 4 # Note: ckBoxGroup parameter contains only checked items def buildLanesClosedString(ckBoxGroup,lanecount): result = "" if ckBoxGroup is not None: # Append each checked value to a string for item in ckBoxGroup: result = result + "#"+item + " " result = result + "of " + lanecount return result