eraz3r commited on
Commit
9d75f48
·
verified ·
1 Parent(s): 239347e

Delete database.py

Browse files
Files changed (1) hide show
  1. database.py +0 -386
database.py DELETED
@@ -1,386 +0,0 @@
1
- """
2
- core/database.py
3
- SQLite persistence for Second Brain.
4
- Tables: users, life_areas, goals, tasks, user_context (AI memory)
5
- """
6
-
7
- import sqlite3
8
- import json
9
- import bcrypt
10
- from datetime import datetime, date
11
- from typing import Optional
12
-
13
- DB_PATH = "second_brain.db"
14
-
15
-
16
- # ── Connection ────────────────────────────────────────────────────────────────
17
-
18
- def get_db() -> sqlite3.Connection:
19
- conn = sqlite3.connect(DB_PATH)
20
- conn.row_factory = sqlite3.Row
21
- conn.execute("PRAGMA foreign_keys = ON")
22
- return conn
23
-
24
-
25
- def init_db():
26
- """Create all tables on first run."""
27
- conn = get_db()
28
- c = conn.cursor()
29
-
30
- c.execute("""
31
- CREATE TABLE IF NOT EXISTS users (
32
- id INTEGER PRIMARY KEY AUTOINCREMENT,
33
- username TEXT UNIQUE NOT NULL,
34
- password_hash TEXT NOT NULL,
35
- created_at TEXT DEFAULT (datetime('now'))
36
- )
37
- """)
38
-
39
- c.execute("""
40
- CREATE TABLE IF NOT EXISTS life_areas (
41
- id INTEGER PRIMARY KEY AUTOINCREMENT,
42
- user_id INTEGER NOT NULL,
43
- name TEXT NOT NULL,
44
- color TEXT DEFAULT '#6366f1',
45
- created_at TEXT DEFAULT (datetime('now')),
46
- FOREIGN KEY (user_id) REFERENCES users(id)
47
- )
48
- """)
49
-
50
- c.execute("""
51
- CREATE TABLE IF NOT EXISTS user_goals (
52
- id INTEGER PRIMARY KEY AUTOINCREMENT,
53
- user_id INTEGER NOT NULL,
54
- goal_text TEXT NOT NULL,
55
- created_at TEXT DEFAULT (datetime('now')),
56
- FOREIGN KEY (user_id) REFERENCES users(id)
57
- )
58
- """)
59
-
60
- c.execute("""
61
- CREATE TABLE IF NOT EXISTS tasks (
62
- id INTEGER PRIMARY KEY AUTOINCREMENT,
63
- user_id INTEGER NOT NULL,
64
- title TEXT NOT NULL,
65
- life_area TEXT DEFAULT '',
66
- urgency TEXT DEFAULT 'Not Urgent',
67
- importance TEXT DEFAULT 'Important',
68
- state_of_mind TEXT DEFAULT 'Easy',
69
- time_estimate INTEGER DEFAULT 30,
70
- scheduled_date TEXT DEFAULT (date('now')),
71
- is_completed INTEGER DEFAULT 0,
72
- actual_duration INTEGER,
73
- is_habit INTEGER DEFAULT 0,
74
- habit_interval TEXT DEFAULT '',
75
- raw_input TEXT DEFAULT '',
76
- created_at TEXT DEFAULT (datetime('now')),
77
- FOREIGN KEY (user_id) REFERENCES users(id)
78
- )
79
- """)
80
-
81
- # AI-learned context stored as a JSON blob per user
82
- c.execute("""
83
- CREATE TABLE IF NOT EXISTS user_context (
84
- user_id INTEGER PRIMARY KEY,
85
- context TEXT NOT NULL,
86
- updated_at TEXT DEFAULT (datetime('now')),
87
- FOREIGN KEY (user_id) REFERENCES users(id)
88
- )
89
- """)
90
-
91
- conn.commit()
92
- conn.close()
93
-
94
-
95
- # ── Auth ──────────────────────────────────────────────────────────────────────
96
-
97
- def register_user(username: str, password: str) -> tuple:
98
- """Returns (user_id, message). user_id is None on failure."""
99
- username = username.strip().lower()
100
- if not username or not password:
101
- return None, "Username and password cannot be empty."
102
- if len(password) < 6:
103
- return None, "Password must be at least 6 characters."
104
- conn = get_db()
105
- try:
106
- pw_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
107
- conn.execute(
108
- "INSERT INTO users (username, password_hash) VALUES (?, ?)",
109
- (username, pw_hash)
110
- )
111
- conn.commit()
112
- row = conn.execute("SELECT id FROM users WHERE username = ?", (username,)).fetchone()
113
- return row["id"], "Account created!"
114
- except sqlite3.IntegrityError:
115
- return None, "Username already taken."
116
- finally:
117
- conn.close()
118
-
119
-
120
- def login_user(username: str, password: str) -> tuple:
121
- """Returns (user_id, message). user_id is None on failure."""
122
- username = username.strip().lower()
123
- if not username or not password:
124
- return None, "Please enter your credentials."
125
- conn = get_db()
126
- row = conn.execute(
127
- "SELECT id, password_hash FROM users WHERE username = ?", (username,)
128
- ).fetchone()
129
- conn.close()
130
- if not row:
131
- return None, "Username not found."
132
- if not bcrypt.checkpw(password.encode(), row["password_hash"].encode()):
133
- return None, "Incorrect password."
134
- return row["id"], f"Welcome back, {username}!"
135
-
136
-
137
- def get_username(user_id: int) -> str:
138
- conn = get_db()
139
- row = conn.execute("SELECT username FROM users WHERE id = ?", (user_id,)).fetchone()
140
- conn.close()
141
- return row["username"].capitalize() if row else "User"
142
-
143
-
144
- # ── Life Areas ────────────────────────────────────────────────────────────────
145
-
146
- DEFAULT_AREAS = [
147
- ("Work", "#4F8EF7"),
148
- ("Health", "#4CAF87"),
149
- ("Finance", "#F7A84F"),
150
- ("Learning", "#A855F7"),
151
- ("Personal", "#EC4899"),
152
- ("Family", "#F59E0B"),
153
- ]
154
-
155
-
156
- def create_default_life_areas(user_id: int):
157
- conn = get_db()
158
- for name, color in DEFAULT_AREAS:
159
- conn.execute(
160
- "INSERT INTO life_areas (user_id, name, color) VALUES (?, ?, ?)",
161
- (user_id, name, color)
162
- )
163
- conn.commit()
164
- conn.close()
165
-
166
-
167
- def get_life_areas(user_id: int) -> list:
168
- conn = get_db()
169
- rows = conn.execute(
170
- "SELECT id, name, color FROM life_areas WHERE user_id = ? ORDER BY id",
171
- (user_id,)
172
- ).fetchall()
173
- conn.close()
174
- return [dict(r) for r in rows]
175
-
176
-
177
- def get_life_area_names(user_id: int) -> list:
178
- return [a["name"] for a in get_life_areas(user_id)]
179
-
180
-
181
- def add_life_area(user_id: int, name: str, color: str = "#6366f1") -> tuple:
182
- name = name.strip()
183
- if not name:
184
- return False, "Name cannot be empty."
185
- conn = get_db()
186
- exists = conn.execute(
187
- "SELECT id FROM life_areas WHERE user_id = ? AND LOWER(name) = LOWER(?)",
188
- (user_id, name)
189
- ).fetchone()
190
- if exists:
191
- conn.close()
192
- return False, f'"{name}" already exists.'
193
- conn.execute(
194
- "INSERT INTO life_areas (user_id, name, color) VALUES (?, ?, ?)",
195
- (user_id, name, color)
196
- )
197
- conn.commit()
198
- conn.close()
199
- return True, f'"{name}" added.'
200
-
201
-
202
- def delete_life_area(user_id: int, name: str) -> tuple:
203
- conn = get_db()
204
- conn.execute(
205
- "DELETE FROM life_areas WHERE user_id = ? AND name = ?", (user_id, name)
206
- )
207
- conn.commit()
208
- conn.close()
209
- return True, f'"{name}" removed.'
210
-
211
-
212
- # ── Goals ─────────────────────────────────────────────────────────────────────
213
-
214
- def save_goals(user_id: int, goals_text: str):
215
- conn = get_db()
216
- conn.execute("DELETE FROM user_goals WHERE user_id = ?", (user_id,))
217
- for line in goals_text.strip().splitlines():
218
- line = line.strip("•- ").strip()
219
- if line:
220
- conn.execute(
221
- "INSERT INTO user_goals (user_id, goal_text) VALUES (?, ?)",
222
- (user_id, line)
223
- )
224
- conn.commit()
225
- conn.close()
226
-
227
-
228
- def get_goals(user_id: int) -> list:
229
- conn = get_db()
230
- rows = conn.execute(
231
- "SELECT goal_text FROM user_goals WHERE user_id = ? ORDER BY id",
232
- (user_id,)
233
- ).fetchall()
234
- conn.close()
235
- return [r["goal_text"] for r in rows]
236
-
237
-
238
- # ── Tasks ─────────────────────────────────────────────────────────────────────
239
-
240
- def save_task(user_id: int, task: dict, scheduled_date: str = None) -> int:
241
- conn = get_db()
242
- cursor = conn.execute("""
243
- INSERT INTO tasks
244
- (user_id, title, life_area, urgency, importance, state_of_mind,
245
- time_estimate, scheduled_date, raw_input, is_habit, habit_interval)
246
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
247
- """, (
248
- user_id,
249
- task.get("title", "Untitled"),
250
- task.get("life_area", ""),
251
- task.get("urgency", "Not Urgent"),
252
- task.get("importance", "Important"),
253
- task.get("state_of_mind", "Easy"),
254
- int(task.get("time_estimate") or 30),
255
- scheduled_date or str(date.today()),
256
- task.get("raw_input", ""),
257
- 1 if task.get("is_habit") else 0,
258
- task.get("habit_interval", ""),
259
- ))
260
- task_id = cursor.lastrowid
261
- conn.commit()
262
- conn.close()
263
- return task_id
264
-
265
-
266
- def get_tasks(user_id: int, filter_area: str = "All", only_today: bool = False,
267
- include_completed: bool = True) -> list:
268
- conn = get_db()
269
- q = "SELECT * FROM tasks WHERE user_id = ?"
270
- params = [user_id]
271
- if filter_area and filter_area != "All":
272
- q += " AND life_area = ?"
273
- params.append(filter_area)
274
- if only_today:
275
- q += " AND scheduled_date = ?"
276
- params.append(str(date.today()))
277
- if not include_completed:
278
- q += " AND is_completed = 0"
279
- q += " ORDER BY is_completed ASC, created_at DESC"
280
- rows = conn.execute(q, params).fetchall()
281
- conn.close()
282
- return [dict(r) for r in rows]
283
-
284
-
285
- def toggle_task_complete(task_id: int, user_id: int, actual_duration: int = None):
286
- conn = get_db()
287
- task = conn.execute(
288
- "SELECT is_completed FROM tasks WHERE id = ? AND user_id = ?", (task_id, user_id)
289
- ).fetchone()
290
- if task:
291
- new_status = 1 - task["is_completed"]
292
- if actual_duration and new_status == 1:
293
- conn.execute(
294
- "UPDATE tasks SET is_completed = ?, actual_duration = ? WHERE id = ? AND user_id = ?",
295
- (new_status, actual_duration, task_id, user_id)
296
- )
297
- else:
298
- conn.execute(
299
- "UPDATE tasks SET is_completed = ? WHERE id = ? AND user_id = ?",
300
- (new_status, task_id, user_id)
301
- )
302
- conn.commit()
303
- conn.close()
304
-
305
-
306
- def delete_task(task_id: int, user_id: int):
307
- conn = get_db()
308
- conn.execute("DELETE FROM tasks WHERE id = ? AND user_id = ?", (task_id, user_id))
309
- conn.commit()
310
- conn.close()
311
-
312
-
313
- def get_today_stats(user_id: int) -> dict:
314
- tasks = get_tasks(user_id, only_today=True)
315
- total = len(tasks)
316
- done = sum(1 for t in tasks if t["is_completed"])
317
- return {"total": total, "done": done, "remaining": total - done}
318
-
319
-
320
- # ── Habit recurrence ──────────────────────────────────────────────────────────
321
-
322
- def spawn_due_habits(user_id: int):
323
- """
324
- Check all habit tasks. If a habit's scheduled_date < today and
325
- it's not already scheduled for today, create a fresh copy for today.
326
- Called on login / tab load.
327
- """
328
- today = str(date.today())
329
- conn = get_db()
330
- habits = conn.execute(
331
- "SELECT * FROM tasks WHERE user_id = ? AND is_habit = 1",
332
- (user_id,)
333
- ).fetchall()
334
-
335
- for h in habits:
336
- # Check if already exists today
337
- existing = conn.execute(
338
- "SELECT id FROM tasks WHERE user_id = ? AND title = ? AND is_habit = 1 AND scheduled_date = ?",
339
- (user_id, h["title"], today)
340
- ).fetchone()
341
- if existing:
342
- continue
343
- # Only spawn if original was scheduled before today
344
- if h["scheduled_date"] and h["scheduled_date"] >= today:
345
- continue
346
- conn.execute("""
347
- INSERT INTO tasks (user_id, title, life_area, urgency, importance,
348
- state_of_mind, time_estimate, scheduled_date, is_habit,
349
- habit_interval, raw_input)
350
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
351
- """, (
352
- user_id, h["title"], h["life_area"], "Habit",
353
- h["importance"], h["state_of_mind"], h["time_estimate"],
354
- today, h["habit_interval"], h["raw_input"]
355
- ))
356
- conn.commit()
357
- conn.close()
358
-
359
-
360
- # ── AI Context ────────────────────────────────────────────────────────────────
361
-
362
- def load_user_context(user_id: int) -> Optional[dict]:
363
- conn = get_db()
364
- row = conn.execute(
365
- "SELECT context FROM user_context WHERE user_id = ?", (user_id,)
366
- ).fetchone()
367
- conn.close()
368
- if row:
369
- try:
370
- return json.loads(row["context"])
371
- except Exception:
372
- return None
373
- return None
374
-
375
-
376
- def save_user_context(user_id: int, context: dict):
377
- conn = get_db()
378
- conn.execute("""
379
- INSERT INTO user_context (user_id, context, updated_at)
380
- VALUES (?, ?, datetime('now'))
381
- ON CONFLICT(user_id) DO UPDATE SET
382
- context = excluded.context,
383
- updated_at = excluded.updated_at
384
- """, (user_id, json.dumps(context)))
385
- conn.commit()
386
- conn.close()