commit 1196103e66282b2dd4447e184bcd6fd24c4a2467
parent 88f792f655122eb4bee6859eb79a2094d206d524
Author: Youth Employment Program Production <youthemployment22@gmail.com>
Date: Tue, 16 Apr 2024 15:57:32 -0600
refactor hours route, add project name and updateProjectTime. Cleanup hours dev. add remove_agreement
Diffstat:
5 files changed, 199 insertions(+), 85 deletions(-)
diff --git a/app/routes.py b/app/routes.py
@@ -317,7 +317,14 @@ def dashboard():
@login_required
def updateProjectTime(mod_username,timeid):
timeid = ObjectId(timeid)
+ availableProjects = [] #change to get_available_projects() -> projects where user branch == project['branch']
form = updateProject()
+
+ for project in mongo.db.projects_collection.find():
+ availableProjects.append((project['_id'],project['project_name']))
+
+ form.projectSel.choices = availableProjects
+
if form.validate_on_submit():
app.logger.info('update project route')
try:
@@ -328,11 +335,11 @@ def updateProjectTime(mod_username,timeid):
try:
mongo.db.time_collection.update_one({'_id':timeid},{'$set':{'project':ObjectId(form.projectSel.data)}})
except:
- flash("unable to set project {}".format(ObjectId(form.projectSel.data)))
+ flash("unable to set project for {} to {}".format(timeid, form.projectSel.data))
else:
- flash("Updated project to {}".format(ObjectId(form.projectSel.data)))
+ flash("Updated project")
finally:
- return redirect(url_for('dashboard'))#change to hours and mod_username redirect
+ return redirect(url_for('hours',username=entry['modified_by'][0]))#change to hours and mod_username redirect
return render_template('dashboard/punchclock/update/project.html',form=form, ORGNAME=OrganizationName)
@app.route("/update/start/<mod_username>/<timeid>",methods=['GET','POST']) #TODO MAKE OTHER VALUE FOR LAST PAGE, RETURNS LAST PAGE ELSE DASHBOARD
@@ -753,11 +760,6 @@ def admin():
@login_required
def hoursd(username):#userid goes into call to db to get user[] -> then returns formatted table (punchclock/index.html
- availableProjects = []
-
- 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
- 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'
- #'completed':{'$exists':False}
aghours = mongo.db.time_collection.find({'modified_by.0':username})
dbhours = mongo.db.time_collection.aggregate( [
{
@@ -779,21 +781,6 @@ def hoursd(username):#userid goes into call to db to get user[] -> then returns
}
] )
- hours = []
- deltas=[]
-# for hour in dbhours: # Currenty acts wrong with longer than 1 day
-# for x, y in availableProjects:
-# if x is ObjectId(hour['project']):
-# hour['projectName'] = y
-# if 'clock_out' not in hour:
-# hour['clock_out']=[datetime.datetime.now()]
-# time = hour['clock_out'][-1] - hour['clock_in'][-1]
-# hour['total_time'] = time
-# hours.append(hour)
-# deltas.append(time)
-
- total_hours = sum(deltas,datetime.timedelta())
- statement_hours = "{} Hours, {} Minutes".format(total_hours.seconds//3600,(total_hours.seconds//60)%60)
tspp = mongo.db.time_collection.aggregate( [
{
@@ -823,17 +810,16 @@ def hoursd(username):#userid goes into call to db to get user[] -> then returns
] )# Time Spent Per Project (filter entries by username, then group and sum hours by project)
- return render_template ('dashboard/punchclock/index.dev.html',hours=dbhours,availableProjects=availableProjects,total_hours=total_hours,statement_hours=statement_hours,tspp=tspp,ORGNAME=OrganizationName)
+ return render_template ('dashboard/punchclock/index.dev.html',hours=dbhours,tspp=tspp,ORGNAME=OrganizationName)
#### ####
####### Hours Route #######
#### ####
-@app.route('/hours/<username>', methods=['GET','POST'])#modify to take userid ex. /hours<userid> for "admin" currently pulls from current_user... simply always pass username to hours(if possible set a default to current_user)
+@app.route('/hours/<username>', methods=['GET','POST'])
@login_required
-def hours(username):#userid goes into call to db to get user[] -> then returns formatted table (punchclock/index.html
- #set var user_hours = []
+def hours(username):
#query time collection for all time entries
#for entry in entries:
# time = entry.get('clock_out'[0],datetime.datetime.now()) - entry['clock_in'][0]
@@ -851,46 +837,65 @@ def hours(username):#userid goes into call to db to get user[] -> then returns f
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
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'
#'completed':{'$exists':False}
- dbhours = mongo.db.time_collection.find({'modified_by.0':user['username']})
- hours = []
- deltas=[]
- for hour in dbhours: # Currenty acts wrong with longer than 1 day
- for x, y in availableProjects:
- if x is ObjectId(hour['project']):
- hour['projectName'] = y
- if 'clock_out' not in hour:
- hour['clock_out']=[datetime.datetime.now()]
- time = hour['clock_out'][-1] - hour['clock_in'][-1]
- hour['total_time'] = time
- hours.append(hour)
- deltas.append(time)
+ dbhours = mongo.db.time_collection.aggregate( [
+ {
+ "$match":{'modified_by.0':username}
+ },
+ {
+ '$lookup': {
+ 'from': 'projects_collection',
+ 'localField': 'project',
+ 'foreignField': '_id',
+ 'as': 'project'
+ }
+ },
+ {
+ "$sort":{'clock_in':-1}
+ },
+ {
+ "$limit":10 #change to ~ 30 days OR to prior 1st or 15th of month
+ }
+
+ ] )
+ #hours = []
+ #deltas=[]
+ #for hour in dbhours: # Currenty acts wrong with longer than 1 day
+ # for x, y in availableProjects:
+ # if x is ObjectId(hour['project']):
+ # hour['projectName'] = y
+ # if 'clock_out' not in hour:
+ # hour['clock_out']=[datetime.datetime.now()]
+ # time = hour['clock_out'][-1] - hour['clock_in'][-1]
+ # hour['total_time'] = time
+ # hours.append(hour)
+ # deltas.append(time)
form = NewHoursForm()
form.dateSel.data = datetime.datetime.today()
form.projectSel.choices = availableProjects
form.startTime.data = datetime.datetime.now()
- total_hours = sum(deltas,datetime.timedelta())
- statement_hours = "{} Hours, {} Minutes".format(total_hours.seconds//3600,(total_hours.seconds//60)%60)
-
- if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry
- indt = datetime.combine(form.dateSel.data,form.startTime.data)
- outdt = datetime.combine(form.dateSel.data,form.endTime.data)
- if user['username'] is current_user.username and not form.perDiemSel.data and not form.lunchSel.data:
- mongo.db.time_collection.insert_one({'clock_in' : [indt],
- 'modified_by' : [current_user.username],
- 'date' : datetime.datetime.today(),
- 'project' : ObjectId(form.projectSel.data),
- 'clock_out':[outdt]})
- return redirect(url_for('hours',username=user['username']))
-
- if user['username'] is not current_user.username and not form.perDiemSel.data and not form.lunchSel.data:
- mongo.db.time_collection.insert_one({'clock_in' : [indt],
- 'modified_by' : [user['username'], current_user.username],
- 'date' : datetime.datetime.today(),
- 'project' : ObjectId(form.projectSel.data),
- 'clock_out':[outdt]})
-
- return redirect(url_for('hours',username=user['username']))
+ #total_hours = sum(deltas,datetime.timedelta())
+ #statement_hours = "{} Hours, {} Minutes".format(total_hours.seconds//3600,(total_hours.seconds//60)%60)
+
+# if form.validate_on_submit(): # Possible bug iff user clocks in between page load and form submit... will create additional time_collection entry
+# indt = datetime.combine(form.dateSel.data,form.startTime.data)
+# outdt = datetime.combine(form.dateSel.data,form.endTime.data)
+# if user['username'] is current_user.username and not form.perDiemSel.data and not form.lunchSel.data:
+# mongo.db.time_collection.insert_one({'clock_in' : [indt],
+# 'modified_by' : [current_user.username],
+# 'date' : datetime.datetime.today(),
+# 'project' : ObjectId(form.projectSel.data),
+# 'clock_out':[outdt]})
+# return redirect(url_for('hours',username=user['username']))
+#
+# if user['username'] is not current_user.username and not form.perDiemSel.data and not form.lunchSel.data:
+# mongo.db.time_collection.insert_one({'clock_in' : [indt],
+# 'modified_by' : [user['username'], current_user.username],
+# 'date' : datetime.datetime.today(),
+# 'project' : ObjectId(form.projectSel.data),
+# 'clock_out':[outdt]})
+#
+# return redirect(url_for('hours',username=user['username']))
#hours = mongo.db.time_collection.find({'modified_by.0':user.username})
@@ -923,7 +928,33 @@ def hours(username):#userid goes into call to db to get user[] -> then returns f
] )# Time Spent Per Project (filter entries by username, then group and sum hours by project)
- return render_template ('dashboard/punchclock/index.html',form=form,hours=hours,availableProjects=availableProjects,total_hours=total_hours,statement_hours=statement_hours,user=user,tspp=tspp,ORGNAME=OrganizationName)
+ return render_template ('dashboard/punchclock/index.html',form=form,hours=dbhours,user=user,tspp=tspp,ORGNAME=OrganizationName)
+
+@app.route('/allhours/<username>', methods=['GET','POST'])
+@login_required
+def all_user_hours(username):
+ user = mongo.db.user_collection.find_one({"username": username})
+ #need a function which checks for dated(archived) time collections, and appends them to list. Might look like below
+ # hours = [ {'current_year()":{'current pay period':{mongo_aggregate_lookup(returned doc)}},(each pay period in current year)},{'last_year().date':[{data},{data}]},etc...
+ # This setup might benefit from time entries being in their own db?
+ dbhours = mongo.db.time_collection.aggregate( [
+ {
+ "$match":{'modified_by.0':username}
+ },
+ {
+ '$lookup': {
+ 'from': 'projects_collection',
+ 'localField': 'project',
+ 'foreignField': '_id',
+ 'as': 'project'
+ }
+ },
+ {
+ "$sort":{'clock_in':-1}
+ }
+ ] )
+
+ return render_template ('dashboard/punchclock/all.html',hours=dbhours,user=user,ORGNAME=OrganizationName)
# Don't really need this until additional functionality is added, not in current project scope
#@app.route("/fleet")
@@ -1446,6 +1477,20 @@ def rename_agreement(agreement_id):
@login_required
def remove_agreement(agreement_id):
#TODO
+ form = ConfirmRemove()
+ if form.validate_on_submit():
+ try:
+ #TODO replace with get project(s) fn
+ agreement = mongo.db.agreement_collection.find_one({'_id':ObjectId(agreement_id)})
+ #END
+ except:
+ flash('Issue finding agreement id {}'.format(agreement_id))
+ else:
+ #for each project either remove or move
+ flash('WARNING: This action removes all projects currently associated with this agreement, AND removes all time entries tied to the projects.')
+# try:
+ # for project in agreement['projects']
+
return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName)#TODO FIX
@app.route('/admin/change/agreement/dates',methods=["GET","POST"])
diff --git a/app/templates/dashboard/punchclock/all.html b/app/templates/dashboard/punchclock/all.html
@@ -0,0 +1,42 @@
+{% extends 'base.html' %}
+
+{% block title %}Hours{% endblock %}
+
+{% block content %}
+ <section class="hours-grid">
+ <h3>{{ user.fname }} {{ user.lname }}</h3><!-- IF logged in user has permission allow this username section to be a dropdown for modifying user time sheets. -->
+ <h1 id="clock"></h1>
+ <table>
+ {% for event in hours %}
+ <tr>
+ {% for entry in event %}
+ <td>{{ event[entry] }}</br></td>
+ {% endfor %}
+ </tr>
+ {# <td><a href="{{url_for('updateDate',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.date.date().isoformat() }}</a></td>
+ <td><a href="{{url_for('updateProjectTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.project[0]['project_name'] }}</a></td>
+ <td><a href="{{url_for('updateStartTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.clock_in[-1].time().isoformat(timespec='minutes') }}</a></td>
+ <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.clock_out[-1].time().isoformat(timespec='minutes') }}</a></td>
+ {% if entry.lunch %}
+ <td><a href="{{url_for('toggle_lunch',timeid=entry._id)}}">Yes</a></td>
+ {% else %}
+ <td><a href="{{url_for('toggle_lunch',timeid=entry._id)}}">No</a></td>
+ {% endif %}
+ {% if entry.per_diem %}
+ <td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">Yes</a></td>
+ {% else %}
+ <td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">No</a></td>
+ {% endif %}
+ {% if entry.note %}
+ <td><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.note }}</a></td>
+ {% else %}
+ <td><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">Add Note</a></td>
+ {% endif %}
+ <td><a href="{{url_for( 'removetime',timeid=entry._id) }}" class="action-button">Remove</a></td>
+ #}
+ </tr>
+ {% endfor %}
+ </table>
+ </form>
+ </section>
+{% endblock %}
diff --git a/app/templates/dashboard/punchclock/index.dev.html b/app/templates/dashboard/punchclock/index.dev.html
@@ -8,23 +8,9 @@
<div><!-- abstract to payPeriod() -->
<h6>$payperiod range</h6>
<!--<h5>Total:{# {{ statement_hours }} #}</h5> -->
- {{availableProjects}}
</div>
<form action="" method="POST" novalidate>
<table><tr>
- {% for field in form %}{% if field.widget.input_type != 'hidden' and field.widget.input_type != 'submit' %}
- <th>{{ field.label }}</th>
- {% endif %}{% endfor %}</tr>
- <!--{# <tr>
- <td>{{ form.dateSel() }}</td>
- <td>{{ form.projectSel() }}</td>
- <td>{{ form.startTime() }}</td>
- <td>{{ form.endTime() }}</td>
- <td>{{ form.lunchSel() }}</td>
- <td>{{ form.perDiemSel() }}</td>
- <td>{{ form.note() }}</td>
- <td>{{ form.submitEntr() }}</td>
- </tr> #}-->
{% for entry in hours %}
<tr>
@@ -50,13 +36,15 @@
<td><a href="{{url_for( 'removetime',timeid=entry._id) }}" class="action-button">Remove</a></td>
{#{{ form.hidden_tag() }}#}
</tr>
- {{entry}}
+ {# {{entry}} #}
{% endfor %}
</table>
</form>
</section>
+ {#
{% for time in tspp %}
{{ time }}
</br>
{% endfor %}
+ #}
{% endblock %}
diff --git a/app/templates/dashboard/punchclock/index.html b/app/templates/dashboard/punchclock/index.html
@@ -2,7 +2,7 @@
{% block title %}Hours{% endblock %}
-{% block content %}
+{% block content %}{# change table to div structure https://css-tricks.com/complete-guide-table-element/ #}
<section class="hours-grid">
<h3>{{ user.fname }} {{ user.lname }}</h3><!-- IF logged in user has permission allow this username section to be a dropdown for modifying user time sheets. -->
<h1 id="clock"></h1>
@@ -39,9 +39,17 @@
{% for entry in hours %}
<tr>
<td><a href="{{url_for('updateDate',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.date.date().isoformat() }}</a></td>
- <td>{{ entry.projectName }}</td>
+ {% if entry.project[0] %}
+ <td><a href="{{url_for('updateProjectTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.project[0]['project_name'] }}</a></td>
+ {% else %}
+ <td><a href="{{url_for('updateProjectTime',mod_username=current_user.username,timeid=entry._id)}}">Project not Found</a></td>
+ {% endif %}
<td><a href="{{url_for('updateStartTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.clock_in[-1].time().isoformat(timespec='minutes') }}</a></td>
- <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.clock_out[-1].time().isoformat(timespec='minutes') }}</a></td>
+ {% if entry.clock_out %}
+ <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.clock_out[-1].time().isoformat(timespec='minutes') }}</a></td>
+ {% else %}
+ <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=entry._id)}}">Clocked In</a></td>
+ {% endif %}
{% if entry.lunch %}
<td><a href="{{url_for('toggle_lunch',timeid=entry._id)}}">Yes</a></td>
{% else %}
@@ -51,22 +59,26 @@
<td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">Yes</a></td>
{% else %}
<td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">No</a></td>
+ <td><a href="{{url_for( 'removetime',timeid=entry._id) }}" class="action-button">Remove</a></td>
+ </tr>
+ <tr>
{% endif %}
{% if entry.note %}
- <td><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.note }}</a></td>
+ <td colspan='6'><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.note }}</a></td>
{% else %}
- <td><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">Add Note</a></td>
+ <td colspan='6'><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">Add Note</a></td>
{% endif %}
- <td><a href="{{url_for( 'removetime',timeid=entry._id) }}" class="action-button">Remove</a></td>
- {#{{ form.hidden_tag() }}#}
</tr>
{# {{entry}} #}
{% endfor %}
</table>
</form>
+ <a href="{{url_for('all_user_hours',username=user.username)}}"class="action-button">Past Hours</a>
</section>
+ {#
{% for time in tspp %}
{{ time }}
</br>
{% endfor %}
+ #}
{% endblock %}
diff --git a/app/templates/dashboard/punchclock/update/project.html b/app/templates/dashboard/punchclock/update/project.html
@@ -0,0 +1,27 @@
+{% extends 'base.html' %}
+
+{% block title %}Update Project{% endblock %}
+
+{% block content %}
+<section class="new-agreement-grid">
+ <h3>Update Project</h3>
+ <form action="" method="POST" novalidate>
+ {{ form.hidden_tag() }}
+ {% for error in form.errors %}
+ <span style="color:red;">[{{ error }}]</span>
+ {% endfor %}
+ {% for ferror in form.form_errors %}
+ <span style="color:yellow;">[{{ ferror }}]</span>
+ {% endfor %}
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+ {% for message in messages %}
+ <p style='color:red'>{{ message }}</p>
+ {% endfor %}
+ {% endif %}
+ {% endwith %}
+ {{ form.projectSel.label }}{{ form.projectSel() }}<br>
+ {{ form.submitEntr() }}
+ </form>
+</section>
+{% endblock %}