diff --git a/db/README.md b/db/README.md index 5f4748d..7a5af19 100644 --- a/db/README.md +++ b/db/README.md @@ -17,4 +17,20 @@ ### tstamp is the exact Date at the first ever creation of the db entry and does not say anything about completion or rejection. ## NOTE: -### A created command is marked as failed after 5 minutes if its status is still pending, to ensure that no processes continue after a communication failure in the chain. \ No newline at end of file +### A created command is marked as failed after 5 minutes if its status is still pending, to ensure that no processes continue after a communication failure in the chain. + + +# COFFEE DB description + +## one command contains: +- user +- status +- tstamp + +### user may contain a string about the user that started the coffee making process + +### status may contain one out of following strings: +```["pending","failed","served","rejected"]``` + +## NOTE +### A Coffee making process is going through a command creation in the backend, therefore a coffee status can be rejected by the esp or fail after 5 minutes. If the according command fails the coffee fails. \ No newline at end of file diff --git a/db/coffee.db b/db/coffee.db index 1b3181e..54bf884 100644 Binary files a/db/coffee.db and b/db/coffee.db differ diff --git a/db/init_scripts/initCoffee.py b/db/init_scripts/initCoffee.py index 8a3c944..045c6b9 100644 --- a/db/init_scripts/initCoffee.py +++ b/db/init_scripts/initCoffee.py @@ -13,7 +13,7 @@ cursor = conn.cursor() # Tabelle erstellen mit entsprechenden Atributen cursor.execute(""" -CREATE TABLE IF NOT EXISTS commands ( +CREATE TABLE IF NOT EXISTS coffee ( id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT NOT NULL, status TEXT NOT NULL, diff --git a/modules/README.md b/modules/README.md new file mode 100644 index 0000000..f1e839a --- /dev/null +++ b/modules/README.md @@ -0,0 +1,13 @@ +# THE MODULES + +## the modules in this folder are used to make a more viewable server code possible. +## code that is either needed in different filed over the server folder structure is placed in an module and imported by some other files or just code sections which are doing replicated things like reading or writing data to something. + +## db.py +### in the DB module are some SQLite commands in functions. Each of them returns datasets according to it names so e.g. in Blueprint routing u only have to call a function to get your dataset which leads to more structured code. + +## persistence +### the persistence module contains just some functions to manage the in the persistence given JSON objects + +## socketio +### socketio is the main socket/flask server instance and some functions to manage communication \ No newline at end of file diff --git a/modules/db.py b/modules/db.py index 001a5d0..564dde3 100644 --- a/modules/db.py +++ b/modules/db.py @@ -3,6 +3,7 @@ import os from datetime import datetime, timedelta DB_PATH = os.path.join(os.path.dirname(__file__), '../db/commands.db') +DB_PATH_COFFEE = os.path.join(os.path.dirname(__file__), '../db/coffee.db') ### THIS CODE IS NOT PROOFED BUT LOOKS RIGHT### def update_command_status(command_id, status): @@ -19,4 +20,24 @@ def update_command_status(command_id, status): conn.close() print(f"[DB] Befehl {command_id} auf {status} aktualisiert.") return status -### THIS CODE IS NOT PROOFED BUT LOOKS RIGHT### \ No newline at end of file +### THIS CODE IS NOT PROOFED BUT LOOKS RIGHT### + +def get_coffee_count(): + conn = sqlite3.connect(DB_PATH_COFFEE) + cursor = conn.cursor() + + cursor.execute("SELECT COUNT(*) FROM coffee") + count = cursor.fetchone()[0] + + conn.close() + return count + +def get_coffees(): + conn = sqlite3.connect(DB_PATH_COFFEE) + cursor = conn.cursor() + + cursor.execute("SELECT * FROM coffee") + coffees = cursor.fetchall() + + conn.close() + return coffees \ No newline at end of file diff --git a/persistence/README.md b/persistence/README.md new file mode 100644 index 0000000..ca43835 --- /dev/null +++ b/persistence/README.md @@ -0,0 +1,6 @@ +# THE PERSISTENCE DATA + +## these datasets are some suborderst storage for longer terms. +## u could use a Database for that but these datasets are unique and therefore they are stored in jsons to ensure their persistance. + +## an acourding module to read and write to these datasets is given in the modules folder. \ No newline at end of file diff --git a/persistence/beans.json b/persistence/beans.json index d4203b3..0411e9a 100644 --- a/persistence/beans.json +++ b/persistence/beans.json @@ -1,6 +1,6 @@ { - "lastFilled": "2025-05-06 20:56:49.436340+00:00", + "lastFilled": "2025-05-09 17:43:12.947434", "fill": 100, - "coffeesOnFill": 0, - "refilled": 0 + "refilled": 0, + "coffeesOnFill": 0 } \ No newline at end of file diff --git a/init.py b/persistence/init.py similarity index 100% rename from init.py rename to persistence/init.py diff --git a/persistence/water.json b/persistence/water.json index 76aa60e..07092b6 100644 --- a/persistence/water.json +++ b/persistence/water.json @@ -1,6 +1,6 @@ { - "lastFilled": "2025-05-06 20:56:49.436328+00:00", - "fill": 21, + "lastFilled": "2025-05-09 17:40:58.290140", + "fill": 100, "coffeesOnFill": 0, "refilled": 0 } \ No newline at end of file diff --git a/routes/unsecure_routes.py b/routes/unsecure_routes.py index 0b408f4..31de82b 100644 --- a/routes/unsecure_routes.py +++ b/routes/unsecure_routes.py @@ -2,29 +2,57 @@ from flask import Blueprint, render_template, request, jsonify unsecure = Blueprint('unsecure', __name__, url_prefix='/unsecure') from modules.persistence import load_dict, save_dict from modules.persistence import esp_conn_infos -# from flask_socketio import SocketIO +from datetime import datetime from modules.socketio import resend_static_data +from modules.db import get_coffee_count, get_coffees -# def resend_static_data(): -# water = load_dict("water") -# beans = load_dict("beans") -# machine = load_dict("machine") -# socketio.emit('static_data', { -# 'water': water, -# 'beans': beans, -# 'machine': machine, -# 'esp_conn_infos': esp_conn_infos -# }) @unsecure.route('/') def index(): water = load_dict("water") beans = load_dict("beans") machine = load_dict("machine") + coffee_count = get_coffee_count() print(f"Water: {water}, Beans: {beans}, Machine: {machine}") - return render_template('index.html', title='gimmiCoffee', water=water, beans=beans, machine=machine, esp_conn_infos=esp_conn_infos) + return render_template('index.html', title='gimmiCoffee', water=water, beans=beans, machine=machine, esp_conn_infos=esp_conn_infos, coffee_count=coffee_count) -@unsecure.route('/update') -def update(): +# @unsecure.route('/update') +# def update(): +# resend_static_data() +# return jsonify({"status": "ok", "task": "update-executed"}) + +@unsecure.route('/refill-water', methods=['POST']) +def update_water(): + water = load_dict("water") + water["lastFilled"] = datetime.now() + water["fill"] = 100 + water["coffeesOnFill"] = 0 + water["refilled"] = water["refilled"] + 1 + save_dict("water", water) resend_static_data() - return jsonify({"status": "ok"}) + return jsonify({"status": "ok", "task": "refill-water-executed"}) + +@unsecure.route('/refill-beans', methods=['POST']) +def update_beans(): + beans = load_dict("beans") + beans["lastFilled"] = datetime.now() + beans["fill"] = 100 + beans["coffeesOnFill"] = 0 + beans["refilled"] = beans["refilled"] + 1 + save_dict("beans", beans) + resend_static_data() + return jsonify({"status": "ok", "task": "refill-beans-executed"}) + +@unsecure.route('/coffees-made') +def coffees_made(): + coffees = get_coffees() + return render_template('coffees.html', title='gimmiCoffee', coffees=coffees) + +@unsecure.route('/water') +def water(): + water = load_dict("water") + return render_template('water.html', title='gimmiCoffee', last_filled=water["lastFilled"], current_level=water["fill"], total_refills=water["refilled"], coffees_made=water["coffeesOnFill"]) +@unsecure.route('/beans') +def beans(): + beans = load_dict("beans") + return render_template('beans.html', title='gimmiCoffee', last_filled=beans["lastFilled"], current_level=beans["fill"], total_refills=beans["refilled"], coffees_made=beans["coffeesOnFill"]) diff --git a/static/script.js b/static/script.js index 65c5518..6762a60 100644 --- a/static/script.js +++ b/static/script.js @@ -95,4 +95,40 @@ function toggleMachine() { .then(data => { console.log(data); }); -} \ No newline at end of file +} + +function waterRefill(){ + if (gebId("water-fill").parentElement.classList.contains("deniePress")){ + return; + } + console.log(water) + if (water.fill == 100){ + alert("Wassertank ist bereits voll!"); + return; + } + console.log("waterRefill"); + fetch('/unsecure/refill-water', { method: 'POST' }) + .then(res => res.json()) + .then(data => { + console.log(data); + }); +} + +function beansRefill(){ + if (gebId("beans-fill").parentElement.classList.contains("deniePress")){ + return; + } + if (beans.fill == 100){ + alert("Bohnentank ist bereits voll!"); + return; + } + console.log("beansRefill"); + fetch('/unsecure/refill-beans', { method: 'POST' }) + .then(res => res.json()) + .then(data => { + console.log(data); + }); +} +function showCoffeeHistory(){ + window.location.href = "/unsecure/coffees-made"; +} diff --git a/static/style.css b/static/style.css index eaef9ed..879d03b 100644 --- a/static/style.css +++ b/static/style.css @@ -264,4 +264,52 @@ background-color: red; } .deniePress:hover{ cursor: not-allowed; +} + + + +/* STYLING FOR COFFEES MADE*/ +table { + width: 100%; + border-collapse: collapse; +} +th, td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; +} +th { + background-color: #f4f4f4; +} +.homeBut { + background-color: #3498db; + color: white; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + cursor: pointer; + border-radius: 5px; +} + + +/* STYLING FOR BEANS AND WATER*/ +.container { + font-family: Arial, sans-serif; + max-width: 600px; + margin: 50px auto; + padding: 20px; + border: 1px solid #ccc; + border-radius: 10px; + background-color: #f9f9f9; + text-align: center; +} +.status p { + font-size: 1.2em; + margin: 10px 0; +} +.actions { + margin-top: 20px; } \ No newline at end of file diff --git a/templates/beans.html b/templates/beans.html new file mode 100644 index 0000000..bc99900 --- /dev/null +++ b/templates/beans.html @@ -0,0 +1,23 @@ + + + + + + {{ title }} + + + + +
+

