stc

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

commit 6bfabd811896546f65313bd7d63d67ce20d2f953
parent bf96e08770de0d97021b57eddcf2ded640613d89
Author: Youth Employment Program Production <youthemployment22@gmail.com>
Date:   Tue, 23 Apr 2024 22:46:51 -0600

Refactor and abstract equipment fns, newequipment, update_equipment, and MVCs

Diffstat:
Aapp/equipment/__pycache__/equipment.cpython-310.pyc | 0
Mapp/equipment/__pycache__/forms.cpython-310.pyc | 0
Mapp/equipment/__pycache__/routes.cpython-310.pyc | 0
Aapp/equipment/equipment.py | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Mapp/equipment/forms.py | 12+++++++++++-
Mapp/equipment/routes.py | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Dapp/equipment/templates/dev.html | 0
Aapp/equipment/templates/dev_equipment.html | 11+++++++++++
Mapp/equipment/templates/equipment.html | 26++++++++++++++++++--------
Dapp/equipment/templates/form.html | 0
Aapp/equipment/templates/form_equipment.html | 31+++++++++++++++++++++++++++++++
11 files changed, 262 insertions(+), 23 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 @@ -0,0 +1,53 @@ +from app import app +from flask_pymongo import PyMongo +from bson.objectid import ObjectId + +mongo = PyMongo(app) + +class EquipmentNotFoundError(Exception): + pass + +def fetch_equipment(equipment_id): + equipment = mongo.db.equipment_collection.find_one({"_id":ObjectId(equipment_id)}) + + if equipment == None: + raise EquipmentNotFoundError(f'Equipment Id {equipment_id} returned None') + else: + return equipment + +# return all past and present equipment +def get_all_equipment(): + equipment = mongo.db.equipment_collection.find() + return equipment + +# return all equipment not damaged, in use, and not in use presently +def get_usable_equipment(): + equipment = mongo.db.equipment_collection.find({'retired':{'$exists':False}}) + return equipment + +# return equipment not damaged, and ready to use, does not return equipment in use presently +def get_available_equipment(): + equipment = mongo.db.equipment_collection.find({'retired':{'$exists':False},'checked_out':{'$exists':False}}) + return equipment + +# return document of equipment type (_id) and count(number of occurances) +def get_equipment_types(): + types = mongo.db.equipment_collection.aggregate( [ + { + '$group':{ + '_id':'$equipment_type', + 'count': {'$count':{} } + } + }, + { '$sort':{'equipment_type':1} } + ] ) + return types + +# return tuple list ('type','type (count)') +def count_equipment_types(): + counts = [] + for tool in get_equipment_types(): + label = "{} ({})".format(tool['_id'],tool['count']) + counts.append((tool['_id'],label)) + return counts + diff --git a/app/equipment/forms.py b/app/equipment/forms.py @@ -1,11 +1,21 @@ from flask_wtf import FlaskForm -from wtforms import StringField, SubmitField, SelectField, DateField, FloatField +from wtforms import StringField, SubmitField, SelectField, DateField, FloatField, IntegerField from wtforms.validators import DataRequired, optional, length, InputRequired, EqualTo class NewEquipment(FlaskForm): equipment_type = SelectField("Type",validators=[DataRequired()]) + equipment_type_number = IntegerField("Equipment Number",validators=[optional()]) purchase_timestamp = DateField("Purchase Date",validators=[DataRequired()]) purchase_price = FloatField("Purchase Price", validators=[optional()]) match_percentage = FloatField("Percentage Match",validators=[optional()]) purchasing_project = SelectField("Purchasing Project",validators=[optional()]) submit_equipment = SubmitField("Submit Equipment") + +class UpdateEquipment(FlaskForm): + equipment_type = SelectField("Type",validators=[optional()]) + equipment_type_number = IntegerField("Equipment Number",validators=[optional()]) + purchase_timestamp = DateField("Purchase Date",validators=[optional()]) + purchase_price = FloatField("Purchase Price", validators=[optional()]) + match_percentage = FloatField("Percentage Match",validators=[optional()]) + purchasing_project = SelectField("Purchasing Project",validators=[optional()]) + submit_equipment = SubmitField("Submit Equipment") diff --git a/app/equipment/routes.py b/app/equipment/routes.py @@ -3,15 +3,65 @@ from flask_pymongo import PyMongo 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.equipment import bp -from app.equipment.forms import NewEquipment +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 import datetime mongo = PyMongo(app) +#class EquipmentNotFoundError(Exception): +# pass +# +#def fetch_equipment(equipment_id): +# equipment = mongo.db.equipment_collection.find_one({"_id":ObjectId(equipment_id)}) +# +# if equipment == None: +# raise EquipmentNotFoundError(f'Equipment Id {equipment_id} returned None') +# else: +# return equipment +# +## return all past and present equipment +#def get_all_equipment(): +# equipment = mongo.db.equipment_collection.find() +# return equipment +# +## return all equipment not damaged, in use, and not in use presently +#def get_usable_equipment(): +# equipment = mongo.db.equipment_collection.find({'retired':{'$exists':False}}) +# return equipment +# +## return equipment not damaged, and ready to use, does not return equipment in use presently +#def get_available_equipment(): +# equipment = mongo.db.equipment_collection.find({'retired':{'$exists':False},'checked_out':{'$exists':False}}) +# return equipment +# +## return document of equipment type (_id) and count(number of occurances) +#def get_equipment_types(): +# types = mongo.db.equipment_collection.aggregate( [ +# { +# '$group':{ +# '_id':'$equipment_type', +# 'count': {'$count':{} } +# } +# }, +# { '$sort':{'equipment_type':1} } +# ] ) +# return types +# +## return tuple list ('type','type (count)') +#def count_equipment_types(): +# counts = [] +# for tool in get_equipment_types(): +# label = "{} ({})".format(tool['_id'],tool['count']) +# counts.append((tool['_id'],label)) +# return counts + ## DEV and SEED Routes ## @bp.route("/dev/equipment",methods=["GET"]) +@bp.route("/dev/equipment/",methods=["GET"]) @login_required def devEquipment(): equipment = mongo.db.equipment_collection.find({}) @@ -24,8 +74,7 @@ def seedEquipment(): seeds = [ { # If equipment_type == "vehicle" pass data as params to NewVehicle(FlaskForm) to add additional details like vehicle_number "equipment_type":"vehicle", - "branch":"Dillon", #TODO Change all references to branches to branch_id for scaling via a branch_collection - "vehicle_id": 1, + "branch":ObjectId("6627f69645530484ae5a4ade"), #TODO Change all references to branches to branch_id for scaling via a branch_collection "purchase_timestamp": datetime.datetime.now(), "purchase_price": 26500.68, "match_percentage": 10.25, @@ -33,7 +82,7 @@ def seedEquipment(): }, { "equipment_type":"shovel", - "branch":"Salmon", + "branch":ObjectId("6627f69645530484ae5a4adf"), "purchase_timestamp": datetime.datetime.now(), "purchase_price": 16.68, "match_percentage": 2.5, @@ -41,8 +90,7 @@ def seedEquipment(): }, { "equipment_type":"vehicle", - "branch":"Salmon", - "vehicle_id": 2, + "branch":ObjectId("6627f69645530484ae5a4adf"), "purchase_timestamp": datetime.datetime.now(), "purchase_price": 65238.2, "match_percentage": 12.5, @@ -51,8 +99,7 @@ def seedEquipment(): }, { "equipment_type":"chainsaw", - "gear_id":1, - "branch":"Dillon", + "branch":ObjectId("6627f69645530484ae5a4ade"), "purchase_timestamp": datetime.datetime.now(), "purchase_price": 16.68, "match_percentage": 2.5, @@ -60,8 +107,7 @@ def seedEquipment(): }, { "equipment_type":"chainsaw", - "gear_id":2, - "branch":"Dillon", + "branch":ObjectId("6627f69645530484ae5a4ade"), "purchase_timestamp": datetime.datetime.now(), "purchase_price": 16.68, "match_percentage": 2.5, @@ -77,17 +123,25 @@ def seedEquipment(): @bp.route("/equipment") @bp.route("/equipment/") @login_required -def equipment(): +def all_equipment(): equipment = mongo.db.equipment_collection.find({}) return render_template("equipment.html",equipment=equipment) -#### TODO pymongo.errors.OperationFailure: A pipeline stage specification object must contain exactly one field. errorcode: 40323 -@bp.route("/equipment/<equipmentType>",methods=["GET"]) +@bp.route("/equipment/<equipment_id>") +@login_required +def equipment(equipment_id): + equipment = mongo.db.equipment_collection.find({'_id':ObjectId(equipment_id)}) + return render_template("equipment.html",equipment_id=equipment_id,equipment=equipment) + +@bp.route("/equipment/t/<equipmentType>",methods=["GET"]) @login_required def EquipmentType(equipmentType): equipment = mongo.db.equipment_collection.aggregate( [ { - "$match": {"equipment_type":equipmentType}, + "$match": {"equipment_type":equipmentType + } + }, + { "$sort": { "equipment_type":1, "purchase_timestamp":1 @@ -95,3 +149,73 @@ def EquipmentType(equipmentType): } ] ) return render_template("equipment.html",equipment=equipment) + +@bp.route("/newequipment") +@bp.route("/newequipment/") +@login_required +def new_equipment(): + form = NewEquipment() + + types = [('',"Select Type")] + for t in count_equipment_types(): + types.append(t) + types.append(('Create New Type','Create New Type')) + form.equipment_type.choices = types + form.purchasing_project.choices = get_available_projects() + + return render_template("form_equipment.html",form=form,new=True) + +# TODO change to retire? How should this work? +@bp.route("/equipment/<equipment_id>/remove",methods=["GET","POST"]) +@login_required +def remove_equipment(equipment_id): + form = RemoveEquipment() + return render_template("form_equipment.html") + +@bp.route("/equipment/<equipment_id>/<update>",methods=["GET","POST"]) +@login_required +def update_equipment(equipment_id,update): + form = UpdateEquipment() + + types = [('',"Select Type")] + for t in count_equipment_types(): + types.append(t) + types.append(('Create New Type','Create New Type')) + form.equipment_type.choices = types + form.purchasing_project.choices = get_available_projects() + + try: + equipment = fetch_equipment(equipment_id) + except EquipmentNotFoundError as e: + return render_template('error.html',error=e) + else: + if form.validate_on_submit(): + match update: + case "equipment_type": + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'equipment_type':form.equipment_type.data}}) + flash("Updated Equipment Type to {}".format(form.equipment_type.data)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "equipment_type_number": + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'equipment_type_number':form.equipment_type_number.data}}) + flash("Updated Equipment Number for {} to {}".format(equipment['equipment_type'],form.equipment_type_number.data)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "purchase_timestamp": + timestamp = datetime.datetime.combine(form.date.data,datetime.min.time()) + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'purchase_timestamp':form.purchase_timestamp.data}}) + flash("Updated Purchase Timestamp to {}".format(timestamp)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "purchase_price": + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'purchase_price':form.purchase_price.data}}) + flash("Updated Purchase Price to {}".format(form.purchase_price.data)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "match_percentage": + mongo.db.equipment_collection.update_one({'_id':equipment['_id']},{'$set':{'match_percentage':form.match_percentage.data}}) + flash("Updated Match Percentage to {}".format(form.match_percentage.data)) + return redirect(url_for('equipment.equipment',equipment_id=equipment_id)) + case "purchasing_project": + 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)) + + return render_template("form_equipment.html",update=update,form=form,equipment=equipment) + diff --git a/app/equipment/templates/dev.html b/app/equipment/templates/dev.html diff --git a/app/equipment/templates/dev_equipment.html b/app/equipment/templates/dev_equipment.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% block title %}DEV Equipment{% endblock %} +{% block content %} + + {%- for x in dev_equipment %} + {%- print(x) %} + </br> + </br> + {%- endfor %} + +{% endblock %} diff --git a/app/equipment/templates/equipment.html b/app/equipment/templates/equipment.html @@ -12,20 +12,30 @@ {% endif %} {% endwith %} + {% if equipment_id or dev %} + <a href="{{url_for("equipment.all_equipment")}}"><div>back to equipment</div></a> + {% endif %} + <section> {% for tool in equipment %} {% if dev %} - {{ tool }} + <article>{{ tool }}</article> {% else %} - <article style="padding-top:2em;margin:0;"> - {% for key,value in tool.items() %} - <div style="display:grid;grid-gap:2em; grid-auto-flow:column; grid-template-columns:min-content"> - <div>{{ key }}</div> - <div>{{ value }}</div> - </div> - {% endfor %} + {% if not equipment_id %}<a href="{{url_for("equipment.equipment",equipment_id=tool['_id'])}}">{%else%}<a href="{{url_for("equipment.remove_equipment",equipment_id=tool['_id'])}}"style="color:red">remove</a>{% endif %} + <article style="padding-bottom:1em;padding-top:1em;margin:0;"> + {% for key,value in tool.items() %} + <div style="display:grid;grid-gap:2em; grid-auto-flow:column; grid-template-columns:min-content"> + <div>{{ key }}</div> + <div>{{ value }}</div> + {% if equipment_id %}<a href="{{ url_for("equipment.update_equipment",equipment_id=tool['_id'],update=key ) }}"style="color:red;">change</a>{% endif %} + </div> + {% endfor %} </article> + {% if not equipment_id %}</a>{% endif %} {% endif %} {% endfor %} + {% if not equipment_id %} + <a href="{{url_for("equipment.new_equipment")}}"><div>new equipment</div></a> + {% endif %} </section> {% endblock %} diff --git a/app/equipment/templates/form.html b/app/equipment/templates/form.html diff --git a/app/equipment/templates/form_equipment.html b/app/equipment/templates/form_equipment.html @@ -0,0 +1,31 @@ +{% extends 'base.html' %} +{% block title %}Equipment{% endblock %} +{% block content %} + +<a href="{{url_for('equipment.equipment',equipment_id=equipment['_id'])}}">back to {{equipment['equipment_type']}}</a> +<section> + <form action="" method="POST" novalidate> + {{ form.hidden_tag() }} + {% for error in form.errors %} + <span style="color:red;">[ {{ error }} ]</span> + {% endfor %} + {% if new %} + <div>NEW Equipment Block </div> + {{ form.equipment_type.label }}{{ form.equipment_type() }} + {{ form.purchase_timestamp.label }}{{ form.purchase_timestamp() }} + {{ form.purchase_price.label }}{{ form.purchase_price() }} + {{ form.match_percentage.label }}{{ form.match_percentage() }} + {{ form.purchasing_project.label }}{{ form.purchasing_project() }} + {% else %} + <div>Update Equipment Block </div> + {% if update == 'equipment_type' %}{{ form.equipment_type.label }}{{ form.equipment_type() }}{% endif %} + {% if update == 'purchase_timestamp' %}{{ form.purchase_timestamp.label }}{{ form.purchase_timestamp() }}{% endif %} + {% if update == 'purchase_price' %}{{ form.purchase_price.label }}{{ form.purchase_price() }}{% endif %} + {% if update == 'match_percentage' %}{{ form.match_percentage.label }}{{ form.match_percentage() }}{% endif %} + {% if update == 'purchasing_project' %}{{ form.purchasing_project.label }}{{ form.purchasing_project() }}{% endif %} + {% endif %} + {{ form.submit_equipment() }} + </form> +</section> + +{% endblock %}