abstract-solcatcher-database 0.0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. abstract_solcatcher_database/__init__.py +6 -0
  2. abstract_solcatcher_database/dbUtils.py +327 -0
  3. abstract_solcatcher_database/db_funcs.py +454 -0
  4. abstract_solcatcher_database/managers/__init__.py +1 -0
  5. abstract_solcatcher_database/managers/meta_data_manager.py +120 -0
  6. abstract_solcatcher_database/pair_updates.py +186 -0
  7. abstract_solcatcher_database/python/__init__.py +2 -0
  8. abstract_solcatcher_database/python/db/__init__.py +2 -0
  9. abstract_solcatcher_database/python/db/dbConfig.py +51 -0
  10. abstract_solcatcher_database/python/db/dbQueries.py +21 -0
  11. abstract_solcatcher_database/python/utils/__init__.py +2 -0
  12. abstract_solcatcher_database/python/utils/pathUtils.py +15 -0
  13. abstract_solcatcher_database/python/utils/queryUtils.py +8 -0
  14. abstract_solcatcher_database/query_functions/__init__.py +3 -0
  15. abstract_solcatcher_database/query_functions/call_functions/__init__.py +7 -0
  16. abstract_solcatcher_database/query_functions/call_functions/call_account_data.py +17 -0
  17. abstract_solcatcher_database/query_functions/call_functions/call_log_data.py +11 -0
  18. abstract_solcatcher_database/query_functions/call_functions/call_meta_data.py +25 -0
  19. abstract_solcatcher_database/query_functions/call_functions/call_misc.py +66 -0
  20. abstract_solcatcher_database/query_functions/call_functions/call_pair_data.py +48 -0
  21. abstract_solcatcher_database/query_functions/call_functions/call_transactions_data.py +25 -0
  22. abstract_solcatcher_database/query_functions/call_functions/misc_calls.py +54 -0
  23. abstract_solcatcher_database/query_functions/call_functions/queries.py +3 -0
  24. abstract_solcatcher_database/query_functions/filter_functions/__init__.py +1 -0
  25. abstract_solcatcher_database/query_functions/filter_functions/filter_data_functions.py +62 -0
  26. abstract_solcatcher_database/query_functions/filter_functions/queries.py +3 -0
  27. abstract_solcatcher_database/query_functions/queries.py +3 -0
  28. abstract_solcatcher_database/query_functions/query_functions/__init__.py +2 -0
  29. abstract_solcatcher_database/query_functions/query_functions/fetch_utils.py +105 -0
  30. abstract_solcatcher_database/query_functions/query_functions/queries.py +132 -0
  31. abstract_solcatcher_database/query_functions/utils/__init__.py +2 -0
  32. abstract_solcatcher_database/query_functions/utils/get_time_data.py +34 -0
  33. abstract_solcatcher_database/query_functions/utils/utils.py +78 -0
  34. abstract_solcatcher_database/transaction_utils/__init__.py +3 -0
  35. abstract_solcatcher_database/transaction_utils/charts/__init__.py +1 -0
  36. abstract_solcatcher_database/transaction_utils/charts/price_chart.py +131 -0
  37. abstract_solcatcher_database/transaction_utils/pair_txns/__init__.py +2 -0
  38. abstract_solcatcher_database/transaction_utils/pair_txns/pool_metrics.py +255 -0
  39. abstract_solcatcher_database/transaction_utils/pair_txns/transaction_processor.py +428 -0
  40. abstract_solcatcher_database/transaction_utils/user_txns/__init__.py +2 -0
  41. abstract_solcatcher_database/transaction_utils/user_txns/tally_profits.py +35 -0
  42. abstract_solcatcher_database/transaction_utils/user_txns/tally_utils.py +53 -0
  43. abstract_solcatcher_database/transaction_utils/user_txns/transaction_history.py +115 -0
  44. abstract_solcatcher_database/utils/__init__.py +3 -0
  45. abstract_solcatcher_database/utils/get_time_data.py +182 -0
  46. abstract_solcatcher_database/utils/image_utils.py +120 -0
  47. abstract_solcatcher_database/utils/utils.py +103 -0
  48. abstract_solcatcher_database-0.0.0.1.dist-info/METADATA +37 -0
  49. abstract_solcatcher_database-0.0.0.1.dist-info/RECORD +51 -0
  50. abstract_solcatcher_database-0.0.0.1.dist-info/WHEEL +5 -0
  51. abstract_solcatcher_database-0.0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,6 @@
