stc

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

commit 8dceaeecd45b24493b6906193dc764e7ac59bf6f
parent fa1a16b584a17e98e524123acb962b317b7c851e
Author: Youth Employment Program Production <youthemployment22@gmail.com>
Date:   Fri, 24 May 2024 08:51:04 -0600

various fixes to equipment and meetings pages, admin pages and hoursd

Diffstat:
Mapp/equipment/__pycache__/equipment.cpython-310.pyc | 0
Mapp/equipment/__pycache__/forms.cpython-310.pyc | 0
Mapp/equipment/__pycache__/routes.cpython-310.pyc | 0
Mapp/equipment/equipment.py | 31+++++++++++++++++++++++++++++++
Mapp/equipment/routes.py | 21++++++++-------------
Mapp/meetings/__pycache__/routes.cpython-310.pyc | 0
Mapp/meetings/routes.py | 40+++++++++++++++++++++++++++++++++++-----
Mapp/meetings/templates/meetings.html | 10+++++-----
Mapp/meetings/templates/upload_meeting_document.html | 2+-
Mapp/templates/admin/agreements/index.html | 4++--
Mapp/templates/admin/agreements/projects/index.html | 10+++++-----
Mapp/templates/admin/agreements/projects/newproject.html | 1+
Mapp/templates/admin/agreements/projects/update/rename.html | 1+
Mapp/templates/admin/bound_timedata_report/widget.html | 31++++++++++++++++++++++++-------
Mapp/templates/admin/layout.html | 9+++++++++
Mapp/templates/dashboard/punchclock/index.dev.html | 17+++++++++++++++++
16 files changed, 139 insertions(+), 38 deletions(-)

