cdechoch commited on
Commit
72098e8
·
verified ·
1 Parent(s): 3e718bc

Update database.py

Browse files
Files changed (1) hide show
  1. database.py +232 -124
database.py CHANGED
@@ -1,47 +1,62 @@
1
- """Database operations for NBA Buzz"""
2
 
3
  import sqlite3
4
  import json
5
  from datetime import datetime, timezone, timedelta
6
- from typing import List, Dict
7
- from nba_players import PLAYER_TEAMS
 
8
 
9
- DB_PATH = "nba_buzz.db"
10
- _conn = None
11
 
12
  def get_conn():
13
- global _conn
14
- if _conn is None:
15
- _conn = sqlite3.connect(DB_PATH, check_same_thread=False)
16
- _conn.row_factory = sqlite3.Row
17
- return _conn
18
 
19
  def init_db():
20
  conn = get_conn()
21
  c = conn.cursor()
22
- c.execute("""CREATE TABLE IF NOT EXISTS posts (
23
- id INTEGER PRIMARY KEY AUTOINCREMENT,
24
- uri TEXT UNIQUE,
25
- author_handle TEXT,
26
- author_name TEXT,
27
- author_avatar TEXT,
28
- text TEXT,
29
- created_at TEXT,
30
- web_url TEXT,
31
- quote_post TEXT
32
- )""")
33
- c.execute("""CREATE TABLE IF NOT EXISTS mentions (
34
- id INTEGER PRIMARY KEY AUTOINCREMENT,
35
- post_id INTEGER,
36
- player_name TEXT,
37
- team_name TEXT,
38
- UNIQUE(post_id, player_name)
39
- )""")
40
- c.execute("""CREATE TABLE IF NOT EXISTS app_state (key TEXT PRIMARY KEY, value TEXT)""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  c.execute("CREATE INDEX IF NOT EXISTS idx_posts_created ON posts(created_at)")
42
  c.execute("CREATE INDEX IF NOT EXISTS idx_mentions_player ON mentions(player_name)")
43
  c.execute("CREATE INDEX IF NOT EXISTS idx_mentions_team ON mentions(team_name)")
44
- # Add columns if missing (migration)
 
45
  try:
46
  c.execute("ALTER TABLE posts ADD COLUMN author_avatar TEXT")
47
  except:
@@ -50,97 +65,171 @@ def init_db():
50
  c.execute("ALTER TABLE posts ADD COLUMN quote_post TEXT")
51
  except:
52
  pass
 
53
  conn.commit()
 
 
 
 
54
 
55
- def store_post(post: dict) -> int:
56
- conn = get_conn()
57
- c = conn.cursor()
58
- quote_json = json.dumps(post.get("quote_post")) if post.get("quote_post") else None
59
- try:
60
- c.execute("""INSERT OR IGNORE INTO posts (uri, author_handle, author_name, author_avatar, text, created_at, web_url, quote_post)
61
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
62
- (post.get("uri"), post.get("author_handle"), post.get("author_name"),
63
- post.get("author_avatar"), post.get("text"), post.get("created_at"),
64
- post.get("web_url"), quote_json))
65
- conn.commit()
66
- if c.lastrowid:
67
- return c.lastrowid
68
- c.execute("SELECT id FROM posts WHERE uri = ?", (post.get("uri"),))
69
- row = c.fetchone()
70
- return row["id"] if row else None
71
- except Exception as e:
72
- print(f"Error storing post: {e}")
73
- return None
74
 
75
- def store_mention(post_id: int, player_name: str):
76
- if not post_id:
77
- return
 
78
  conn = get_conn()
79
  c = conn.cursor()
80
- team = PLAYER_TEAMS.get(player_name)
 
81
  try:
82
- c.execute("INSERT OR IGNORE INTO mentions (post_id, player_name, team_name) VALUES (?, ?, ?)",
83
- (post_id, player_name, team))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  conn.commit()
 
85
  except Exception as e:
86
- print(f"Error storing mention: {e}")
87
-
88
- def get_post_count() -> int:
89
- conn = get_conn()
90
- c = conn.cursor()
91
- c.execute("SELECT COUNT(*) FROM posts")
92
- return c.fetchone()[0]
93
 
