--- /dev/null
+__pycache__
+coffee.db
--- /dev/null
+from flask import Flask, render_template, send_file, request, session, redirect, url_for, make_response
+from flask_cors import CORS
+
+import numpy as np
+import matplotlib
+matplotlib.use('Agg')
+import matplotlib.pyplot as plt
+from io import BytesIO
+
+import coffee_db as db
+import time
+
+db.init_db()
+app = Flask(__name__)
+CORS(app, supports_credentials=True)
+app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
+
+@app.route('/')
+def hello():
+ if "uid" in session:
+ uid = session["uid"];
+ return render_template('hello.html', name=db.get_name(uid))
+ return render_template('hello.html')
+
+@app.route('/login', methods=["POST"])
+@app.route('/login/<uid>')
+def login(uid=None):
+ if request.method == "POST":
+ uid = request.data.decode("utf-8")
+ if uid is not None:
+ db.add_user(uid)
+ session["uid"] = uid;
+ return redirect(url_for('user'))
+
+@app.route('/logout')
+def logout():
+ session.pop('uid', None)
+ return redirect(url_for('user'))
+
+@app.route('/user')
+def user():
+ if "uid" in session:
+ uid = session["uid"];
+ return render_template('user.html',
+ name=db.get_name(uid),
+ flavors=db.flavors(),
+ count=db.coffee_count(uid, 0),
+ stamp=time.time()
+ )
+ return render_template('user.html')
+
+@app.route('/user/rename')
+def user_rename():
+ name = request.args.get("name")
+ if name and "uid" in session:
+ uid = session["uid"];
+ db.name_user(uid, name)
+ return redirect(url_for('user'))
+
+@app.route("/coffee/graph_flavors")
+def coffee_graph_flavors():
+ b = BytesIO()
+ if "uid" in session:
+ uid = session["uid"]
+ flavors, counts = zip(*db.coffee_flavors(uid))
+ else:
+ flavors, counts = zip(*db.coffee_flavors())
+ fig = plt.figure()
+ ax = fig.add_subplot(111)
+ ax.set_aspect(1)
+ ax.pie(counts)
+ ax.legend(flavors)
+ ax.set_title("Your taste")
+ fig.savefig(b, format="svg", bbox_inches="tight")
+ b.seek(0)
+ return send_file(b, mimetype="image/svg+xml")
+
+@app.route("/coffee/graph_history")
+def coffee_graph_history():
+ b = BytesIO()
+ if "uid" in session:
+ uid = session["uid"]
+ hist = db.coffee_history(uid)
+ else:
+ hist = db.coffee_history()
+ if hist == []:
+ days = tuple()
+ counts = tuple()
+ else:
+ days, counts = zip(*hist)
+ fig = plt.figure()
+ ax = fig.add_subplot(111)
+ ax.bar(days, counts)
+ ax.set_title("Your week")
+ fig.savefig(b, format="svg", bbox_inches="tight")
+ b.seek(0)
+ return send_file(b, mimetype="image/svg+xml")
+
+@app.route("/coffee/add", methods=["POST"])
+def coffee_add():
+ if request.method == "POST":
+ json = request.json
+ if json and "uid" in session:
+ db.add_coffee(session["uid"], json["flavor"], json["time"])
+ return redirect(url_for('user'))
+
+@app.route("/coffee/count")
+def coffee_count():
+ start = request.args.get("start")
+ stop = request.args.get("stop")
+ return str(db.coffee_count(session.get("uid"), start, stop))
+
+@app.route('/js')
+def js():
+ response = make_response(render_template('main.js'))
+ response.headers['Content-Type'] = "text/javascript"
+ return response
+
+@app.route("/log", methods=["POST"])
+def log():
+ if request.method == "POST":
+ data = request.data.decode("utf-8")
+ print(data)
+ return data
+ return "nope"
--- /dev/null
+import sqlite3
+
+dbdef = "coffee_db.sql"
+dbfile = "coffee.db"
+
+def open_db():
+ conn = sqlite3.connect(dbfile)
+ c = conn.cursor()
+ return conn, c
+
+def close_db(conn):
+ conn.commit()
+ conn.close()
+
+def init_db():
+ conn, c = open_db()
+ with open(dbdef, "r") as f:
+ c.executescript(f.read())
+ close_db(conn)
+
+def add_user(uid):
+ conn, c = open_db()
+ c.execute("insert or ignore into users (id) values (?)", (uid,))
+ close_db(conn)
+
+def get_name(uid):
+ conn, c = open_db()
+ for name, in c.execute("select name from users where id = ?",(uid,)):
+ close_db(conn)
+ return name
+ close_db(conn)
+ return None
+
+
+def name_user(uid, name):
+ conn, c = open_db()
+ c.execute("update users set name = ? where id = ?", (name, uid))
+ close_db(conn)
+
+def list_users():
+ conn, c = open_db()
+ for row in c.execute("select * from users"):
+ print(row)
+ close_db(conn)
+
+
+def add_coffee(uid, flavor, time=None):
+ conn, c = open_db()
+ print(uid, flavor, time)
+ if time is None:
+ c.execute("insert into coffees (id, flavor) values (?,?)", (uid,flavor))
+ else:
+ c.execute("insert into coffees (id, flavor, time) values (?,?,?)", (uid, flavor, time))
+ close_db(conn)
+
+
+def list_coffees(uid=None):
+ c = conn.cursor()
+ if uid is None:
+ for row in c.execute("select id, time, flavor from coffees"):
+ print(row)
+ else:
+ for row in c.execute("select time, flavor from coffees where id = ?", (uid,)):
+ print(row)
+
+def flavors():
+ conn, c = open_db()
+ res = [row for row, in c.execute("select distinct name from flavors")]
+ close_db(conn)
+ return res
+
+def coffee_flavors(uid=None):
+ conn, c = open_db()
+
+ if uid is None:
+ res = list(c.execute("""
+ select f.name, count(c.flavor) from flavors f left join
+ (select * from coffees) c
+ on f.name=c.flavor group by f.name
+ """))
+ else:
+ res = list(c.execute("""
+ select f.name, count(c.flavor) from flavors f left join
+ (select * from coffees where id = ?) c
+ on f.name=c.flavor group by f.name
+ """, (uid,)))
+
+ close_db(conn)
+ return res
+
+def coffee_history(uid=None):
+ conn, c = open_db()
+
+ if uid is None:
+ res = list(c.execute("""
+ select strftime('%d', ds.d),count(c.time) from
+ (select num,date('now',-num || ' days') as d from days) ds
+ left join coffees c
+ on d = date(c.time) group by d
+ """))
+ else:
+ res = list(c.execute(
+ """
+ select strftime('%d', ds.d),count(c.time) from
+ (select num,date('now',-num || ' days') as d from days) ds
+ left join
+ (select time from coffees where id = ? ) c
+ on d = date(c.time) group by d
+ """
+ , (uid,)))
+
+ close_db(conn)
+ return res
+
+def coffee_count(uid=None, start=None, stop=None):
+ conn, c = open_db()
+
+ args = []
+
+ if uid is not None:
+ uid_q = "id = ?"
+ args.append(uid)
+ else:
+ uid_q = "true"
+
+ if start is not None:
+ start_q = "date(time) >= date('now', -? || ' days')"
+ args.append(start)
+ else:
+ start_q = "true"
+
+ if stop is not None:
+ stop_q = "date(time) <= date('now', -? || ' days')"
+ args.append(stop)
+ else:
+ stop_q = "true"
+
+ for count, in c.execute(
+ "select count(*) from coffees where " +
+ " and ".join([uid_q, start_q, stop_q])
+ , args):
+ res = count
+
+ if not res:
+ res = "0"
+
+ return res
--- /dev/null
+pragma foreign_keys = ON;
+
+create table if not exists users (
+ id varchar(24) primary key not null,
+ name varchar(255) default "human"
+);
+
+create table if not exists flavors (
+ name varchar(255) primary key not null
+);
+
+insert or ignore into flavors values
+ ("espresso"),
+ ("espresso lungo"),
+ ("cappuccino"),
+ ("latte macchiato")
+;
+
+create table if not exists coffees (
+ num integer primary key,
+ id varchar(24) references users(id), -- id may be unknown
+ flavor varchar(255) not null references flavors(name),
+ time datetime default current_timestamp
+);
+
+
+create table if not exists days (
+ num integer primary key not null
+);
+
+insert or ignore into days values
+ (0),(1),(2),(3),(4),(5),(6)
+;
--- /dev/null
+<center>
+this is truhlik calling...
+<div id="user">login</div>
+<p id="log"></p>
+</center>
--- /dev/null
+var flask = "{{ url_for('hello', _external=True) }}"
+
+var updateRemote = undefined;
+var loggedIn = false;
+
+console.log("hello from flask");
+//sendJSON("{\"type\":\"empty\"}");
+
+function update(id, msg) {
+ document.getElementById(id).innerHTML = msg;
+}
+
+function loadLocalStorage() {
+ if (localStorage) {
+ if (localStorage.length) {
+ for (var i = 0; i < localStorage.length; i++) {
+ var value = localStorage.getItem(localStorage.key(i));
+ try {
+ updateRemote(value);
+ } catch (err) {
+ console.log("no json: " + value)
+ }
+ }
+ localStorage.clear();
+ }
+ }
+}
+
+function hiddenUpdateRemote(json) {
+ var msg = JSON.parse(json);
+
+ //update("json", "json: " + JSON.stringify(msg))
+
+ switch(msg.type) {
+ case "empty":
+ //update("json","");
+ break;
+ case "rfid":
+ /*update("json",
+ "uid: " + msg.uid + "<br>" +
+ "card type: " + msg.card_type + "<br>" +
+ "uid size: " + msg.size + " bytes<br>" +
+ "sak: " + msg.sak
+ );*/
+ login(msg.uid);
+ break;
+ case "keys":
+ //update("json", "key: " + msg.key);
+ if (loggedIn) {
+ var flavor = getFlavor(msg.key);
+ if (flavor !== "") {
+ addCoffee(flavor);
+ loggedIn = false;
+ }
+ }
+ break;
+ case "fuck":
+ ajax(msg.method, msg.route, msg.data, msg.id);
+ break;
+ }
+ sendLog(json);
+}
+
+function loadRemote(string) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ update("remote", this.responseText);
+ updateRemote = hiddenUpdateRemote;
+ loadLocalStorage();
+ if (loggedIn) {
+ document.getElementById("local").style.display = "none";
+ }
+ }
+ };
+ xhr.open("GET", flask, true);
+ xhr.send();
+}
+
+loadRemote();
+
+function ajax(method, route, data, id) {
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ if (this.readyState == 4) {
+ if (this.status == 200) {
+ update(id, this.responseText);
+ } else {
+ updateRemote = undefined;
+ update("remote", "<center>all alone in a danger zone...</center>");
+ document.getElementById("local").style.display = "block";
+ if (localStorage) {
+ var now = Date.now();
+ var fuck = JSON.stringify({
+ type: "fuck",
+ method: method,
+ route: route,
+ data: data,
+ id: id
+ });
+ localStorage.setItem(now, fuck);
+ console.log(now + ": " + fuck);
+ }
+ }
+ }
+ };
+ xhr.open(method, flask + route, true);
+ xhr.withCredentials = true;
+ xhr.setRequestHeader("Content-type", "application/json");
+ if (method === "POST") {
+ xhr.send(data);
+ } else {
+ xhr.send();
+ }
+}
+
+//var timer;
+
+function login(id) {
+ ajax("POST", "login", id, "user");
+ loggedIn = true;
+ document.getElementById("local").style.display = "none";
+ //timer = setTimeout(logout, 20000);
+}
+
+function logout() {
+ //clearTimeout(timer);
+ sendReset();
+ ajax("GET", "logout", "", "user");
+ loggedIn = false;
+ document.getElementById("local").style.display = "block";
+}
+
+function renameUser() {
+ ajax("GET", "user/rename?name=" + document.getElementById("username").value, "", "user");
+}
+
+function getFlavor(letter) {
+ switch (letter) {
+ case "E": return "espresso";
+ case "C": return "cappuccino";
+ case "B": return "latte macchiato";
+ case "D": return "espresso lungo";
+ default: return "";
+ }
+}
+
+function addCoffee(flavor) {
+ var data = JSON.stringify({
+ time: new Date().toISOString(),
+ flavor: flavor
+ });
+ ajax("POST", "coffee/add", data, "user");
+}
+
+function sendLog(json) {
+ ajax("POST", "log", json, "log");
+}
--- /dev/null
+{% if name %}
+ <form style="position: absolute; right: 10%">
+ <input type="button" value="logout" onclick="logout()">
+ </form>
+
+ <h1>Hello, {{ name }}!</h1>
+
+ {% if count %}
+ <p>You've had {{ count }} coffee{% if count != 1 %}s{% endif %} today.</p>
+ {% endif %}
+
+ <img src="{{ url_for('coffee_graph_history', _external=True, stamp=stamp) }}">
+ <img src="{{ url_for('coffee_graph_flavors', _external=True, stamp=stamp) }}">
+
+ <p>
+ {% if flavors %}
+ <form>
+ {% for flavor in flavors %}
+ <input type="button" value="{{ flavor }}" onclick="addCoffee(this.value)">
+ {% endfor %}
+ </form>
+ {% endif %}
+ </p>
+
+ <p>
+ <form>
+ <input id="username" type="text" name="name">
+ <input type="button" value="rename" onclick="renameUser()">
+ </form>
+ </p
+{% else %}
+ login
+{% endif %}