Bohnenstatus

+ +
+

Zuletzt gefüllt: {{ last_filled }}

+

Aktueller Füllstand: {{ current_level }}%

+

Kaffees seit letzter Füllung: {{ coffees_made }}

+

Gesamtanzahl der Füllungen: {{ total_refills }}

+
+
+ + + \ No newline at end of file diff --git a/templates/coffees.html b/templates/coffees.html new file mode 100644 index 0000000..170db87 --- /dev/null +++ b/templates/coffees.html @@ -0,0 +1,34 @@ + + + + + + {{ title }} + + + + + +

Coffee History

+ + + + + + + + + + + {% for coffee in coffees %} + + + + + + {% endfor %} + +
UserStatusTimestamp
{{ coffee.user }}{{ coffee.status }}{{ coffee.tstamp }}
+ + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 68125d3..24ac754 100644 --- a/templates/index.html +++ b/templates/index.html @@ -52,20 +52,19 @@
Maschine
AUS
-
-
Maschine
-
Nicht Bereit
-
-
Fehler
Keiner
-
+
+
Maschine
+
Nicht Bereit
+
+
Wasser
Nachgefüllt?
-
+
Bohnen
Nachgefüllt?
@@ -74,15 +73,15 @@

Statistiken

-
+
Coffee made
-
XXX
+
{{ coffee_count }}
-
+
Bohnen Füllstand
XX%
-
+
Wasser Füllstand
XX%
@@ -101,10 +100,4 @@ - - - - - - - + \ No newline at end of file diff --git a/templates/water.html b/templates/water.html new file mode 100644 index 0000000..852521f --- /dev/null +++ b/templates/water.html @@ -0,0 +1,23 @@ + + + + + + {{ title }} + + + + +
+

Wasserstatus

+ +
+

Zuletzt gefüllt: {{ last_filled }}

+

Aktueller Füllstand: {{ current_level }}%

+

Kaffees seit letzter Füllung: {{ coffees_made }}

+

Gesamtanzahl der Füllungen: {{ total_refills }}

+
+
+ + + \ No newline at end of file