1
+ from .db_funcs import *
2
+ from .pair_updates import *
3
+ from .query_functions import *
4
+ from .utils import *
5
+ from .transaction_utils import *
6
+ from .managers import *
@@ -0,0 +1,327 @@
1
+ import psycopg2,logging,traceback
2
+ from psycopg2.extras import DictCursor
3
+ from psycopg2 import sql, pool
4
+ from abstract_utilities import make_list,SingletonMeta
5
+ from abstract_security import get_env_value
6
+ from abstract_utilities import get_logFile
7
+ import logging,requests,httpx
8
+ from logging.handlers import RotatingFileHandler
9
+ from .python import *
10
+ from typing import *
11
+ from psycopg2.extras import RealDictCursor
12
+ SOLCATCHER_POSTGRESQL_DBURL = get_env_value("SOLCATCHER_POSTGRESQL_DBURL")
13
+ from threading import Lock
14
+ from abstract_apis import get_headers
15
+ from .python.db.dbConfig import getConnection
16
+ # Load queries from JSON file
17
+ # Asynchronous function to call Solcatcher TS
18
+ async def async_call_solcatcher_ts(endpoint: str, **kwargs) -> Optional[Dict[str, Any]]:
19
+ async with httpx.AsyncClient() as client:
20
+ try:
21
+ response = await client.post(
22
+ url=f'https://solcatcher.io/ts/{endpoint}',
23
+ json=kwargs,
24
+ headers=get_headers()
25
+ )
26
+ response.raise_for_status()
27
+ response_data = response.json()
28
+ if isinstance(response_data, dict):
29
+ return response_data.get('result', response_data)
30
+ return response_data
31
+ except httpx.HTTPError as http_err:
32
+ logger.error(f"HTTP error occurred in async_call_solcatcher_ts: {http_err}")
33
+ return None
34
+ except Exception as err:
35
+ logger.error(f"An error occurred in async_call_solcatcher_ts: {err}")
36
+ return None
37
+ def call_solcatcher_ts(endpoint: str, **kwargs) -> Optional[Dict[str, Any]]:
38
+ try:
39
+ response = requests.post(
40
+ url=f'https://solcatcher.io/ts/{endpoint}',
41
+ json=kwargs,
42
+ headers=get_headers()
43
+ )
44
+ response.raise_for_status()
45
+ response_data = response.json()
46
+ if isinstance(response_data, dict):
47
+ return response_data.get('result', response_data)
48
+ return response_data
49
+ except httpx.HTTPError as http_err:
50
+ logger.error(f"HTTP error occurred in async_call_solcatcher_ts: {http_err}")
51
+ return None
52
+ except Exception as err:
53
+ logger.error(f"An error occurred in async_call_solcatcher_ts: {err}")
54
+ return None
55
+ # Asynchronous function to call Solcatcher DB
56
+ async def async_call_solcatcher_db(endpoint: str, **kwargs) -> Optional[Dict[str, Any]]:
57
+ async with httpx.AsyncClient() as client:
58
+ try:
59
+ response = await client.post(
60
+ url=f'https://solcatcher.io/dbCalls/{endpoint}',
61
+ json=kwargs,
62
+ headers=get_headers()
63
+ )
64
+ response.raise_for_status()
65
+ response_data = response.json()
66
+ if isinstance(response_data, dict):
67
+ return response_data.get('result', response_data)
68
+ return response_data
69
+ except httpx.HTTPError as http_err:
70
+ logger.error(f"HTTP error occurred in async_call_solcatcher_db: {http_err}")
71
+ return None
72
+ except Exception as err:
73
+ logger.error(f"An error occurred in async_call_solcatcher_db: {err}")
74
+ return None
75
+ # Asynchronous function to call Solcatcher DB
76
+ def call_solcatcher_db(endpoint: str, **kwargs) -> Optional[Dict[str, Any]]:
77
+
78
+ try:
79
+ response = requests.post(
80
+ url=f'https://solcatcher.io/dbCalls/{endpoint}',
81
+ json=kwargs,
82
+ headers=get_headers()
83
+ )
84
+ response.raise_for_status()
85
+ response_data = response.json()
86
+ if isinstance(response_data, dict):
87
+ return response_data.get('result', response_data)
88
+ return response_data
89
+ except httpx.HTTPError as http_err:
90
+ logger.error(f"HTTP error occurred in async_call_solcatcher_db: {http_err}")
91
+ return None
92
+ except Exception as err:
93
+ logger.error(f"An error occurred in async_call_solcatcher_db: {err}")
94
+ return None
95
+ logger=get_logFile('db')
96
+ def get_connection():
97
+ """Get a connection from the pool."""
98
+ try:
99
+ logger.debug("Acquiring connection...")
100
+ conn = getConnection()
101
+ logger.debug("Connection acquired.")
102
+ return conn
103
+ except Exception as e:
104
+ logger.error(f"Error getting a connection from the pool: {e}")
105
+ return None
106
+ def execute_query(query, values=None):
107
+ conn = get_connection()
108
+ try:
109
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
110
+ logger.debug(f"Executing query: {query} with values: {values}")
111
+ cursor.execute(query, values)
112
+ if query.strip().lower().startswith("select"):
113
+ results = cursor.fetchall() # Returns list of dictionaries
114
+ return results
115
+ else:
116
+ conn.commit()
117
+ return None
118
+ except Exception as e:
119
+ logger.error(f"Error executing query: {query} | Values: {values} | Error: {e}")
120
+ raise
121
+ finally:
122
+ release_connection(conn)
123
+ def release_connection(conn):
124
+ """Release a connection back to the pool."""
125
+ try:
126
+ if conn:
127
+ logger.debug("Releasing connection...")
128
+ conn.close() # Use conn.close() to release the connection
129
+ logger.debug("Connection released.")
130
+ except Exception as e:
131
+ logger.error(f"Error releasing the connection back to the pool: {e}")
132
+ class columnNamesManager(metaclass=SingletonMeta):
133
+ def __init__(self):
134
+ if not hasattr(self, 'initialized'):
135
+ self.initialized = True
136
+ self.columnNames = {}
137
+ self.lock = Lock()
138
+
139
+ def get_column_names(self, tableName, schema='public'):
140
+ with self.lock:
141
+ if tableName not in self.columnNames:
142
+ self.columnNames[tableName] = self.fetch_column_names(tableName, schema)
143
+ return self.columnNames[tableName]
144
+
145
+ def fetch_column_names(self, tableName, schema='public'):
146
+ """
147
+ Query column names for a table in a schema using information_schema.columns.
148
+ """
149
+ query = sql.SQL("""
150
+ SELECT column_name
151
+ FROM information_schema.columns
152
+ WHERE table_name = %s AND table_schema = %s
153
+ ORDER BY ordinal_position;
154
+ """)
155
+
156
+ results = execute_query(query, [tableName, schema])
157
+
158
+ if not results:
159
+ logger.warning(f"No columns found for table {tableName} in schema {schema}")
160
+
161
+ return [row['column_name'] for row in results]
162
+
163
+ def zip_rows(self, tableName, rows, schema='public'):
164
+ column_names = self.get_column_names(tableName, schema)
165
+ return [dict(zip(column_names, row)) for row in rows]
166
+
167
+
168
+
169
+
170
+ def run_query(query, values=()):
171
+ """
172
+ Run a SQL query and return the result.
173
+ """
174
+ conn = get_connection()
175
+ try:
176
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
177
+ logger.debug(f"Running query: {query} with values: {values}")
178
+ cursor.execute(query, values)
179
+ if query.strip().lower().startswith("select"):
180
+ return cursor.fetchall() # Return results as list of dictionaries
181
+ conn.commit() # Commit for non-SELECT queries
182
+ return None
183
+ except Exception as e:
184
+ logger.error(f"Error running query: {query} | Values: {values} | Error: {e}")
185
+ raise
186
+ finally:
187
+ release_connection(conn)
188
+
189
+ def aggregate_rows(query, values=None, errorMsg='Error Fetching Rows'):
190
+
191
+ try:
192
+ execute_query(query, values=values)
193
+ except Exception as e:
194
+ error_details = f"{errorMsg}:\n{str(e)}\nTraceback:\n{traceback.format_exc()}"
195
+
196
+
197
+
198
+
199
+ def fetch_any_combo(columnNames='*', tableName='*', searchColumn=None, searchValue=None, anyValue=False, notNull=False, zipit=True, schema='public'):
200
+ conn = get_connection()
201
+ try:
202
+ # Validate inputs
203
+ if anyValue:
204
+ if not isinstance(searchValue, (list, tuple)):
205
+ raise ValueError("When 'anyValue' is True, 'searchValue' must be a list or tuple.")
206
+ if not searchValue:
207
+ raise ValueError("'searchValue' list cannot be empty when 'anyValue' is True.")
208
+ else:
209
+ if isinstance(searchValue, (list, tuple)):
210
+ if len(searchValue) != 1:
211
+ raise ValueError("When 'anyValue' is False, 'searchValue' must be a single value.")
212
+ searchValue = searchValue[0] # Extract the single value
213
+
214
+ # Prepare base query
215
+ if columnNames != '*':
216
+ if not isinstance(columnNames, list):
217
+ raise ValueError("'columnNames' must be a list of column names or '*'")
218
+ columns = sql.SQL(", ").join([sql.Identifier(col) for col in columnNames])
219
+ else:
220
+ columns = sql.SQL('*')
221
+
222
+ query = sql.SQL("SELECT {} FROM {}.{}").format(
223
+ columns,
224
+ sql.Identifier(schema),
225
+ sql.Identifier(tableName),
226
+ )
227
+ params = []
228
+
229
+ # Handle search conditions
230
+ if searchColumn:
231
+ if searchValue is not None:
232
+ if anyValue: # Handle multiple values using IN
233
+ query += sql.SQL(" WHERE {} IN %s").format(sql.Identifier(searchColumn))
234
+ params.append(tuple(searchValue))
235
+ else: # Single value match
236
+ query += sql.SQL(" WHERE {} = %s").format(sql.Identifier(searchColumn))
237
+ params.append(searchValue)
238
+ elif notNull:
239
+ query += sql.SQL(" WHERE {} IS NOT NULL").format(sql.Identifier(searchColumn))
240
+
241
+ rows = execute_query(query, params)
242
+
243
+ # Zip results if required
244
+ if zipit:
245
+ rows = getZipRows(tableName, rows, schema)
246
+ return rows
247
+
248
+ except Exception as e:
249
+ logger.error(f"Error in fetch_any_combo: {e}", exc_info=True)
250
+ return []
251
+ finally:
252
+ release_connection(conn)
253
+
254
+
255
+
256
+ def real_query(query,params=[]):
257
+ conn = get_connection()
258
+ try:
259
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor: # Use RealDictCursor for named columns
260
+ cursor.execute(query, params)
261
+ results = cursor.fetchall() # Fetch all rows as dictionaries
262
+ return results # Already zipped by column names
263
+ except Exception as e:
264
+ logger.error(f"Error executing query: {e}", exc_info=True)
265
+ return []
266
+ finally:
267
+ release_connection(conn)
268
+
269
+ def fetch_filtered_transactions_paginated(
270
+ sol_amount,
271
+ operator="<",
272
+ timestamp=None,
273
+ timestamp_operator=None,
274
+ limit=50,
275
+ offset=0
276
+ ):
277
+ # SQL Query
278
+ query = f"""
279
+ SELECT
280
+ p.*,
281
+ t.*,
282
+ m.*,
283
+ t2.id AS related_txn
284
+ FROM
285
+ transactions t
286
+ JOIN
287
+ pairs p ON t.pair_id = p.id
288
+ LEFT JOIN
289
+ metadata m ON p.meta_id = m.id
290
+ LEFT JOIN
291
+ transactions t2 ON t.pair_id = t2.pair_id AND t.signature != t2.signature
292
+ WHERE
293
+ t.signature IN (
294
+ SELECT signature FROM pairs WHERE signature IS NOT NULL
295
+ )
296
+ AND
297
+ t.program_id = p.program_id
298
+ AND
299
+ EXISTS (
300
+ SELECT 1
301
+ FROM jsonb_array_elements(t.tcns) AS elem
302
+ WHERE (elem ->> 'sol_amount')::numeric {operator} %s
303
+ """
304
+
305
+ # Parameters for query execution
306
+ params = [sol_amount]
307
+
308
+ # Add timestamp filtering if provided
309
+ if timestamp and timestamp_operator:
310
+ query += f" AND (elem ->> 'timestamp')::bigint {timestamp_operator} %s"
311
+ params.append(int(timestamp))
312
+
313
+ # Add sorting and pagination
314
+ query += f") ORDER BY t.updated_at DESC LIMIT %s OFFSET %s;"
315
+ params.extend([limit, offset])
316
+
317
+ # Database connection and execution
318
+ response = execute_query(query, params)
319
+ return response
320
+
321
+ def get_column_names(tableName):
322
+ columns = columnNamesManager().get_column_names(tableName, schema='public')
323
+ return columns
324
+ def getZipRows(tableName, rows, schema='public'):
325
+ columnNames = get_column_names(tableName)
326
+ return [dict(zip(columnNames, row)) for row in rows]
327
+