KrishnaCosmic commited on
Commit
161a02e
·
1 Parent(s): 0958581

Fix: Try multiple libsql package names for compatibility

Browse files
Files changed (2) hide show
  1. config/turso.py +82 -47
  2. requirements.txt +3 -2
config/turso.py CHANGED
@@ -1,13 +1,26 @@
1
  """
2
  Turso (libsql) Database Connection Utility.
3
 
4
- Provides async database operations for messages and mentorship data.
 
5
  """
6
 
7
  import os
8
  import logging
9
  from typing import List, Dict, Any, Optional
10
- import libsql_client
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  logger = logging.getLogger(__name__)
13
 
@@ -17,68 +30,69 @@ TURSO_AUTH_TOKEN = os.environ.get('TURSO_AUTH_TOKEN', '')
17
 
18
 
19
  class TursoDatabase:
20
- """Turso database wrapper with async operations."""
21
 
22
  def __init__(self):
23
- self._client = None
24
  self._initialized = False
25
 
26
- def _get_client(self):
27
- """Get or create the libsql client."""
28
- if self._client is None:
 
 
 
29
  if not TURSO_DATABASE_URL:
30
  raise ValueError("TURSO_DATABASE_URL environment variable is not set")
31
 
32
- # Convert libsql:// to https:// for HTTP transport
33
- url = TURSO_DATABASE_URL
34
- if url.startswith("libsql://"):
35
- url = url.replace("libsql://", "https://")
36
-
37
- self._client = libsql_client.create_client_sync(
38
- url=url,
39
  auth_token=TURSO_AUTH_TOKEN if TURSO_AUTH_TOKEN else None
40
  )
41
- return self._client
 
 
 
 
 
 
 
 
42
 
43
  def execute(self, sql: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]:
44
  """Execute a SQL query and return results."""
45
- client = self._get_client()
46
  try:
47
  if params:
48
- result = client.execute(sql, params)
49
  else:
50
- result = client.execute(sql)
51
 
52
- # Convert result rows to list of dicts
53
- if result.rows:
54
- columns = result.columns
55
- return [dict(zip(columns, row)) for row in result.rows]
 
 
 
 
 
56
  return []
57
  except Exception as e:
58
  logger.error(f"Turso execute error: {e}")
59
  raise
60
 
61
- def executemany(self, sql: str, params_list: List[tuple]) -> int:
62
- """Execute a SQL query with multiple parameter sets."""
63
- client = self._get_client()
64
- count = 0
65
- try:
66
- for params in params_list:
67
- client.execute(sql, params)
68
- count += 1
69
- return count
70
- except Exception as e:
71
- logger.error(f"Turso executemany error: {e}")
72
- raise
73
-
74
  def init_tables(self):
75
  """Initialize database tables if they don't exist."""
76
  if self._initialized:
77
  return
78
 
 
79
  try:
80
  # Messages table
81
- self.execute("""
82
  CREATE TABLE IF NOT EXISTS messages (
83
  id TEXT PRIMARY KEY,
84
  sender_id TEXT NOT NULL,
@@ -90,7 +104,7 @@ class TursoDatabase:
90
  """)
91
 
92
  # Mentorships table
93
- self.execute("""
94
  CREATE TABLE IF NOT EXISTS mentorships (
95
  id TEXT PRIMARY KEY,
96
  mentor_id TEXT NOT NULL,
@@ -104,7 +118,7 @@ class TursoDatabase:
104
  """)
105
 
106
  # Mentorship requests table
107
- self.execute("""
108
  CREATE TABLE IF NOT EXISTS mentorship_requests (
109
  id TEXT PRIMARY KEY,
110
  mentee_id TEXT NOT NULL,
@@ -118,6 +132,9 @@ class TursoDatabase:
118
  )
119
  """)
120
 
 
 
 
121
  self._initialized = True
122
  logger.info("Turso tables initialized successfully")
123
  except Exception as e:
@@ -127,8 +144,13 @@ class TursoDatabase:
127
  # Message operations
128
  def insert_message(self, message: Dict[str, Any]) -> bool:
129
  """Insert a message into the messages table."""
 
130
  try:
