Merge pull request 'Merge in dev to Master' (#1) from dev into master
Some checks failed
Build and Push Image / Build and push image (push) Failing after 12m41s

Reviewed-on: #1
This commit is contained in:
armistace 2024-09-16 13:27:12 +10:00
commit 4baf08813c
14 changed files with 134 additions and 126 deletions

7
README.md Normal file
View File

@ -0,0 +1,7 @@
# BEER SG data storer
This container will store SG data for beer runs in Mongo and then use that data to calculate Alchohol Content of each beer
It's pretty loose and requires the Data Entry operator to know whats happening
Luck for me... That's me!

View File

@ -1,4 +1,5 @@
FROM git.aridgwayweb.com/armistace/beer_base_image AS flask
#FROM git.aridgwayweb.com/armistace/beer_base_image AS flask
FROM beer_base_image AS flask
COPY requirements.txt .
@ -8,6 +9,6 @@ ENV FLASK_ENV production
ENV FLASK_DEBUG 1
ENTRYPOINT ["flask", "--app", "/pool_data/src/flask/beer_data", "run", "--host=0.0.0.0"]
ENTRYPOINT ["flask", "--app", "/beer_data/src/flask/beer_data", "run", "--host=0.0.0.0"]
#ENTRYPOINT ["python", "/pool_data/src/flask/pool_data.py"]

View File

@ -7,3 +7,5 @@ Flask-WTF
bootstrap-flask
waitress
bokeh
pandas
duckdb

View File

@ -20,7 +20,7 @@ def main(username, password):
"password" : f"{password}"
}
if user_collection.user_exists(new_record["username"]):
user_collect.update_user(user_collection.existing_record["_id"], new_record["password"])
user_collection.update_user(user_collection.existing_record["_id"], new_record["password"])
else:
user_collection.add_user(new_record["username"], new_record["password"])

View File

