stc

a simple time card webapp
git clone _git@git.brennen.work:stc.git
Log | Files | Refs | README

commit 53c5990a822636e6d7534df8dc949c9bd537dffd
parent 6f27d065358383c7566605492a8c14010520d222
Author: Brennen T. Mazur <brennen@madis.cool>
Date:   Fri, 10 Mar 2023 23:42:08 -0700

completed fleet checkin checkout logic

Diffstat:
Mapp/forms.py | 6+++---
Mapp/routes.py | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mapp/templates/dashboard/fleet/widget.html | 15++++++++++++---
Mseeds.py | 10++++++----
4 files changed, 160 insertions(+), 39 deletions(-)

diff --git a/app/forms.py b/app/forms.py @@ -60,7 +60,7 @@ class AdmnPermissionsForm(FlaskForm):# for each module make Boolean field. Gets class FleetCheckoutForm(FlaskForm): vehicle = SelectField('Vehicle', validators = [DataRequired()]) - start_milage = IntegerField('Starting Milage', validators=[DataRequired()])# Require some sort of validator for check... + start_mileage = IntegerField('Starting Mileage', validators=[DataRequired()])# Require some sort of validator for check... horn = BooleanField('Horn') signals = BooleanField('Signals') tires = BooleanField('Tires') @@ -76,6 +76,6 @@ class FleetCheckoutForm(FlaskForm): checkout = SubmitField('Checkout Vehicle')#Update to take role name for pass to write fn class FleetCheckinForm(FlaskForm): - endmilage = IntegerField('Ending Milage',validators=[DataRequired()])# Require some sort of validator for check... - additionalnotes = StringField('Additional Notes',validators=[optional()])# May not need this at all? + end_mileage = IntegerField('Ending Mileage',validators=[DataRequired()])# Require some sort of validator for check... + incident_notes = StringField('Incident Notes',validators=[optional()])# May not need this at all? checkin = SubmitField('Checkin Vehicle')#Update to take role name for pass to write fn diff --git a/app/routes.py b/app/routes.py @@ -15,12 +15,17 @@ import functools OrganizationName = 'Youth Employment Program' # Maybe pass this as a value though the object for relevant pages??? +#### #### +####### Application Modules Init ####### +#### #### # DB Connection & LoginManager init mongo = PyMongo(app) login_manager = LoginManager(app) login_manager.login_view = 'login' -# User Routes / Queries +#### #### +####### User Routes/Queries ####### +#### #### @app.route('/user/signup', methods=['GET']) def signup(): return User().signup() @@ -32,9 +37,10 @@ def loginModel(): @app.route('/user/signout') def signout(): return User().signout() -# User Routes / Queries -# Time Routes / Queries +#### #### +####### Time Routes/Queries ####### +#### #### @app.route('/time/clockin', methods=['GET', 'POST']) def clockin(): return Time().clockin() @@ -42,23 +48,25 @@ def clockin(): @app.route('/time/clockout', methods=['GET', 'POST']) def clockout(): return Time().clockout() -# Time Routes / Queries -# Fleet Routes / Queries -@app.route('/fleet/vehicle_repair', methods=['GET', 'POST', 'PUT']) +#### #### +####### Vehicle Routes/Queries ####### +#### #### +@app.route('/fleet/vehicle_repair', methods=['GET', 'POST']) def vehicle_repair(): return Fleet().vehicle_repair() -# Fleet Routes / Queries -# Agreement Routes / Queries +#### #### +####### Agreement Routes/Queries ####### +#### #### @app.route('/agreement/document', methods=['GET', 'POST']) def document(): return Agreement().document() -# Agreement Routes / Queries -# Projects Routes / Queries +#### #### +####### Project Routes/Queries ####### +#### #### @app.route('/projects/project', methods=['GET', 'POST', 'PUT']) -# Projects Routes / Queries ##{{{{{{{{{{{{{{{{{{{{{ Decorator Functions }}}}}}}}}}}}}}}}}}}}}## #@login_required @@ -77,13 +85,24 @@ def document(): # return decorated_view ##{{{{{{{{{{{{{{{{{{{{{ END Decorator Functions }}}}}}}}}}}}}}}}}}}}}## -# Page Routes +#================================# +########### ############ +#### PAGE ROUTING #### +########### ############ +#================================# + +#### #### +####### Logout User Route ####### +#### #### @app.route("/logout", methods=['GET']) def logout(): logout_user() return redirect(url_for('login')) +#### #### +####### Login/Root Route ####### +#### #### @app.route('/') @app.route("/login", methods=['GET', 'POST']) def login(): @@ -107,6 +126,9 @@ def login(): return redirect(url_for('login')) return render_template('login.html',form=form,ORGNAME = OrganizationName) +#### #### +####### Load User Data ####### +#### #### #load user_data into User object iff username == stored username @login_manager.user_loader def load_user(username): @@ -117,15 +139,22 @@ def load_user(username): user_obj.password_hash = u['password_hash'] return user_obj +#### #### +####### Dashboard Route ####### +#### #### + @app.route('/index') def hello(): return redirect(url_for('dashboard')) @app.route("/dashboard", methods=['GET', 'POST']) @login_required def dashboard(): - - if datetime.datetime.date == current_user.birthday: - flash("Happy Birthday {}!").format(current_user.fname) + + currentdate = datetime.datetime.now() + #Banner, TODO create a MOTD framework, for admin messages + if currentdate.strftime('%Y-%m-%d') == current_user.birthday: + flash("Happy Birthday {}!".format(current_user.fname)) + ## END Banner dashperms=mongo.db.permissions_collection.find_one({'label': current_user.role},{'dashboard':1,'_id':0}) dashperms=dashperms['dashboard'] @@ -152,12 +181,37 @@ def dashboard(): else: clocked_out = True #End isUserClockedIn() + #Start fleetCheckedOut() + if mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}}): + fleetCheckedOut = True + fleet_id = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["_id"] + vehicle_name = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["vehicle"] + start_mileage = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["start_mileage"] + else: + fleetCheckedOut = False + vehicle_name = '' + #End fleetCheckedOut() + def fleet_check_in(fleet_id,start_mileage,end_mileage,notes=''): + if mongo.db.fleet_collection.find({'_id': fleet_id}, {'end_mileage':{'$exists':False}}): + if end_mileage <= start_mileage: + flash('end mileage less than starting mileage') + elif notes is not '' and end_mileage >= start_mileage: + mongo.db.fleet_collection.update_one({'_id':fleet_id},{'$set':{'end_mileage':end_mileage,'incident_notes':{notes:False}}})#incident_note dict isResolved and string value to display on admin page + elif notes == '' and end_mileage >= start_mileage: + mongo.db.fleet_collection.update_one({'_id':fleet_id},{'$set':{'end_mileage':end_mileage}}) + else: + flash('uncaught logic updating {} with {}'.format(fleet_id,end_mileage)) + mongo.db.fleet_collection.update_one({'_id':fleet_id},{'$set':{'end_mileage':end_mileage}}) + return redirect(url_for('dashboard')) + else: + flash('Unable to check out vehicle') + return redirect(url_for('dashboard')) # Temp values, change to db dependent values availableProjects = ['Project 1', 'Project 2', 'Project 3'] availableVehicles = ['Vehicle 1', 'Vehicle 2', 'Vehicle 3', 'Vehicle 4'] currentProject = 'Project 2' - lastMilage = 103483 + lastMileage = 103483 # END default form values clockinform=PunchclockinWidget() @@ -171,25 +225,20 @@ def dashboard(): clockoutform.projectsSel.default = currentProject fleetoutform.vehicle.choices = availableVehicles fleetoutform.vehicle.default = availableVehicles[0] - fleetoutform.start_milage.default = lastMilage + fleetoutform.start_mileage.default = lastMileage # Currently broken... appears to call constructor(wanted for setting default values) but regenerates csrf_token... clone fn without this call? or overload fn? Haven't looked at documentation yet +# See: https://wtforms.readthedocs.io/en/3.0.x/forms/#wtforms.form.BaseForm.__init__ #clockinform.process() #clockoutform.process() #fleetoutform.process() - #if clockoutform.validate_on_submit(): - #if request.method == 'POST' and clockoutform.validate(): - # mongo.db.time_collection.insert({'clock_out' : [datetime.datetime.utcnow()], - # 'lunch' : clockoutform.lunchBox.data, - # 'per_diem' : clockoutform.per_diemBox.data}) - # return redirect(url_for('dashboard')) - - #if clockinform.validate_on_submit(): Currently will submit all present forms... + #if clockinform.validate_on_submit(): Currently will submit all present forms... replaced w/ below if not clocked_out and request.method == 'POST' and clockoutform.validate(): clock_user_out(time_id,clockoutform.lunchBox.data,clockoutform.per_diemBox.data) return redirect(url_for('dashboard')) +# Can still clock in without checking current vehicle status!!! TODO FIX ME !!! if clocked_out and request.method == 'POST' and clockinform.validate(): mongo.db.time_collection.insert_one({'clock_in' : [datetime.datetime.utcnow()], 'modified_by' : [current_user.username], @@ -198,15 +247,23 @@ def dashboard(): return redirect(url_for('dashboard')) #if fleetoutform.validate_on_submit(): - if request.method == 'POST' and fleetoutform.validate(): + if not fleetCheckedOut and request.method == 'POST' and fleetoutform.validate(): mongo.db.fleet_collection.insert_one({'date':datetime.datetime.today(), # NEED to work on modular way of storing safety checks... might condence to single true if all checked. else returns false and records false datavalue.label in incident_report[] If incident report, remove vehicle from available pool and display widget in admin layout 'vehicle':fleetoutform.vehicle.data, - 'start_milage':fleetoutform.start_milage.data, + 'start_mileage':fleetoutform.start_mileage.data, 'operator':current_user.username, 'additional_notes':fleetoutform.additionalnotes.data}) return redirect(url_for('dashboard')) - return render_template('dashboard/layout.html',permissions=dashperms,clocked_out=clocked_out,clockoutform=clockoutform,clockinform=clockinform,fleetinform=fleetinform,fleetoutform=fleetoutform,clocked_in_users=clocked_in_users,ORGNAME=OrganizationName) + if fleetCheckedOut and request.method == 'POST' and fleetinform.validate(): + fleet_check_in(fleet_id,start_mileage,fleetinform.end_mileage.data,fleetinform.incident_notes.data) + return redirect(url_for('dashboard')) + + return render_template('dashboard/layout.html',permissions=dashperms,clocked_out=clocked_out,clockoutform=clockoutform,clockinform=clockinform,fleetCheckedOut=fleetCheckedOut,vehicle_name=vehicle_name,fleetinform=fleetinform,fleetoutform=fleetoutform,clocked_in_users=clocked_in_users,ORGNAME=OrganizationName) + +#### #### +####### Admin Route ####### +#### #### @app.route("/admin") #@admin_required def admin(): @@ -217,28 +274,43 @@ def admin(): #admnperms=all_permissions.admin return render_template ('admin/layout.html',permissions=adminperms,ORGNAME=OrganizationName) +#### #### +####### Agreement Report Route ####### +#### #### # Report Routes @app.route('/admin/agreement') @login_required def agreement_report(): return render_template ('admin/reports/agreement_report.html', ORGNAME=OrganizationName) +#### #### +####### Employee Report Route ####### +#### #### @app.route('/admin/employee') @login_required def employee_report(): return render_template ('admin/reports/employee_report.html', ORGNAME=OrganizationName) +#### #### +####### Pay Period Report Route ####### +#### #### @app.route('/admin/pay-period') @login_required def pay_period_report(): return render_template ('admin/reports/pay_period_report.html', ORGNAME=OrganizationName) +#### #### +####### Vehicle Report Route ####### +#### #### @app.route('/admin/vehicle') @login_required def vehicle_report(): return render_template ('admin/reports/vehicle_report.html', ORGNAME=OrganizationName) +#### #### +####### Hours Route ####### +#### #### @app.route('/hours/<username>')#modify to take userid ex. /hours<userid> for "admin" currently pulls from current_user... simply always pass username to hours(if possible set a default to current_user) @login_required def hours(username):#userid goes into call to db to get user[] -> then returns formatted table (punchclock/index.html @@ -253,6 +325,9 @@ def hours(username):#userid goes into call to db to get user[] -> then returns f #def fleet(): # return render_template('dashboard/fleet/index.html',ORGNAME=OrganizationName) +#### #### +####### Roles Admin Route ####### +#### #### @app.route("/admin/roles") @login_required def roles(): @@ -260,18 +335,27 @@ def roles(): dashform = DashPermissionsForm() return render_template('admin/roles/updateroles.html',dashform=dashform,admnform=admnform,ORGNAME=OrganizationName) +#### #### +####### Active Users Admin Route ####### +#### #### @app.route("/admin/users/active") @login_required def activeusers(): active = mongo.db.user_collection.find({'is_active':True}) return render_template('admin/users/active.html',activeusers=active,ORGNAME=OrganizationName) +#### #### +####### Inactive Users Admin Route ####### +#### #### @app.route("/admin/users/inactive") @login_required def inactiveusers(): inactive = mongo.db.user_collection.find({'is_active':False}) return render_template('admin/users/inactive.html',inactiveusers=inactive,ORGNAME=OrganizationName) +#### #### +####### New User Admin Route ####### +#### #### @app.route("/admin/newuser", methods=["GET","POST"]) @login_required def newuser(): @@ -318,44 +402,70 @@ def newuser(): return render_template('admin/users/newuser.html',form=form,ORGNAME=OrganizationName) +#### #### +####### Agreement Admin Route ####### +#### #### @app.route("/admin/agreement") @login_required def agreement(): return render_template('admin/agreement/index.html',ORGNAME=OrganizationName) +#### #### +####### Knowlegebase Route ####### +#### #### @app.route("/docs") @login_required def knowlegebase(): return render_template('knowlegebase/index.html',ORGNAME=OrganizationName) -# Page Routes + +#====================================# +############# ############## +#### DEVELOPMENT ROUTES #### +############# ############## +#====================================# # DEVELOPMENT ROUTES, remove/modify permissions before production +#### #### +####### Fleet Data Route ####### +#### #### @app.route("/dev/fleetdata") @login_required def fleetdatalist(): allfleetdata = mongo.db.fleet_collection.find() return render_template('dev/fleetdata.html', allfleetdata=allfleetdata) +#### #### +####### User Data Route ####### +#### #### @app.route("/dev/usrs") @login_required def userslist(): allusers = mongo.db.user_collection.find() return render_template('dev/usrs.html', allusers=allusers) +#### #### +####### Time Data Route ####### +#### #### @app.route("/dev/timedata", methods=['GET']) @login_required def timedata(): alltimedata = mongo.db.time_collection.find() return render_template('dev/timedata.html', alltimedata=alltimedata) +#### #### +####### Agreement Data Route ####### +#### #### @app.route("/dev/agreementdata", methods=['GET']) @login_required def agreementdata(): allagreementdata = mongo.db.agreement_collection.find() return render_template('dev/agreementdata.html', allagreementdata=allagreementdata) +#### #### +####### Project Data Route ####### +#### #### @app.route("/dev/projectdata", methods=['GET']) @login_required def projectdata(): diff --git a/app/templates/dashboard/fleet/widget.html b/app/templates/dashboard/fleet/widget.html @@ -1,14 +1,23 @@ <section class="fleet"> <h3>Fleet</h3> <form class="widget-form" action="" method="POST" novalidate> - {{ fleetoutform.hidden_tag() }} - {% for error in fleetoutform.errors %} + {% for error in fleetoutform.errors or fleetinform.errors %} <span style="color:red;">[{{ error }}]</span> - {% endfor %} + {% endfor %} + {% if not fleetCheckedOut %} + {{ fleetoutform.hidden_tag() }} <div class="safetychecks"> {% for field in fleetoutform %} {{ field }}{% if field.widget.input_type != 'submit' and field.widget.input_type != 'hidden' %}{{ field.label }}{% endif %}<br> {% endfor %} </div> + {% elif fleetCheckedOut %} + {{ fleetinform.hidden_tag() }} + <h5> You have {{ vehicle_name }} checked out</h5> + {% for field in fleetinform %} + {{ field }}{% if field.widget.input_type != 'submit' and field.widget.input_type != 'hidden' %}{{ field.label }}{% endif %}<br> + {% endfor %} + </div> + {% endif %} </form> </section> diff --git a/seeds.py b/seeds.py @@ -97,21 +97,23 @@ time3 = { fleet1 = { 'date': datetime.datetime.today(), 'operator': 'brennentmazur', #forign key to userID + 'start_mileage': 33, + 'end_mileage': 33, 'safety_checks': [True,True,True,True,True],#array for different safety checks 'additional_notes': 'Oil needs checked', 'vehicle': 'The Big Truck', #vehicleID - 'incident_report': '', - 'mileage': 33, + 'incident_report': '' } fleet2 = { 'date': datetime.datetime.today(), 'operator': 'nikolasmmazur', #forign key to userID + 'start_mileage': 33, + 'end_mileage': 33, # 'safety_checks': True, #array for different safety checks 'additional_notes': 'Tires need replaced', 'vehicle': 'The Small Truck', #vehicleID - 'incident_report': 'I dunno what goes in incident reports.', - 'mileage': 90, + 'incident_report': 'I dunno what goes in incident reports.' } # Agreement documents