diff --git a/app/equipment/__pycache__/equipment.cpython-310.pyc b/app/equipment/__pycache__/equipment.cpython-310.pyc Binary files differ. diff --git a/app/equipment/__pycache__/forms.cpython-310.pyc b/app/equipment/__pycache__/forms.cpython-310.pyc Binary files differ. diff --git a/app/equipment/__pycache__/routes.cpython-310.pyc b/app/equipment/__pycache__/routes.cpython-310.pyc Binary files differ. diff --git a/app/equipment/equipment.py b/app/equipment/equipment.py @@ -30,6 +30,24 @@ def get_available_equipment(): equipment = mongo.db.equipment_collection.find({'retired':{'$exists':False},'checked_out':{'$exists':False}}) return equipment +# return available equipment by type +def get_available_equipment_type(equipment_type,branch=None): + matchdoc = {"equipment_type":equipment_type,'checked_out':{'$exists':False},'retired':{'$exists':False}} + if branch != None: + matchdoc["branch"] = branch + equipment = mongo.db.equipment_collection.aggregate( [ + { + "$match": matchdoc + }, + { + "$sort": { + "equipment_type":1, + "purchase_timestamp":1 + } + } + ] ) + return equipment + # return document of equipment type (_id) and count(number of occurances) def get_equipment_types(): types = mongo.db.equipment_collection.aggregate( [ @@ -51,3 +69,16 @@ def count_equipment_types(): counts.append((tool['_id'],label)) return counts +# update db and returns equipment with checked_out attribute set to True by id +def checkout_equipment_id(equipment_id): + equipment = fetch_equpiment(equipment_id) + equipment['checked_out'] = True + mongo.db.equipment_collection.update({'_id':equipment_id},{'$set':{'checked_out':True}}) + return equipment + +# update db and returns equipment with popped checked_out attribute by id +def checkin_equipment_id(equipment_id): + equipment = fetch_equipment(equipment_id) + equipment.pop('checked_out') + mongo.db.equipment_collection.update({'_id':equipment_id},{'$unset':{'checked_out':""}}) + return equipment diff --git a/app/equipment/routes.py b/app/equipment/routes.py @@ -4,9 +4,10 @@ from flask import render_template, redirect, url_for, flash, request from flask_login import login_required from bson.objectid import ObjectId from app.routes import get_available_projects +from app.branches.routes import get_available_branches from app.equipment import bp from app.equipment.forms import NewEquipment, UpdateEquipment -from app.equipment.equipment import fetch_equipment, EquipmentNotFoundError, get_all_equipment, get_usable_equipment, get_available_equipment, get_equipment_types, count_equipment_types +from app.equipment.equipment import fetch_equipment, EquipmentNotFoundError, get_all_equipment, get_usable_equipment, get_available_equipment, get_available_equipment_type, get_equipment_types, count_equipment_types import datetime mongo = PyMongo(app) @@ -136,18 +137,7 @@ def equipment(equipment_id): @bp.route("/equipment/t/<equipmentType>",methods=["GET"]) @login_required def EquipmentType(equipmentType): - equipment = mongo.db.equipment_collection.aggregate( [ - { - "$match": {"equipment_type":equipmentType - } - }, - { - "$sort": { - "equipment_type":1, - "purchase_timestamp":1 - } - } - ] ) + equipment = get_available_equipment_type(equipmentType) return render_template("equipment.html",equipment=equipment) @bp.route("/newequipment") @@ -183,6 +173,7 @@ def update_equipment(equipment_id,update): types.append(('Create New Type','Create New Type')) form.equipment_type.choices = types form.purchasing_project.choices = get_available_projects() + form.branch.choices = get_available_branches() try: equipment = fetch_equipment(equipment_id) @@ -216,6 +207,10 @@ def update_equipment(equipment_id,update): mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'purchasing_project':form.purchasing_project.data}}) flash("Updated Purchasing Project to {}".format(form.purchasing_project.data)) return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "branch": + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'branch':form.branch.data}}) + flash("Updated Branch to {}".format(form.branch.data)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) return render_template("form_equipment.html",update=update,form=form,equipment=equipment) diff --git a/app/meetings/__pycache__/routes.cpython-310.pyc b/app/meetings/__pycache__/routes.cpython-310.pyc Binary files differ. diff --git a/app/meetings/routes.py b/app/meetings/routes.py @@ -6,6 +6,7 @@ from app.meetings import bp from bson.objectid import ObjectId import datetime, hashlib import os +from gridfs import GridFS from app.meetings.forms import NewMeeting, UpdateMeeting, NewFileUpload #from app.meetings.update import #from app.meetings.meeting import @@ -107,6 +108,7 @@ def meetings(): '$limit':7 } ]) + return render_template('meetings.html',ongoingMeetings=ongoingMeetings,upcomingMeetings=upcomingMeetings,pastMeetings=pastMeetings) @bp.route('/meetings/past',methods=["GET"]) @@ -156,7 +158,7 @@ def new_meeting(): # new_meeting['meeting_name']=form.name.data return render_template('new_meeting.html',form=form) -@bp.route('/meeting/<meeting_id>/upload',methods=["GET","POST"]) +@bp.route('/meeting/<meeting_id>/upload',methods=["GET","POST","PUT"]) @login_required def upload_meeting_document(meeting_id): try: @@ -165,12 +167,40 @@ def upload_meeting_document(meeting_id): return render_template('error.html',error=e) else: form = NewFileUpload() - upload = datetime.datetime.utcnow() + #upload.filename = "test" if form.validate_on_submit(): - flash("submitted") - mongo.db.meeting_collection.update_one({'_id':meeting['_id']},{'$push':{'documents':'brennen'}}) + fs = GridFS(mongo.db) + upload = request.files['file'] + if upload.filename != '': + fs.put(upload) + flash("submitted {}".format(upload.filename)) + mongo.db.meeting_collection.update_one({'_id':meeting['_id']},{'$push':{'documents':upload.filename}}) return redirect(url_for('meetings.upload_meeting_document',meeting_id=meeting['meeting_id'])) - return render_template('upload_meeting_document.html',meeting=meeting,form=form,upload=upload) +# app_root = os.path.dirname(os.path.abspath(__file__)) +# target = os.path.join(app_root, 'static/meeting-docs') +# if not os.path.isdir(target): +# os.mkdir(target) +# if request.method == "POST": +# upload = request.files['file'] +# if upload.filename != '': +# upload.save(os.path.join('static/meeting-docs',upload.filename)) +# flash("submitted {}".format(upload.filename)) +# mongo.db.meeting_collection.update_one({'_id':meeting['_id']},{'$push':{'documents':upload.filename}}) +# return redirect(url_for('meetings.upload_meeting_document',meeting_id=meeting['meeting_id'])) + return render_template('upload_meeting_document.html',meeting=meeting,form=form) + +@bp.route('/meeting/get-file/<fid>') +@bp.route('/meeting/get-file') +def get_meeting_file(fid=None): + fs = GridFS(mongo.db) + #fs = GridFS(MongoClient().CINEfs_example) + + if fid is not None: + file = fs.get(ObjectId(fid)) + rfile = app.response_class(file, direct_passthrough=True, mimetype='application/octet-stream') + rfile.headers.set('Content-Disposition','attachment', filename=fid) #maybe change to actually return real filename instead of file_id + return rfile + return render_template('get_meeting_file.html', files=fs.list()) @bp.route('/meeting/<meeting_id>/update',methods=["GET","POST"]) @login_required diff --git a/app/meetings/templates/meetings.html b/app/meetings/templates/meetings.html @@ -10,8 +10,8 @@ {% endfor %} {% endif %} {% endwith %} - <section> - {% if ongoingMeetings %} + <section class="hours-grid"> + {% if ongoingMeetings is defined %} <section class="ongoing"> <h2 style="color:red">Ongoing</h2> {% for ongoing in ongoingMeetings %} @@ -33,14 +33,14 @@ </section> {% endif %} {% if pastMeetings %} - <section> + <section style="margin-bottom:1em;"> <h2>Past Meetings:</h2> {%- for meeting in pastMeetings %} - <div> + <div style="padding-bottom:1em;"> <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}"><div>{{ meeting.timestamp.date().isoformat() }} @ {{ meeting.timestamp.time().isoformat(timespec='minutes') }}</div>{% if allOldMeetings %}<div>{{meeting['meeting_description']}}</div>{% endif %}</a> </div> {%- endfor %} - {% if not allOldMeetings %}<a href="{{ url_for('meetings.past_meetings') }}"class="action-button">View older meetings</a>{% endif %} + {% if not allOldMeetings %}<a href="{{ url_for('meetings.past_meetings') }}" class="action-button">View older meetings</a>{% endif %} {% if allOldMeetings %}<a href="{{ url_for('meetings.meetings') }}"class="action-button">Back to meetings</a>{% endif %} </section> {% endif %} diff --git a/app/meetings/templates/upload_meeting_document.html b/app/meetings/templates/upload_meeting_document.html @@ -12,7 +12,7 @@ {% endwith %} <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}">back to meeting</a> <section class="hours-grid">{#class="meeting">#} - <h2>{{ upload.date() }} {{ upload.time().isoformat(timespec='minutes') }}</h2> + {# <h2>{{ upload.date() }} {{ upload.time().isoformat(timespec='minutes') }}</h2> #} <p>Upload document to {{ meeting['timestamp'].date() }} Meeting for {{ meeting['meeting_description'] }}</p> <form action="" method="POST" novalidate enctype="multipart/form-data"> {{ form.hidden_tag() }} diff --git a/app/templates/admin/agreements/index.html b/app/templates/admin/agreements/index.html @@ -14,8 +14,8 @@ <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] }} + {%- for element in agreement['agency'] %} + {{ agreement['agency'] }} {%- endfor %} </dd> <dd>{{ agreement['start_date'] }} - {{ agreement['end_date'] }}<a href="{{url_for('change_agreement_dates')}}" style="color:var(--accent);"> change</a></dd> diff --git a/app/templates/admin/agreements/projects/index.html b/app/templates/admin/agreements/projects/index.html @@ -36,13 +36,13 @@ </tr> {% for entry in payperiod_times %} <tr> - <td>{{entry['date'].date().isoformat()}}</td> + <td><a href="{{url_for('updateDate',mod_username=current_user.username,timeid=entry._id)}}">{{entry['date'].date().isoformat()}}</a></td> <td>{{entry.modified_by.0}}</td> {% if entry['clock_out'] %}<td>{{(entry['clock_out'][-1]-entry['clock_in'][-1])}}</td>{% else %}<td>Clocked In</td>{% endif %} - <td>{{entry['clock_in'][-1].time().isoformat(timespec='minutes')}}</td> - {% if entry['clock_out'] %}<td>{{entry['clock_out'][-1].time().isoformat(timespec='minutes')}}</td>{% else %}<td>Clocked In</td>{% endif %} - {% if entry['lunch'] %}<td>{{entry['lunch']}}</td>{% else %}<td>&nbsp;</td>{% endif %}<!-- make this an image of checkbox so can link to toggle route... --> - {% if entry['perdiem'] %}<td>{{entry['perdiem']}}</td>{% else %}<td>&nbsp;</td>{% endif %}<!-- make this an image of checkbox so can link to toggle route... --> + <td><a href="{{url_for('updateStartTime',mod_username=current_user.username,timeid=entry._id)}}">{{entry['clock_in'][-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)}}">{{entry['lunch']}}</a></td>{% else %}<td><a href="{{url_for('toggle_lunch',timeid=entry._id)}}">&nbsp;</a></td>{% endif %}<!-- make this an image of checkbox so can link to toggle route... --> + {% if entry['perdiem'] %}<td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">{{entry['perdiem']}}</a></td>{% else %}<td><a href="{{url_for('toggle_per_diem',timeid=entry._id)}}">&nbsp;</a></td>{% endif %}<!-- make this an image of checkbox so can link to toggle route... --> <td>{{entry['note']}}</td> </tr> {% endfor %} diff --git a/app/templates/admin/agreements/projects/newproject.html b/app/templates/admin/agreements/projects/newproject.html @@ -13,6 +13,7 @@ {% for ferror in form.form_errors %} <span style="color:yellow;">[{{ ferror }}]</span> {% endfor %} + {{ form.fenumber.label }}{{ form.fenumber() }}<br> {{ form.projectName.label }}{{ form.projectName() }}<br> {{ form.agreement.label }}{{ form.agreement() }}<br> {{ form.branch.label }}{{ form.branch() }}<br> diff --git a/app/templates/admin/agreements/projects/update/rename.html b/app/templates/admin/agreements/projects/update/rename.html @@ -20,6 +20,7 @@ {% endfor %} {% endif %} {% endwith %} + {{ form.fenumber.label }}{{ form.fenumber() }}<br> {{ form.newName.label }}{{ form.newName() }}<br> {{ form.renameProject() }} </form> diff --git a/app/templates/admin/bound_timedata_report/widget.html b/app/templates/admin/bound_timedata_report/widget.html @@ -67,21 +67,36 @@ <th>Time(hrs)</th> </tr> {% for time in times['times'] %} + <!-- {# <a href="{{url_for('hours',username=time['modified_by'][0])}}#{{time['_id']}}"> #} --> <tr> - <td>{{time['date'].date().isoformat()}}</td> - <td>{{time['project_data'][0]['project_name']}}</td> - <td>{{time['clock_in'][-1].time().isoformat(timespec="minutes")}}</td> + <td><a href="{{url_for('updateDate',mod_username=current_user.username,timeid=time._id)}}">{{time['date'].date().isoformat()}}</a></td> + <td><a href="{{url_for('updateProjectTime',mod_username=current_user.username,timeid=time._id)}}">{{time['project_data'][0]['project_name']}}</a></td> + <td><a href="{{url_for('updateStartTime',mod_username=current_user.username,timeid=time._id)}}">{{time['clock_in'][-1].time().isoformat(timespec="minutes")}}</a></td> {% if time['clock_out'] is defined %} - <td>{{time['clock_out'][-1].time().isoformat(timespec="minutes")}}</td> + <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=time._id)}}">{{time['clock_out'][-1].time().isoformat(timespec="minutes")}}</a></td> {% else %} - <td>Clocked In</td> + <td><a href="{{url_for('updateEndTime',mod_username=current_user.username,timeid=time._id)}}">Clocked In</a></td> {% endif %} - <td>{{time['lunchCount']}}</td> - <td>{{time['perdiemCount']}}</td> + <td><a href="{{url_for('toggle_lunch',timeid=time._id)}}">{{time['lunchCount']}}</a></td> + <td><a href="{{url_for('toggle_per_diem',timeid=time._id)}}">{{time['perdiemCount']}}</a></td> <td><a href="{{url_for('hours',username=time['modified_by'][0])}}">{{ (time['totalHoursWorked']/(1000*60*60))|round(2) }}</a></td> </tr> {% endfor %} </table></div> + <table> + <tr> + <th>Date</th> + <th>Note</th> + </tr> + {% for item in times['times'] %} + {% if item['note'] %} + <tr> + <td>{{item['date'].date().isoformat()}}</td> + <td>{{item['note']}}</td> + </tr> + {% endif %} + {% endfor %} + </table> {% endfor %} <!-- </section> @@ -129,11 +144,13 @@ <th>Note</th> </tr> {% for item in times['times'] %} + {% if item['note'] %} <tr> <td>{{item['date'].date().isoformat()}}</td> <td>{{item['modified_by'][0]}}</td> <td>{{item['note']}}</td> </tr> + {% endif %} {% endfor %} </table> {% endfor %} diff --git a/app/templates/admin/layout.html b/app/templates/admin/layout.html @@ -3,6 +3,15 @@ {% block title %}Admin{% endblock %} {% block content %} + +{% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} + <div id="messagebanner"><p>{{ message }}</p></div> + {% endfor %} + {% endif %} +{% endwith %} + <section class="admin-grid"> <!-- returned values from admin check is array of permissive ACCESS else return 'missing permissions response' --> {%- for x in permissions %} diff --git a/app/templates/dashboard/punchclock/index.dev.html b/app/templates/dashboard/punchclock/index.dev.html @@ -3,7 +3,20 @@ {% block title %}Hours{% endblock %} {% block content %} +{% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} + <div id='messagebanner'><p>{{message}}</p></div> + {% endfor %} + {% endif %} +{% endwith %} + <section class="hours-grid"> + {{ cd[2] }} + {{ cd[1] }} + {{ bd[2] }} + {{ bd[1] }} + {{ current_user.birthday }} <h1 id="clock"></h1> <div><!-- abstract to payPeriod() --> <h6>$payperiod range</h6> @@ -15,7 +28,11 @@ {% 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> + {% 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.lunch %}