@ -1,11 +1,12 @@
from bokeh.core.enums import SpatialUnitsType
import mongo.build_db as pool_database
import mongo.query_db as pool_database_query
import mongo.build_db as beer_database
import mongo.query_db as beer_database_query
from table import table_builder
from flask import Flask, render_template, request, jsonify, redirect, session
from flask_wtf import FlaskForm, CSRFProtect
from flask_bootstrap import Bootstrap5
from wtforms import StringField, SubmitField, DateField, IntegerField, PasswordField, DecimalField, RadioField, TextAreaField
from wtforms import StringField, SubmitField, DateField, IntegerField, PasswordField, DecimalField, RadioField, TextAreaField, BooleanField
from wtforms.validators import DataRequired, Length, Optional
from waitress import serve
from bokeh.models.layouts import HBox
@ -14,6 +15,7 @@ from charts import BeerCharts
from bokeh.io import output_file, show
from bokeh.layouts import row
app = Flask(__name__)
app.secret_key = 'testsecret' #this value will change
@ -26,7 +28,7 @@ output_file("/beer_data/src/flask/static/data_plot.html")
def create_graphs():
chart = BeerCharts.BeerCharts()
sg = chart.line_chart("SG", "sg", 30)
show(row(column(sg)))
show(column(sg))
class userForm(FlaskForm):
@ -39,6 +41,7 @@ class dataForm(FlaskForm):
beer_run_type = TextAreaField("Beer Type")
Date = DateField("Date:")
sg = IntegerField("SG Reading")
final_run = BooleanField("Final Reading?")
comment = TextAreaField("Any Comments?", validators=[Optional()])
submit = SubmitField("Write it, Write it REAAAAAAL GOOOD")
@ -48,7 +51,7 @@ def index():
if form.validate_on_submit():
username = form.username.data
password = form.password.data
db = pool_database_query.pool_query()
db = beer_database_query.pool_query()
if db.user_check(username, password):
session['logged_in'] = True
return redirect("/updater")
@ -61,18 +64,24 @@ def index():
def updater():
if 'logged_in' not in session:
return redirect("/")
create_graphs()
query_db = pool_database_query.pool_query()
predicted_alc_table = table_builder.TableBuilder().table_build()
tr_replace_string = '<tr align="center" style="border-bottom:1pt solid black;">'
beer_html = predicted_alc_table.to_html(col_space='75px', index=False,
justify='center', border=3).replace('<tr>', tr_replace_string)
query_db = beer_database_query.pool_query()
query = query_db.get_top(10, "sg")
form = dataForm()
if form.validate_on_submit():
database = pool_database.pool_data()
database = beer_database.beer_data()
new_record = {
"date": f'{form.Date.data}',
"beer_run_id": f'{form.beer_run_id.data}',
"beer_type": f'{form.beer_run_type.data}',
"sg" : f'{form.sg.data}',
"final_reading" : F'{form.final_run.data}',
"comment": f'{form.comment.data}'
}
if database.record_exists(new_record["date"], new_record["beer_run_id"]):
@ -82,34 +91,40 @@ def updater():
if database.existing_record[field] != new_record[field]:
database.update_re_record(database.existing_record["_id"], field, new_record[field])
else:
database.create_re_record(new_record["beer_run_id"], new_record["beer_type"], new_record["sg"],
new_record["date"], new_record["comment"])
return render_template("updater.html", list=query, form=form, success=True, updater_name = new_record["test_user"])
else:
return render_template("updater.html", list=query, form=form, sucess=False)
@app.route("/update_db", methods=["POST"])
def pool_data_update():
database = pool_database.pool_data()
new_record = request.json
if database.record_exists(new_record["date"], new_record["test_user"]):
for field in database.existing_record:
for new_field in new_record:
if field == new_field:
if database.existing_record[field] != new_record[field]:
database.update_re_record(database.existing_record["_id"], field, new_record[field])
if new_record["final_reading"] == "True":
final_run_value = True
else:
database.create_re_record(new_record["beer_run_id"], new_record["beer_type"], new_record["sg"],
new_record["date"], new_record["comment"])
final_run_value = False
database.create_re_record(new_record["beer_run_id"], new_record["beer_type"], new_record["sg"],
new_record["date"], final_run_value, new_record["comment"])
return render_template("updater.html", beer_data = beer_html
, list=query, form=form, success=True, updater_name = "Saucy Beer Maker")
else:
return render_template("updater.html", beer_data = beer_html
, list=query, form=form, sucess=False)
# @app.route("/update_db", methods=["POST"])
# def beer_data_update():
# database = beer_database.beer_data()
# new_record = request.json
# if database.record_exists(new_record["date"], new_record["test_user"]):
# for field in database.existing_record:
# for new_field in new_record:
# if field == new_field:
# if database.existing_record[field] != new_record[field]:
# database.update_re_record(database.existing_record["_id"], field, new_record[field])
# else:
# database.create_re_record(new_record["beer_run_id"], new_record["beer_type"], new_record["sg"],
# new_record["date"], new_record["comment"])
@app.route("/pool_top/<int:return_number>/<string:field>")
def user_detail(id):
query_db = pool_database_query.pool_query()
query = query_db.get_top(return_number, field)
return jsonify([row.to_json() for row in query])
# @app.route("/pool_top/<int:return_number>/<string:field>")
# def user_detail(id):
# query_db = beer_database_query.pool_query()
# query = query_db.get_top(return_number, field)
# return jsonify([row.to_json() for row in query])
if __name__ == '__main__':
#app.run(host='0.0.0.0')

View File