94
- def get_mention_count() -> int:
95
- conn = get_conn()
96
- c = conn.cursor()
97
- c.execute("SELECT COUNT(*) FROM mentions")
98
- return c.fetchone()[0]
99
-
100
- def get_unique_player_count() -> int:
101
- conn = get_conn()
102
- c = conn.cursor()
103
- c.execute("SELECT COUNT(DISTINCT player_name) FROM mentions")
104
- return c.fetchone()[0]
105
 
106
- def get_top_players(hours: int = 24, limit: int = 50) -> List[Dict]:
107
  conn = get_conn()
108
  c = conn.cursor()
109
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
 
110
  c.execute("""
111
- SELECT m.player_name, m.team_name, COUNT(*) as mention_count
112
- FROM mentions m JOIN posts p ON m.post_id = p.id
 
 
 
113
  WHERE p.created_at >= ?
114
  GROUP BY m.player_name
115
  ORDER BY mention_count DESC
116
  LIMIT ?
117
  """, (cutoff, limit))
118
- return [{"player": r["player_name"], "team": r["team_name"], "mentions": r["mention_count"]} for r in c.fetchall()]
 
 
 
119
 
120
- def get_top_teams(hours: int = 24, limit: int = 30) -> List[Dict]:
 
121
  conn = get_conn()
122
  c = conn.cursor()
123
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
 
124
  c.execute("""
125
- SELECT m.team_name, COUNT(*) as mention_count
126
- FROM mentions m JOIN posts p ON m.post_id = p.id
127
- WHERE m.team_name IS NOT NULL AND p.created_at >= ?
 
 
 
128
  GROUP BY m.team_name
129
  ORDER BY mention_count DESC
130
  LIMIT ?
131
  """, (cutoff, limit))
