]> us-delaporte.pagekite.me Git - matrix-goal-bot.git/blob - matrix_goal_bot/db.py
22dca51b66a62e1136691c6698abb547bfb166f8
[matrix-goal-bot.git] / matrix_goal_bot / db.py
1 import logging
2 import os
3 from datetime import datetime as dt
4 from datetime import timedelta
5 from typing import List
6
7 from sqlmodel import Field, Session, SQLModel, create_engine, select, text, Relationship
8
9 logger = logging.getLogger(__name__)
10
11 DATE_FORMAT = '%Y-%m-%d'
12
13 def today():
14 return dt.now().strftime(DATE_FORMAT)
15
16
17 class Vision(SQLModel, table=True):
18 id: int | None = Field(default=None, primary_key=True)
19 name: str = Field(default=None, unique=True)
20 complete_date: dt | None = Field(default=None)
21 goals: list["Goal"] = Relationship(back_populates="vision")
22 def __str__(self):
23 return f'{self.name} (Vision {self.id})'
24
25
26 class PreferGoal(SQLModel, table=True):
27 goal_id: int | None = Field(default=None, foreign_key="goal.id", primary_key=True)
28
29
30 class Goal(SQLModel, table=True):
31 id: int | None = Field(default=None, primary_key=True)
32 name: str = Field(default=None, unique=True)
33 start_date: dt | None = Field(default=dt.today())
34 complete_date: dt | None = Field(default=None)
35 vision_id: int | None = Field(default=None, foreign_key="vision.id")
36 notes: str | None = Field(default=None)
37 tasks: list["Task"] = Relationship(back_populates="goal")
38 vision: Vision = Relationship(back_populates="goals")
39 def __str__(self):
40 if not self.complete_date:
41 return f'{self.name} (Goal {self.id})'
42 else:
43 completed = self.complete_date.strftime(DATE_FORMAT)
44 return f'(Done {completed}) {self.name} (Goal {self.id})'
45
46
47 class Task(SQLModel, table=True):
48 id: int | None = Field(default=None, primary_key=True)
49 name: str = Field(default=None, unique=True)
50 goal_id: int | None = Field(default=None, foreign_key="goal.id")
51 complete_date: dt | None = Field(default=None)
52 goal: Goal = Relationship(back_populates="tasks")
53 def __str__(self):
54 return f'{self.name} ({self.id})'
55
56
57 class Win(SQLModel, table=True):
58 id: int | None = Field(default=None, primary_key=True)
59 goal: str = Field(default=None, unique=False)
60 vision: str | None = Field(default=None, unique=False)
61 complete_date: dt | None = Field(default=dt.today())
62 def __str__(self):
63 completed = self.complete_date.strftime(DATE_FORMAT)
64 return f'{self.goal} ({completed} {self.vision})'
65
66 def pretty_print(goal, tasks):
67 results = []
68 results.append(f' ## {goal}')
69 if not tasks:
70 results.append('-- Nothing to list --')
71
72 for task in tasks:
73 if(task.complete_date):
74 results.append(f' - [x] {task}')
75 else:
76 results.append(f' - [ ] {task}')
77 return results
78
79
80 def _get_with_id(cls, item_id):
81 return select(cls).where(cls.id == item_id)
82
83 def _get_with_name(cls, name):
84 return select(cls).where(func.lower(cls.name) == func.lower(name))
85
86
87 def _get_goals_by_vision(goals) -> List[str]:
88 results = []
89 visions = {}
90
91 for goal in goals:
92 vision = str(goal.vision)
93 if not vision in visions:
94 visions[vision] = []
95 visions[vision].append(str(goal))
96 for vision in visions:
97 results.append(f' ## {vision}')
98 for item in visions[vision]:
99 results.append(item)
100 return results
101
102
103 class Data():
104 def __init__(self, db_file=None):
105 if not db_file:
106 db_file = os.path.join(os.environ.get('MATRIX_DATA_DIR', ''), 'goal.db')
107
108 self.file = db_file
109 self.engine = self.get_engine()
110
111
112 def get_engine(self):
113 return create_engine(f"sqlite:///{self.file}")
114
115 def setup(self):
116 SQLModel.metadata.create_all(self.engine)
117 return 'Success'
118
119 def migrate(self):
120 with Session(self.engine) as session:
121 session.exec(text(''' alter table goal add start_date DATETIME; '''))
122 session.commit()
123 return 'Success'
124
125
126 def _set_pref_goal_id(self, goal_id):
127 with Session(self.engine) as session:
128 prefs = session.exec(select(PreferGoal)).all()
129 for pref in prefs:
130 session.delete(pref)
131 session.commit()
132 pref = PreferGoal()
133 pref.goal_id = goal_id
134 session.add(pref)
135 session.commit()
136
137 def _get_pref_goal_id(self):
138 with Session(self.engine) as session:
139 pref = session.exec(select(PreferGoal)).one()
140 return pref.goal_id
141
142
143 def clear(self, goal_id, clear_all=False):
144 with Session(self.engine) as session:
145 goal = session.exec(_get_with_id(Goal, goal_id)).first()
146 for item in goal.tasks:
147 if clear_all or item.complete_date:
148 session.delete(item)
149 session.commit()
150
151 def get_all_visions(self):
152 with Session(self.engine) as session:
153 cmd = select(Vision).where(Vision.complete_date == None)
154 return session.exec(cmd).all()
155
156 def get_all_goals(self) -> List[str]:
157 '''List all goals'''
158 results = []
159 with Session(self.engine) as session:
160 cmd = select(Goal).where(Goal.complete_date == None)
161 goals = session.exec(cmd).all()
162 results.append(_get_goals_by_vision(goals))
163 return results
164
165
166 def get_current_goals(self):
167 '''List current goals'''
168 with Session(self.engine) as session:
169 cmd = select(Goal).where(Goal.complete_date == None).where(Goal.start_date <= dt.today())
170 goals = session.exec(cmd).all()
171 logger.debug(f'Current goals: {goals}')
172 return _get_goals_by_vision(goals)
173
174 def get_done_goals(self):
175 '''List completed goals'''
176 with Session(self.engine) as session:
177 cmd = select(Goal).where(Goal.complete_date != None)
178 goals = session.exec(cmd).all()
179 return _get_goals_by_vision(goals)
180
181 def get_goal(self, goal_id):
182 self._set_pref_goal_id(goal_id)
183 with Session(self.engine) as session:
184 cmd = select(Goal).where(Goal.id == goal_id)
185 goal = session.exec(cmd).first()
186 return (goal, goal.tasks)
187
188 def get_vision(self, id):
189 with Session(self.engine) as session:
190 cmd = select(Vision).where(Vision.id == id)
191 vision = session.exec(cmd).first()
192 if vision:
193 return (vision, vision.goals)
194 else:
195 return ('Not found', [])
196
197
198 def complete_vision(self, id):
199 with Session(self.engine) as session:
200 cmd = select(Vision).where(Vision.id == id)
201 vision = session.exec(cmd).first()
202 vision.complete_date = dt.today()
203 session.commit()
204
205
206 def complete_goal(self, goal_id):
207 vision_id = -1
208 with Session(self.engine) as session:
209 cmd = select(Goal).where(Goal.id == goal_id)
210 goal = session.exec(cmd).first()
211 goal.complete_date = dt.today()
212 vision_id = goal.vision_id
213
214 cmd = select(Vision).where(Vision.id == vision_id)
215 win = Win()
216 win.goal = goal.name
217
218 vision = session.exec(cmd).first()
219 if vision:
220 win.vision = vision.name
221
222 session.add(win)
223 session.commit()
224 return vision_id
225
226
227 def put_off_vision(self, vision_id):
228 with Session(self.engine) as session:
229 cmd = select(Goal).where(Vision.id == vision_id)
230 goal = session.exec(cmd).first()
231 goal.start_date = dt.today() + timedelta(days=14)
232 session.commit()
233 return True
234
235 def start_goal(self, goal_id):
236 vision = None
237 with Session(self.engine) as session:
238 cmd = select(Goal).where(Goal.id == goal_id)
239 goal = session.exec(cmd).first()
240 goal.start_date = dt.today()
241 vision_id = goal.vision_id
242 session.commit()
243 if vision:
244 return (vision, vision.goals)
245 else:
246 return ('No Vision Assigned', [])
247
248
249 def rename_goal(self, goal_id, name):
250 self._set_pref_goal_id(goal_id)
251 vision_id = -1
252 with Session(self.engine) as session:
253 cmd = select(Goal).where(Goal.id == goal_id)
254 goal = session.exec(cmd).first()
255 goal.name = name
256 vision_id = goal.vision_id
257 session.commit()
258 return vision_id
259
260
261 def complete_task(self, id):
262 with Session(self.engine) as session:
263 cmd = select(Task).where(Task.id == id)
264 task = session.exec(cmd).first()
265 task.complete_date = dt.today()
266 goal_id = task.goal_id
267 session.commit()
268 self._set_pref_goal_id(goal_id)
269 return goal_id
270
271 def add_with_name(self, cls, name):
272 id = -1
273 with Session(self.engine) as session:
274 new = cls(name=name)
275 session.add(new)
276 session.commit()
277 id = new.id
278 return id
279
280 def add_vision(self, name):
281 return self.add_with_name(Vision, name)
282
283 def add_goal(self, name):
284 goal_id = self.add_with_name(Goal, name)
285 self._set_pref_goal_id(goal_id)
286 return goal_id
287
288 def quick_add_task(self, name):
289 goal_id = self._get_pref_goal_id()
290 task_id = self.add_with_name(Task, name)
291 with Session(self.engine) as session:
292 cmd = select(Task).where(Task.id == task_id)
293 new = session.exec(cmd).first()
294 new.goal_id = goal_id
295 session.commit()
296 return goal_id
297
298 def remove_task(self, task_id):
299 task_name = ''
300 logger.debug(f'Removing task with id {task_id}')
301 with Session(self.engine) as session:
302 cmd = select(Task).where(Task.id == int(task_id))
303 to_remove = session.exec(cmd).first()
304 if not to_remove:
305 return f'Unable to find task with id {task_id}'
306
307 logger.debug(f'Removing task with name {to_remove.name}')
308 task_name = to_remove.name
309 session.delete(to_remove)
310 session.commit()
311 return f'Removed task "{task_name}"'
312
313 def assign_vision(self, goal_id, vision_id):
314 '''Goals may move between visions'''
315 self._set_pref_goal_id(goal_id)
316 with Session(self.engine) as session:
317 cmd = select(Goal).where(Goal.id == goal_id)
318 goal = session.exec(cmd).first()
319 goal.vision_id = vision_id
320 session.commit()
321 return True
322
323 def visionless(self):
324 '''Get goals with no vision'''
325 with Session(self.engine) as session:
326 cmd = select(Goal).where(Goal.vision_id == None).where(Goal.complete_date == None)
327 return session.exec(cmd).all()
328
329
330 def add_note(self, note, goal):
331 '''Notes print with the goal.'''
332 pass
333
334
335 def wins(self):
336 results = []
337 with Session(self.engine) as session:
338 results = session.exec(select(Win)).all()
339 return results
340
341 # Someday: Notes on each
342