@ -1,61 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bokeh Plot</title>
<style>
html, body {
box-sizing: border-box;
display: flow-root;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-3.5.0.min.js"></script>
<script type="text/javascript">
Bokeh.set_log_level("info");
</script>
</head>
<body>
<div id="bb5c0e1d-dc95-4d96-9cbc-a29ed2eade5c" data-root-id="p1001" style="display: contents;"></div>
<script type="application/json" id="af8187ed-c6d3-40c3-84a3-bbe58799b466">
{"00bec54e-f52e-4b7f-b2b6-8e6e4eb73c82":{"version":"3.5.0","title":"Bokeh Application","roots":[{"type":"object","name":"Figure","id":"p1001","attributes":{"height":250,"x_range":{"type":"object","name":"FactorRange","id":"p1011","attributes":{"factors":["2024-03-27","2024-03-25","2024-03-15","2024-03-13"]}},"y_range":{"type":"object","name":"DataRange1d","id":"p1003","attributes":{"start":6}},"x_scale":{"type":"object","name":"CategoricalScale","id":"p1012"},"y_scale":{"type":"object","name":"LinearScale","id":"p1013"},"title":{"type":"object","name":"Title","id":"p1004","attributes":{"text":"Pool data"}},"renderers":[{"type":"object","name":"GlyphRenderer","id":"p1030","attributes":{"data_source":{"type":"object","name":"ColumnDataSource","id":"p1024","attributes":{"selected":{"type":"object","name":"Selection","id":"p1025","attributes":{"indices":[],"line_indices":[]}},"selection_policy":{"type":"object","name":"UnionRenderers","id":"p1026"},"data":{"type":"map","entries":[["x",["2024-03-27","2024-03-25","2024-03-15","2024-03-13"]],["y",[7.25,7.5,7.6,7.0]]]}}},"view":{"type":"object","name":"CDSView","id":"p1031","attributes":{"filter":{"type":"object","name":"AllIndices","id":"p1032"}}},"glyph":{"type":"object","name":"Line","id":"p1027","attributes":{"x":{"type":"field","field":"x"},"y":{"type":"field","field":"y"},"line_color":"#1f77b4"}},"nonselection_glyph":{"type":"object","name":"Line","id":"p1028","attributes":{"x":{"type":"field","field":"x"},"y":{"type":"field","field":"y"},"line_color":"#1f77b4","line_alpha":0.1}},"muted_glyph":{"type":"object","name":"Line","id":"p1029","attributes":{"x":{"type":"field","field":"x"},"y":{"type":"field","field":"y"},"line_color":"#1f77b4","line_alpha":0.2}}}}],"toolbar":{"type":"object","name":"Toolbar","id":"p1010"},"toolbar_location":null,"left":[{"type":"object","name":"LinearAxis","id":"p1019","attributes":{"ticker":{"type":"object","name":"BasicTicker","id":"p1020","attributes":{"mantissas":[1,2,5]}},"formatter":{"type":"object","name":"BasicTickFormatter","id":"p1021"},"major_label_policy":{"type":"object","name":"AllLabels","id":"p1022"}}}],"below":[{"type":"object","name":"CategoricalAxis","id":"p1014","attributes":{"ticker":{"type":"object","name":"CategoricalTicker","id":"p1015"},"formatter":{"type":"object","name":"CategoricalTickFormatter","id":"p1016"},"major_label_policy":{"type":"object","name":"AllLabels","id":"p1017"}}}],"center":[{"type":"object","name":"Grid","id":"p1018","attributes":{"axis":{"id":"p1014"},"grid_line_color":null}},{"type":"object","name":"Grid","id":"p1023","attributes":{"dimension":1,"axis":{"id":"p1019"}}}]}}]}}
</script>
<script type="text/javascript">
(function() {
const fn = function() {
Bokeh.safely(function() {
(function(root) {
function embed_document(root) {
const docs_json = document.getElementById('af8187ed-c6d3-40c3-84a3-bbe58799b466').textContent;
const render_items = [{"docid":"00bec54e-f52e-4b7f-b2b6-8e6e4eb73c82","roots":{"p1001":"bb5c0e1d-dc95-4d96-9cbc-a29ed2eade5c"},"root_ids":["p1001"]}];
root.Bokeh.embed.embed_items(docs_json, render_items);
}
if (root.Bokeh !== undefined) {
embed_document(root);
} else {
let attempts = 0;
const timer = setInterval(function(root) {
if (root.Bokeh !== undefined) {
clearInterval(timer);
embed_document(root);
} else {
attempts++;
if (attempts > 100) {
clearInterval(timer);
console.log("Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing");
}
}
}, 10, root)
}
})(window);
});
};
if (document.readyState != "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
})();
</script>
</body>
</html>

View File

@ -1,17 +0,0 @@
from bokeh.models.layouts import HBox
from bokeh.plotting import column
from charts import PoolCharts
from bokeh.io import output_file, show
from bokeh.layouts import row
output_file("static/data_plot.html")
chart = PoolCharts.PoolCharts()
ph = chart.line_chart("Pool PH", "ph", 50)
total_chlorine = chart.line_chart("Pool Total Chlorine", "total_chlorine", 50)
free_chlorine = chart.line_chart("Pool Free Chlorine", "free_chlorine", 50)
show(column(ph, total_chlorine, free_chlorine))

View File

@ -9,9 +9,12 @@ class BeerCharts():
def __init__(self):
self.db = db_conn()
def bar_chart(self, items, data):
return 0
def line_chart(self, title, field, limit):
data = self.db.pool_db
data = self.db.beer_db
data = data.find({}).sort("date", -1).limit(limit)
dates = []
data_list = []

View File

@ -15,14 +15,14 @@ class beer_data:
#we can get self.db.real_db etc
self.db = db_conn()
def record_exists(self, date, test_user):
def record_exists(self, date, beer_run_id):
"""
This function will accept an address
if it find that address in the database it will return True
and set set the existing_record variable of the class to the
queried record
"""
query = { "date" : f"{date}", "test_user": f"{test_user}"}
query = { "date" : f"{date}", "beer_run_id": f"{beer_run_id}"}
record = self.db.beer_db.find_one(query)
if record:
self.existing_record = record
@ -31,7 +31,7 @@ class beer_data:
return False
def create_re_record(self, beer_run_id, beer_type, sg, date, comment=""):
def create_re_record(self, beer_run_id, beer_type, sg, date, final_reading=False, comment=""):
"""
create_re_record creates a whole new record
takes the required 7 inputs
@ -51,6 +51,7 @@ class beer_data:
"beer_type": f"{beer_type}",
"sg": f"{sg}",
"date": f"{date}",
"final_reading": f"{final_reading}",
"comment": f"{comment}"
}

View File

@ -8,7 +8,7 @@ class db_conn:
self.db_host = os.getenv('MONGO_HOST')
self.client = self.get_client()
self.db = self.client['beer_db']
self.pool_db = self.db['beer_data']
self.beer_db = self.db['beer_data']
self.users = self.db['users']
self.inspections = self.db['inspections_db']

View File

View File

@ -0,0 +1,54 @@
import sys
from mongo.get_conn import db_conn
import pandas as pd
import duckdb
class TableBuilder():
def __init__(self) -> None:
self.db = db_conn()
def table_build(self, limit=10) -> pd.DataFrame:
data = self.db.beer_db
data = data.find({}).sort("date", -1)
df_dict = {}
df_dict["beer_run_id"]=[]
df_dict["sg"] = []
df_dict["date"] = []
df_dict["final_reading"] = []
for record in data:
df_dict["beer_run_id"].append(record["beer_run_id"])
df_dict["sg"].append(record["sg"])
df_dict["date"].append(record["date"])
df_dict["final_reading"].append(record["final_reading"])
df = pd.DataFrame(data=df_dict)
sql = f"""
SELECT x.beer_run_id as beer_run_id,
max(sg) as max,
min(sg) as min,
y.date as final_reading_date
FROM df x
JOIN
( SELECT DISTINCT beer_run_id, date
FROM df
WHERE final_reading = 'True'
) y ON x.beer_run_id = y.beer_run_id
GROUP BY x.beer_run_id, y.date
ORDER BY x.beer_run_id desc
LIMIT {limit}
"""
df_sum = duckdb.sql(sql).df()
sql = f"""
SELECT x.beer_run_id as "Beer Run",
x.max as "Max",
x.min as "Min",
ROUND(((CAST (max AS INTEGER) - CAST(min AS INTEGER)) / 7.36) + 0.5, 2) AS "Alcohol Prediction",
cast(final_reading_date as DATE) + INTERVAL 14 DAY as "Ready Date"
FROM df_sum x
"""
df_calc = duckdb.sql(sql).df()
return df_calc

View File

@ -46,17 +46,15 @@
</div>
</div>
</td> -->
<td>
<div class="container">
<div class= "col-xs-12 col-sm-12 col-md-12">
<iframe style="width: 100vw;height: 80vh;position: relative;" src="static/data_plot.html" frameborder="0" allowfullscreen>
</iframe>
</div>
</div>
</td>
<!--<iframe style="width: 100vw; height: 40vh;" src="static/data_plot.html" frameborder="0" allowfullscreen>
<!--<iframe style="width: 100vw;height: 80vh;position: relative;" src="static/data_plot.html" frameborder="0" allowfullscreen>-->
</tr>
</table>
</div>
<div class="col-md-6">
{{ beer_data | safe }}
</div>
</div>
</div>
</div>

5
src/flask/test.py Normal file
View File

@ -0,0 +1,5 @@
import table.table_builder as table_builder
test = table_builder.TableBuilder()
print(test.table_build())