Fixed version that takes into account timezone related issue, where coffees made close to midnight are counted for wrong day. --- app.py | 37 +++++++++++++++++++++++++++++++++++-- coffee_db.py | 25 ++++++++++++++++++++++++- templates/user.html | 5 ++++- 3 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/app.py b/app.py index 807f614..a597ad6 100644 --- a/app.py +++ b/app.py @@ -78,7 +78,35 @@ def coffee_graph_flavors(): ax.set_aspect(1) ax.pie(counts, autopct=lambda p: '{:.0f}'.format(p * sum(counts)/100) if p != 0 else '') ax.legend(flavors, bbox_to_anchor=(1.0, 1.0)) - ax.set_title("Your taste") + + if "uid" in session: + ax.set_title("Your taste") + else: + ax.set_title("Team's taste") + + fig.savefig(b, format="svg", bbox_inches="tight") + b.seek(0) + return send_file(b, mimetype="image/svg+xml") + +@app.route("/coffee/graph_flavors_history") +def coffee_graph_flavors_history(): + b = BytesIO() + if "uid" in session: + uid = session["uid"] + flavors, counts = zip(*db.coffee_flavors_history(uid)) + else: + flavors, counts = zip(*db.coffee_flavors_history()) + fig = plt.figure(figsize=(3, 3)) + ax = fig.add_subplot(111) + ax.set_aspect(1) + ax.pie(counts, autopct=lambda p: '{:.0f}'.format(p * sum(counts)/100) if p != 0 else '') + ax.legend(flavors, bbox_to_anchor=(1.0, 1.0)) + + if "uid" in session: + ax.set_title("Your taste") + else: + ax.set_title("This week taste") + fig.savefig(b, format="svg", bbox_inches="tight") b.seek(0) return send_file(b, mimetype="image/svg+xml") @@ -133,7 +161,12 @@ def coffee_graph_history(): xdays[-2] = "YDA" ax.set_xticks(range(len(unix_days))) ax.set_xticklabels(xdays) - ax.set_title("Your week") + + if "uid" in session: + ax.set_title("Your week") + else: + ax.set_title("This week total") + ax.yaxis.set_major_locator(MaxNLocator(integer=True)) fig.savefig(b, format="svg", bbox_inches="tight") b.seek(0) diff --git a/coffee_db.py b/coffee_db.py index b708d03..ee84c90 100644 --- a/coffee_db.py +++ b/coffee_db.py @@ -79,6 +79,28 @@ def coffee_flavors(uid=None): close_db(conn) return res
+def coffee_flavors_history(uid=None): + conn, c = open_db() + + if uid is None: + res = list(c.execute(""" + select f.name, count(c.flavor) from + (select num,date('now', 'localtime', -num || ' days') as d from days) ds + left join flavors f + left join (select * from coffees) c + on f.name=c.flavor and d = date(c.time) group by f.name + """)) + else: + res = list(c.execute(""" + select f.name, count(c.flavor) from + (select num,date('now', 'localtime', -num || ' days') as d from days) ds + left join flavors f + left join (select * from coffees where id = ?) c + on f.name=c.flavor and d = date(c.time) group by f.name + """, (uid,))) + + close_db(conn) + return res
def coffee_history(uid=None): conn, c = open_db() @@ -87,7 +109,8 @@ def coffee_history(uid=None): res = list(c.execute(""" select strftime('%s', ds.d),count(c.flavor),c.flavor from (select num,date('now', 'localtime', -num || ' days') as d from days) ds - left join coffees c + left join + (select time,flavor from coffees) c on d = date(c.time) group by d, c.flavor """)) else: diff --git a/templates/user.html b/templates/user.html index 95ee95e..51a41f6 100644 --- a/templates/user.html +++ b/templates/user.html @@ -30,5 +30,8 @@ </form> </p {% else %} - Use your card/token to log in... + <img src="{{ url_for('coffee_graph_history', _external=True, stamp=stamp) }}"> + <img src="{{ url_for('coffee_graph_flavors_history', _external=True, stamp=stamp) }}"> + + <p>Use your card/token to log in...</p> {% endif %}
Adding timestamp to url for composing the graphs makes the url different -- thus forcing the website to ask for the graph again and do not use cached one. --- app.py | 2 +- templates/user.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app.py b/app.py index a597ad6..d662c00 100644 --- a/app.py +++ b/app.py @@ -53,7 +53,7 @@ def user(): count=db.coffee_count(uid, 0), stamp=time.time() ) - return render_template('user.html') + return render_template('user.html', stamp=time.time())
@app.route('/user/rename') diff --git a/templates/user.html b/templates/user.html index 51a41f6..f680629 100644 --- a/templates/user.html +++ b/templates/user.html @@ -28,7 +28,7 @@ <input id="username" type="text" name="name"> <input type="button" value="rename" onclick="renameUser()"> </form> - </p + </p> {% else %} <img src="{{ url_for('coffee_graph_history', _external=True, stamp=stamp) }}"> <img src="{{ url_for('coffee_graph_flavors_history', _external=True, stamp=stamp) }}">
On Fri, Sep 14 2018, Jaroslav Klapalek wrote:
Adding timestamp to url for composing the graphs makes the url different -- thus forcing the website to ask for the graph again and do not use cached one.
app.py | 2 +- templates/user.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app.py b/app.py index a597ad6..d662c00 100644 --- a/app.py +++ b/app.py @@ -53,7 +53,7 @@ def user(): count=db.coffee_count(uid, 0), stamp=time.time() )
- return render_template('user.html')
- return render_template('user.html', stamp=time.time())
Tohle je jen workaround. Správné řešení je při generování grafu poslat hlavičku, která řekne prohlížeči, jak má nebo nemá výsledek cachovat. Nevím přesně, která hlavička je na to nejlepší, ani jak se nastavují hlavičky ve Flasku, ale něco se člověk snad dozví na https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
@app.route('/user/rename') diff --git a/templates/user.html b/templates/user.html index 51a41f6..f680629 100644 --- a/templates/user.html +++ b/templates/user.html @@ -28,7 +28,7 @@ <input id="username" type="text" name="name"> <input type="button" value="rename" onclick="renameUser()"> </form>
</p
</p>
Na tohle pošli samostatný patch.
-M.
{% else %} <img src="{{ url_for('coffee_graph_history', _external=True, stamp=stamp) }}"> <img src="{{ url_for('coffee_graph_flavors_history', _external=True, stamp=stamp) }}"> -- 2.7.4
Coffee mailing list Coffee@rtime.felk.cvut.cz https://rtime.felk.cvut.cz/mailman/listinfo/coffee
I haven't test it, just pythonic feeling of syntax. Think that the patch does not look bad, I found just minor issues.
flavors, counts = zip(*db.coffee_flavors_history(uid))
I like tuple here but it's not used anywhere in the code, so lets keep it consistent.
- ax.pie(counts, autopct=lambda p: '{:.0f}'.format(p * sum(counts)/100) if p != 0 else '')
Yes, I don't like lines *not wrapped* to 80 characters. Suggest at least new line after `... p:` - it helps with the readability also.
res = list(c.execute("""
select f.name, count(c.flavor) from
(select num,date('now', 'localtime', -num || ' days') as d from days) ds
Again this wrapping (sorry). Also, for the first view, the query doesn't look nice. Guess that db queries can't be written nicely...
jiri
Ahoj Jardo,
tak jsem se konečně dostal k tomu patchi.
On Fri, Sep 14 2018, Jaroslav Klapalek wrote:
Fixed version that takes into account timezone related issue, where coffees made close to midnight are counted for wrong day.
Tohle nepiš do commit message, protože ten, kdo si bude prohlížet historii gitu neuvidí historii toho, jak jsi posílal patche do mailing listu. Tyto poznámky můžeš napsat za "---" níže. Vše, co je tam není součástí commitu. Taky je dobré, když generovat opravenou verzi patche s přepínačem -v a číslem verze ať se lépe vyznám v tom, která verze je poslední - např.: git format-patch -v2.
app.py | 37 +++++++++++++++++++++++++++++++++++-- coffee_db.py | 25 ++++++++++++++++++++++++- templates/user.html | 5 ++++- 3 files changed, 63 insertions(+), 4 deletions(-)
diff --git a/app.py b/app.py index 807f614..a597ad6 100644 --- a/app.py +++ b/app.py @@ -78,7 +78,35 @@ def coffee_graph_flavors(): ax.set_aspect(1) ax.pie(counts, autopct=lambda p: '{:.0f}'.format(p * sum(counts)/100) if p != 0 else '') ax.legend(flavors, bbox_to_anchor=(1.0, 1.0))
- ax.set_title("Your taste")
- if "uid" in session:
ax.set_title("Your taste")
- else:
ax.set_title("Team's taste")
- fig.savefig(b, format="svg", bbox_inches="tight")
- b.seek(0)
- return send_file(b, mimetype="image/svg+xml")
+@app.route("/coffee/graph_flavors_history") +def coffee_graph_flavors_history():
Jestli to dobře chápu, tak jediný rozdíl mezi coffee_graph_flavors a coffee_graph_flavors_history je v tom, že první vrátí počty příchutí za celou historii, kdežto ta druhá jen za poslední týden. Takže ty názvy jsou trochu zavádějící. Myslím, že by bylo lepší mít jen jednu funkci a té předat parametr, kolik dní do historie nás zajímá. Viz také komentář níže. Graf by se pak generoval následujícími URL:
http://localhost/coffee/graph_flavors?days=7 http://localhost/coffee/graph_flavors?days=inf http://localhost/coffee/graph_flavors
Poslední dva řádky by vracely stejný výsledek.
- b = BytesIO()
- if "uid" in session:
uid = session["uid"]
flavors, counts = zip(*db.coffee_flavors_history(uid))
- else:
flavors, counts = zip(*db.coffee_flavors_history())
- fig = plt.figure(figsize=(3, 3))
- ax = fig.add_subplot(111)
- ax.set_aspect(1)
- ax.pie(counts, autopct=lambda p: '{:.0f}'.format(p * sum(counts)/100) if p != 0 else '')
- ax.legend(flavors, bbox_to_anchor=(1.0, 1.0))
- if "uid" in session:
ax.set_title("Your taste")
- else:
ax.set_title("This week taste")
- fig.savefig(b, format="svg", bbox_inches="tight") b.seek(0) return send_file(b, mimetype="image/svg+xml")
@@ -133,7 +161,12 @@ def coffee_graph_history(): xdays[-2] = "YDA" ax.set_xticks(range(len(unix_days))) ax.set_xticklabels(xdays)
- ax.set_title("Your week")
- if "uid" in session:
ax.set_title("Your week")
- else:
ax.set_title("This week total")
- ax.yaxis.set_major_locator(MaxNLocator(integer=True)) fig.savefig(b, format="svg", bbox_inches="tight") b.seek(0)
diff --git a/coffee_db.py b/coffee_db.py index b708d03..ee84c90 100644 --- a/coffee_db.py +++ b/coffee_db.py @@ -79,6 +79,28 @@ def coffee_flavors(uid=None): close_db(conn) return res
+def coffee_flavors_history(uid=None):
Stačilo by rozšířit funkci coffee_flavors o parametr days, který by udával, kolik dní do historie nás to zajímá. Výchozí hodnota by byla None nebo Inf, což by znamenalo celou historii.
- conn, c = open_db()
- if uid is None:
res = list(c.execute("""
select f.name, count(c.flavor) from
(select num,date('now', 'localtime', -num || ' days') as d from days) ds
left join flavors f
left join (select * from coffees) c
on f.name=c.flavor and d = date(c.time) group by f.name
A tahle hrůza s vnořeným selectem a joinem by šla jednoduše nahradit něčím jako:
WHERE c.time >= date('now', 'localtime', '-%(days)s days')
Případně by se funkce date dala nahradit řetězcem spočítaným pythonem, aby se nemuselo složitě řešit days == None.
"""))
- else:
res = list(c.execute("""
select f.name, count(c.flavor) from
(select num,date('now', 'localtime', -num || ' days') as d from days) ds
left join flavors f
left join (select * from coffees where id = ?) c
on f.name=c.flavor and d = date(c.time) group by f.name
""", (uid,)))
- close_db(conn)
- return res
def coffee_history(uid=None): conn, c = open_db() @@ -87,7 +109,8 @@ def coffee_history(uid=None): res = list(c.execute(""" select strftime('%s', ds.d),count(c.flavor),c.flavor from (select num,date('now', 'localtime', -num || ' days') as d from days) ds
left join coffees c
left join
else:(select time,flavor from coffees) c on d = date(c.time) group by d, c.flavor """))
diff --git a/templates/user.html b/templates/user.html index 95ee95e..51a41f6 100644 --- a/templates/user.html +++ b/templates/user.html @@ -30,5 +30,8 @@ </form> </p {% else %}
- Use your card/token to log in...
<img src="{{ url_for('coffee_graph_history', _external=True, stamp=stamp) }}">
<img src="{{ url_for('coffee_graph_flavors_history', _external=True, stamp=stamp) }}">
<p>Use your card/token to log in...</p>
Nevím, ale řekl bych, že pro neznalého uživatele je důležitější vědět, že má použít kartu, než jaká byla spotřeba kafe. Nechal bych tuto větu nad obrázky.
-Michal