131
- self.execute(
 
 
 
 
132
  """
133
  INSERT OR IGNORE INTO messages (id, sender_id, receiver_id, content, read, timestamp)
134
  VALUES (?, ?, ?, ?, ?, ?)
@@ -139,10 +161,11 @@ class TursoDatabase:
139
  message.get('receiver_id'),
140
  message.get('content'),
141
  1 if message.get('read') else 0,
142
- message.get('timestamp') if isinstance(message.get('timestamp'), str)
143
- else message.get('timestamp').isoformat() if message.get('timestamp') else None
144
  )
145
  )
 
 
146
  return True
147
  except Exception as e:
148
  logger.error(f"Failed to insert message: {e}")
@@ -163,8 +186,13 @@ class TursoDatabase:
163
  # Mentorship operations
164
  def insert_mentorship(self, mentorship: Dict[str, Any]) -> bool:
165
  """Insert a mentorship record."""
 
166
  try:
167
- self.execute(
 
 
 
 
168
  """
169
  INSERT OR IGNORE INTO mentorships
170
  (id, mentor_id, mentor_username, mentee_id, mentee_username, status, created_at, disconnected_at)
@@ -177,11 +205,12 @@ class TursoDatabase:
177
  mentorship.get('mentee_id'),
178
  mentorship.get('mentee_username'),
179
  mentorship.get('status', 'active'),
180
- mentorship.get('created_at') if isinstance(mentorship.get('created_at'), str)
181
- else mentorship.get('created_at').isoformat() if mentorship.get('created_at') else None,
182
  mentorship.get('disconnected_at')
183
  )
184
  )
 
 
185
  return True
186
  except Exception as e:
187
  logger.error(f"Failed to insert mentorship: {e}")
@@ -189,8 +218,13 @@ class TursoDatabase:
189
 
190
  def insert_mentorship_request(self, request: Dict[str, Any]) -> bool:
191
  """Insert a mentorship request."""
 
192
  try:
193
- self.execute(
 
 
 
 
194
  """
195
  INSERT OR IGNORE INTO mentorship_requests
196
  (id, mentee_id, mentee_username, mentor_id, mentor_username, issue_id, message, status, created_at)
@@ -205,10 +239,11 @@ class TursoDatabase:
205
  request.get('issue_id'),
206
  request.get('message'),
207
  request.get('status', 'pending'),
208
- request.get('created_at') if isinstance(request.get('created_at'), str)
209
- else request.get('created_at').isoformat() if request.get('created_at') else None
210
  )
211
  )
 
 
212
  return True
213
  except Exception as e:
214
  logger.error(f"Failed to insert mentorship request: {e}")
 
1
  """
2
  Turso (libsql) Database Connection Utility.
3
 
4
+ Provides sync database operations for messages and mentorship data.
5
+ Uses libsql-experimental for reliable Turso connectivity.
6
  """
7
 
8
  import os
9
  import logging
10
  from typing import List, Dict, Any, Optional
11
+
12
+ # Try multiple libsql package names (different versions use different names)
13
+ libsql = None
14
+ LIBSQL_AVAILABLE = False
15
+ try:
16
+ import libsql_experimental as libsql
17
+ LIBSQL_AVAILABLE = True
18
+ except ImportError:
19
+ try:
20
+ import libsql_client as libsql
21
+ LIBSQL_AVAILABLE = True
22
+ except ImportError:
23
+ pass # Neither package available
24
 
25
  logger = logging.getLogger(__name__)
26
 
 
30
 
31
 
32
  class TursoDatabase:
33
+ """Turso database wrapper with sync operations."""
34
 
35
  def __init__(self):
36
+ self._conn = None
37
  self._initialized = False
38
 
39
+ def _get_connection(self):
40
+ """Get or create the libsql connection."""
41
+ if self._conn is None:
42
+ if libsql is None:
43
+ raise ImportError("libsql_experimental is not installed. Run: pip install libsql-experimental")
44
+
45
  if not TURSO_DATABASE_URL:
46
  raise ValueError("TURSO_DATABASE_URL environment variable is not set")
47
 
48
+ # libsql-experimental uses a local file with sync to remote
49
+ self._conn = libsql.connect(
50
+ "local_opentriage.db",
51
+ sync_url=TURSO_DATABASE_URL,
 
 
 
52
  auth_token=TURSO_AUTH_TOKEN if TURSO_AUTH_TOKEN else None
53
  )
54
+ # Sync with remote database
55
+ self._conn.sync()
56
+ logger.info("Connected to Turso database")
57
+ return self._conn
58
+
59
+ def sync(self):
60
+ """Sync local database with remote Turso."""
61
+ conn = self._get_connection()
62
+ conn.sync()
63
 
64
  def execute(self, sql: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]:
65
  """Execute a SQL query and return results."""
66
+ conn = self._get_connection()
67
  try:
68
  if params:
69
+ cursor = conn.execute(sql, params)
70
  else:
71
+ cursor = conn.execute(sql)
72
 
73
+ # For SELECT queries, return results as list of dicts
74
+ if cursor.description:
75
+ columns = [col[0] for col in cursor.description]
76
+ rows = cursor.fetchall()
77
+ return [dict(zip(columns, row)) for row in rows]
78
+
79
+ # For INSERT/UPDATE/DELETE, commit and return empty
80
+ conn.commit()
81
+ self.sync() # Sync changes to remote
82
  return []
83
  except Exception as e:
84
  logger.error(f"Turso execute error: {e}")
85
  raise
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  def init_tables(self):
88
  """Initialize database tables if they don't exist."""
89
  if self._initialized:
90
  return
91
 
92
+ conn = self._get_connection()
93
  try:
94
  # Messages table
95
+ conn.execute("""
96
  CREATE TABLE IF NOT EXISTS messages (
97
  id TEXT PRIMARY KEY,
98
  sender_id TEXT NOT NULL,
 
104
  """)
105
 
106
  # Mentorships table
107
+ conn.execute("""
108
  CREATE TABLE IF NOT EXISTS mentorships (
109
  id TEXT PRIMARY KEY,
110
  mentor_id TEXT NOT NULL,
 
118
  """)
119
 
120
  # Mentorship requests table
121
+ conn.execute("""
122
  CREATE TABLE IF NOT EXISTS mentorship_requests (
123
  id TEXT PRIMARY KEY,
124
  mentee_id TEXT NOT NULL,
 
132
  )
133
  """)
134
 
135
+ conn.commit()
136
+ self.sync() # Sync table creation to remote
137
+
138
  self._initialized = True
139
  logger.info("Turso tables initialized successfully")
140
  except Exception as e:
 
144
  # Message operations
145
  def insert_message(self, message: Dict[str, Any]) -> bool:
146
  """Insert a message into the messages table."""
147
+ conn = self._get_connection()
148
  try:
149
+ timestamp = message.get('timestamp')
150
+ if timestamp and not isinstance(timestamp, str):
151
+ timestamp = timestamp.isoformat()
152
+
153
+ conn.execute(
154
  """
155
  INSERT OR IGNORE INTO messages (id, sender_id, receiver_id, content, read, timestamp)
156
  VALUES (?, ?, ?, ?, ?, ?)
 
161
  message.get('receiver_id'),
162
  message.get('content'),
163
  1 if message.get('read') else 0,
164
+ timestamp
 
165
  )
166
  )
167
+ conn.commit()
168
+ self.sync()
169
  return True
170
  except Exception as e:
171
  logger.error(f"Failed to insert message: {e}")
 
186
  # Mentorship operations
187
  def insert_mentorship(self, mentorship: Dict[str, Any]) -> bool:
188
  """Insert a mentorship record."""
189
+ conn = self._get_connection()
190
  try:
191
+ created_at = mentorship.get('created_at')
192
+ if created_at and not isinstance(created_at, str):
193
+ created_at = created_at.isoformat()
194
+
195
+ conn.execute(
196
  """
197
  INSERT OR IGNORE INTO mentorships
198
  (id, mentor_id, mentor_username, mentee_id, mentee_username, status, created_at, disconnected_at)
 
205
  mentorship.get('mentee_id'),
206
  mentorship.get('mentee_username'),
207
  mentorship.get('status', 'active'),
208
+ created_at,
 
209
  mentorship.get('disconnected_at')
210
  )
211
  )
