-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
177 lines (152 loc) · 5.83 KB
/
app.py
File metadata and controls
177 lines (152 loc) · 5.83 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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from flask import Flask, render_template, request, jsonify, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta
from sqlalchemy import desc
import enum
# Create Flask app first
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks2.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Create db instance
db = SQLAlchemy(app)
# Priority weights (tunable)
URGENCY_WEIGHT = 2.0
IMPORTANCE_WEIGHT = 3.0
DEFERRAL_PENALTY = 1.5
RECENT_DEFER_DECAY_HOURS = 24
class Status(enum.Enum):
new = 'new'
active = 'active'
deferred = 'deferred'
done = 'done'
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(256), nullable=False)
description = db.Column(db.Text, default='')
tags = db.Column(db.String(256), default='') # comma-separated
context = db.Column(db.String(128), default='')
estimate_minutes = db.Column(db.Integer, default=0)
interruption_source = db.Column(db.String(128), default='')
status = db.Column(db.Enum(Status), default=Status.new)
urgency = db.Column(db.Integer, default=1) # 1-5
importance = db.Column(db.Integer, default=1) # 1-5
deferral_count = db.Column(db.Integer, default=0)
created_at = db.Column(db.DateTime, default=datetime.now)
last_updated = db.Column(db.DateTime, default=datetime.now)
deferred_until = db.Column(db.DateTime, nullable=True)
css_class = db.Column(db.String(64), default='normal')
def priority_score(self):
score = 0.0
score += URGENCY_WEIGHT * self.urgency
score += IMPORTANCE_WEIGHT * self.importance
# deferral penalty: each deferral reduces priority unless enough time passed
if self.status == Status.deferred and self.deferred_until:
now = datetime.now()
if self.deferred_until > now:
score -= DEFERRAL_PENALTY * self.deferral_count
return score
class ActiveTask(db.Model):
active_task_id = db.Column(db.Integer, primary_key=True, nullable=True)
def init_db():
with app.app_context():
db.create_all()
# Initialize database tables
init_db()
@app.route('/')
def index():
# Ensure there's always an active task record
if not ActiveTask.query.first():
db.session.add(ActiveTask(active_task_id=None))
db.session.commit()
tasks = Task.query.filter(Task.status != Status.done).all()
# compute dynamic ordering
tasks.sort(key=lambda t: t.priority_score(), reverse=True)
return render_template('index.html', tasks=tasks)
@app.route('/queue')
def queue():
tasks = Task.query.filter(Task.status != Status.done).all()
# compute dynamic ordering
tasks.sort(key=lambda t: t.priority_score(), reverse=True)
return render_template('_task_queue.html', tasks=tasks)
@app.route('/capture', methods=['POST'])
def capture():
data = request.form
title = data.get('title', '').strip()
if not title:
return "Title required", 400
task = Task(
title=title,
description=data.get('description',''),
tags=data.get('tags',''),
context=data.get('context',''),
estimate_minutes=int(data.get('estimate',0) or 0),
interruption_source=data.get('source',''),
urgency=int(data.get('urgency',1)),
importance=int(data.get('importance',1)),
status=Status.active
)
db.session.add(task)
db.session.commit()
return redirect(url_for('queue'))
@app.route('/task/<int:task_id>/action', methods=['POST'])
def action_task(task_id):
task = Task.query.get_or_404(task_id)
action = request.form.get('action')
if action == 'complete':
task.status = Status.done
elif action == 'defer':
# simple 1h defer example
task.status = Status.deferred
task.deferral_count += 1
task.deferred_until = datetime.now() + timedelta(hours=1)
elif action == 'escalate':
task.importance = min(5, task.importance +1)
elif action == 'activate':
task.status = Status.active
task.deferred_until = None
task.last_updated = datetime.now()
db.session.commit()
return redirect(url_for('queue'))
@app.route('/task/<int:task_id>/delete', methods=['POST'])
def delete_task(task_id):
task = Task.query.get_or_404(task_id)
db.session.delete(task)
db.session.commit()
return redirect(url_for('queue'))
@app.route('/task/<int:task_id>/adjust', methods=['POST'])
def adjust_task(task_id):
change_active_task(task_id)
task = Task.query.get_or_404(task_id)
field = request.form.get('field')
direction = request.form.get('direction')
if field in ['urgency', 'importance']:
current_value = getattr(task, field)
if direction == 'increase':
new_value = min(5, current_value + 1)
else: # decrease
new_value = max(1, current_value - 1)
setattr(task, field, new_value)
task.last_updated = datetime.now()
db.session.commit()
return redirect(url_for('queue'))
def set_active_task(task_id):
task = Task.query.get(task_id)
if task is not None:
setattr(task, 'css_class', 'highlighted')
db.session.commit()
def change_active_task(task_id):
# get the current active task ID
active_task = ActiveTask.query.first()
highlighted_task_id = getattr(active_task, 'active_task_id')
if highlighted_task_id is not None:
# Reset the previous active task's CSS class
previous_task = Task.query.get(highlighted_task_id)
if previous_task is not None:
setattr(previous_task, 'css_class', 'normal')
# change the current active task ID
setattr(active_task, 'active_task_id', task_id)
db.session.commit()
# highlight the new active task
set_active_task(task_id)
if __name__ == '__main__':
app.run(debug=True)