132
- return [{"team": r["team_name"], "mentions": r["mention_count"]} for r in c.fetchall()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  def get_player_recent_mentions(player_name: str, limit: int = 50, hours: int = 168) -> List[Dict]:
135
  conn = get_conn()
136
  c = conn.cursor()
137
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
 
138
  c.execute("""
139
- SELECT DISTINCT p.author_handle, p.author_name, p.author_avatar, p.text, p.created_at, p.web_url, p.quote_post
140
- FROM mentions m JOIN posts p ON m.post_id = p.id
 
 
141
  WHERE m.player_name = ? AND p.created_at >= ?
142
- ORDER BY p.created_at DESC LIMIT ?
 
143
  """, (player_name, cutoff, limit))
 
144
  results = []
145
  for r in c.fetchall():
146
  quote = None
@@ -150,26 +239,33 @@ def get_player_recent_mentions(player_name: str, limit: int = 50, hours: int = 1
150
  except:
151
  pass
152
  results.append({
153
- "author": r["author_name"] or r["author_handle"],
154
  "author_handle": r["author_handle"],
 
155
  "author_avatar": r["author_avatar"],
156
  "text": r["text"],
157
  "created_at": r["created_at"],
158
- "url": r["web_url"],
159
  "quote_post": quote
160
  })
 
161
  return results
162
 
 
163
  def get_team_recent_mentions(team_name: str, limit: int = 50, hours: int = 168) -> List[Dict]:
164
  conn = get_conn()
165
  c = conn.cursor()
166
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
 
167
  c.execute("""
168
- SELECT DISTINCT p.author_handle, p.author_name, p.author_avatar, p.text, p.created_at, p.web_url, p.quote_post, m.player_name
169
- FROM mentions m JOIN posts p ON m.post_id = p.id
 
 
170
  WHERE m.team_name = ? AND p.created_at >= ?
171
- ORDER BY p.created_at DESC LIMIT ?
 
172
  """, (team_name, cutoff, limit))
 
173
  results = []
174
  for r in c.fetchall():
175
  quote = None
@@ -180,50 +276,62 @@ def get_team_recent_mentions(team_name: str, limit: int = 50, hours: int = 168)
180
  pass
181
  results.append({
182
  "player": r["player_name"],
183
- "author": r["author_name"] or r["author_handle"],
184
  "author_handle": r["author_handle"],
 
185
  "author_avatar": r["author_avatar"],
186
  "text": r["text"],
187
  "created_at": r["created_at"],
188
- "url": r["web_url"],
189
  "quote_post": quote
190
  })
 
191
  return results
192
 
193
- def get_player_latest_mention(player_name: str) -> dict:
194
- """Get the most recent mention for a player."""
195
  conn = get_conn()
196
  c = conn.cursor()
197
- c.execute("""
198
- SELECT p.text, p.author_name, p.author_handle, p.created_at
199
- FROM mentions m JOIN posts p ON m.post_id = p.id
200
- WHERE m.player_name = ?
201
- ORDER BY p.created_at DESC LIMIT 1
202
- """, (player_name,))
203
- row = c.fetchone()
204
- if row:
205
- return {
206
- "text": row["text"],
207
- "author": row["author_name"] or row["author_handle"],
208
- "created_at": row["created_at"]
209
- }
210
- return None
211
-
212
- def get_team_latest_mention(team_name: str) -> dict:
213
- """Get the most recent mention for a team."""
 
 
214
  conn = get_conn()
215
  c = conn.cursor()
216
- c.execute("""
217
- SELECT p.text, p.author_name, p.author_handle, p.created_at
218
- FROM mentions m JOIN posts p ON m.post_id = p.id
219
- WHERE m.team_name = ?
220
- ORDER BY p.created_at DESC LIMIT 1
221
- """, (team_name,))
222
  row = c.fetchone()
223
- if row:
224
- return {
225
- "text": row["text"],
226
- "author": row["author_name"] or row["author_handle"],
227
- "created_at": row["created_at"]
228
- }
229
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Database module for NBA Buzz - SQLite storage"""
2
 
3
  import sqlite3
4
  import json
5
  from datetime import datetime, timezone, timedelta
6
+ from typing import List, Dict, Optional
7
+
8
+ DATABASE_PATH = "/tmp/nba_mentions.db"
9
 
 
 
10
 
11
  def get_conn():
12
+ conn = sqlite3.connect(DATABASE_PATH)
13
+ conn.row_factory = sqlite3.Row
14
+ return conn
15
+
 
16
 
17
  def init_db():
18
  conn = get_conn()
19
  c = conn.cursor()
20
+
21
+ c.execute("""
22
+ CREATE TABLE IF NOT EXISTS posts (
23
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
24
+ uri TEXT UNIQUE,
25
+ platform TEXT,
26
+ author_handle TEXT,
27
+ author_name TEXT,
28
+ author_avatar TEXT,
29
+ text TEXT,
30
+ created_at TEXT,
31
+ web_url TEXT,
32
+ quote_post TEXT,
33
+ fetched_at TEXT
34
+ )
35
+ """)
36
+
37
+ c.execute("""
38
+ CREATE TABLE IF NOT EXISTS mentions (
39
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
40
+ post_id INTEGER,
41
+ player_name TEXT,
42
+ team_name TEXT,
43
+ FOREIGN KEY (post_id) REFERENCES posts(id),
44
+ UNIQUE(post_id, player_name)
45
+ )
46
+ """)
47
+
48
+ c.execute("""
49
+ CREATE TABLE IF NOT EXISTS app_state (
50
+ key TEXT PRIMARY KEY,
51
+ value TEXT
52
+ )
53
+ """)
54
+
55
  c.execute("CREATE INDEX IF NOT EXISTS idx_posts_created ON posts(created_at)")
56
  c.execute("CREATE INDEX IF NOT EXISTS idx_mentions_player ON mentions(player_name)")
57
  c.execute("CREATE INDEX IF NOT EXISTS idx_mentions_team ON mentions(team_name)")
58
+
59
+ # Add columns if they don't exist (migration)
60
  try:
61
  c.execute("ALTER TABLE posts ADD COLUMN author_avatar TEXT")
62
  except:
 
65
  c.execute("ALTER TABLE posts ADD COLUMN quote_post TEXT")
66
  except:
67
  pass
68
+
69
  conn.commit()
70
+ conn.close()
71
+
72
+
73
+ init_db()
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ def process_and_store_posts(posts: List[Dict], player_matcher_module, get_player_team_func) -> tuple:
77
+ posts_added = 0
78
+ mentions_added = 0
79
+
80
  conn = get_conn()
81
  c = conn.cursor()
82
+ now = datetime.now(timezone.utc).isoformat()
83
+
84
  try:
85
+ for post in posts:
86
+ text = post.get("text", "")
87
+ if not text:
88
+ continue
89
+
90
+ quote_json = json.dumps(post.get("quote_post")) if post.get("quote_post") else None
91
+
92
+ c.execute("""
93
+ INSERT OR IGNORE INTO posts
94
+ (uri, platform, author_handle, author_name, author_avatar, text, created_at, web_url, quote_post, fetched_at)
95
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
96
+ """, (
97
+ post.get("uri"),
98
+ post.get("platform"),
99
+ post.get("author_handle"),
100
+ post.get("author_name"),
101
+ post.get("author_avatar"),
102
+ text,
103
+ post.get("created_at"),
104
+ post.get("web_url"),
105
+ quote_json,
106
+ now
107
+ ))
108
+
109
+ if c.rowcount > 0:
110
+ post_id = c.lastrowid
111
+ posts_added += 1
112
+
113
+ mentions_dict = player_matcher_module.find_player_mentions(text)
114
+
115
+ for player in mentions_dict.keys():
116
+ team = get_player_team_func(player)
117
+ c.execute("""
118
+ INSERT OR IGNORE INTO mentions (post_id, player_name, team_name)
119
+ VALUES (?, ?, ?)
120
+ """, (post_id, player, team))
121
+ if c.rowcount > 0:
122
+ mentions_added += 1
123
+
124
  conn.commit()
125
+
126
  except Exception as e:
127
+ print(f"Error processing posts: {e}")
128
+ finally:
129
+ conn.close()
130
+
131
+ return posts_added, mentions_added
 
 
132
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
+ def get_player_mention_counts(hours: int = 24, limit: int = 50) -> List[Dict]:
135
  conn = get_conn()
136
  c = conn.cursor()
137
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
138
+
139
  c.execute("""
140
+ SELECT
141
+ m.player_name, m.team_name,
142
+ COUNT(DISTINCT m.post_id) as mention_count
143
+ FROM mentions m
144
+ JOIN posts p ON m.post_id = p.id
145
  WHERE p.created_at >= ?
146
  GROUP BY m.player_name
147
  ORDER BY mention_count DESC
148
  LIMIT ?
149
  """, (cutoff, limit))
150
+
151
+ results = [dict(row) for row in c.fetchall()]
152
+ conn.close()
153
+ return results
154
 
155
+
156
+ def get_team_mention_counts(hours: int = 24, limit: int = 30) -> List[Dict]:
157
  conn = get_conn()
158
  c = conn.cursor()
159
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
160
+
161
  c.execute("""
162
+ SELECT
163
+ m.team_name,
164
+ COUNT(DISTINCT m.post_id) as mention_count
165
+ FROM mentions m
166
+ JOIN posts p ON m.post_id = p.id
167
+ WHERE p.created_at >= ? AND m.team_name != ''
168
  GROUP BY m.team_name
169
  ORDER BY mention_count DESC
170
  LIMIT ?
171
  """, (cutoff, limit))
172
+
173
+ results = [dict(row) for row in c.fetchall()]
174
+ conn.close()
175
+ return results
176
+
177
+
178
+ def get_latest_mention_for_player(player_name: str, hours: int = 24) -> Optional[str]:
179
+ """Get the most recent mention text for a player."""
180
+ conn = get_conn()
181
+ c = conn.cursor()
182
+ cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
183
+
184
+ c.execute("""
185
+ SELECT p.text
186
+ FROM mentions m
187
+ JOIN posts p ON m.post_id = p.id
188
+ WHERE m.player_name = ? AND p.created_at >= ?
189
+ ORDER BY p.created_at DESC
190
+ LIMIT 1
191
+ """, (player_name, cutoff))
192
+
193
+ row = c.fetchone()
194
+ conn.close()
195
+ return row["text"] if row else None
196
+
197
+
198
+ def get_latest_mention_for_team(team_name: str, hours: int = 24) -> Optional[str]:
199
+ """Get the most recent mention text for a team."""
200
+ conn = get_conn()
201
+ c = conn.cursor()
202
+ cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
203
+
204
+ c.execute("""
205
+ SELECT p.text
206
+ FROM mentions m
207
+ JOIN posts p ON m.post_id = p.id
208
+ WHERE m.team_name = ? AND p.created_at >= ?
209
+ ORDER BY p.created_at DESC
210
+ LIMIT 1
211
+ """, (team_name, cutoff))
212
+
213
+ row = c.fetchone()
214
+ conn.close()
215
+ return row["text"] if row else None
216
+
217
 
218
  def get_player_recent_mentions(player_name: str, limit: int = 50, hours: int = 168) -> List[Dict]:
219
  conn = get_conn()
220
  c = conn.cursor()
221
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
222
+
223
  c.execute("""
224
+ SELECT DISTINCT p.uri, p.author_handle, p.author_name, p.author_avatar, p.text,
225
+ p.created_at, p.web_url, p.quote_post
226
+ FROM mentions m
227
+ JOIN posts p ON m.post_id = p.id
228
  WHERE m.player_name = ? AND p.created_at >= ?
229
+ ORDER BY p.created_at DESC
230
+ LIMIT ?
231
  """, (player_name, cutoff, limit))
232
+
233
  results = []
234
  for r in c.fetchall():
235
  quote = None
 
239
  except:
240
  pass
241
  results.append({
 
242
  "author_handle": r["author_handle"],
243
+ "author_name": r["author_name"],
244
  "author_avatar": r["author_avatar"],
245
  "text": r["text"],
246
  "created_at": r["created_at"],
247
+ "web_url": r["web_url"],
248
  "quote_post": quote
249
  })
250
+ conn.close()
251
  return results
252
 
253
+
254
  def get_team_recent_mentions(team_name: str, limit: int = 50, hours: int = 168) -> List[Dict]:
255
  conn = get_conn()
256
  c = conn.cursor()
257
  cutoff = (datetime.now(timezone.utc) - timedelta(hours=hours)).isoformat()
258
+
259
  c.execute("""
260
+ SELECT DISTINCT p.uri, p.author_handle, p.author_name, p.author_avatar, p.text,
261
+ p.created_at, p.web_url, p.quote_post, m.player_name
262
+ FROM mentions m
263
+ JOIN posts p ON m.post_id = p.id
264
  WHERE m.team_name = ? AND p.created_at >= ?
265
+ ORDER BY p.created_at DESC
266
+ LIMIT ?
267
  """, (team_name, cutoff, limit))
268
+
269
  results = []
270
  for r in c.fetchall():
271
  quote = None
 
276
  pass
277
  results.append({
278
  "player": r["player_name"],
 
279
  "author_handle": r["author_handle"],
280
+ "author_name": r["author_name"],
281
  "author_avatar": r["author_avatar"],
282
  "text": r["text"],
283
  "created_at": r["created_at"],
284
+ "web_url": r["web_url"],
285
  "quote_post": quote
286
  })
287
+ conn.close()
288
  return results
289
 
290
+
291
+ def get_database_stats() -> Dict:
292
  conn = get_conn()
293
  c = conn.cursor()
294
+
295
+ c.execute("SELECT COUNT(*) as count FROM posts")
296
+ total_posts = c.fetchone()["count"]
297
+
298
+ c.execute("SELECT COUNT(*) as count FROM mentions")
299
+ total_mentions = c.fetchone()["count"]
300
+
301
+ c.execute("SELECT COUNT(DISTINCT player_name) as count FROM mentions")
302
+ unique_players = c.fetchone()["count"]
303
+
304
+ conn.close()
305
+ return {
306
+ "total_posts": total_posts,
307
+ "total_mentions": total_mentions,
308
+ "unique_players": unique_players
309
+ }
310
+
311
+
312
+ def get_last_update() -> Optional[str]:
313
  conn = get_conn()
314
  c = conn.cursor()
315
+ c.execute("SELECT value FROM app_state WHERE key = 'last_update'")
 
 
 
 
 
316
  row = c.fetchone()
317
+ conn.close()
318
+ return row["value"] if row else None
319
+
320
+
321
+ def set_last_update():
322
+ conn = get_conn()
323
+ c = conn.cursor()
324
+ now = datetime.now(timezone.utc).isoformat()
325
+ c.execute("INSERT OR REPLACE INTO app_state (key, value) VALUES ('last_update', ?)", (now,))
326
+ conn.commit()
327
+ conn.close()
328
+
329
+
330
+ def cleanup_old_data(days: int = 3):
331
+ conn = get_conn()
332
+ c = conn.cursor()
333
+ cutoff = (datetime.now(timezone.utc) - timedelta(days=days)).isoformat()
334
+ c.execute("DELETE FROM mentions WHERE post_id IN (SELECT id FROM posts WHERE created_at < ?)", (cutoff,))
335
+ c.execute("DELETE FROM posts WHERE created_at < ?", (cutoff,))
336
+ conn.commit()
337
+ conn.close()