-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathiteration.py
More file actions
127 lines (102 loc) · 4.67 KB
/
Copy pathiteration.py
File metadata and controls
127 lines (102 loc) · 4.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from config import ITERATION_INTERVALS, VERSION
from flask import Blueprint, request, render_template, redirect, url_for, jsonify
from datetime import datetime, timedelta
from database import get_db_connection
from auth import require_login
from scheduler import remove_reminder, schedule_reminder
import pytz
import logging
logger = logging.getLogger(__name__)
TIMEZONE = pytz.timezone('Europe/Prague')
iteration_bp = Blueprint('iteration', __name__)
@iteration_bp.route("/iteration/<int:goal_id>", methods=["GET", "POST"])
@require_login
def iteration_view(goal_id):
conn = get_db_connection()
goal = conn.execute("SELECT * FROM goals WHERE id = ?", (goal_id,)).fetchone()
if not goal:
conn.close()
return "Goal not found", 404
if request.method == "POST":
completed = request.form.get("completed", "") # "yes" / "no" — iteration status
was_done = request.form.get("was_done", "")
next_steps = request.form.get("next_steps", goal["next_steps"])
reward = request.form.get("reward", goal["reward"])
# The form lets the user change the cadence here too. Only accept a
# valid key; otherwise keep the stored value (so a malformed/missing
# field can't blank out the iteration). Persisted below, and the
# APScheduler job is rebuilt if it changed.
iteration = request.form.get("iteration", goal["iteration"])
if iteration not in ITERATION_INTERVALS:
iteration = goal["iteration"]
iteration_changed = iteration != goal["iteration"]
# Informational only — APScheduler's interval trigger is the source of
# truth for when the next reminder fires; we just record what we'd
# expect for history purposes.
interval_args = ITERATION_INTERVALS.get(iteration) or {"minutes": 2}
next_run = datetime.now(TIMEZONE) + timedelta(**interval_args)
conn.execute("""
INSERT INTO iteration_history (iteration_id, status, next_run, updated_at)
VALUES (?, ?, ?, ?)
""", (goal_id, completed, next_run.strftime('%Y-%m-%d %H:%M:%S'), datetime.now(TIMEZONE)))
# User responded → clear the silenced flag so the next scheduled tick
# can email again. Do NOT touch goals.completed here — that field
# tracks whether the whole goal is finished, set only via the edit
# form or the API /complete endpoint.
conn.execute("""
UPDATE goals
SET next_steps = ?, reward = ?, iteration = ?, is_silenced = 0
WHERE id = ?
""", (next_steps, reward, iteration, goal_id))
conn.execute("""
INSERT INTO goal_history (goal_id, completed, was_done, next_steps, reward, timestamp)
VALUES (?, ?, ?, ?, ?, ?)
""", (goal_id, completed, was_done, next_steps, reward, datetime.now(TIMEZONE)))
conn.commit()
conn.close()
# Rebuild the interval job so the new cadence takes effect. schedule_reminder
# is a no-op if the job already exists, so remove it first.
if iteration_changed:
remove_reminder(goal_id)
schedule_reminder(goal_id, iteration, send_confirmation=False)
return redirect(url_for("index"))
conn.close()
return render_template("iteration.html", goal=goal, version=VERSION)
@iteration_bp.route('/iteration/<int:iteration_id>/history', methods=['GET'])
@require_login
def get_iteration_history(iteration_id):
conn = get_db_connection()
rows = conn.execute('''
SELECT status, updated_at FROM iteration_history
WHERE iteration_id = ?
ORDER BY updated_at DESC;
''', (iteration_id,)).fetchall()
conn.close()
return jsonify([dict(row) for row in rows])
@iteration_bp.route('/goal/<int:goal_id>/history', methods=['GET'])
@require_login
def get_goal_history(goal_id):
conn = get_db_connection()
rows = conn.execute('''
SELECT completed, was_done, next_steps, reward, timestamp
FROM goal_history
WHERE goal_id = ?
ORDER BY timestamp DESC;
''', (goal_id,)).fetchall()
conn.close()
return jsonify([dict(row) for row in rows])
@iteration_bp.route('/iteration/<int:iteration_id>/status', methods=['POST'])
@require_login
def update_iteration_status(iteration_id):
data = request.json
status = data.get("status")
if status not in ["Yes", "No"]:
return jsonify({"error": "Invalid status value."}), 400
conn = get_db_connection()
conn.execute('''
INSERT INTO iteration_history (iteration_id, status)
VALUES (?, ?);
''', (iteration_id, status))
conn.commit()
conn.close()
return jsonify({"message": "Status updated successfully."})