212
+ conn.commit()
213
+ self.sync()
214
  return True
215
  except Exception as e:
216
  logger.error(f"Failed to insert mentorship: {e}")
 
218
 
219
  def insert_mentorship_request(self, request: Dict[str, Any]) -> bool:
220
  """Insert a mentorship request."""
221
+ conn = self._get_connection()
222
  try:
223
+ created_at = request.get('created_at')
224
+ if created_at and not isinstance(created_at, str):
225
+ created_at = created_at.isoformat()
226
+
227
+ conn.execute(
228
  """
229
  INSERT OR IGNORE INTO mentorship_requests
230
  (id, mentee_id, mentee_username, mentor_id, mentor_username, issue_id, message, status, created_at)
 
239
  request.get('issue_id'),
240
  request.get('message'),
241
  request.get('status', 'pending'),
242
+ created_at
 
243
  )
244
  )
245
+ conn.commit()
246
+ self.sync()
247
  return True
248
  except Exception as e:
249
  logger.error(f"Failed to insert mentorship request: {e}")
requirements.txt CHANGED
@@ -44,5 +44,6 @@ asyncio-throttle>=1.0.2
44
  # JWT Authentication
45
  PyJWT>=2.8.0
46
 
47
- # Turso (libsql) Database
48
- libsql-experimental>=0.0.55
 
 
44
  # JWT Authentication
45
  PyJWT>=2.8.0
46
 
47
+ # Turso (libsql) Database - try both package names
48
+ libsql-experimental>=0.0.55
49
+ libsql_client