stc

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

commit 177812e32e0e64b888ace44b8e6907d4d6c8b750
parent 41c9c44abf69a64b598de5d639021b2c0e595ce8
Author: Youth Employment Program Production <youthemployment22@gmail.com>
Date:   Sun, 17 Mar 2024 21:47:25 -0600

Cleanup after website work.

Diffstat:
Mapp/routes.py | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/static/css/main.css | 12+++++++++++-
Mapp/templates/admin/agreements/index.html | 28+++++++++++++++++-----------
Mapp/templates/admin/agreements/projects/index.html | 6+++---
Aapp/templates/admin/agreements/update/rename.html | 27+++++++++++++++++++++++++++
Mapp/templates/dashboard/activeusers/widget.html | 10+++++-----
Aapp/templates/dashboard/punchclock/index.dev.html | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/templates/dashboard/punchclock/index.html | 2+-
8 files changed, 253 insertions(+), 21 deletions(-)

diff --git a/app/routes.py b/app/routes.py @@ -707,6 +707,93 @@ def admin(): #def vehicle_report(): # return render_template ('admin/reports/vehicle_report.html', ORGNAME=OrganizationName) +############################# +###### DEV HOURS ROUTE ###### +############################# +@app.route('/hoursd/<username>', methods=['GET','POST']) +@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} + 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) + + 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'])) + + #hours = mongo.db.time_collection.find({'modified_by.0':user.username}) + + + tspp = mongo.db.time_collection.aggregate( [ + { + "$lookup":{ + 'from':'projects_collection', + 'localField':'project', + 'foreignField':'_id', + 'as':'project_data' + } + }, + { + "$group": { + "_id":"$project_data", + "laborHoursWorked": { "$sum": { "$subtract": [{"$last":"$clock_out"}, {"$last":"$clock_in"}] } }, + "lunchCount": { "$sum" : { '$cond':["$lunch",1,0] } }, + "perdiemCount": { "$sum" :{'$cond':["$per_diem",1,0] } } + } + }, + { + "$addFields": { + "totalHoursWorked": {"$subtract":[ "$laborHoursWorked", {"$multiply":["$lunchCount", 30 * 60 * 1000]}] } + } + }, + { + "$sort": { "_id": -1 } + } + ] )# Time Spent Per Project (filter entries by username, then group and sum hours by project) + + + return render_template ('dashboard/punchclock/index.dev.html',form=form,hours=hours,availableProjects=availableProjects,total_hours=total_hours,statement_hours=statement_hours,user=user,tspp=tspp,ORGNAME=OrganizationName) + + #### #### ####### Hours Route ####### @@ -1305,6 +1392,36 @@ def project_report(): return render_template('admin/reports/project.html',by_project=by_project,tspp=tspp,projectlookup=ptl,ORGNAME=OrganizationName) +@app.route('/admin/rename/agreement/<agreement_id>',methods=["GET","POST"]) +@login_required +def rename_agreement(agreement_id): + form = RenameAgreementForm() + if form.validate_on_submit(): + try: + project = mongo.db.projects_collection.find_one({'_id':project_id}) + except: + flash("Issue finding Project with id {}".format(project_id)) + else: + try: + mongo.db.projects_collection.update_one({'_id':ObjectId(project_id)},{'$set':{'project_name':form.newName.data}}) + except: + flash("Issue setting {} as project name for {}".format(form.newName.data,project_id)) + finally: + return redirect(url_for('project', project_id=project_id)) + return render_template('admin/agreements/projects/update/rename.html',form=form,ORGNAME=OrganizationName) + +@app.route("/admin/remove/agreement/<agreement_id>",methods=["GET","POST"]) +@login_required +def remove_agreement(agreement_id): + #TODO + return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName)#TODO FIX + +@app.route('/admin/change/agreement/dates',methods=["GET","POST"]) +@login_required +def change_agreement_dates(): + #TODO + return render_template('admin/agreements/projects/update/move.html',form=form,ORGNAME=OrganizationName)#TODO FIX + # Payperiod Routes #TODO marked 06.29,23 ###### TESTING START ####### diff --git a/app/static/css/main.css b/app/static/css/main.css @@ -7,7 +7,6 @@ --progressfill:var(--maincolor); --totalprogressfill:var(--accent); --progressbg:var(--zoning); - font-family: 'Courier New'; /* --zoning:#dcd; --maincolor:#dff; --accent:#f3f; @@ -141,6 +140,17 @@ dl { box-shadow: 0 3px 6px 0 rgba(0,0,0,0.2); /* &:hover { transform: translateY(-10px); box-shadow: 0 6px 6px 0 rgba(0,0,0,0.2); };*/ } +.action-button { + border: none; + background: var(--accent); + cursor: pointer; + border-radius: 3px; + padding: 6px; + width:150px; + height:1.2em; + color: white; + box-shadow: 0 3px 6px 0 rgba(0,0,0,0.2); +} /********** NAVIGATION **********/ header { font-size:1.5em; diff --git a/app/templates/admin/agreements/index.html b/app/templates/admin/agreements/index.html @@ -11,17 +11,23 @@ {% endif %} {% endwith %} <section class="agreement-grid"> - <h3>{{ agreement['agreement_name'] }}</h3> - - {%- for element in agreement['signing_agency'] %} - {{ agreement[element] }} - {%- endfor %} - {{ agreement['start_date'] }} - {{ agreement['end_date'] }} - {% if agreement['contacts'] %} - {% for contact in agreement['contacts'] %} - {{ contact }} - {% endfor %} - {% endif %} + <dl> + <dt>{{ agreement['agreement_name'] }}<a href="{{url_for('rename_agreement',agreement_id=agreement['_id'])}}" style="color:var(--accent)"> change</a></dt> + <dd> + {%- for element in agreement['signing_agency'] %} + {{ agreement[element] }} + {%- endfor %} + </dd> + <dd>{{ agreement['start_date'] }} - {{ agreement['end_date'] }}<a href="{{url_for('change_agreement_dates')}}" style="color:var(--accent);"> change</a></dd> + <dd> + {% if agreement['contacts'] %} + {% for contact in agreement['contacts'] %} + {{ contact }} + {% endfor %} + {% endif %} + </dd> + <dd><a href="{{url_for('remove_agreement',agreement_id=agreement['_id'])}}" style="color:var(--accent)">remove</a></dd> + </dl> <h5>Projects</h5> diff --git a/app/templates/admin/agreements/projects/index.html b/app/templates/admin/agreements/projects/index.html @@ -13,12 +13,12 @@ <section class="project-grid"> <dl> - <dt>{{ project['project_name'] }}<a href="{{url_for('rename_project', project_id=project['_id'])}}"> change</a></dt> + <dt>{{ project['project_name'] }}<a href="{{url_for('rename_project', project_id=project['_id'])}}"style="color:var(--accent);"> change</a></dt> <dd>ID: {{ project['_id'] }}</dd> - <dd>A_ID:{{ project['agreement']}}<a href="{{url_for('move_project',project_id=project['_id'])}}"> change</a></dd> + <dd>A_ID:{{ project['agreement']}}<a href="{{url_for('move_project',project_id=project['_id'])}}"style="color:var(--accent);"> change</a></dd> <dd>Budget:{{ project['budget']}}</dd> <dd>Costs:{{ project['costs']}}</dd> - <dd><a href="{{url_for('remove_project',project_id=project['_id'])}}"style="color:red;">remove</a></dd> + <dd><a href="{{url_for('remove_project',project_id=project['_id'])}}" style="color:var(--accent);">REMOVE</a></dd> </dl> <div class="current-pay-period"> diff --git a/app/templates/admin/agreements/update/rename.html b/app/templates/admin/agreements/update/rename.html @@ -0,0 +1,27 @@ +{% extends 'base.html' %} + +{% block title %}Update Project Name {% endblock %} + +{% block content %} +<section class="new-agreement-grid"> + <h3>Update Project Name</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.newName.label }}{{ form.newName() }}<br> + {{ form.renameProject() }} + </form> +</section> +{% endblock %} diff --git a/app/templates/dashboard/activeusers/widget.html b/app/templates/dashboard/activeusers/widget.html @@ -10,7 +10,7 @@ <table><!-- replace w/ function getUserHours(username) --> {% for user in clocked_in_users %} <tr> - <td><button><a href="{{ url_for('hours',username=user.modified_by.0) }}">{{ user.modified_by.0 }}</a></button></td> + <td><a href="{{ url_for('hours',username=user.modified_by.0) }}" class="action-button">{{ user.modified_by.0 }}</a></td> <td><a href="{{ url_for('toggle_lunch',timeid=user._id) }}"> {% if user.lunch %} <input type="checkbox" name="lunch" checked><label for="lunch">Lunch</label> @@ -25,14 +25,14 @@ <input type="checkbox" name="per_diem"><label for="per_diem">Per Diem</label> {% endif %} </a></td> - <td><button><a href="{{ url_for('hours',username=user.modified_by.0) }}">{{ user.clock_in.0.isoformat(timespec='minutes') }}</a></button></td><!-- can format/display non-military time with format %I:%M%p removed.time() after index to try and show date value--> - <td><button><a href="{{ url_for('clockout_by_id',modusernm=current_user.username,timeid=user._id) }}">Clock Out</a></button></td> + <td><a href="{{ url_for('hours',username=user.modified_by.0) }}" class="action-button">{{ user.clock_in.0.isoformat(timespec='minutes') }}</a></td><!-- can format/display non-military time with format %I:%M%p removed.time() after index to try and show date value--> + <td><a href="{{ url_for('clockout_by_id',modusernm=current_user.username,timeid=user._id) }}" class="action-button">Clock Out</a></td> </tr> {% endfor %} </table> </form> - <a href="{{ url_for('clockin_new_user') }}"><button>Clock in User</button></a> - <a href="{{ url_for('new_user_time') }}"><button>New User Entry</button></a> + <a href="{{ url_for('clockin_new_user') }}" class="action-button">Clock in User</a> + <a href="{{ url_for('new_user_time') }}" class="action-button">New User Entry</a> <!-- clock in clocked out user MIGHT NEED TO COMMENT OUT... DOES NOT GET DATA FROM FORMS ON SUBMIT... ONLY USES DEFAULT.data --> <!-- <form> <table> diff --git a/app/templates/dashboard/punchclock/index.dev.html b/app/templates/dashboard/punchclock/index.dev.html @@ -0,0 +1,72 @@ +{% 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> + <div><!-- abstract to payPeriod() --> + <h6>$payperiod range</h6> + <!--<h5>Total:{# {{ statement_hours }} #}</h5> --> + {{availableProjects}} + </div> + <a href="{{url_for('new_time',usernm=user.username)}}" style=" border: none; + background: var(--accent); + cursor: pointer; + border-radius: 3px; + padding: 6px; + width: 200px; + color: white; + margin-left: 25px; + box-shadow: 0 3px 6px 0 rgba(0,0,0,0.2);">New Time</a> + <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> + <td><a href="{{url_for('updateDate',mod_username=current_user.username,timeid=entry._id)}}">{{ entry.date.date().isoformat() }}</a></td> + <td>{{ entry.projectName }}</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> + {#{{ form.hidden_tag() }}#} + </tr> + {# {{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 @@ -57,7 +57,7 @@ {% else %} <td><a href="{{url_for('updateNote',mod_username=current_user.username,timeid=entry._id)}}">Add Note</a></td> {% endif %} - <td><button><a href="{{url_for( 'removetime',timeid=entry._id) }}">Remove</a></button></td> + <td><a href="{{url_for( 'removetime',timeid=entry._id) }}" class="action-button">Remove</a></td> {#{{ form.hidden_tag() }}#} </tr> {# {{entry}} #}