]> rtime.felk.cvut.cz Git - coffee/coffee-flask.git/commitdiff
clean slate
authorJiří Matěják <jiri.matejak@fel.cvut.cz>
Thu, 24 May 2018 22:59:17 +0000 (00:59 +0200)
committerJiří Matěják <jiri.matejak@fel.cvut.cz>
Thu, 24 May 2018 22:59:17 +0000 (00:59 +0200)
.gitignore [new file with mode: 0644]
app.py [new file with mode: 0644]
coffee_db.py [new file with mode: 0644]
coffee_db.sql [new file with mode: 0644]
templates/hello.html [new file with mode: 0644]
templates/main.js [new file with mode: 0644]
templates/user.html [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..7b25ae2
--- /dev/null
@@ -0,0 +1,2 @@
+__pycache__
+coffee.db
diff --git a/app.py b/app.py
new file mode 100644 (file)
index 0000000..5f6552d
--- /dev/null
+++ b/app.py
@@ -0,0 +1,125 @@
+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"
diff --git a/coffee_db.py b/coffee_db.py
new file mode 100644 (file)
index 0000000..97082ea
--- /dev/null
@@ -0,0 +1,147 @@
+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
diff --git a/coffee_db.sql b/coffee_db.sql
new file mode 100644 (file)
index 0000000..d9f7d8e
--- /dev/null
@@ -0,0 +1,33 @@
+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)
+;
diff --git a/templates/hello.html b/templates/hello.html
new file mode 100644 (file)
index 0000000..b8aa208
--- /dev/null
@@ -0,0 +1,5 @@
+<center>
+this is truhlik calling...
+<div id="user">login</div>
+<p id="log"></p>
+</center>
diff --git a/templates/main.js b/templates/main.js
new file mode 100644 (file)
index 0000000..3aa29bc
--- /dev/null
@@ -0,0 +1,158 @@
+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");
+}
diff --git a/templates/user.html b/templates/user.html
new file mode 100644 (file)
index 0000000..e481c76
--- /dev/null
@@ -0,0 +1,33 @@
+{% 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 %}