routes.py (102885B)
1 import datetime 2 import random, string 3 from app import app 4 from flask_pymongo import PyMongo 5 from flask_login import LoginManager 6 from flask import render_template, url_for, request, flash, redirect 7 from app.forms import LoginForm, PunchclockinWidget, PunchclockoutWidget, FleetCheckoutForm, FleetCheckinForm, NewUserForm, AdmnPermissionsForm, DashPermissionsForm, NewHoursForm, ConfirmRemove, NewAgreementForm, RenameAgreementForm, NewProjectForm, MoveProjectForm, RenameProjectForm, CrewClockinWidget, NewUserHourForm, ChangePasswordForm, ChangeUserForm, upDate, updateTime, updateProject, newNote, dateRange, ChangeBranchForm 8 from flask import request 9 from werkzeug.urls import url_parse 10 from werkzeug.security import generate_password_hash, check_password_hash 11 from flask_login import current_user, login_user, logout_user, login_required 12 from app.branches.routes import get_available_branches 13 from app.equipment.equipment import get_available_equipment_type 14 from app.models import User, Time, Fleet, Agreement, Projects 15 from bson.objectid import ObjectId 16 import bson.json_util as json_util 17 #from .config import EXEMPT_METHODS 18 import functools 19 20 OrganizationName = 'Youth Employment Program' # Maybe pass this as a value though the object for relevant pages??? 21 22 #### #### 23 ####### Application Modules Init ####### 24 #### #### 25 # DB Connection & LoginManager init 26 mongo = PyMongo(app) 27 login_manager = LoginManager(app) 28 login_manager.login_view = 'login' 29 30 class UserNotFoundError(Exception): 31 pass 32 33 def fetch_user(user_id): 34 user = mongo.db.user_collection.find_one({"_id":ObjectId(user_id)}) 35 if user == None: 36 raise UserNotFoundError(f"User id {user_id} returned none") 37 else: 38 return user 39 40 def fetch_user_from_username(username): 41 user = mongo.db.user_collection.find_one({"username":username}) 42 if user == None: 43 raise UserNotFoundError(f"User name {username} returned none") 44 else: 45 return user 46 47 # Add ability to get users by role type... case match statement inside if for default no arguments? 48 # TODO currently doesn't care about active state. FIX 49 def get_available_users(active=True,role=None,branch=None): 50 availableUsers = [("","Select User")] 51 for user in mongo.db.users_collection.find(): 52 if branch: 53 if 'branch' in user: 54 if user['branch'] == 'Global' or user['branch'] == branch: 55 availableUsers.append((user['_id'],user['username'])) 56 else: 57 availableUsers.append((user['_id'],user['username'])) 58 return availableUsers 59 60 def get_available_projects(branch=None): 61 availableProjects = [("","Select Project")] 62 for project in mongo.db.projects_collection.find(): 63 if branch: 64 if 'branch' in project: 65 if project['branch'] == 'Global' or project['branch'] == ObjectId(branch): 66 availableProjects.append((project['_id'],project['project_name'])) 67 else: 68 availableProjects.append((project['_id'],project['project_name'])) 69 return availableProjects 70 71 #### #### 72 ####### User Routes/Queries ####### 73 #### #### 74 #@app.route('/user/signup', methods=['GET']) 75 #def signup(): 76 # return User().signup() 77 78 #@app.route('/user/loginModel', methods=['GET']) 79 #def loginModel(): 80 # return User().loginModel() 81 82 #@app.route('/user/signout') 83 #def signout(): 84 # return User().signout() 85 86 #### #### 87 ####### Time Routes/Queries ####### 88 #### #### 89 #@app.route('/time/clockin', methods=['GET', 'POST']) 90 #def clockin(): 91 # return Time().clockin() 92 93 #@app.route('/time/clockout', methods=['GET', 'POST']) 94 #def clockout(): 95 # return Time().clockout() 96 97 #### #### 98 ####### Vehicle Routes/Queries ####### 99 #### #### 100 #@app.route('/fleet/vehicle_repair', methods=['GET', 'POST']) 101 #def vehicle_repair(): 102 # return Fleet().vehicle_repair() 103 104 #### #### 105 ####### Agreement Routes/Queries ####### 106 #### #### 107 #@app.route('/agreement/document', methods=['GET', 'POST']) 108 #def document(): 109 # return Agreement().document() 110 111 #### #### 112 ####### Project Routes/Queries ####### 113 #### #### 114 #@app.route('/projects/project', methods=['GET', 'POST', 'PUT']) 115 116 ##{{{{{{{{{{{{{{{{{{{{{ Decorator Functions }}}}}}}}}}}}}}}}}}}}}## 117 #@login_required 118 #def admin_required(func): 119 # """Ensure logged in user has admin permissions""" 120 # @functools.wraps(func) 121 # def decorated_view(*args, **kwargs): 122 # if current_user.role in : 123 # pass 124 # elif not current_user.role in : 125 # return current_app.login_manager.unauthorized() 126 # 127 # #if not has_permission: 128 # return func(*args, **kwargs) 129 # 130 # return decorated_view 131 ##{{{{{{{{{{{{{{{{{{{{{ END Decorator Functions }}}}}}}}}}}}}}}}}}}}}## 132 133 134 #================================# 135 ########### ############ 136 #### PAGE ROUTING #### 137 ########### ############ 138 #================================# 139 140 #### #### 141 ####### Logout User Route ####### 142 #### #### 143 @app.route("/logout", methods=['GET']) 144 def logout(): 145 logout_user() 146 return redirect(url_for('login')) 147 148 #### #### 149 ####### Login/Root Route ####### 150 #### #### 151 @app.route('/', methods=['GET', 'POST']) 152 @app.route("/login", methods=['GET', 'POST']) 153 def login(): 154 if current_user.is_authenticated: 155 return redirect(url_for('dashboard')) 156 form = LoginForm() 157 if form.validate_on_submit(): 158 # check form value for identity in db, if found AND form password matches stored hash, create User object 159 try: 160 u = fetch_user_from_username(form.username.data) 161 except: 162 flash("Invalid username or password") 163 return redirect(url_for('login')) 164 else: 165 if u['is_active'] and User.check_password(u, form.password.data): 166 user_obj = User(fname=u['fname'],mname=u['mname'],lname=u['lname'],email=u['email'],branch=u['branch'],address=u['address'],birthday=u['birthday'],role=u['role'],phonenumber=u['phonenumber']) 167 #login with new user object 168 login_user(user_obj) 169 # check next redirect to stop cross-site-redirects, another example here : http://flask.pocoo.org/snippets/62/ 170 next = request.args.get('next') 171 if not next or url_parse(next).netloc != '': 172 next = url_for('dashboard') 173 return redirect(next) 174 else: 175 flash("Invalid username or password") 176 return redirect(url_for('login')) 177 return render_template('login.html',form=form,ORGNAME = OrganizationName) 178 179 #### #### 180 ####### Load User Data ####### 181 #### #### 182 #load user_data into User object iff username == stored username 183 @login_manager.user_loader 184 def load_user(username): 185 u = mongo.db.user_collection.find_one({"username": username}) 186 if not u: 187 return None 188 user_obj = User(fname=u['fname'],mname=u['mname'],lname=u['lname'],email=u['email'],branch=u['branch'],address=u['address'],birthday=u['birthday'],role=u['role'],phonenumber=u['phonenumber']) 189 user_obj.password_hash = u['password_hash'] 190 return user_obj 191 192 @app.route('/update/<update_t>/<username>',methods=['GET','POST']) 193 @login_required 194 def update_user(update_t,username): #username=current_user): 195 form = ChangeUserForm() 196 form.role.choices = mongo.db.permissions_collection.find({},{'label':1})#get_available_roles() 197 198 form.branch.choices = [("",'Select Branch'),("Global","Global")] 199 for branch in get_available_branches(): 200 form.branch.choices.append(branch) 201 202 user = fetch_user_from_username(username) 203 match update_t: 204 case "fname": 205 if form.validate_on_submit(): 206 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'fname':form.fname.data,'username':form.fname.data.lower()+user['mname'].lower()+user['lname'].lower()}}) 207 flash("change {}.".format(update_t)) 208 return redirect(url_for('activeusers')) 209 case "mname": 210 if form.validate_on_submit(): 211 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'mname':form.mname.data,'username':user['fname'].lower()+form.mname.data.lower()+user['lname'].lower()}}) 212 flash("change {}.".format(update_t)) 213 return redirect(url_for('activeusers')) 214 case "lname": 215 if form.validate_on_submit(): 216 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'lname':form.lname.data,'username':user['fname'].lower()+user['mname'].lower()+form.lname.data.lower()}}) 217 flash("change {}.".format(update_t)) 218 return redirect(url_for('activeusers')) 219 case "birthday": 220 if form.validate_on_submit(): 221 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'birthday':form.birthday.data.strftime('%Y-%m-%d')}}) 222 flash("change {}.".format(update_t)) 223 return redirect(url_for('activeusers')) 224 case "role": 225 if form.validate_on_submit(): 226 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'role':form.role.data}}) 227 flash("change {}.".format(update_t)) 228 return redirect(url_for('activeusers')) 229 case "address": 230 if form.validate_on_submit(): 231 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'address':form.address.data}}) 232 flash("change {}.".format(update_t)) 233 return redirect(url_for('activeusers')) 234 case "branch": 235 if form.validate_on_submit(): 236 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'branch':form.branch.data}}) 237 flash("change {}.".format(update_t)) 238 return redirect(url_for('activeusers')) 239 case "phonenumber": 240 if form.validate_on_submit(): 241 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'phonenumber':form.phonenumber.data}}) 242 flash("change {}.".format(update_t)) 243 return redirect(url_for('activeusers')) 244 case "email": 245 if form.validate_on_submit(): 246 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'email':form.email.data}}) 247 flash("change {}.".format(update_t)) 248 return redirect(url_for('activeusers')) 249 case "payPeriod": 250 if form.validate_on_submit(): 251 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'pay_period':form.payPeriod.data}}) 252 flash("change {}.".format(update_t)) 253 return redirect(url_for('activeusers')) 254 case "payValue": 255 if form.validate_on_submit(): 256 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'pay_value':form.payValue.data}}) 257 flash("change {}.".format(update_t)) 258 return redirect(url_for('activeusers')) 259 260 return render_template("admin/users/update_user.html",user=user,form=form,update_t=update_t) 261 262 @app.route('/newpass',methods=['GET','POST']) 263 @login_required 264 def chgpass(): 265 form = ChangePasswordForm() 266 if form.validate_on_submit(): 267 mongo.db.user_collection.update_one({'username':current_user.username},{'$set':{'password_hash':generate_password_hash(form.newpass.data)}}) 268 flash("Changed password for {} to {}".format(current_user.username,form.newpass.data)) #Will need to sendmail password to form.email.data later 269 270 return redirect(url_for('dashboard')) 271 272 return render_template('admin/users/newpass.html',form=form,ORGNAME=OrganizationName) 273 274 @app.route('/newpass/<uid>',methods=['GET','POST']) 275 @login_required 276 def chgpass_by_uid(uid): 277 try: 278 user = fetch_user(uid) 279 except: 280 return "User not Found" 281 else: 282 form = ChangePasswordForm() 283 if form.validate_on_submit(): 284 mongo.db.user_collection.update_one({'username':user['username']},{'$set':{'password_hash':generate_password_hash(form.newpass.data)}}) 285 flash("Changed password for {} to {}".format(user['username'],form.newpass.data)) #Will need to sendmail password to form.email.data later 286 287 return redirect(url_for('dashboard')) 288 289 return render_template('admin/users/newpass.html',form=form,ORGNAME=OrganizationName) 290 #### #### 291 ####### Dashboard Route ####### 292 #### #### 293 294 @app.route('/index') 295 @app.route("/dashboard", methods=['GET', 'POST']) 296 @login_required 297 def dashboard(): 298 299 currentdate = datetime.datetime.now() 300 301 dashperms=mongo.db.permissions_collection.find_one({'label': current_user.role},{'dashboard':1,'_id':0}) 302 dashperms=dashperms['dashboard'] 303 304 clocked_in_users = mongo.db.time_collection.find({'clock_out': {'$exists':False}}) 305 def clock_user_out(time_id,notes='',lunch=False,perdiem=False): 306 if notes != '' and notes != None: 307 if mongo.db.time_collection.find({'_id': time_id}, {'clock_out':{'$exists':False}}): 308 if lunch==True and perdiem==True: 309 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'note':notes,'lunch':True,'per_diem':True}}) 310 elif lunch==True and perdiem==False: 311 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'note':notes,'lunch':True}}) 312 elif lunch==False and perdiem==True: 313 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'note':notes,'per_diem':True}}) 314 else: 315 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'note':notes}}) 316 return redirect(url_for('dashboard')) 317 else: 318 flash('No time entry found, or user has checked out already') 319 return redirect(url_for('dashboard')) 320 else: 321 if mongo.db.time_collection.find({'_id': time_id}, {'clock_out':{'$exists':False}}): 322 if lunch==True and perdiem==True: 323 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'lunch':True,'per_diem':True}}) 324 elif lunch==True and perdiem==False: 325 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'lunch':True}}) 326 elif lunch==False and perdiem==True: 327 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()],'per_diem':True}}) 328 else: 329 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()]}}) 330 return redirect(url_for('dashboard')) 331 else: 332 flash('No time entry found, or user has checked out already') 333 334 # Move to a isUserClockedIn(default: username=current_user) 335 if mongo.db.time_collection.find_one({'modified_by.0': current_user.username,'clock_out':{'$exists':False}}): 336 clocked_out = False 337 time_id = mongo.db.time_collection.find_one({'modified_by.0': current_user.username,'clock_out':{'$exists':False}})["_id"] 338 else: 339 clocked_out = True 340 #End isUserClockedIn() 341 #Start fleetCheckedOut() 342 if mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}}): 343 fleetCheckedOut = True 344 fleet_id = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["_id"] 345 vehicle_name = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["vehicle"] 346 start_mileage = mongo.db.fleet_collection.find_one({'operator': current_user.username,'end_mileage':{'$exists':False}})["start_mileage"] 347 else: 348 fleetCheckedOut = False 349 vehicle_name = '' 350 #End fleetCheckedOut() 351 def fleet_check_in(fleet_id,start_mileage,end_mileage,notes=''): 352 if mongo.db.fleet_collection.find({'_id': fleet_id}, {'end_mileage':{'$exists':False}}): 353 if end_mileage <= start_mileage: 354 flash('end mileage less than starting mileage') 355 elif notes != '' and end_mileage >= start_mileage: 356 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 357 elif notes == '' and end_mileage >= start_mileage: 358 mongo.db.fleet_collection.update_one({'_id':fleet_id},{'$set':{'end_mileage':end_mileage}}) 359 else: 360 flash('uncaught logic updating {} with {}'.format(fleet_id,end_mileage)) 361 mongo.db.fleet_collection.update_one({'_id':fleet_id},{'$set':{'end_mileage':end_mileage}}) 362 return redirect(url_for('dashboard')) 363 else: 364 flash('Unable to check out vehicle') 365 return redirect(url_for('dashboard')) 366 367 #def newfleet_check_out(vehicle_id,start_milage,additional_notes=None,safety_checks=None) 368 #availableVehicles = mongo.db.fleet_collection.find_one({'_id':'Fleet Pool'},{'available':1})['available'] 369 availableVehicles = [("","Select Vehicle")] 370 for vehicle in get_available_equipment_type('vehicle',branch=current_user.branch): 371 availableVehicles.append((vehicle['_id'],"Vehicle {}".format(vehicle['vehicle_id']))) 372 373 #currently gets ALL projects TODO make filter by available agreements/projects 374 availableProjects = get_available_projects(current_user.branch) 375 # GET_CLOCKED out users 376 clocked_out_active_users=[] 377 clocked_in_active_users=[] 378 for active in mongo.db.user_collection.find({'is_active':True},{'username':1,'fname':1,'mname':1,'lname':1}): 379 funame = active['fname']+' '+active['lname'] 380 alreadyin = [] 381 for user in clocked_in_users: 382 alreadyin.append(user['modified_by'][0]) 383 if any(element in active['username'] for element in alreadyin): 384 clocked_in_active_users.append((active['_id'],funame)) 385 else: 386 clocked_out_active_users.append((active['username'],funame)) 387 388 #reset clocked_in_users 389 clocked_in_users = mongo.db.time_collection.find({'clock_out': {'$exists':False}}) 390 391 # END default form values 392 393 clockinform=PunchclockinWidget() 394 clockoutform=PunchclockoutWidget() 395 fleetoutform=FleetCheckoutForm() 396 fleetinform=FleetCheckinForm() 397 crewform=CrewClockinWidget() 398 399 clockinform.projectsSel.choices = availableProjects 400 #clockoutform.projectsSel.choices = availableProjects 401 fleetoutform.vehicle.choices = availableVehicles 402 #fleetoutform.vehicle.default = availableVehicles[0]# Doesn't function 403 #fleetoutform.start_mileage.data = lastMileage 404 crewform.time.data = datetime.datetime.now() 405 crewform.projectSel.choices = availableProjects 406 crewform.projectSel.data = availableProjects[0] 407 crewform.userSel.choices = clocked_out_active_users 408 crewform.userSel.data = clocked_out_active_users[0] 409 410 # 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 411 # See: https://wtforms.readthedocs.io/en/3.0.x/forms/#wtforms.form.BaseForm.__init__ 412 #clockinform.process() 413 #clockoutform.process() 414 #fleetoutform.process() 415 416 #if clockinform.validate_on_submit(): Currently will submit all present forms... replaced w/ below 417 if not clocked_out and request.method == 'POST' and clockoutform.validate(): 418 clock_user_out(time_id,clockoutform.recapOrNote.data,clockoutform.lunchBox.data,clockoutform.per_diemBox.data)#add clockoutform.recapOrNote.data (will take some thought for clock_user_out fn potentially?) 419 return redirect(url_for('dashboard')) 420 421 if clocked_out and request.method == 'POST' and clockinform.validate(): 422 mongo.db.time_collection.insert_one({'clock_in' : [datetime.datetime.now()], 423 'modified_by' : [current_user.username], 424 'date' : datetime.datetime.today(), 425 'project' : ObjectId(clockinform.projectsSel.data)}) 426 return redirect(url_for('dashboard')) 427 428 # Can still clock in without checking current vehicle status!!! TODO FIX ME !!! 429 #if fleetoutform.validate_on_submit(): 430 if not fleetCheckedOut and request.method == 'POST' and fleetoutform.validate(): 431 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 432 'vehicle':fleetoutform.vehicle.data, 433 'start_mileage':fleetoutform.start_mileage.data, 434 'operator':current_user.username, 435 'additional_notes':fleetoutform.additionalnotes.data}) 436 #TODO mongo.db.fleet_collection.update_one 437 return redirect(url_for('dashboard')) 438 439 if fleetCheckedOut and request.method == 'POST' and fleetinform.validate(): 440 fleet_check_in(fleet_id,start_mileage,fleetinform.end_mileage.data,fleetinform.incident_notes.data) 441 return redirect(url_for('dashboard')) 442 443 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) 444 445 @app.route("/update/project/<mod_username>/<timeid>",methods=['GET','POST']) 446 @login_required 447 def updateProjectTime(mod_username,timeid): 448 timeid = ObjectId(timeid) 449 availableProjects = get_available_projects(current_user.branch)#[] #change to get_available_projects() -> projects where user branch == project['branch'] 450 form = updateProject() 451 452 # for project in mongo.db.projects_collection.find(): 453 # availableProjects.append((project['_id'],project['project_name'])) 454 455 form.projectSel.choices = availableProjects 456 457 if form.validate_on_submit(): 458 app.logger.info('update project route') 459 try: 460 entry = mongo.db.time_collection.find_one({'_id':timeid}) 461 except: 462 flash("Issue finding/assigning time_id: {}".format(timeid)) 463 else: 464 try: 465 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'project':ObjectId(form.projectSel.data)}}) 466 except: 467 flash("unable to set project for {} to {}".format(timeid, form.projectSel.data)) 468 else: 469 flash("Updated project") 470 finally: 471 return redirect(url_for('hours',username=entry['modified_by'][0]))#change to hours and mod_username redirect 472 return render_template('dashboard/punchclock/update/project.html',form=form, ORGNAME=OrganizationName) 473 474 @app.route("/update/start/<mod_username>/<timeid>",methods=['GET','POST']) #TODO MAKE OTHER VALUE FOR LAST PAGE, RETURNS LAST PAGE ELSE DASHBOARD 475 @login_required 476 def updateStartTime(mod_username,timeid): 477 # projects = [] 478 # agreement_id = ObjectId(agreement_id) 479 # agreement = {'_id': 'defaultagreement', 'agreement_name': 'Default Agreement', 'agency': ['YEP'], 'projects': ['no projects'], 'start_date': 'No Start Date', 'end_date': 'No End Date'} 480 # try: 481 # agreement = mongo.db.agreements_collection.find_one({'_id':agreement_id}) 482 # #mongo.db.agreements_collection.find_one({'_id':agreement_id}) 483 # except: 484 # flash("Issue assigning Agreement data for agreement id {}".format(agreement_id)) 485 # else: 486 # for project in agreement['projects']: 487 # try: 488 # pj = mongo.db.projects_collection.find_one({'_id':project}) 489 # except: 490 # flash("Issue assigning project data for id {}".format(project)) 491 # else: 492 # projects.append(pj) 493 # finally: 494 # return render_template('admin/agreements/index.html',projects=projects,agreement=agreement,ORGNAME=OrganizationName) 495 timeid = ObjectId(timeid) 496 form = updateTime() 497 498 if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry 499 try: 500 entry = mongo.db.time_collection.find_one({'_id': timeid}) 501 except: 502 flash("Issue finding/assigning time_id: {}".format(timeid)) 503 else: 504 day = entry['date'].date() 505 newtime = datetime.datetime.combine(day, form.timeSel.data)#TODO FINISH Creating variable for datetime.combine(date,timeSel.data) 506 try: 507 mongo.db.time_collection.update_one({'_id':timeid},{'$push':{'modified_by':mod_username,'clock_in':newtime}}) 508 except: 509 flash("Unable to push mod_username {}".format(mod_username)) 510 else: 511 flash('Updated time to {}'.format(form.timeSel.data)) 512 finally: 513 return redirect(url_for('dashboard'))#TODO RETURN LAST PAGE HERE! 514 515 return render_template('dashboard/punchclock/update/startTime.html',form=form, ORGNAME=OrganizationName) 516 517 @app.route("/update/end/<mod_username>/<timeid>",methods=['GET','POST']) #TODO MAKE OTHER VALUE FOR LAST PAGE, RETURNS LAST PAGE ELSE DASHBOARD 518 @login_required 519 def updateEndTime(mod_username,timeid): 520 timeid = ObjectId(timeid) 521 form = updateTime() 522 523 if form.validate_on_submit(): 524 try: 525 entry = mongo.db.time_collection.find_one({'_id': timeid}) 526 except: 527 flash("Issue finding/assigning time_id: {}".format(timeid)) 528 else: 529 day = entry['date'].date() 530 newtime = datetime.datetime.combine(day, form.timeSel.data) 531 try: 532 mongo.db.time_collection.update_one({'_id':timeid},{'$push':{'modified_by':mod_username,'clock_out':newtime}}) 533 except: 534 flash("Unable to push mod_username {}".format(mod_username)) 535 else: 536 flash('Updated time to {}'.format(form.timeSel.data)) 537 finally: 538 return redirect(url_for('dashboard'))#TODO RETURN LAST PAGE HERE! 539 540 return render_template('dashboard/punchclock/update/endTime.html',form=form, ORGNAME=OrganizationName) 541 542 @app.route("/update/day/<mod_username>/<timeid>",methods=['GET','POST']) #TODO MAKE OTHER VALUE FOR LAST PAGE, RETURNS LAST PAGE ELSE DASHBOARD 543 @login_required 544 def updateDate(mod_username,timeid): 545 timeid = ObjectId(timeid) 546 form = upDate() 547 548 if form.validate_on_submit(): 549 try: 550 entry = mongo.db.time_collection.find_one({'_id': timeid}) 551 except: 552 flash("Issue finding/assigning time_id: {}".format(timeid)) 553 else: 554 fdate = datetime.datetime.combine(form.dateSel.data,datetime.time()) 555 newstart = entry['clock_in'][-1].replace(year = fdate.year, month = fdate.month, day = fdate.day) 556 try: 557 newend = entry['clock_out'][-1].replace(year = fdate.year, month = fdate.month, day = fdate.day) 558 except: 559 flash("User currently clocked in") # TODO FIGURE OUT WHAT TO DO IF USER IS CLOCKED IN AND ATTEMPTING TO CHANGE ENTRY DATE... POSSIBLY SHOULDN'T UPDATE JUST THE CLOCKIN TIME AND LEAVE CLOCK OUT... ALSO WOULD BE ODD TO CLOCK OUT USER... 560 else: 561 try: 562 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'date':fdate},'$push':{'modified_by':mod_username,'clock_in':newstart,'clock_out':newend}}) 563 except: 564 flash("Unable to push mod_username {} date {} clockin {} and clockout {}".format(mod_username,fdate,newstart,newend)) 565 try: 566 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'date':fdate}}) 567 except: 568 flash("Unable to set date {}".format(fdate)) 569 finally: 570 try: 571 mongo.db.time_collection.update_one({'_id':timeid},{'$push':{'modified_by':mod_username,'clock_in':newstart,'clock_out':newend}}) 572 except: 573 flash("Unable to push mod_username {} date {} clockin {} and clockout {}".format(mod_username,fdate,newstart,newend)) 574 else: 575 flash('Updated date to {}'.format(form.dateSel.data)) 576 finally: 577 return redirect(url_for('dashboard'))#TODO RETURN LAST PAGE HERE! 578 579 return render_template('dashboard/punchclock/update/date.html',form=form, ORGNAME=OrganizationName) 580 581 @app.route("/update/note/<mod_username>/<timeid>",methods=['GET','POST']) #TODO MAKE OTHER VALUE FOR LAST PAGE, RETURNS LAST PAGE ELSE DASHBOARD 582 @login_required 583 def updateNote(mod_username,timeid): 584 timeid = ObjectId(timeid) 585 form = newNote() 586 587 if form.validate_on_submit(): 588 try: 589 entry = mongo.db.time_collection.find_one({'_id': timeid}) 590 except: 591 flash("Issue finding/assigning time_id: {}".format(timeid)) 592 else: 593 if form.note.data != None and form.note.data != '': 594 newNoted = form.note.data 595 try: 596 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'note':newNoted},'$push':{'modified_by':mod_username}}) 597 except: 598 flash("{}: Unable to set note to {}".format(mod_username,newNoted)) 599 else: 600 flash('Updated note') 601 finally: 602 return redirect(url_for('dashboard'))#TODO RETURN LAST PAGE HERE! 603 else: 604 try: 605 mongo.db.time_collection.update_one({'_id':timeid},{'$unset':{"note":""},'$push':{'modified_by':mod_username}}) 606 except: 607 flash("Unable to remove note") 608 else: 609 flash('Removed note') 610 finally: 611 return redirect(url_for('dashboard')) 612 613 return render_template('dashboard/punchclock/update/note.html',form=form, ORGNAME=OrganizationName) 614 615 #TODO FIGURE OUT WHY IT TAKES TWO CLICKS TO SET VALUES TO TRUE ON HOURS PAGE AND ALSO CREW CLOCKED IN LIST 616 @app.route("/toggle-lunch/<timeid>",methods=['GET','POST']) 617 @login_required 618 def toggle_lunch(timeid): 619 timeid = ObjectId(timeid) 620 621 if mongo.db.time_collection.find_one({'_id': timeid, 'lunch':{'$exists':False}}): 622 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'lunch':True}}) 623 if mongo.db.time_collection.find_one({'_id': timeid})['lunch'] == True: 624 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'lunch':False}}) 625 else: 626 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'lunch':True}}) 627 628 return redirect(url_for('dashboard')) 629 630 @app.route("/toggle-per-diem/<timeid>",methods=['GET','POST']) 631 @login_required 632 def toggle_per_diem(timeid): 633 timeid = ObjectId(timeid) 634 635 if mongo.db.time_collection.find_one({'_id': timeid, 'per_diem':{'$exists':False}}): 636 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'per_diem':True}}) 637 if mongo.db.time_collection.find_one({'_id': timeid})['per_diem'] == True: 638 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'per_diem':False}}) 639 else: 640 mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'per_diem':True}}) 641 642 return redirect(url_for('dashboard')) 643 #TODO 644 @app.route("/clockinuser",methods=['GET','POST']) 645 @login_required 646 def clockin_new_user(): 647 clocked_in_users = mongo.db.time_collection.find({'clock_out': {'$exists':False}}) 648 availableProjects = get_available_projects(current_user.branch)#[] 649 #TODO Might be helpful to make available projects search for projects available to the selected user... 650 # for project in mongo.db.projects_collection.find(): 651 # availableProjects.append((project['_id'],project['project_name'])) 652 # GET_CLOCKED out users 653 clocked_out_active_users=[] 654 clocked_in_active_users=[] 655 for active in mongo.db.user_collection.find({'is_active':True},{'username':1,'fname':1,'mname':1,'lname':1}): 656 funame = active['fname']+' '+active['lname'] 657 alreadyin = [] 658 for user in clocked_in_users: 659 alreadyin.append(user['modified_by'][0]) 660 if any(element in active['username'] for element in alreadyin): 661 clocked_in_active_users.append((active['_id'],funame)) 662 else: 663 clocked_out_active_users.append((active['username'],funame)) 664 665 form=CrewClockinWidget() 666 form.time.data = datetime.datetime.now() 667 form.projectSel.choices = availableProjects 668 #form.projectSel.data = availableProjects[0] 669 form.userSel.choices = clocked_out_active_users 670 #form.userSel.data = clocked_out_active_users[0] 671 672 if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry 673 mongo.db.time_collection.insert_one({'clock_in' : [form.time.data], 674 'modified_by' : [form.userSel.data, current_user.username], 675 'date' : datetime.datetime.today(), 676 'project' : ObjectId(form.projectSel.data)}) 677 return redirect(url_for('dashboard')) 678 679 return render_template('dashboard/punchclock/otheruser.html',form=form,ORGNAME=OrganizationName) 680 681 @app.route("/newtime/<usernm>",methods=['GET','POST']) 682 @login_required 683 def new_time(usernm): 684 user = mongo.db.user_collection.find_one({"username": usernm}) 685 availableProjects = get_available_projects(current_user.branch) #[] 686 # for project in mongo.db.projects_collection.find(): 687 # availableProjects.append((project['_id'],project['project_name'])) 688 # availableProjects = [("","Select Project")] 689 # for project in mongo.db.projects_collection.find(): 690 # if 'branch' in project: 691 # if project['branch'] == 'Global' or project['branch'] == user['branch']: 692 # availableProjects.append((project['_id'],project['project_name'])) 693 694 form=NewHoursForm() 695 form.projectSel.choices = availableProjects 696 697 if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry 698 # if form.projectSel.data == "" or form.projectSel.data == "Select Project": 699 # flash("You must Select a Project")# This part doesn't seem to function? 700 # return redirect(url_for('new_time',usernm=usernm)) 701 dateentry = datetime.datetime.combine(form.dateSel.data,datetime.time()) 702 starttime = datetime.datetime.combine(form.dateSel.data,form.startTime.data) 703 endtime = datetime.datetime.combine(form.dateSel.data,form.endTime.data) 704 entryevent = { 705 'date' : dateentry, 706 'clock_in' : [starttime], 707 'clock_out' : [endtime], 708 'project' : ObjectId(form.projectSel.data), 709 'lunch': form.lunchSel.data, 710 'per_diem':form.perDiemSel.data 711 } 712 if usernm is current_user.username: 713 entryevent['modified_by'] = [usernm] 714 else: 715 entryevent['modified_by'] = [usernm,current_user.username] 716 if form.note.data != '': 717 entryevent['note'] = form.note.data 718 mongo.db.time_collection.insert_one(entryevent) 719 720 return redirect(url_for('hours',username=user['username'])) 721 722 return render_template('dashboard/punchclock/otheruser.html',user=user,form=form,ORGNAME=OrganizationName) 723 724 @app.route("/newusertime",methods=['GET','POST']) 725 @login_required 726 def new_user_time(): 727 clocked_in_users = mongo.db.time_collection.find({'clock_out': {'$exists':False}}) 728 availableProjects = get_available_projects(current_user.branch) 729 # for project in mongo.db.projects_collection.find(): 730 # availableProjects.append((project['_id'],project['project_name'])) 731 # GET_CLOCKED out users 732 clocked_out_active_users=[] 733 clocked_in_active_users=[] 734 for active in mongo.db.user_collection.find({'is_active':True},{'username':1,'fname':1,'mname':1,'lname':1}): 735 funame = active['fname']+' '+active['lname'] 736 alreadyin = [] 737 for user in clocked_in_users: 738 alreadyin.append(user['modified_by'][0]) 739 if any(element in active['username'] for element in alreadyin): 740 clocked_in_active_users.append((active['_id'],funame)) 741 else: 742 clocked_out_active_users.append((active['username'],funame)) 743 744 form=NewUserHourForm() 745 #form.startTime.data = datetime.datetime.now() 746 form.projectSel.choices = availableProjects 747 #form.projectSel.data = availableProjects[0] 748 form.userSel.choices = clocked_out_active_users 749 #form.userSel.data = clocked_out_active_users[0] 750 751 if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry 752 dateentry = datetime.datetime.combine(form.dateSel.data,datetime.time()) 753 starttime = datetime.datetime.combine(form.dateSel.data,form.startTime.data) 754 endtime = datetime.datetime.combine(form.dateSel.data,form.endTime.data) 755 entryevent = { 756 'date' : dateentry, 757 'clock_in' : [starttime], 758 'clock_out' : [endtime], 759 'project' : ObjectId(form.projectSel.data), 760 'lunch': form.lunchSel.data, 761 'per_diem':form.perDiemSel.data 762 } 763 if form.userSel.data is current_user.username: 764 entryevent['modified_by'] = [form.userSel.data] 765 else: 766 entryevent['modified_by'] = [form.userSel.data,current_user.username] 767 if form.note.data != '' and form.note.data != None: 768 entryevent['note'] = form.note.data 769 try: 770 mongo.db.time_collection.insert_one(entryevent) 771 except: 772 flash("Unhandled error occured") 773 else: 774 flash("Created time entry for {}".format(userSel.data)) 775 finally: 776 return redirect(url_for('new_user_time')) 777 778 return render_template('dashboard/punchclock/otheruser.html',form=form,ORGNAME=OrganizationName) 779 780 @app.route("/clockinuser/<modusernm>/<usertoinid>/<project>/<intime>", methods=['GET','POST']) 781 @login_required 782 def clockin_by_id(modusernm,usertoinid,project,intime): 783 timeid = ObjectId() 784 user2 = eval(usertoinid) 785 project = eval(project) 786 787 mongo.db.time_collection.insert_one({'_id':timeid, 788 'modified_by' :[user2[0], modusernm], 789 'clock_in' : [datetime.datetime.strptime(intime, '%Y-%m-%d %H:%M:%S.%f')], 790 'project' : ObjectId(project[0])}) 791 792 return redirect(url_for('dashboard')) 793 794 @app.route("/clockoutuser/<modusernm>/<timeid>", methods=['GET','POST']) 795 @login_required 796 def clockout_by_id(modusernm,timeid): 797 # if modified_by.last != modusernm: modified_by.append(modusernm) 798 799 timeid = ObjectId(timeid) 800 801 def clock_otheruser_out(time_id,mod_username): 802 if mongo.db.time_collection.find({'_id': time_id}, {'clock_out':{'$exists':False}}): 803 mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.now()]}}) 804 mongo.db.time_collection.update_one({'_id':time_id},{'$push':{'modified_by':mod_username}}) 805 flash('Clocked out') 806 else: 807 flash('No time entry found, or user has checked out already') 808 809 clock_otheruser_out(timeid,modusernm) 810 811 return redirect(url_for('dashboard')) 812 813 #### #### 814 ####### Admin Route ####### 815 #### #### 816 @app.route("/admin") 817 @login_required 818 #@admin_required 819 def admin(): 820 adminperms=mongo.db.permissions_collection.find_one({'label': current_user.role},{'admin':1,'_id':0}) 821 adminperms=adminperms['admin'] 822 #AgreementsData 823 allagreements=mongo.db.agreements_collection.find()#All agreements, including outside endDate... filter to active agreements? 824 agreements = [] 825 for agreement in allagreements: 826 agreement['total_budget']=0 827 agreement['total_cost']=0 828 for i in range(len(agreement['projects'])): 829 try: 830 if mongo.db.projects_collection.find_one({'_id': agreement['projects'][i]}) is None: 831 flash("could not find project {} in agreement {}".format(agreement['projects'][i],agreement['_id'])) 832 return redirect(url_for('dashboard')) 833 else: 834 agreement['projects'][i]= mongo.db.projects_collection.find_one({'_id': agreement['projects'][i]}) 835 except: 836 flash("could not find project {} in agreement {}".format(agreement['projects'][i],agreement['_id'])) 837 return redirect(url_for('dashboard')) 838 else: 839 try: 840 if agreement['projects'][i]:#['budget'][0] 841 agreement['projects'][i]['total_budget'] = sum(agreement['projects'][i]['budget'][0].values()) 842 else: 843 agreement['projects'][i]['total_budget'] = 1 844 except: 845 flash("could not assign agreement {} total budget") 846 agreement['projects'][i]['total_budget'] = 1 847 finally: 848 agreement['projects'][i]['total_cost'] = sum(agreement['projects'][i]['costs'][0].values()) 849 agreement['total_budget']=agreement['total_budget']+agreement['projects'][i]['total_budget'] 850 agreement['total_cost']=agreement['total_cost']+agreement['projects'][i]['total_cost'] 851 agreements.append(agreement) 852 853 #all_permissions=mongo.db.permissions_collection.find_one({"label":current_user.role}) 854 #admnperms=all_permissions.admin 855 return render_template ('admin/layout.html',agreements=agreements,permissions=adminperms,ORGNAME=OrganizationName) 856 857 #### #### 858 ####### Agreement Report Route ####### 859 #### #### 860 # Report Routes 861 862 #@app.route('/admin/agreement') 863 #@login_required 864 #def agreement_report(): 865 # return render_template ('admin/reports/agreement_report.html', ORGNAME=OrganizationName) 866 867 #### #### 868 ####### Employee Report Route ####### 869 #### #### 870 #@app.route('/admin/employee') 871 #@login_required 872 #def employee_report(): 873 # return render_template ('admin/reports/employee_report.html', ORGNAME=OrganizationName) 874 875 #### #### 876 ####### Pay Period Report Route ####### 877 #### #### 878 #@app.route('/admin/pay-period') 879 #@login_required 880 #def pay_period_report(): 881 # return render_template ('admin/reports/pay_period_report.html', ORGNAME=OrganizationName) 882 883 #### #### 884 ####### Vehicle Report Route ####### 885 #### #### 886 #@app.route('/admin/vehicle') 887 #@login_required 888 #def vehicle_report(): 889 # return render_template ('admin/reports/vehicle_report.html', ORGNAME=OrganizationName) 890 891 ############################# 892 ###### DEV HOURS ROUTE ###### 893 ############################# 894 @app.route('/hoursd/<username>', methods=['GET','POST']) 895 @login_required 896 def hoursd(username):#userid goes into call to db to get user[] -> then returns formatted table (punchclock/index.html 897 898 aghours = mongo.db.time_collection.find({'modified_by.0':username}) 899 dbhours = mongo.db.time_collection.aggregate( [ 900 { 901 "$match":{'modified_by.0':username} 902 }, 903 { 904 '$lookup': { 905 'from': 'projects_collection', 906 'localField': 'project', 907 'foreignField': '_id', 908 'as': 'project' 909 } 910 }, 911 { 912 "$sort":{'clock_in':-1} 913 }, 914 { 915 "$limit":10 #change to ~ 30 days OR to prior 1st or 15th of month 916 } 917 918 ] ) 919 920 tspp = mongo.db.time_collection.aggregate( [ 921 { 922 "$lookup":{ 923 'from':'projects_collection', 924 'localField':'project', 925 'foreignField':'_id', 926 'as':'project_data' 927 } 928 }, 929 { 930 "$group": { 931 "_id":"$project_data", 932 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 933 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 934 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 935 } 936 }, 937 { 938 "$addFields": { 939 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 940 } 941 }, 942 { 943 "$sort": { "_id": -1 } 944 } 945 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 946 947 #Banner, TODO create a MOTD framework, for admin messages 948 currentdate = datetime.datetime.now() 949 currentdate = currentdate.strftime('%Y-%m-%d') 950 currentdate = currentdate.split('-') 951 birthday = current_user.birthday.split("-") 952 if currentdate[1] == birthday[1] and currentdate[2] == birthday[2]: 953 flash("Happy Birthday {}!".format(current_user.fname)) 954 ## END Banner 955 956 return render_template ('dashboard/punchclock/index.dev.html',cd=currentdate, bd=birthday,hours=dbhours,tspp=tspp,ORGNAME=OrganizationName) 957 958 959 960 #### #### 961 ####### Hours Route ####### 962 #### #### 963 @app.route('/hours/<username>', methods=['GET','POST']) 964 @login_required 965 def hours(username): 966 #query time collection for all time entries 967 #for entry in entries: 968 # time = entry.get('clock_out'[0],datetime.datetime.now()) - entry['clock_in'][0] 969 # if entry['modified_by'][0] in user_hours: 970 # user_hours[entry['modified_by'][0]] =+ time 971 # else: 972 # user_hours.append({entry['modified_by'][0],time}) 973 974 # user = load_user(username) 975 #dashperms=mongo.db.permissions_collection.find_one({'label': current_user.role},{'dashboard':1,'_id':0}) 976 #dashperms=dashperms['dashboard'] 977 #total_hours=0 978 user = mongo.db.user_collection.find_one({"username": username}) 979 availableProjects = get_available_projects(current_user.branch) 980 # for project in mongo.db.projects_collection.find(): #TODO TO RESOLVE BELOW ISSUE, JUST USE aggregator QUERY, WILL ALLOW SORTING AS WELL AS PROPER SUMMATION OF HOURS 981 # availableProjects.append((project['_id'],project['project_name']))#TODO FIND OUT WHY THIS RETURNS THE OBJECTID VALUE .str THROWS AttributeError: 'ObjectId' object has no attribute 'str' 982 #'completed':{'$exists':False} 983 dbhours = mongo.db.time_collection.aggregate( [ 984 { 985 "$match":{'modified_by.0':username} 986 }, 987 { 988 '$lookup': { 989 'from': 'projects_collection', 990 'localField': 'project', 991 'foreignField': '_id', 992 'as': 'project' 993 } 994 }, 995 { 996 "$sort":{'clock_in':-1} 997 }, 998 { 999 "$limit":10 #change to ~ 30 days OR to prior 1st or 15th of month 1000 } 1001 1002 ] ) 1003 #hours = [] 1004 #deltas=[] 1005 #for hour in dbhours: # Currenty acts wrong with longer than 1 day 1006 # for x, y in availableProjects: 1007 # if x is ObjectId(hour['project']): 1008 # hour['projectName'] = y 1009 # if 'clock_out' not in hour: 1010 # hour['clock_out']=[datetime.datetime.now()] 1011 # time = hour['clock_out'][-1] - hour['clock_in'][-1] 1012 # hour['total_time'] = time 1013 # hours.append(hour) 1014 # deltas.append(time) 1015 1016 form = NewHoursForm() 1017 form.dateSel.data = datetime.datetime.today() 1018 form.projectSel.choices = availableProjects 1019 form.startTime.data = datetime.datetime.now() 1020 #total_hours = sum(deltas,datetime.timedelta()) 1021 #statement_hours = "{} Hours, {} Minutes".format(total_hours.seconds//3600,(total_hours.seconds//60)%60) 1022 1023 # if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry 1024 # indt = datetime.combine(form.dateSel.data,form.startTime.data) 1025 # outdt = datetime.combine(form.dateSel.data,form.endTime.data) 1026 # if user['username'] is current_user.username and not form.perDiemSel.data and not form.lunchSel.data: 1027 # mongo.db.time_collection.insert_one({'clock_in' : [indt], 1028 # 'modified_by' : [current_user.username], 1029 # 'date' : datetime.datetime.today(), 1030 # 'project' : ObjectId(form.projectSel.data), 1031 # 'clock_out':[outdt]}) 1032 # return redirect(url_for('hours',username=user['username'])) 1033 # 1034 # if user['username'] is not current_user.username and not form.perDiemSel.data and not form.lunchSel.data: 1035 # mongo.db.time_collection.insert_one({'clock_in' : [indt], 1036 # 'modified_by' : [user['username'], current_user.username], 1037 # 'date' : datetime.datetime.today(), 1038 # 'project' : ObjectId(form.projectSel.data), 1039 # 'clock_out':[outdt]}) 1040 # 1041 # return redirect(url_for('hours',username=user['username'])) 1042 1043 #hours = mongo.db.time_collection.find({'modified_by.0':user.username}) 1044 1045 1046 tspp = mongo.db.time_collection.aggregate( [ 1047 { 1048 "$lookup":{ 1049 'from':'projects_collection', 1050 'localField':'project', 1051 'foreignField':'_id', 1052 'as':'project_data' 1053 } 1054 }, 1055 { 1056 "$group": { 1057 "_id":"$project_data", 1058 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1059 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1060 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 1061 } 1062 }, 1063 { 1064 "$addFields": { 1065 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1066 } 1067 }, 1068 { 1069 "$sort": { "_id": -1 } 1070 } 1071 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 1072 1073 1074 return render_template ('dashboard/punchclock/index.html',form=form,hours=dbhours,user=user,tspp=tspp,ORGNAME=OrganizationName) 1075 1076 @app.route('/allhours/<username>', methods=['GET','POST']) 1077 @login_required 1078 def all_user_hours(username): 1079 user = mongo.db.user_collection.find_one({"username": username}) 1080 #need a function which checks for dated(archived) time collections, and appends them to list. Might look like below 1081 # hours = [ {'current_year()":{'current pay period':{mongo_aggregate_lookup(returned doc)}},(each pay period in current year)},{'last_year().date':[{data},{data}]},etc... 1082 # This setup might benefit from time entries being in their own db? 1083 dbhours = mongo.db.time_collection.aggregate( [ 1084 { 1085 "$match":{'modified_by.0':username} 1086 }, 1087 { 1088 '$lookup': { 1089 'from': 'projects_collection', 1090 'localField': 'project', 1091 'foreignField': '_id', 1092 'as': 'project' 1093 } 1094 }, 1095 { 1096 "$sort":{'clock_in':-1} 1097 } 1098 ] ) 1099 1100 return render_template ('dashboard/punchclock/all.html',hours=dbhours,user=user,ORGNAME=OrganizationName) 1101 1102 # Don't really need this until additional functionality is added, not in current project scope 1103 #@app.route("/fleet") 1104 #def fleet(): 1105 # return render_template('dashboard/fleet/index.html',ORGNAME=OrganizationName) 1106 1107 #### #### 1108 ####### Roles Admin Route ####### 1109 #### #### 1110 @app.route("/admin/roles") 1111 @login_required 1112 def roles(): 1113 admnform = AdmnPermissionsForm() 1114 dashform = DashPermissionsForm() 1115 return render_template('admin/roles/updateroles.html',dashform=dashform,admnform=admnform,ORGNAME=OrganizationName) 1116 1117 #### #### 1118 ####### Active Users Admin Route ####### 1119 #### #### 1120 @app.route("/admin/users/active") 1121 @login_required 1122 def activeusers(): 1123 active = mongo.db.user_collection.find({'is_active':True}) 1124 return render_template('admin/users/active.html',activeusers=active,ORGNAME=OrganizationName) 1125 1126 @app.route("/user/<user_id>") 1127 @login_required 1128 def user(user_id): 1129 usr = mongo.db.user_collection.find({"_id":ObjectId(user_id)}) 1130 return render_template('admin/users/active.html',activeusers=usr,ORGNAME=OrganizationName) 1131 1132 @app.route("/admin/deactivate/<userid>",methods=['GET','POST']) 1133 @login_required 1134 def deactivate_user(userid): 1135 userid = ObjectId(userid) 1136 1137 if mongo.db.user_collection.find_one({'_id': userid})['is_active'] == True: 1138 mongo.db.user_collection.update_one({'_id':userid},{'$set':{'is_active':False}}) 1139 1140 return redirect(url_for('activeusers')) 1141 1142 #### #### 1143 ####### Inactive Users Admin Route ####### 1144 #### #### 1145 @app.route("/admin/users/inactive") 1146 @login_required 1147 def inactiveusers(): 1148 inactive = mongo.db.user_collection.find({'is_active':False}) 1149 return render_template('admin/users/inactive.html',inactiveusers=inactive,ORGNAME=OrganizationName) 1150 1151 @app.route("/admin/activate/<userid>",methods=['GET','POST']) 1152 @login_required 1153 def activate_user(userid): 1154 userid = ObjectId(userid) 1155 1156 if mongo.db.user_collection.find_one({'_id': userid})['is_active'] == False: 1157 mongo.db.user_collection.update_one({'_id':userid},{'$set':{'is_active':True}}) 1158 1159 return redirect(url_for('inactiveusers')) 1160 1161 #### #### 1162 ####### New User Admin Route ####### 1163 #### #### 1164 @app.route("/removetime/<timeid>",methods=['GET','POST']) 1165 @login_required 1166 def removetime(timeid): 1167 timeid = ObjectId(timeid) 1168 try: 1169 mongo.db.time_collection.delete_one({'_id':timeid}) 1170 except: 1171 flash("An unhandled error occured removing time") 1172 else: 1173 flash("Removed Time") 1174 finally: 1175 return redirect(url_for('dashboard')) 1176 #@app.route("/clockoutuser/<modusernm>/<timeid>", methods=['GET','POST']) 1177 #@login_required 1178 #def clockout_by_id(modusernm,timeid): 1179 # # if modified_by.last != modusernm: modified_by.append(modusernm) 1180 # 1181 # timeid = ObjectId(timeid) 1182 # 1183 # def clock_otheruser_out(time_id,mod_username): 1184 # if mongo.db.time_collection.find({'_id': time_id}, {'clock_out':{'$exists':False}}): 1185 # mongo.db.time_collection.update_one({'_id':time_id},{'$set':{'clock_out':[datetime.datetime.utcnow()]}}) 1186 # mongo.db.time_collection.update_one({'_id':time_id},{'$push':{'modified_by':mod_username}}) 1187 # flash('Clocked user out') 1188 # else: 1189 # flash('No time entry found, or user has checked out already') 1190 # 1191 # clock_otheruser_out(timeid,modusernm) 1192 # 1193 # return redirect(url_for('dashboard')) 1194 #### #### 1195 @app.route("/admin/users/modify/<uid>", methods=["GET","POST"]) 1196 @login_required 1197 def moduser(uid): 1198 # Temp values, change to modular db dependent values 1199 availableBranches = ['Dillon','Salmon'] 1200 allRoles=mongo.db.permissions_collection.find({},{'label':1}) 1201 availableRoles = [] 1202 1203 dashperms=mongo.db.permissions_collection.find_one({'label': current_user.role},{'dashboard':1,'_id':0}) 1204 dashperms=dashperms['dashboard'] 1205 1206 for perm in dashperms: 1207 availableRoles.append((perm['_id'],perm['label'])) 1208 1209 defaultBranch = 'Dillon' 1210 defaultRole = 'Crew' 1211 # END TMP Values 1212 1213 form = ChangeUserForm() 1214 1215 form.branch.choices = [("",'Select Branch'),("Global","Global")] 1216 for branch in get_available_branches(): 1217 form.branch.choices.append(branch) 1218 1219 form.role.choices = availableRoles 1220 form.role.default = defaultRole 1221 1222 if form.validate_on_submit(): 1223 mongo.db.user_collection.update_one({"_id":ObjectId(uid)},{ '$set': { 1224 'fname':form.fname.data, 1225 'mname':form.mname.data, 1226 'lname':form.lname.data, 1227 'username':form.fname.data.lower()+form.mname.data.lower()+form.lname.data.lower(), 1228 'birthday':form.birthday.data.strftime('%Y-%m-%d'), 1229 'role':form.role.data, 1230 'branch':form.branch.data, 1231 'phonenumber':form.phonenumber.data, 1232 'address':form.address.data, 1233 'email':form.email.data, 1234 'pay_period':form.payPeriod.data, 1235 'pay_value':form.role.data, 1236 'is_active':form.setActive.data 1237 }}) 1238 flash("Updated user information for {} {}".format(form.fname.data, form.lname.data)) 1239 return redirect(url_for('activeusers')) 1240 1241 return render_template('admin/users/moduser.html',form=form,ORGNAME=OrganizationName) 1242 1243 @app.route("/admin/users/new", methods=["GET","POST"]) 1244 @login_required 1245 def newuser(): 1246 # Temp values, change to modular db dependent values 1247 availableBranches = ['Dillon','Salmon'] 1248 1249 availableRoles = [] 1250 for perm in mongo.db.permissions_collection.find(): 1251 availableRoles.append((perm['label'])) 1252 1253 defaultBranch = 'Dillon' 1254 # END TMP Values 1255 1256 form = NewUserForm() 1257 1258 #form.branch.choices = availableBranches 1259 form.branch.choices = [("",'Select Branch'),("Global","Global")] 1260 for branch in get_available_branches(): 1261 form.branch.choices.append(branch) 1262 1263 form.role.choices = availableRoles 1264 #form.process() 1265 1266 if form.validate_on_submit(): 1267 genpasswd = ''.join(random.choice(string.ascii_letters) for _ in range(14)) 1268 if form.payValue.data is not None: 1269 rolePayValue = form.payValue.data 1270 else: 1271 roleValue = mongo.db.permissions_collection.find_one({'label':form.role.data}) 1272 rolePayValue = roleValue['base_pay_value'] 1273 1274 mongo.db.user_collection.insert_one({ 1275 'fname':form.fname.data, 1276 'mname':form.mname.data, 1277 'lname':form.lname.data, 1278 'username':form.fname.data.lower()+form.mname.data.lower()+form.lname.data.lower(), 1279 'birthday':form.birthday.data.strftime('%Y-%m-%d'), 1280 'password_hash':generate_password_hash(genpasswd), 1281 'role':form.role.data, 1282 'branch':form.branch.data, 1283 'phonenumber':form.phonenumber.data, 1284 'address':form.address.data, 1285 'email':form.email.data, 1286 'pay_period':form.payPeriod.data, 1287 'pay_value':rolePayValue, 1288 'is_active':form.setActive.data 1289 }) 1290 flash("New user for {} {} added with a password of {}".format(form.fname.data, form.lname.data, genpasswd)) #Will need to sendmail password to form.email.data later 1291 return redirect(url_for('newuser')) 1292 1293 return render_template('admin/users/newuser.html',form=form,ORGNAME=OrganizationName) 1294 1295 #### #### 1296 ####### Agreement Admin Route ####### 1297 #### #### 1298 @app.route("/admin/agreement/<agreement_id>",methods=["GET"]) 1299 @login_required 1300 def agreement(agreement_id): 1301 projects = [] 1302 agreement_id = ObjectId(agreement_id) 1303 agreement = {'_id': 'defaultagreement', 'agreement_name': 'Default Agreement', 'agency': ['YEP'], 'projects': ['no projects'], 'start_date': 'No Start Date', 'end_date': 'No End Date'} 1304 try: 1305 agreement = mongo.db.agreements_collection.find_one({'_id':agreement_id}) 1306 #mongo.db.agreements_collection.find_one({'_id':agreement_id}) 1307 except: 1308 flash("Issue assigning Agreement data for agreement id {}".format(agreement_id)) 1309 else: 1310 for project in agreement['projects']: 1311 try: 1312 pj = mongo.db.projects_collection.find_one({'_id':project}) 1313 except: 1314 flash("Issue assigning project data for id {}".format(project)) 1315 else: 1316 projects.append(pj) 1317 finally: 1318 return render_template('admin/agreements/index.html',projects=projects,agreement=agreement,ORGNAME=OrganizationName) 1319 1320 @app.route("/admin/agreements/new", methods=["GET","POST"]) 1321 @login_required 1322 def newagreement(): 1323 # Temp values, change to modular db dependent values 1324 # END TMP Values 1325 1326 form = NewAgreementForm() 1327 1328 if form.validate_on_submit(): 1329 # create deterministic agreement unique _id? Example being genpasswd in new user validate on submit 1330 1331 mongo.db.agreements_collection.insert_one({ 1332 'agreement_name':form.agreementName.data, 1333 'agency':[form.agency.data], 1334 'projects':[], 1335 'start_date':form.startDate.data.strftime('%Y-%m-%d'), 1336 'end_date':form.endDate.data.strftime('%Y-%m-%d'), 1337 # 'budget':[{ 1338 # 'labor':form.laborBudget.data, 1339 # 'travel':form.travelBudget.data, 1340 # 'supplies':form.suppliesBudget.data, 1341 # 'contact':form.contactBudget.data, 1342 # 'equipment':form.equipmentBudget.data, 1343 # 'other':form.otherBudget.data 1344 # }] # most recent labor budget accessed via budget.0.labor 1345 }) 1346 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 1347 return redirect(url_for('newproject')) 1348 1349 return render_template('admin/agreements/newagreement.html',form=form,ORGNAME=OrganizationName) 1350 1351 #### #### 1352 ####### Project Admin Route ####### 1353 #### #### 1354 ####### TODO Need to filter out available agrements key=agreement_name:value=_id(agreement) Assign _id to agreement, and write _id(project) to agreement.projects[] 1355 1356 #@app.route("/admin/projects/new/<agreementid>", methods=["GET","POST"]) 1357 @app.route("/admin/projects/new", methods=["GET","POST"]) 1358 @login_required 1359 def newproject(agreementid=None): 1360 # Available Agreements. Move to fn() 1361 availableAgreements = [] 1362 for agreement in mongo.db.agreements_collection.find(): 1363 availableAgreements.append((agreement['_id'],agreement['agreement_name'])) 1364 # END Available Agreements 1365 1366 form = NewProjectForm() 1367 # TODO If statement for optional newproject('argument') if new or none return all choices, else return (agreement_id, agreement_name) for new agreement ID passed <-- What did I mean by this? OH It's a conditional to redirect to create a new agreement... this would require passing the form data to the next route as params? 1368 form.agreement.choices = availableAgreements 1369 #form.branch.choices = ['Dillon','Salmon'] 1370 form.branch.choices = [("",'Select Branch'),("Global","Global")] 1371 for branch in get_available_branches(): 1372 form.branch.choices.append(branch) 1373 #TODO MAKE BELOW WORK!!! Apply to other branch dependent areas 1374 #branches = [] 1375 #for branch in mongo.db.branch_collection.find(): 1376 # branches.append((branch['_id'],branch['location'])) 1377 #form.branch.choices = branches 1378 1379 if form.validate_on_submit(): 1380 # create deterministic agreement unique _id? Example being genpasswd in new user validate on submit 1381 ### TODO MAKE THIS A FOR submit IN form IF submit.data == None, 'submit.label':0, else 'submit.label':submit.data ## probably can use f-strings to accomplish the 'submit.label' part 1382 if form.laborBudget.data is None: 1383 form.laborBudget.data = 0.0 1384 if form.travelBudget.data is None: 1385 form.travelBudget.data = 0.0 1386 if form.suppliesBudget.data is None: 1387 form.suppliesBudget.data = 0.0 1388 if form.perdiemBudget.data is None: 1389 form.perdiemBudget.data = 0.0 1390 if form.equipmentBudget.data is None: 1391 form.equipmentBudget.data = 0.0 1392 if form.indirectBudget.data is None: 1393 form.indirectBudget.data = 0.0 1394 if form.contractingBudget.data is None: 1395 form.contractingBudget.data = 0.0 1396 if form.lodgingBudget.data is None: 1397 form.lodgingBudget.data = 0.0 1398 if form.otherBudget.data is None: 1399 form.otherBudget.data = 0.0 1400 1401 pjname = "FE " + str(form.fenumber.data) + ": " + form.projectName.data 1402 1403 mongo.db.projects_collection.insert_one({ 1404 'project_name':pjname, 1405 'agreement':ObjectId(form.agreement.data), 1406 'branch':form.branch.data, 1407 #'branch':ObjectId(form.branch.data), 1408 'budget':[{ 1409 'labor':form.laborBudget.data, 1410 'travel':form.travelBudget.data, 1411 'supplies':form.suppliesBudget.data, 1412 'perdiem':form.perdiemBudget.data, 1413 'equipment':form.equipmentBudget.data, 1414 'indirect':form.indirectBudget.data, 1415 'contracting':form.contractingBudget.data, 1416 'lodging':form.lodgingBudget.data, 1417 'other':form.otherBudget.data 1418 }], # most recent labor budget accessed via budget.0.labor 1419 'costs':[{ 1420 'labor':0, 1421 'travel':0, 1422 'supplies':0, 1423 'perdiem':0, 1424 'equipment':0, 1425 'indirect':0, 1426 'contracting':0, 1427 'lodging':0, 1428 'other':0 1429 }] 1430 }) 1431 pj_id = mongo.db.projects_collection.find_one({'project_name':pjname})['_id'] 1432 mongo.db.agreements_collection.update_one({ '_id':ObjectId(form.agreement.data) },{ '$push':{ 'projects':pj_id }}) 1433 1434 flash("{} part of {} added".format(form.projectName.data, form.agreement.data )) #Will need to sendmail password to form.email.data later 1435 return redirect(url_for('admin')) 1436 1437 return render_template('admin/agreements/projects/newproject.html',form=form,ORGNAME=OrganizationName) 1438 1439 @app.route("/admin/project/<project_id>",methods=["GET"]) 1440 @login_required 1441 def project(project_id): 1442 payperiod_times = [] 1443 #project_id = project_id 1444 probject_id = ObjectId(project_id) 1445 project = {'_id': 'defaultproject', 'project_name': 'Template Project', 'agreement': 'YEP General', 'budget': [{'labor':2, 'No Start Date':2, 'travel':2, 'end_date':2, 'supplies':2, 'No End Date':2}],'cost':[{'labor':1, 'No Start Date':1, 'travel':1, 'end_date':1, 'supplies':1, 'No End Date':1}]} 1446 try: 1447 project = mongo.db.projects_collection.find_one({'_id':probject_id}) 1448 except: 1449 flash("Issue assigning Project data for project id {}".format(probject_id)) 1450 else: 1451 #for project in agreement['projects']: 1452 try: 1453 tme = mongo.db.time_collection.find({'project':probject_id}) 1454 if tme is None: 1455 flash('No Current Hours submitted for project {}'.format(probject_id)) 1456 except: 1457 flash("Issue assigning time data for project id {}".format(probject_id)) 1458 else: 1459 for time in tme: 1460 payperiod_times.append(time) 1461 finally: 1462 return render_template('admin/agreements/projects/index.html',project=project,payperiod_times=payperiod_times,ORGNAME=OrganizationName) 1463 1464 @app.route("/admin/rename/project/<project_id>",methods=["GET","POST"]) 1465 @login_required 1466 def rename_project(project_id): 1467 form = RenameProjectForm() 1468 if form.validate_on_submit(): 1469 pjname = "FE " + str(form.fenumber.data) + ": " + form.newName.data 1470 try: 1471 project = mongo.db.projects_collection.find_one({'_id':project_id}) 1472 except: 1473 flash("Issue finding Project with id {}".format(project_id)) 1474 else: 1475 try: 1476 mongo.db.projects_collection.update_one({'_id':ObjectId(project_id)},{'$set':{'project_name':pjname}}) 1477 except: 1478 flash("Issue setting {} as project name for {}".format(pjname,project_id)) 1479 finally: 1480 return redirect(url_for('project', project_id=project_id)) 1481 return render_template('admin/agreements/projects/update/rename.html',form=form,ORGNAME=OrganizationName) 1482 1483 @app.route("/admin/update/branch/<collection>/<document_id>",methods=["GET","POST"]) 1484 @login_required 1485 def change_branch(collection,document_id): 1486 form = ChangeBranchForm() 1487 1488 form.branch.choices = [("",'Select Branch'),("Global","Global")] 1489 for branch in get_available_branches(): 1490 form.branch.choices.append(branch) 1491 1492 if form.validate_on_submit(): 1493 match collection: 1494 case "project": 1495 mongo.db.projects_collection.update_one({"_id":ObjectId(document_id)},{'$set':{'branch':ObjectId(form.branch.data)}}) 1496 flash("Changed Branch for Project {} to {}".format(document_id,form.branch.data)) 1497 return redirect(url_for('project',project_id=document_id)) 1498 case "user": 1499 mongo.db.user_collection.update_one({"_id":ObjectId(document_id)},{'$set':{'branch':ObjectId(form.branch.data)}}) 1500 flash("Changed Branch for User {} to {}".format(document_id,form.branch.data)) 1501 return redirect(url_for('user',user_id=document_id)) 1502 1503 return render_template('admin/update/branch.html',form=form,ORGNAME=OrganizationName) 1504 1505 @app.route("/admin/move/project/<project_id>",methods=["GET","POST"]) 1506 @login_required 1507 def move_project(project_id): 1508 #TODO replace w/ get agreement(s) fn 1509 availableAgreements = [] 1510 for agreement in mongo.db.agreements_collection.find(): 1511 availableAgreements.append((agreement['_id'],agreement['agreement_name'])) 1512 # END 1513 form = MoveProjectForm() 1514 form.newAgreement.choices = availableAgreements # assign the fn as above 1515 if form.validate_on_submit(): 1516 try: 1517 #TODO replace w/ get project(s) fn 1518 project = mongo.db.projects_collection.find_one({'_id':ObjectId(project_id)}) 1519 #END 1520 except: 1521 flash('Issue finding project with id {}'.format(project_id)) 1522 else: 1523 try: 1524 mongo.db.agreements_collection.find_one({'_id':ObjectId(project['agreement'])}) 1525 except: 1526 flash('Issue finding agreement {} for project {}'.format(project['agreement'],project_id)) 1527 else: 1528 # NOTE To refactor the below into a moveProject(s) fn I can pass a list of projects as a variable for the $pull and $push as {'$pull':{'projects'{'$in':varOfProjects}}} etcv This will be clean when I need to move all the projects from an agreement that's in queue for deletement 1529 try: 1530 mongo.db.agreements_collection.update_one({'_id':ObjectId(project['agreement'])},{'$pull':{'projects':ObjectId(project_id)}}) 1531 except: 1532 flash('Issue removing project {} from agreement {}'.format(project_id,project['agreement'])) 1533 else: 1534 try: 1535 mongo.db.agreements_collection.update_one({'_id':ObjectId(form.newAgreement.data)},{'$push':{'projects':ObjectId(project_id)}}) 1536 except: 1537 flash('Issue adding project {} to agreement {}'.format(project_id,form.newAgreement.data)) 1538 else: 1539 mongo.db.projects_collection.update_one({'_id':ObjectId(project_id)},{'$set':{'agreement':form.newAgreement.data}}) 1540 finally: 1541 return redirect(url_for('project', project_id=project_id)) 1542 return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName) 1543 1544 @app.route("/admin/remove/project/<project_id>",methods=["GET","POST"]) 1545 @login_required 1546 def remove_project(project_id): 1547 form = ConfirmRemove() 1548 if form.validate_on_submit(): 1549 try: 1550 #TODO replace w/ get project(s) fn 1551 project = mongo.db.projects_collection.find_one({'_id':ObjectId(project_id)}) 1552 #END 1553 except: 1554 flash('Issue finding project with id {}'.format(project_id)) 1555 else: 1556 try: 1557 mongo.db.agreements_collection.find_one({'_id':ObjectId(project['agreement'])}) 1558 except: 1559 flash('Issue finding agreement for project {}'.format(project_id)) 1560 else: 1561 #NOTE abstract to removeProject(s) fn for mass deletion. Ex {'$pull':{'projects':{'$in':passedListOfDeletableProjects}}} 1562 if mongo.db.agreements_collection.update_one({'_id':ObjectId(project['agreement'])},{'$pull':{'projects':ObjectId(project_id)}}): 1563 mongo.db.projects_collection.delete_one({'_id':ObjectId(project_id)}) 1564 1565 return redirect(url_for('agreement',agreement_id=project['agreement'])) 1566 1567 return render_template('admin/confirm_remove.html',form=form,ORGNAME=OrganizationName) 1568 #### #### 1569 ####### Knowlegebase Route ####### 1570 #### #### 1571 @app.route("/docs") 1572 @login_required 1573 def knowlegebase(): 1574 return render_template('knowlegebase/index.html',ORGNAME=OrganizationName) 1575 1576 1577 # Report Routes 1578 # Agreement Routes 1579 @app.route('/admin/reports/agreement') 1580 @login_required 1581 def agreement_report(): 1582 return render_template('admin/reports/agreement_report.html', ORGNAME=OrganizationName) 1583 1584 @app.route('/admin/reports/agreements') 1585 @login_required 1586 def project_report(): 1587 tspp = mongo.db.time_collection.aggregate( [ 1588 { 1589 "$group": { 1590 "_id":{"projectId": "$project"}, 1591 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1592 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1593 "perdiemCount": { "$sum" :{'$cond':["$perdiem",1,0] } } 1594 } 1595 }, 1596 { 1597 "$addFields": { 1598 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1599 } 1600 }, 1601 { 1602 "$sort": { "_id": -1 } 1603 } 1604 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 1605 ptl = mongo.db.time_collection.aggregate( [ 1606 { 1607 "$lookup":{ # TODO TODO TODO THIS WILL REQUIRE CHANGING ALL DB WRITES TO time_collection['project'] TO BECOME ObjectId() OBJECTS THIS WILL LIKELY BREAK THINGS!!!! NEED TO ITERATE THROUGH DB ENTRIES AS WELL AS ENSURE READ OPERATIONS STILL GUNCTION PROPERLY AFTERWARDS 1608 'from':'projects_collection', 1609 'localField':'project', 1610 'foreignField':'_id', 1611 'as':'project_data' 1612 } 1613 }, 1614 { 1615 "$addFields": { 1616 "totalTime": { "$cond":{ "$if":"$clock_out","$then":{"$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }},"$else":{'Clocked in'}}, 1617 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1618 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1619 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 1620 } 1621 }, 1622 { 1623 "$addFields": { 1624 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1625 } 1626 }, 1627 { 1628 "$sort": {'date': -1} 1629 } 1630 ] ) 1631 1632 by_project = tspp 1633 # by_project = {} 1634 # for project in tspp: 1635 # by_project[project['_id'][0]['project_name']]=project['_id'][0] 1636 # by_project[project['_id'][0]['project_name']]['totalHoursWorked']=project['totalHoursWorked'] 1637 # by_project[project['_id'][0]['project_name']]['lunchCount']=project['lunchCount'] 1638 # by_project[project['_id'][0]['project_name']]['perdiemCount']=project['perdiemCount'] 1639 1640 # for time in ptl: 1641 # for user in by_user: 1642 # if time['modified_by'][0] in user: 1643 # if by_user[user].get('times'): 1644 # by_user[user]['times'].append(time) 1645 # else: 1646 # by_user[user].update({'times':[time]}) 1647 # for project in by_project: 1648 # if time['project_data'][0]['project_name'] in project: 1649 # if by_project[project].get('times'): 1650 # by_project[project]['times'].append(time) 1651 # else: 1652 # by_project[project].update({'times':[time]}) 1653 1654 return render_template('admin/reports/project.html',by_project=by_project,tspp=tspp,projectlookup=ptl,ORGNAME=OrganizationName) 1655 1656 @app.route('/admin/rename/agreement/<agreement_id>',methods=["GET","POST"]) 1657 @login_required 1658 def rename_agreement(agreement_id): 1659 form = RenameAgreementForm() 1660 if form.validate_on_submit(): 1661 try: 1662 agreement = mongo.db.agreements_collection.find_one({'_id':agreement_id}) 1663 except: 1664 flash("Issue finding Agreement with id {}".format(agreement_id)) 1665 return redirect(url_for('rename_agreement',agreement_id=agreement_id)) 1666 else: 1667 try: 1668 mongo.db.agreements_collection.update_one({'_id':ObjectId(agreement_id)},{'$set':{'agreement_name':form.newName.data}}) 1669 except: 1670 flash("Issue setting {} as agreement name for {}".format(form.newName.data,agreement_id)) 1671 return redirect(url_for('rename_agreement',agreement_id=agreement_id)) 1672 else: 1673 return redirect(url_for('agreement', agreement_id=agreement_id)) 1674 return render_template('admin/agreements/update/rename.html',form=form,ORGNAME=OrganizationName) 1675 1676 @app.route("/admin/remove/agreement/<agreement_id>",methods=["GET","POST"]) 1677 @login_required 1678 def remove_agreement(agreement_id): 1679 #TODO 1680 form = ConfirmRemove() 1681 if form.validate_on_submit(): 1682 try: 1683 #TODO replace with get project(s) fn 1684 agreement = mongo.db.agreements_collection.find_one({'_id':ObjectId(agreement_id)}) 1685 #END 1686 except: 1687 flash('Issue finding agreement id {}'.format(agreement_id)) 1688 else: 1689 #for each project either remove or move 1690 flash('WARNING: This action removes all projects currently associated with this agreement, AND removes all time entries tied to the projects.') 1691 # try: 1692 # for project in agreement['projects'] 1693 1694 return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName)#TODO FIX 1695 1696 @app.route('/admin/change/agreement/dates',methods=["GET","POST"]) 1697 @login_required 1698 def change_agreement_dates(): 1699 #TODO 1700 return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName)#TODO FIX 1701 1702 # Payperiod Routes 1703 #TODO marked 06.29,23 1704 ###### TESTING START ####### 1705 @app.route('/dev/time-data-total-report') 1706 @login_required 1707 def time_data_total_report(): 1708 hours = mongo.db.time_collection.aggregate( [ 1709 # { 1710 # "$project":{"modified_by":1} 1711 # }, 1712 # { 1713 # "$unwind":"$modified_by" 1714 # }, 1715 ## ADD MATCH TO LIMIT BY DATE FOR REPORTING DATE SELECTION ## 1716 { 1717 "$group": { 1718 "_id": { 1719 "$first":"$modified_by", 1720 }, 1721 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1722 "lunchCount":{ "$sum":{'$cond':["$lunch",1,0] } }, 1723 "perdiemCount":{ "$sum":{'$cond':["$perdiem",1,0] } } 1724 } 1725 }, 1726 { 1727 "$addFields": { 1728 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1729 } 1730 }, 1731 { 1732 "$sort": { "_id": 1 } 1733 } 1734 ] )# Total hours worked 1735 tspp = mongo.db.time_collection.aggregate( [ 1736 { 1737 "$group": { 1738 "_id":{"projectId": "$project"}, 1739 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1740 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1741 "perdiemCount": { "$sum" :{'$cond':["$perdiem",1,0] } } 1742 } 1743 }, 1744 { 1745 "$addFields": { 1746 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1747 } 1748 }, 1749 { 1750 "$sort": { "_id": -1 } 1751 } 1752 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 1753 1754 everyhours_by_user = mongo.db.time_collection.aggregate([ {'$sort':{'modified_by.0':1,'date':1}} ]) 1755 1756 everyhours_by_project = mongo.db.time_collection.aggregate([ {'$sort':{'project':1,'modified_by.0':1}} ]) 1757 1758 return render_template ('admin/reports/total_timedata_report.html', by_user = everyhours_by_user, by_project = everyhours_by_project, hours=hours, tspp=tspp, ORGNAME=OrganizationName) 1759 ####### TESTING END ####### 1760 ###### TESTING Period selection START ####### 1761 @app.route('/dev/select-date-range', methods=["GET","POST"]) 1762 @login_required 1763 def select_date_range(): 1764 form = dateRange() 1765 1766 if form.validate_on_submit(): 1767 1768 try: 1769 begin = form.lowerBound.data 1770 end = form.upperBound.data 1771 #begin = datetime.datetime.strptime(form.lowerBound.data,'%F-%m-%d') 1772 #end = datetime.datetime.strptime(form.upperBound.data, '%Y-%m-%d') 1773 #begin = datetime.datetime.combine(form.lowerBound.data,datetime.time()) 1774 #end = datetime.datetime.combine(form.upperBound.data,datetime.time()) 1775 except: 1776 flash("Error Reporting for time entries between {} and {}.".format(begin,end)) 1777 else: 1778 flash("Report for time entries between {} and {}.".format(form.lowerBound.data, form.upperBound.data )) 1779 return redirect(url_for('time_bound_report',startday=begin,endday=end)) 1780 1781 return render_template('admin/reports/rangeSel.html',form=form,ORGNAME=OrganizationName) 1782 1783 @app.route('/dev/report-range/<startday>/<endday>') 1784 @login_required 1785 def time_bound_report(startday,endday): 1786 begin = datetime.datetime.strptime(startday,'%Y-%m-%d') 1787 end = datetime.datetime.strptime(endday, '%Y-%m-%d') 1788 1789 usertimes = mongo.db.time_collection.aggregate([ 1790 { 1791 "$match": { 1792 "$and":[{"date":{"$gte":begin}},{"date":{"$lt":end}}] 1793 } 1794 }, 1795 { 1796 "$lookup":{ 1797 'from':'user_collection', 1798 'localField':'modified_by.0', 1799 'foreignField':"username", 1800 'as':'userinfo' 1801 } 1802 }, 1803 { 1804 "$sort":{"userinfo.username":1} 1805 } 1806 ]) 1807 1808 allhours = mongo.db.time_collection.aggregate( [ 1809 { 1810 "$match": { 1811 "$and":[{"date":{"$gte":begin}},{"date":{"$lt":end}}] 1812 } 1813 }, 1814 { 1815 "$group": { 1816 "_id":{"_id":'$_id', 1817 "project":"$project"}, 1818 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1819 "lunchCount":{ "$sum":{'$cond':["$lunch",1,0] } }, 1820 "perdiemCount":{ "$sum":{'$cond':["$per_diem",1,0] } } 1821 } 1822 }, 1823 { 1824 "$addFields": { 1825 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1826 } 1827 }, 1828 { 1829 "$sort": { "_id": 1 } 1830 } 1831 ] )# Total hours worked 1832 1833 1834 hours = mongo.db.time_collection.aggregate( [ 1835 { 1836 "$match": { 1837 "$and":[{"date":{"$gte":begin}},{"date":{"$lt":end}}] 1838 } 1839 }, 1840 { 1841 "$group": { 1842 "_id": { 1843 "$first":"$modified_by", 1844 }, 1845 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1846 "lunchCount":{ "$sum":{'$cond':["$lunch",1,0] } }, 1847 "perdiemCount":{ "$sum":{'$cond':["$per_diem",1,0] } } 1848 } 1849 }, 1850 { 1851 "$addFields": { 1852 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1853 } 1854 }, 1855 { 1856 "$sort": { "_id": 1 } 1857 } 1858 ] )# Total hours worked 1859 tspp = mongo.db.time_collection.aggregate( [ 1860 { 1861 "$match": { 1862 "$and":[{"date":{"$gte":begin}},{"date":{"$lt":end}}] 1863 } 1864 }, 1865 { 1866 "$lookup":{ 1867 'from':'projects_collection', 1868 'localField':'project', 1869 'foreignField':'_id', 1870 'as':'project_data' 1871 } 1872 }, 1873 { 1874 "$group": { 1875 "_id":"$project_data", 1876 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1877 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1878 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 1879 } 1880 }, 1881 { 1882 "$addFields": { 1883 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1884 } 1885 }, 1886 { 1887 "$sort": { "_id": -1 } 1888 } 1889 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 1890 1891 ptl = mongo.db.time_collection.aggregate( [ 1892 { 1893 "$match": { 1894 "$and":[{"date":{"$gte":begin}},{"date":{"$lt":end}}] 1895 } 1896 }, 1897 { 1898 "$lookup":{ # TODO TODO TODO THIS WILL REQUIRE CHANGING ALL DB WRITES TO time_collection['project'] TO BECOME ObjectId() OBJECTS THIS WILL LIKELY BREAK THINGS!!!! NEED TO ITERATE THROUGH DB ENTRIES AS WELL AS ENSURE READ OPERATIONS STILL GUNCTION PROPERLY AFTERWARDS 1899 'from':'projects_collection', 1900 'localField':'project', 1901 'foreignField':'_id', 1902 'as':'project_data' 1903 } 1904 }, 1905 { 1906 "$addFields": { 1907 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1908 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 1909 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 1910 } 1911 }, 1912 { 1913 "$addFields": { 1914 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1915 } 1916 }, 1917 { 1918 "$sort": {'date': -1} 1919 } 1920 ] ) 1921 1922 by_project = {} 1923 for project in tspp: 1924 by_project[project['_id'][0]['project_name']]=project['_id'][0] 1925 by_project[project['_id'][0]['project_name']]['totalHoursWorked']=project['totalHoursWorked'] 1926 by_project[project['_id'][0]['project_name']]['lunchCount']=project['lunchCount'] 1927 by_project[project['_id'][0]['project_name']]['perdiemCount']=project['perdiemCount'] 1928 1929 by_user ={} 1930 for user in hours: 1931 by_user[user['_id']]=user 1932 1933 for time in ptl: 1934 for user in by_user: 1935 if time['modified_by'][0] in user: 1936 if by_user[user].get('times'): 1937 by_user[user]['times'].append(time) 1938 else: 1939 by_user[user].update({'times':[time]}) 1940 for project in by_project: 1941 if time['project_data'][0]['project_name'] in project: 1942 if by_project[project].get('times'): 1943 by_project[project]['times'].append(time) 1944 else: 1945 by_project[project].update({'times':[time]}) 1946 1947 # for time in ptl: 1948 # if time['modified_by'][0] not in by_user: 1949 # by_user[time['modified_by'][0]]=[time] 1950 # else: 1951 # by_user[time['modified_by'][0]].append(time) 1952 # if time['project_data'][0]['project_name'] not in by_project: 1953 # by_project[time['project_data'][0]['project_name']]=[time] 1954 # else: 1955 # by_project[time['project_data'][0]['project_name']].append(time) 1956 1957 #return json_util.dumps(by_user) 1958 #return json_util.dumps(by_project) 1959 return render_template ('admin/reports/bound_timedata_report.html', by_project=by_project, by_user=by_user, usertimes=usertimes, allhours=allhours, hours=hours, tspp=tspp, projectlookup=ptl, ORGNAME=OrganizationName) 1960 ## Refactoring fn(s) START ## 1961 def get_all_user_times(): 1962 usertimes = mongo.db.time_collection.aggregate([ 1963 { 1964 "$lookup":{ 1965 'from':'user_collection', 1966 'localField':'modified_by.0', 1967 'foreignField':"username", 1968 'as':'userinfo' 1969 } 1970 }, 1971 { 1972 "$sort":{"userinfo.username":1} 1973 } 1974 ]) 1975 1976 allhours = mongo.db.time_collection.aggregate( [ 1977 { 1978 "$group": { 1979 "_id":{"_id":'$_id', 1980 "project":"$project"}, 1981 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 1982 "lunchCount":{ "$sum":{'$cond':["$lunch",1,0] } }, 1983 "perdiemCount":{ "$sum":{'$cond':["$per_diem",1,0] } } 1984 } 1985 }, 1986 { 1987 "$addFields": { 1988 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 1989 } 1990 }, 1991 { 1992 "$sort": { "_id": 1 } 1993 } 1994 ] )# Total hours worked 1995 1996 1997 hours = mongo.db.time_collection.aggregate( [ 1998 { 1999 "$group": { 2000 "_id": { 2001 "$first":"$modified_by", 2002 }, 2003 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 2004 "lunchCount":{ "$sum":{'$cond':["$lunch",1,0] } }, 2005 "perdiemCount":{ "$sum":{'$cond':["$per_diem",1,0] } } 2006 } 2007 }, 2008 { 2009 "$addFields": { 2010 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 2011 } 2012 }, 2013 { 2014 "$sort": { "_id": 1 } 2015 } 2016 ] )# Total hours worked 2017 tspp = mongo.db.time_collection.aggregate( [ 2018 { 2019 "$lookup":{ 2020 'from':'projects_collection', 2021 'localField':'project', 2022 'foreignField':'_id', 2023 'as':'project_data' 2024 } 2025 }, 2026 { 2027 "$group": { 2028 "_id":"$project_data", 2029 "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 2030 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 2031 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 2032 } 2033 }, 2034 { 2035 "$addFields": { 2036 "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 2037 } 2038 }, 2039 { 2040 "$sort": { "_id": -1 } 2041 } 2042 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 2043 2044 ptl = mongo.db.time_collection.aggregate( [ 2045 { 2046 "$lookup":{ # TODO TODO TODO THIS WILL REQUIRE CHANGING ALL DB WRITES TO time_collection['project'] TO BECOME ObjectId() OBJECTS THIS WILL LIKELY BREAK THINGS!!!! NEED TO ITERATE THROUGH DB ENTRIES AS WELL AS ENSURE READ OPERATIONS STILL GUNCTION PROPERLY AFTERWARDS 2047 'from':'projects_collection', 2048 'localField':'project', 2049 'foreignField':'_id', 2050 'as':'project_data' 2051 } 2052 }, 2053 { 2054 "$addFields": { 2055 "totalTime": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, 2056 "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, 2057 "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } 2058 } 2059 }, 2060 { 2061 "$addFields": { 2062 "totalHoursWorked": {"$subtract":[ "$totalTime", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } 2063 } 2064 }, 2065 { 2066 "$sort": {'date': -1} 2067 } 2068 ] ) 2069 2070 by_project = {} 2071 for project in tspp: 2072 by_project[project['_id'][0]['project_name']]=project['_id'][0] 2073 by_project[project['_id'][0]['project_name']]['totalHoursWorked']=project['totalHoursWorked'] 2074 by_project[project['_id'][0]['project_name']]['lunchCount']=project['lunchCount'] 2075 by_project[project['_id'][0]['project_name']]['perdiemCount']=project['perdiemCount'] 2076 2077 by_user ={} 2078 for user in hours: 2079 by_user[user['_id']]=user 2080 2081 for time in ptl: 2082 for user in by_user: 2083 if time['modified_by'][0] in user: 2084 if by_user[user].get('times'): 2085 by_user[user]['times'].append(time) 2086 else: 2087 by_user[user].update({'times':[time]}) 2088 for project in by_project: 2089 if time['project_data'][0]['project_name'] in project: 2090 if by_project[project].get('times'): 2091 by_project[project]['times'].append(time) 2092 else: 2093 by_project[project].update({'times':[time]}) 2094 return by_user 2095 2096 ## Refartoring fn(s) END ## 2097 2098 ####### TESTING END ####### 2099 2100 @app.route('/admin/reports/pay-period', methods=['GET']) 2101 @login_required 2102 def pay_period_report(): 2103 pay = mongo.db.time_collection.find({}) 2104 dbactiveusers = mongo.db.user_collection.find({'is_active':True}) 2105 users=[] 2106 nouser=[] 2107 times_by_user={} 2108 for user in dbactiveusers: 2109 times_by_user[user['username']]=[datetime.timedelta(seconds=0)] 2110 users.append(user) 2111 hours = {} 2112 deltas=[] 2113 for time in pay: 2114 if time['modified_by'][0] not in times_by_user: 2115 times_by_user[time['modified_by'][0]]=[datetime.timedelta(seconds=0)] 2116 try: 2117 user_lookup = mongo.db.user_collection.find_one({'username':time['modified_by'][0]}) 2118 except: 2119 nouser.append(time['_id']) 2120 else: 2121 users.append(user_lookup) 2122 if 'clock_out' not in time: 2123 time['clock_out']=[datetime.datetime.now()] 2124 t = time['clock_out'][0] - time['clock_in'][0] 2125 hours['total_time'] = t 2126 times_by_user[time['modified_by'][0]].append(t) 2127 deltas.append(time) 2128 2129 for user in users: 2130 total_hours = sum(times_by_user[user['username']],datetime.timedelta()) 2131 statement_hours = (total_hours.seconds//3600,(total_hours.seconds//60)%60) 2132 user['total_hours']=statement_hours 2133 return render_template('admin/reports/pay_period_report.html', nouser=nouser, users=users, pay=pay, ORGNAME=OrganizationName) 2134 2135 # @app.route("/dev/fleetdata") 2136 # @login_required 2137 # def fleetdatalist(): 2138 # allfleetdata = mongo.db.fleet_collection.find() 2139 # return render_template('dev/fleetdata.html', allfleetdata=allfleetdata) 2140 def calculateHours(username=current_user): 2141 if not mongo.db.time_collection.find({"modified_by.0":username}): 2142 return 0 2143 else: 2144 times = mongo.db.time_collection.find({"modified_by.0":username}) 2145 deltas=[] 2146 2147 for time in times: 2148 if 'clock_out' not in time: 2149 time['clock_out']=[datetime.datetime.now()] 2150 t = time['clock_out'][0] - time['clock_in'][0] 2151 deltas.append(t) 2152 2153 total_calculated_hours = sum(deltas,datetime.timedelta()) 2154 2155 return total_calculated_hours 2156 2157 @app.route('/admin/reports/employees') 2158 @login_required 2159 def report_employees(): 2160 by_user = get_all_user_times() 2161 users = mongo.db.user_collection.find() 2162 hours = mongo.db.time_collection.find() 2163 for user in users: 2164 user['total_hours']=calculateHours(user['username']) 2165 return render_template ('admin/employee_report/index.html', by_user=by_user, hours=hours, users=users, ORGNAME=OrganizationName) 2166 2167 @app.route('/admin/reports/employee/<username>') 2168 @login_required 2169 def employee_report(username): 2170 user = mongo.db.user_collection.find_one({"username": username}) 2171 hours = mongo.db.time_collection.aggregate( [ 2172 { 2173 "$match": { 2174 "modified_by.0":username 2175 } 2176 }, 2177 { 2178 "$sort": { "date": -1 } 2179 } 2180 ] )# Total hours worked 2181 #hours = mongo.db.time_collection.find({'modified_by.0':user['username']}) 2182 # hours = mongo.db.time_collection.aggregate( 2183 # { 2184 # "$match": {'modified_by.0':user['username'] } 2185 # }, 2186 # { 2187 # "$lookup": { "from":"projects_collection", "localField":"project", "foreignField":"project_name", "as":"project_data"} 2188 # }) 2189 thw = mongo.db.time_collection.aggregate( [ 2190 { 2191 "$match": { 2192 "modified_by.0":username 2193 } 2194 }, 2195 { 2196 "$group": { 2197 "_id":"$modified_by.0", 2198 "totalHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } } 2199 } 2200 }, 2201 { 2202 "$sort": { "totalHoursWorked": -1 } 2203 } 2204 ] )# Total hours worked 2205 tspp = mongo.db.time_collection.aggregate( [ 2206 { 2207 "$match": { 2208 "modified_by.0":username 2209 } 2210 }, 2211 { 2212 "$group": { 2213 "_id":{"projectId": "$project"}, 2214 "totalHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } } 2215 } 2216 }, 2217 { 2218 "$sort": { "totalHoursWorked": -1 } 2219 } 2220 ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) 2221 2222 return render_template ('admin/reports/employee_report.html', hours=hours, user=user, thw=thw, tspp=tspp, ORGNAME=OrganizationName) 2223 2224 # Vehicle Routes 2225 @app.route('/admin/reports/vehicles') 2226 @login_required 2227 def vehicle_report(): 2228 return render_template ('admin/reports/vehicle_report.html', ORGNAME=OrganizationName) 2229 2230 @app.route('/admin/reports/vehicles/<vehicle>') 2231 @login_required 2232 def vehicle_indepth(): 2233 return render_template ('admin/reports/vehicle_report.html', ORGNAME=OrganizationName) 2234 # Report Routes End 2235 2236 #====================================# 2237 ############# ############## 2238 #### DEVELOPMENT ROUTES #### 2239 ############# ############## 2240 #====================================# 2241 2242 # DEVELOPMENT ROUTES, remove/modify permissions before production 2243 2244 #### #### 2245 ####### Fleet Data Route ####### 2246 #### #### 2247 @app.route("/dev/fleetdata") 2248 @login_required 2249 def fleetdatalist(): 2250 allfleetdata = mongo.db.fleet_collection.find() 2251 return render_template('dev/fleetdata.html', allfleetdata=allfleetdata) 2252 2253 #### #### 2254 ####### User Data Route ####### 2255 #### #### 2256 @app.route("/dev/usrs") 2257 @login_required 2258 def userslist(): 2259 allusers = mongo.db.user_collection.find() 2260 return render_template('dev/usrs.html', allusers=allusers) 2261 2262 #### #### 2263 ####### Time Data Route ####### 2264 #### #### 2265 @app.route("/dev/timedata", methods=['GET']) 2266 @login_required 2267 def timedata(): 2268 alltimedata = mongo.db.time_collection.find() 2269 return render_template('dev/timedata.html', alltimedata=alltimedata) 2270 2271 #### #### 2272 ####### Agreement Data Route ####### 2273 #### #### 2274 @app.route("/dev/agreementdata", methods=['GET']) 2275 @login_required 2276 def agreementdata(): 2277 allagreementdata = mongo.db.agreements_collection.find() 2278 return render_template('dev/agreementdata.html', allagreementdata=allagreementdata) 2279 2280 #### #### 2281 ####### Project Data Route ####### 2282 #### #### 2283 @app.route("/dev/projectdata", methods=['GET']) 2284 @login_required 2285 def projectdata(): 2286 allprojectsdata = mongo.db.projects_collection.find() 2287 return render_template('dev/projectdata.html', allprojectsdata=allprojectsdata)