commit c33c2fa30b12a631a31af5bf878d8c91abccc82a
parent 9c9766c429b18838d0f3b2b1d0e1be83d020f310
Author: Brennen T. Mazur <brennen@madis.cool>
Date: Sun, 26 Mar 2023 20:44:53 -0600
Added and implemented CrewClockinWidget form, fixed adding new projects, and refined agreement project creation pipeline
Diffstat:
6 files changed, 141 insertions(+), 71 deletions(-)
diff --git a/app/forms.py b/app/forms.py
@@ -71,6 +71,11 @@ class PunchclockinWidget(FlaskForm):
# IFF user.role is_in(trusted_role[]) then allow lunch minute definition
clockin = SubmitField('Clock In')
+class CrewClockinWidget(FlaskForm):
+ userSel = SelectField('User:', validators=[DataRequired()])
+ projectSel = SelectField('Project:', validators=[DataRequired()])
+ time = TimeField('Started:')
+
class FleetCheckoutForm(FlaskForm):
vehicle = SelectField('Vehicle', validators = [DataRequired()])
start_mileage = IntegerField('Starting Mileage', validators=[DataRequired()])# Require some sort of validator for check...
diff --git a/app/routes.py b/app/routes.py
@@ -4,7 +4,7 @@ from app import app
from flask_pymongo import PyMongo
from flask_login import LoginManager
from flask import render_template, url_for, request, flash, redirect
-from app.forms import LoginForm, PunchclockinWidget, PunchclockoutWidget, FleetCheckoutForm, FleetCheckinForm, NewUserForm, AdmnPermissionsForm, DashPermissionsForm, ChangeHoursForm, NewAgreementForm, NewProjectForm
+from app.forms import LoginForm, PunchclockinWidget, PunchclockoutWidget, FleetCheckoutForm, FleetCheckinForm, NewUserForm, AdmnPermissionsForm, DashPermissionsForm, ChangeHoursForm, NewAgreementForm, NewProjectForm, CrewClockinWidget
from flask import request
from werkzeug.urls import url_parse
from werkzeug.security import generate_password_hash, check_password_hash
@@ -209,29 +209,48 @@ def dashboard():
return redirect(url_for('dashboard'))
# Temp values, change to db dependent values
- availableVehicles = ['Vehicle 1', 'Vehicle 2', 'Vehicle 3', 'Vehicle 4']
- #availableVehicles = []
- #TODO this needs to be properly coded out
- #for vehicle in mongo.db.fleet_collection.find_one('available_vehicles'):
- # availableVehicles.append((vehicle['_id'],vehicle['name']))
+ #availableVehicles = ['Vehicle 1', 'Vehicle 2', 'Vehicle 3', 'Vehicle 4']
+ availableVehicles = mongo.db.fleet_collection.find_one({'_id':'Fleet Pool'},{'available':1})['available']
currentProject = 'Project 2'
lastMileage = 103483
#currently gets ALL projects TODO make filter by available agreements/projects
availableProjects = []
for project in mongo.db.projects_collection.find():
availableProjects.append((project['_id'],project['project_name']))
+# GET_CLOCKED out users
+ clocked_out_active_users=[]
+ clocked_in_active_users=[]
+ for active in mongo.db.user_collection.find({'is_active':True},{'username':1,'fname':1,'mname':1,'lname':1}):
+ funame = active['fname']+' '+active['lname']
+ alreadyin = []
+ for user in clocked_in_users:
+ alreadyin.append(user['modified_by'][0])
+ if any(element in active['username'] for element in alreadyin):
+ clocked_in_active_users.append((active['_id'],funame))
+ else:
+ clocked_out_active_users.append((active['username'],funame))
+
+ #reset clocked_in_users
+ clocked_in_users = mongo.db.time_collection.find({'clock_out': {'$exists':False}})
+
# END default form values
clockinform=PunchclockinWidget()
clockoutform=PunchclockoutWidget()
fleetoutform=FleetCheckoutForm()
fleetinform=FleetCheckinForm()
+ crewform=CrewClockinWidget()
clockinform.projectsSel.choices = availableProjects
clockoutform.projectsSel.choices = availableProjects
fleetoutform.vehicle.choices = availableVehicles
- fleetoutform.vehicle.default = availableVehicles[0]
+ #fleetoutform.vehicle.default = availableVehicles[0]# Doesn't function
fleetoutform.start_mileage.data = lastMileage
+ crewform.time.data = datetime.datetime.now()
+ crewform.projectSel.choices = availableProjects
+ crewform.projectSel.data = availableProjects[0]
+ crewform.userSel.choices = clocked_out_active_users
+ crewform.userSel.data = clocked_out_active_users[0]
# 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__
@@ -244,7 +263,6 @@ def dashboard():
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],
@@ -252,6 +270,7 @@ def dashboard():
'project' : clockinform.projectsSel.data})
return redirect(url_for('dashboard'))
+# Can still clock in without checking current vehicle status!!! TODO FIX ME !!!
#if fleetoutform.validate_on_submit():
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
@@ -259,14 +278,42 @@ def dashboard():
'start_mileage':fleetoutform.start_mileage.data,
'operator':current_user.username,
'additional_notes':fleetoutform.additionalnotes.data})
+ #TODO mongo.db.fleet_collection.update_one
return redirect(url_for('dashboard'))
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)
-#TODO
+ return render_template('dashboard/layout.html',permissions=dashperms,clocked_out=clocked_out,clockoutform=clockoutform,clockinform=clockinform,fleetCheckedOut=fleetCheckedOut,crewform=crewform,vehicle_name=vehicle_name,fleetinform=fleetinform,fleetoutform=fleetoutform,clocked_in_users=clocked_in_users,ORGNAME=OrganizationName)
+
+@app.route("/toggle-lunch/<timeid>",methods=['GET','POST'])
+def toggle_lunch(timeid):
+ #query timeid
+ #if lunch exists, toggle true if false, and vice versa
+ #else set lunch: True
+ return redirect(url_for('dashboard'))
+
+@app.route("/toggle-per-diem/<timeid>",methods=['GET','POST'])
+def toggle_per_diem(timeid):
+ #query timeid
+ #if perdiem exists, toggle true if false, and vice versa
+ #else set perdiem: True
+ return redirect(url_for('dashboard'))
+
+@app.route("/clockinuser/<modusernm>/<usertoinid>/<project>/<intime>", methods=['GET','POST'])
+def clockin_by_id(modusernm,usertoinid,project,intime):
+ timeid = ObjectId()
+ user2 = eval(usertoinid)
+ project = eval(project)
+
+ mongo.db.time_collection.insert_one({'_id':timeid,
+ 'modified_by' :[user2[0], modusernm],
+ 'clock_in' : [datetime.datetime.strptime(intime, '%Y-%m-%d %H:%M:%S.%f')],
+ 'project' : project[0]})
+
+ return redirect(url_for('dashboard'))
+
@app.route("/clockoutuser/<modusernm>/<timeid>", methods=['GET','POST'])
def clockout_by_id(modusernm,timeid):
# if modified_by.last != modusernm: modified_by.append(modusernm)
@@ -278,7 +325,6 @@ def clockout_by_id(modusernm,timeid):
mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':datetime.datetime.utcnow()}})
mongo.db.time_collection.update_one({'_id':time_id},{'$push':{'modified_by':mod_username}})
flash('Clocked out')
- return redirect(url_for('dashboard'))
else:
flash('No time entry found, or user has checked out already')
@@ -466,21 +512,21 @@ def newagreement():
mongo.db.agreements_collection.insert_one({
'agreement_name':form.agreementName.data,
- 'agency':form.agency.data,
+ 'agency':[form.agency.data],
'projects':[],
'start_date':form.startDate.data.strftime('%Y-%m-%d'),
'end_date':form.endDate.data.strftime('%Y-%m-%d'),
- 'budget':[{
- 'labor':form.laborBudget.data,
- 'travel':form.travelBudget.data,
- 'supplies':form.suppliesBudget.data,
- 'contact':form.contactBudget.data,
- 'equipment':form.equipmentBudget.data,
- 'other':form.otherBudget.data
- }] # most recent labor budget accessed via budget.0.labor
+ # 'budget':[{
+ # 'labor':form.laborBudget.data,
+ # 'travel':form.travelBudget.data,
+ # 'supplies':form.suppliesBudget.data,
+ # 'contact':form.contactBudget.data,
+ # 'equipment':form.equipmentBudget.data,
+ # 'other':form.otherBudget.data
+ # }] # most recent labor budget accessed via budget.0.labor
})
- flash("{} with {} added".format(form.agreementName.data, form.agency.data )) #Will need to sendmail password to form.email.data later
- return redirect(url_for('newagreement'))
+ flash("{} with {} added, please create at least one project.".format(form.agreementName.data, form.agency.data )) #Will need to sendmail password to form.email.data later
+ return redirect(url_for('newproject'))
return render_template('admin/agreements/newagreement.html',form=form,ORGNAME=OrganizationName)
@@ -491,21 +537,22 @@ def newagreement():
@app.route("/admin/projects/new", methods=["GET","POST"])
@login_required
def newproject():
-# Temp values, change to modular db dependent values
+# Available Agreements. Move to fn()
availableAgreements = []
for agreement in mongo.db.agreements_collection.find():
availableAgreements.append((agreement['_id'],agreement['agreement_name']))
-# END TMP Values
+# END Available Agreements
form = NewProjectForm()
form.agreement.choices = availableAgreements
+ form.otherBudget.data = 0
if form.validate_on_submit():
# create deterministic agreement unique _id? Example being genpasswd in new user validate on submit
mongo.db.projects_collection.insert_one({
'project_name':form.projectName.data,
- 'agreement':ObjectId('{}').format(form.agreement.data),#TODO FIX Causes errors
+ 'agreement':ObjectId(form.agreement.data),
'budget':[{
'labor':form.laborBudget.data,
'travel':form.travelBudget.data,
@@ -513,13 +560,21 @@ def newproject():
'contact':form.contactBudget.data,
'equipment':form.equipmentBudget.data,
'other':form.otherBudget.data
- }] # most recent labor budget accessed via budget.0.labor
+ }], # most recent labor budget accessed via budget.0.labor
+ 'costs':[{
+ 'labor':0,
+ 'travel':0,
+ 'supplies':0,
+ 'contact':0,
+ 'equipment':0,
+ 'other':0
+ }]
})
pj_id = mongo.db.projects_collection.find_one({'project_name':form.projectName.data})['_id']
- mongo.db.agreements_collection.update_one({ '_id':ObjectId('{}').format(form.agreement.data) },{ '$push':{ 'projects':pj_id }})#TODO Fix, causes errors
+ mongo.db.agreements_collection.update_one({ '_id':ObjectId(form.agreement.data) },{ '$push':{ 'projects':pj_id }})
flash("{} part of {} added".format(form.projectName.data, form.agreement.data )) #Will need to sendmail password to form.email.data later
- return redirect(url_for('newproject'))
+ return redirect(url_for('admin'))
return render_template('admin/agreements/projects/newproject.html',form=form,ORGNAME=OrganizationName)
diff --git a/app/templates/admin/agreements/widget.html b/app/templates/admin/agreements/widget.html
@@ -12,10 +12,10 @@
{{ agreement.agreement_name }}<br>
{{ agreement['budget'] }}<br>
{% for project in agreement['projects'] %}
- {{ project['budget'] }}
- {{ project['total_budget'] }}
- {{ project['costs'] }}
- {{ project['total_cost'] }}
+ {{ project['budget'] }}<br>
+ {{ project['total_budget'] }}<br>
+ {{ project['costs'] }}<br>
+ {{ project['total_cost'] }}<br>
{% endfor %}
{{ agreement['costs'] }}
<br><br>
diff --git a/app/templates/admin/users/active.html b/app/templates/admin/users/active.html
@@ -3,6 +3,9 @@
{% block title %}All Active Users{% endblock %}
{% block content %}
+ {%- for user in activeusers %}
+ {{ user }}
+ {%- endfor %}
<section class="activeusers-grid">
{%- for user in activeusers %}
<div class="usercard">
diff --git a/app/templates/dashboard/activeusers/widget.html b/app/templates/dashboard/activeusers/widget.html
@@ -11,6 +11,7 @@
{% for user in clocked_in_users %}
<tr>
<td><button><a href="{{ url_for('hours',username=user.modified_by.0) }}">{{ user.modified_by.0 }}</a></button></td>
+ <td><button><a href="{{ url_for('toggle_lunch',timeid=user._id) }}">Clock Out</a></button></td>
<td><input type="checkbox" name="lunch"><label for="lunch">Lunch</label></td>
<td><input type="checkbox" name="per_diem"><label for="per_diem">Per Diem</label></td>
<td><button><a href="{{ url_for('hours',username=user.modified_by.0) }}">{{ user.clock_in.0.time().isoformat(timespec='minutes') }}</a></button></td><!-- can format/display non-military time with format %I:%M%p -->
@@ -21,10 +22,11 @@
</form>
<form>
<table>
- <tr><!-- clock in clocked out user -->
- <th><select><option value="user3.name" selected>user3.name</option><option value="user4.name">user4.name</option><option value="user5.name">user5.name</option></select></th>
- <td><input type="time" value="12:12"/></td>
- <td><input type="submit" value="Clock in"></td>
+ <tr><!-- clock in clocked out user MIGHT NEED TO COMMENT OUT... DOES NOT GET DATA FROM FORMS ON SUBMIT... ONLY USES DEFAULT.data -->
+ <td>{{ crewform.userSel.label }} {{ crewform.userSel() }}</td>
+ <td>{{ crewform.projectSel.label }} {{ crewform.projectSel() }}</td>
+ <td>{{ crewform.time.label }} {{ crewform.time() }}</td>
+ <td><button type="submit"><a href="{{ url_for('clockin_by_id', modusernm=current_user.username, intime=crewform.time.data, project=crewform.projectSel.data, usertoinid=crewform.userSel.data) }}">Clock In</a></button></td>
</tr>
</table>
</form>
diff --git a/seeds.py b/seeds.py
@@ -111,14 +111,19 @@ time3 = {
}
# Fleet documents
+fleetpool = {
+ '_id':'Fleet Pool',
+ 'available':['Vehicle1','Vehicle2','Vehicle3','Vehicle5','Vehicle6'],
+ 'unavailable':[('Vehicle4','brennentmazur')] # Tuple for who checked vehicle lastOR set to 'REPAIRS' for obvious reasons
+ }
+
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
+ 'vehicle': 'Vehicle4', #vehicleID
'incident_report': ''
}
@@ -141,22 +146,22 @@ agreement1 = {
'start_date': '2023-12-5',
'end_date': '2023-8-12',
#'bid_document': '/asset/document/agreements/New-Agreement.pdf', #Filepath to document
- 'budget': [{
- 'labor':1259.40,
- 'travel':220.00,
- 'supplies':320.00,
- 'contact':420.00,
- 'equipment':620.00,
- 'other':20.00
- }],
- 'costs': [{
- 'labor':159.40,
- 'travel':20.00,
- 'supplies':30.00,
- 'contact':40.00,
- 'equipment':60.00,
- 'other':2.00
- }]
+# 'budget': [{
+# 'labor':1259.40,
+# 'travel':220.00,
+# 'supplies':320.00,
+# 'contact':420.00,
+# 'equipment':620.00,
+# 'other':20.00
+# }],
+# 'costs': [{
+# 'labor':159.40,
+# 'travel':20.00,
+# 'supplies':30.00,
+# 'contact':40.00,
+# 'equipment':60.00,
+# 'other':2.00
+# }]
}
agreement2 = {
@@ -166,22 +171,22 @@ agreement2 = {
'start_date': '2021-2-21',
'end_date': '2022-12-13',
#'bid_document': '/asset/document/agreements/Old-Agreement.pdf', #Filepath to document
- 'budget': [{
- 'labor':259.40,
- 'travel':220.00,
- 'supplies':320.00,
- 'contact':420.69,
- 'equipment':20.00,
- 'other':20.00
- }],
- 'costs': [{
- 'labor':159.40,
- 'travel':20.00,
- 'supplies':30.00,
- 'contact':40.00,
- 'equipment':60.00,
- 'other':2.00
- }]
+# 'budget': [{
+# 'labor':259.40,
+# 'travel':220.00,
+# 'supplies':320.00,
+# 'contact':420.69,
+# 'equipment':20.00,
+# 'other':20.00
+# }],
+# 'costs': [{
+# 'labor':159.40,
+# 'travel':20.00,
+# 'supplies':30.00,
+# 'contact':40.00,
+# 'equipment':60.00,
+# 'other':2.00
+# }]
}
# Projects documents
@@ -385,7 +390,7 @@ accountant = {
# Insert documents
user_collection.insert_many([user1, user2, user3])
time_collection.insert_many([time1, time2,time3])
-fleet_collection.insert_many([fleet1, fleet2])
+fleet_collection.insert_many([fleetpool, fleet1, fleet2])
agreements_collection.insert_many([agreement1, agreement2])
projects_collection.insert_many([projects1, projects2, projects3, projects4, projects5])
permissions_collection.insert_many([crew,alead,lead,developer,manager,accountant])