some improvements
This commit is contained in:
16
db/README.md
16
db/README.md
@@ -18,3 +18,19 @@
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
# 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.
|
||||
BIN
db/coffee.db
BIN
db/coffee.db
Binary file not shown.
@@ -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,
|
||||
|
||||
13
modules/README.md
Normal file
13
modules/README.md
Normal file
@@ -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
|
||||
@@ -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):
|
||||
@@ -20,3 +21,23 @@ def update_command_status(command_id, status):
|
||||
print(f"[DB] Befehl {command_id} auf {status} aktualisiert.")
|
||||
return status
|
||||
### 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
|
||||
6
persistence/README.md
Normal file
6
persistence/README.md
Normal file
@@ -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.
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"])
|
||||
|
||||
@@ -96,3 +96,39 @@ function toggleMachine() {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -265,3 +265,51 @@ 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;
|
||||
}
|
||||
23
templates/beans.html
Normal file
23
templates/beans.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<link rel="icon" href="{{ url_for('static', filename='gimmiCoffee_Logo.png') }}" type="image/png">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Bohnenstatus</h1>
|
||||
|
||||
<div class="status">
|
||||
<p><strong>Zuletzt gefüllt:</strong> {{ last_filled }}</p><!-- refactor the variable names-->
|
||||
<p><strong>Aktueller Füllstand:</strong> {{ current_level }}%</p><!-- refactor the variable names-->
|
||||
<p><strong>Kaffees seit letzter Füllung:</strong> {{ coffees_made }}</p><!-- refactor the variable names-->
|
||||
<p><strong>Gesamtanzahl der Füllungen:</strong> {{ total_refills }}</p><!-- refactor the variable names-->
|
||||
</div>
|
||||
</div>
|
||||
<button class="homeBut" onclick="window.location.href = '/unsecure'">Get Me Home</button>
|
||||
</body>
|
||||
</html>
|
||||
34
templates/coffees.html
Normal file
34
templates/coffees.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<link rel="icon" href="{{ url_for('static', filename='gimmiCoffee_Logo.png') }}" type="image/png">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<h1>Coffee History</h1>
|
||||
<button class="homeBut" onclick="window.location.href = '/unsecure'">get me home</button>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Status</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for coffee in coffees %}
|
||||
<tr>
|
||||
<td>{{ coffee.user }}</td>
|
||||
<td>{{ coffee.status }}</td>
|
||||
<td>{{ coffee.tstamp }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<script src="{{ url_for('static', filename='coffeesmade.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -52,20 +52,19 @@
|
||||
<div class="top-left-text">Maschine</div>
|
||||
<div class="center-number" id="machine-status">AUS</div>
|
||||
</div>
|
||||
<div class="grid-button deniePress initBackRed" id="machine-ready-butt">
|
||||
<div class="top-left-text">Maschine</div>
|
||||
<div class="center-number" id="machiene-ready-text">Nicht Bereit</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-button deniePress" id="machine-error-butt">
|
||||
<div class="top-left-text">Fehler</div>
|
||||
<div class="center-number" id="machiene-error-text">Keiner</div>
|
||||
</div>
|
||||
<div class="grid-button defaultGray">
|
||||
<div class="grid-button deniePress initBackRed" id="machine-ready-butt">
|
||||
<div class="top-left-text">Maschine</div>
|
||||
<div class="center-number" id="machiene-ready-text">Nicht Bereit</div>
|
||||
</div>
|
||||
<div class="grid-button defaultGray" onclick="waterRefill()">
|
||||
<div class="top-left-text">Wasser</div>
|
||||
<div class="center-number">Nachgefüllt?</div>
|
||||
</div>
|
||||
<div class="grid-button defaultGray">
|
||||
<div class="grid-button defaultGray" onclick="beansRefill()">
|
||||
<div class="top-left-text">Bohnen</div>
|
||||
<div class="center-number">Nachgefüllt?</div>
|
||||
</div>
|
||||
@@ -74,15 +73,15 @@
|
||||
|
||||
<section class="right stats-box">
|
||||
<h2 class="stats-title">Statistiken</h2>
|
||||
<div class="clickable">
|
||||
<div class="clickable" onclick="showCoffeeHistory()">
|
||||
<div class="top-left-text">Coffee made</div>
|
||||
<div class="center-number">XXX</div>
|
||||
<div class="center-number">{{ coffee_count }}</div>
|
||||
</div>
|
||||
<div class="clickable">
|
||||
<div class="clickable" onclick=" window.location.href = '/unsecure/beans'">
|
||||
<div class="top-left-text">Bohnen Füllstand</div>
|
||||
<div class="center-number" id="beans-fill">XX%</div>
|
||||
</div>
|
||||
<div class="clickable">
|
||||
<div class="clickable" onclick=" window.location.href = '/unsecure/water'">
|
||||
<div class="top-left-text">Wasser Füllstand</div>
|
||||
<div class="center-number" id="water-fill">XX%</div>
|
||||
</div>
|
||||
@@ -102,9 +101,3 @@
|
||||
<script src="{{ url_for('static', filename='socketio.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
23
templates/water.html
Normal file
23
templates/water.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<link rel="icon" href="{{ url_for('static', filename='gimmiCoffee_Logo.png') }}" type="image/png">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Wasserstatus</h1>
|
||||
|
||||
<div class="status">
|
||||
<p><strong>Zuletzt gefüllt:</strong> {{ last_filled }}</p><!-- refactor the variable names-->
|
||||
<p><strong>Aktueller Füllstand:</strong> {{ current_level }}%</p><!-- refactor the variable names-->
|
||||
<p><strong>Kaffees seit letzter Füllung:</strong> {{ coffees_made }}</p><!-- refactor the variable names-->
|
||||
<p><strong>Gesamtanzahl der Füllungen:</strong> {{ total_refills }}</p><!-- refactor the variable names-->
|
||||
</div>
|
||||
</div>
|
||||
<button class="homeBut" onclick="window.location.href = '/unsecure'">Get Me Home</button>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user