diff --git a/coffeebuddy/__init__.py b/coffeebuddy/__init__.py index abd8ebb..a8199da 100644 --- a/coffeebuddy/__init__.py +++ b/coffeebuddy/__init__.py @@ -44,7 +44,7 @@ def create_app(config=None): else: logging.getLogger(__name__).info('Using config file "config"') app.config.from_object("config") - # app.config['SQLALCHEMY_ECHO'] = True + # app.config["SQLALCHEMY_ECHO"] = True if config: app.config.update(config) diff --git a/coffeebuddy/model.py b/coffeebuddy/model.py index fb22ddd..c641d0b 100644 --- a/coffeebuddy/model.py +++ b/coffeebuddy/model.py @@ -1,12 +1,21 @@ -import datetime +import calendar import socket -from typing import Optional +from datetime import date, datetime, timedelta +from typing import List, Optional import flask import sqlalchemy from sqlalchemy import text +def weekday(column): + """Helper to extract weekday for different database backends""" + if flask.current_app.db.engine.name == "postgresql": + return sqlalchemy.func.extract("dow", column) + else: + return sqlalchemy.func.strftime("%w", column) + + class Serializer: @staticmethod def escape(obj): @@ -58,9 +67,7 @@ def by_tag(tag): def drinks_today(self): return ( Drink.query.filter(Drink.user == self) - .filter( - flask.current_app.db.func.Date(Drink.timestamp) == datetime.date.today() - ) + .filter(flask.current_app.db.func.Date(Drink.timestamp) == date.today()) .all() ) @@ -131,6 +138,81 @@ def count_selected_manually(self, host: Optional[str] = None) -> int: host = host or socket.gethostname() return sum(d.selected_manually for d in self.drinks if d.host == host) + def drinks_this_week(self) -> List[int]: + now = date.today() + start_of_week = datetime.combine( + now - timedelta(now.weekday()), datetime.min.time() + ) + + data = tuple( + zip( + *flask.current_app.db.session.query( + weekday(Drink.timestamp).label("weekday"), + flask.current_app.db.func.count( + flask.current_app.db.func.Date(Drink.timestamp) + ), + ) + .filter(self.id == Drink.userid) + .filter(Drink.timestamp >= start_of_week) + .group_by("weekday") + .order_by("weekday") + .all() + ) + ) + if not data: + return [], [] + return [calendar.day_name[int(i)] for i in data[0]], list(data[1]) + + def drinks_avg_week(self): + number_of_weeks = ( + datetime.now() + - flask.current_app.db.session.query(sqlalchemy.func.min(Drink.timestamp)) + .filter(self.id == Drink.userid) + .first()[0] + ).days / 7 + data = tuple( + zip( + *flask.current_app.db.session.query( + weekday(Drink.timestamp).label("weekday"), + flask.current_app.db.func.count( + flask.current_app.db.func.Date(Drink.timestamp) + ) + / number_of_weeks, + ) + .filter(self.id == Drink.userid) + .group_by("weekday") + .order_by("weekday") + .all() + ) + ) + return [calendar.day_name[int(i)] for i in data[0]], list(data[1]) + + @staticmethod + def drinks_avg_week_all(): + number_of_weeks = ( + datetime.now() + - flask.current_app.db.session.query( + sqlalchemy.func.min(Drink.timestamp) + ).first()[0] + ).days / 7 + number_of_active_users = User.query.filter(User.enabled).count() + data = tuple( + zip( + *flask.current_app.db.session.query( + weekday(Drink.timestamp).label("weekday"), + flask.current_app.db.func.count( + flask.current_app.db.func.Date(Drink.timestamp) + ) + / number_of_weeks + / number_of_active_users, + ) + .group_by("weekday") + .order_by("weekday") + .all() + ) + ) + return [calendar.day_name[int(i)] for i in data[0]], list(data[1]) + class Drink(flask.current_app.db.Model): id = flask.current_app.db.Column(flask.current_app.db.Integer, primary_key=True) @@ -148,7 +230,7 @@ class Drink(flask.current_app.db.Model): def __init__(self, *args, **kwargs): if "timestamp" not in kwargs: - kwargs["timestamp"] = datetime.datetime.now() + kwargs["timestamp"] = datetime.now() if "host" not in kwargs: kwargs["host"] = socket.gethostname() super().__init__(*args, **kwargs) @@ -162,7 +244,7 @@ def drinks_vs_days(timedelta): ), flask.current_app.db.func.Date(Drink.timestamp), ) - .filter(Drink.timestamp > datetime.datetime.now() - timedelta) + .filter(Drink.timestamp > datetime.now() - timedelta) .order_by(sqlalchemy.asc(flask.current_app.db.func.Date(Drink.timestamp))) .group_by(flask.current_app.db.func.Date(Drink.timestamp)) .all() @@ -184,7 +266,7 @@ class Pay(flask.current_app.db.Model): def __init__(self, *args, **kwargs): if "timestamp" not in kwargs: - kwargs["timestamp"] = datetime.datetime.now() + kwargs["timestamp"] = datetime.now() if "host" not in kwargs: kwargs["host"] = socket.gethostname() super().__init__(*args, **kwargs) diff --git a/coffeebuddy/route_coffee.py b/coffeebuddy/route_coffee.py index a9e16e0..f7b7977 100644 --- a/coffeebuddy/route_coffee.py +++ b/coffeebuddy/route_coffee.py @@ -8,6 +8,8 @@ def init(): def coffee(): flask.current_app.events.fire("route_coffee") user: User = User.by_tag(escapefromhex(flask.request.args["tag"])) + print("drinks_this_week", user.drinks_this_week()) + print("drinks_avg_week", user.drinks_avg_week()) if user is None: return flask.render_template( "cardnotfound.html", uuid=flask.request.args["tag"] diff --git a/coffeebuddy/templates/coffee.html b/coffeebuddy/templates/coffee.html index c3fdf00..507a950 100644 --- a/coffeebuddy/templates/coffee.html +++ b/coffeebuddy/templates/coffee.html @@ -109,7 +109,6 @@ width: 300px; height: 300px; } - @@ -157,6 +156,7 @@

{{ user.prename }} {{ user.name }}

{{ hexstr(user.tag) }}
+
-
-
-
- COFFEEMETER -
-
- {{ len(user.drinks_today) }} -
-
-
-
-
-
-
-
-
-
+
diff --git a/coffeebuddy/templates/stats.html b/coffeebuddy/templates/stats.html index 3f53a22..3da9557 100644 --- a/coffeebuddy/templates/stats.html +++ b/coffeebuddy/templates/stats.html @@ -6,7 +6,7 @@ - +