{% extends "base.html" %} {% import "_chart.html" as ch %} {% block title %}Progress Β· {{ app_name }}{% endblock %} {# /progress β€” four tabs over one log. Was /stats until 0.8.0, which is still an alias. πŸ”΄ THE TABS ARE LINKS, NOT JAVASCRIPT. Each one is a separate request that computes only itself (see core/routes.py::progress_page) β€” the alternative, four pre-built panels hidden with CSS, makes every visit pay for every tab and gets slower the longer you play. It also means every tab is a real URL you can bookmark and a real surface the tests can open, which matters: a leak hidden behind a tab nobody visits is still a leak. #} {% block body %}

Progress {{ tz }}

{% if tab == 'overview' %}
{{ totals.locked_seconds|dur }}total locked
{{ totals.longest_seconds|dur }}longest session
{{ totals.sessions }}sessions
{{ streak.current }}dcurrent streak Β· best {{ streak.best }}d
{{ totals.tasks_verified }}training done Β· {{ totals.tasks_failed }} failed
{{ totals.reflections }}reflections Β· {{ totals.checkins }} check-ins
{{ totals.spins }}wheel spins
{{ totals.time_added|dur }}time added
{{ totals.time_removed|dur }}time removed
{{ game.rank }}Lv {{ game.level }} Β· {{ game.xp }} xp Β· next at {{ game.next_at }}
{{ totals.photos }}photos
{{ totals.voice_memos }}voice memos Β· {{ totals.voice_seconds|dur }}
{# Rates, last 28 days. πŸ”΄ A rate is None when the log is younger than 14 days, and each one is OMITTED rather than drawn as 0 β€” a zero here would state that he never does the thing, about a log that is simply too young to know. #} {% if locked_rate.pct is not none %}
{{ locked_rate.pct|round|int }}%of the last 28 days locked
{% endif %} {% if reflect_days.per_week is not none %}
{{ reflect_days.per_week|round(1) }}/wkdays reflected
{% endif %} {% for key, label in (("task_verified", "training done"), ("spin", "wheel spins"), ("lock", "locks")) %} {% if rates[key].per_week is not none %}
{{ rates[key].per_week|round(1) }}/wk{{ label }}
{% endif %} {% endfor %}

Activity last 26 weeks Β· {{ tz }} πŸ“–

{% for m in heat.months %}{{ m.label }}{% endfor %}
{% for col in heat.cols %}
{% for d in col %}{% endfor %}
{% endfor %}
{% include "_acts.html" %} {% if strips %}

By activity last 26 weeks Β· each in its own colour

{# πŸ”΄ Four levels here, not the global heatmap's five. A per-activity day is usually a single event, so a 5-step scale over the range 0..1 is theatre β€” it would render three shades that can never occur. #} {% for s in strips %}
{{ s.activity.icon }} {{ s.activity.title }}
{% for col in s.cols %}
{% for d in col %}{% endfor %}
{% endfor %}
{% endfor %}
{% endif %} {% elif tab == 'charts' %}

Every chart here is drawn by this box from your own log. Nothing is fetched and nothing is sent β€” there is no charting library on this machine, and that is on purpose. πŸ“– what each one measures

{{ ch.panel("πŸ”’ Hours locked", ch.bar(locked_chart, note="Split at the week boundary, so an eleven-day lock lands in the weeks it was actually spent in rather than as one tower. Wall-clock, frozen time included β€” the same measure as the β€œtotal locked” tile."), doc="progress#trends") }} {{ ch.panel("βœ… Training", ch.line(training_chart, note="The week in progress is not drawn: a partial point on a line is indistinguishable from a fall, and this one would be one every time you looked."), doc="progress#trends") }} {{ ch.panel("πŸ““ Days reflected", ch.bar(reflect_chart, note="Days, never entries β€” four reflections in one evening is one day, which is the same rule the streak beside it uses."), doc="progress#trends") }} {% for m, p in panels %} {% set c = p %}

{{ p.title }} βš™ {% if p.doc %}πŸ“–{% endif %}

{% include p.template %}
{% endfor %} {% if act_charts %}

⏱ Hours per week, by activity πŸ“–

Only activities that declare a duration source appear β€” an activity nothing measures the length of has no hours to show, and a flat zero would claim it took none. One chart each rather than four lines on one: they share a unit, but the cage's worn hours are your locked hours and would flatten everything beside them.

this week so far β€” in progress before this log began

{% for c in act_charts %}

{{ c.title }}

{{ ch.bar(c, compact=true) }}
{% endfor %}
{% endif %} {% elif tab == 'insight' %}

What the log says πŸ“–

{% if observations %} {# πŸ”΄ ONE CAVEAT, ONCE, AND IT IS NOT DECORATION. Every line above is an association over one person's log: "headspace runs higher on workout days" is equally consistent with good days producing workouts, and with a third thing producing both. Hedging each sentence individually would make them all unreadable; saying it here once keeps them readable and honest. #}

These are associations in your own log, not causes β€” β€œhigher on days you worked out” is just as consistent with good days producing workouts. The questions are fixed in the code and every one that clears its floors is printed, so nothing here is the result of hunting for a result.

{% else %}

Nothing to say yet. Each observation needs a real sample on both sides of the comparison and a difference big enough to be one β€” until the log has that, saying something would be inventing it.

{% endif %}
{% else %}

Training

{% for t in board %}{% endfor %}
taskβœ…βŒopen
{{ t.title }}{% if t.requires_verification %} πŸ“·{% endif %}{{ t.verified }}{{ t.failed }}{{ t.open }}

Events by kind

{% for k in kinds %}{% endfor %}
kindmodulennet time
{{ kind_meta.get(k.kind, ('', k.kind))[1] }}{{ k.module }}{{ k.n }}{{ k.delta|sdur }}

Sessions

{% for s in sessions %}{# πŸ”΄ A FIFTH copy of the same leak: this row renders for a LIVE session too (the "β€” live β€”" branch two cells left), so a hidden lock published its own sentence on the stats page. Once it has ended the sentence is history and is shown. #} {% endfor %}
#startedendeddurationbasenote
{{ s.id }}{{ s.started_at|local }}{{ s.ended_at|local if s.ended_at else 'β€” live β€”' }}{{ ((s.ended_at or now) - s.started_at)|dur }}{% if s.hidden and not s.ended_at %}hidden{% else %}{{ s.base_seconds|dur }}{% endif %}{{ s.note }}
{% endif %} {% endblock %}