diff --git a/app/__init__.py b/app/__init__.py index 1e60430..3c802ba 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -28,4 +28,7 @@ def create_app(): from . import stats app.register_blueprint(stats.blueprint) + from . import graph + app.register_blueprint(graph.blueprint) + return app diff --git a/app/db.py b/app/db.py index b71d0d2..5884277 100644 --- a/app/db.py +++ b/app/db.py @@ -1,3 +1,4 @@ +from sqlalchemy.sql import func from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin @@ -63,6 +64,11 @@ class Accomplishment(db.Model): return Accomplishment.query.filter( Accomplishment.time >= start, Accomplishment.time < end, Accomplishment.user_id == user_id).all() + def get_time_range_total(user_id, start, end): + result = db.session.query(func.sum(Accomplishment.difficulty).label('total')).filter( + Accomplishment.time >= start, Accomplishment.time < end, Accomplishment.user_id == user_id)[0][0] + return result if result is not None else 0 + @staticmethod def get_day(user_id, day): # TODO: allow setting custom "start of day" hour @@ -70,7 +76,17 @@ class Accomplishment(db.Model): end = timeutils.day_after(day) return Accomplishment.get_time_range(user_id, start, end) + def get_day_total(user_id, day): + start = timeutils.day(day) + end = timeutils.day_after(day) + return Accomplishment.get_time_range_total(user_id, start, end) + @staticmethod def get_today(user_id): today = datetime.now() return Accomplishment.get_day(user_id, today) + + @staticmethod + def get_today_total(user_id): + today = datetime.now() + return Accomplishment.get_day_total(user_id, today) diff --git a/app/graph.py b/app/graph.py new file mode 100644 index 0000000..fa5f7b8 --- /dev/null +++ b/app/graph.py @@ -0,0 +1,69 @@ +from flask import Blueprint, render_template +from flask_login import login_required, current_user +from .db import db, Accomplishment +from . import timeutils + + +blueprint = Blueprint('graph', __name__) + + +@blueprint.route('/graph.svg') +@login_required +def graph_svg(): + count = 7 + accomplishments = [0]*count + days = [""]*count + day = timeutils.today() + + for i in range(1, count+1): + total_xp = Accomplishment.get_day_total(current_user.id, day) + accomplishments[-i] = total_xp + days[-i] = day.strftime('%a')[:2] + day = timeutils.day_before(day) + + print(accomplishments) + + return render_template('graph.svg', days=days, **gen_graph_data(accomplishments)), 200, {'Content-Type': 'image/svg+xml'} + + +def gen_scale(base=10): + return [base*i for i in range(0, 5)] + + +def find_scale_base(max_n): + if max_n < 20: + return 10 + + return (max_n - (max_n - 1) % 20 + 20) // 4 + n = max_n % 20 + while n % 20 != 0: + n += 1 + return n//4 + + +GRAPH_TOP_LINE = 16.6 +GRAPH_BOTTOM_LINE = 83 + +GRAPH_RANGE = GRAPH_BOTTOM_LINE - GRAPH_TOP_LINE + + +def absolute_to_percentage_position(n, scale_base): + scale_top = scale_base * 4 + return round(GRAPH_BOTTOM_LINE - n/scale_top * GRAPH_RANGE, 2) + + +def gen_graph_data(numbers): + assert len(numbers) > 1 + max_n = max(numbers) + scale_base = find_scale_base(max_n) + scale = gen_scale(scale_base) + + dots = [absolute_to_percentage_position( + n, scale_base) for n in numbers] + lines = list(zip(dots, dots[1:])) + + return { + "dots": dots, + "lines": lines, + "scale": scale, + } diff --git a/app/templates/graph.svg b/app/templates/graph.svg new file mode 100644 index 0000000..59e0b07 --- /dev/null +++ b/app/templates/graph.svg @@ -0,0 +1,37 @@ +{% macro x(n) -%} +{{ 15+((85-15)/(lines|length))*(n-1) }}% +{%- endmacro %} + + + + {% for n in scale %} + + {% endfor %} + + + {% for n in scale | reverse %} + {{ n }} + {% endfor %} + + + {% for y1, y2 in lines %} + + {% endfor %} + + + {% for cy in dots %} + + {% endfor %} + + + {% for day in days %} + {{ day }} + {% endfor %} + + diff --git a/app/templates/main/app.html b/app/templates/main/app.html index 5ad918c..73513a2 100644 --- a/app/templates/main/app.html +++ b/app/templates/main/app.html @@ -12,6 +12,9 @@ +
+ +