abstract-solcatcher-database 0.0.0.1__tar.gz
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.
- abstract_solcatcher_database-0.0.0.1/PKG-INFO +37 -0
- abstract_solcatcher_database-0.0.0.1/README.md +3 -0
- abstract_solcatcher_database-0.0.0.1/pyproject.toml +3 -0
- abstract_solcatcher_database-0.0.0.1/setup.cfg +4 -0
- abstract_solcatcher_database-0.0.0.1/setup.py +34 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/__init__.py +6 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/db_funcs.py +454 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/managers/__init__.py +1 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/managers/meta_data_manager.py +120 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/pair_updates.py +186 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/__init__.py +3 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/__init__.py +7 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_account_data.py +17 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_log_data.py +11 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_meta_data.py +25 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_misc.py +66 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_pair_data.py +48 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/call_transactions_data.py +25 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/call_functions/queries.py +3 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/filter_functions/__init__.py +1 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/filter_functions/filter_data_functions.py +62 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/queries.py +3 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/query_functions/__init__.py +2 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/query_functions/fetch_utils.py +105 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/query_functions/query_functions/queries.py +132 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/__init__.py +3 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/charts/__init__.py +1 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/charts/price_chart.py +131 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/pair_txns/__init__.py +2 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/pair_txns/pool_metrics.py +255 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/pair_txns/transaction_processor.py +428 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/user_txns/__init__.py +2 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/user_txns/tally_profits.py +35 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/transaction_utils/user_txns/transaction_history.py +115 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/utils/__init__.py +3 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/utils/get_time_data.py +182 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/utils/image_utils.py +120 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/utils/utils.py +103 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database.egg-info/PKG-INFO +37 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database.egg-info/SOURCES.txt +41 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database.egg-info/dependency_links.txt +1 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database.egg-info/requires.txt +5 -0
- abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: abstract_solcatcher_database
|
|
3
|
+
Version: 0.0.0.1
|
|
4
|
+
Home-page: https://github.com/AbstractEndeavors/abstract_solcatcher_database
|
|
5
|
+
Author: putkoff
|
|
6
|
+
Author-email: partners@abstractendeavors.com
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.6
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: psycopg2-binary
|
|
22
|
+
Requires-Dist: abstract_solcatcher
|
|
23
|
+
Requires-Dist: abstract_apis
|
|
24
|
+
Requires-Dist: abstract_security
|
|
25
|
+
Requires-Dist: abstract_utilities
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: requires-dist
|
|
33
|
+
Dynamic: requires-python
|
|
34
|
+
|
|
35
|
+
## Abstract Solana
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from time import time
|
|
2
|
+
import setuptools
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
setuptools.setup(
|
|
6
|
+
name='abstract_solcatcher_database',
|
|
7
|
+
version='0.0.0.1',
|
|
8
|
+
author='putkoff',
|
|
9
|
+
author_email='partners@abstractendeavors.com',
|
|
10
|
+
description="",
|
|
11
|
+
long_description=long_description,
|
|
12
|
+
long_description_content_type='text/markdown',
|
|
13
|
+
url='https://github.com/AbstractEndeavors/abstract_solcatcher_database',
|
|
14
|
+
classifiers=[
|
|
15
|
+
'Development Status :: 3 - Alpha',
|
|
16
|
+
'Intended Audience :: Developers',
|
|
17
|
+
'Topic :: Software Development :: Libraries',
|
|
18
|
+
'License :: OSI Approved :: MIT License',
|
|
19
|
+
'Programming Language :: Python :: 3',
|
|
20
|
+
'Programming Language :: Python :: 3.6',
|
|
21
|
+
'Programming Language :: Python :: 3.7',
|
|
22
|
+
'Programming Language :: Python :: 3.8',
|
|
23
|
+
'Programming Language :: Python :: 3.9',
|
|
24
|
+
'Programming Language :: Python :: 3.10',
|
|
25
|
+
'Programming Language :: Python :: 3.11',
|
|
26
|
+
'Programming Language :: Python :: 3.12',
|
|
27
|
+
],
|
|
28
|
+
install_requires=['psycopg2-binary','abstract_solcatcher','abstract_apis','abstract_security','abstract_utilities'],
|
|
29
|
+
package_dir={"": "src"},
|
|
30
|
+
packages=setuptools.find_packages(where="src"),
|
|
31
|
+
python_requires=">=3.6",
|
|
32
|
+
# Add this line to include wheel format in your distribution
|
|
33
|
+
setup_requires=['wheel'],
|
|
34
|
+
)
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
# Configure logging
|
|
4
|
+
logging.basicConfig(level=logging.INFO) # Set level to INFO or higher
|
|
5
|
+
from psycopg2 import sql, connect
|
|
6
|
+
from psycopg2.extras import DictCursor
|
|
7
|
+
from abstract_utilities import make_list, SingletonMeta, is_number
|
|
8
|
+
from abstract_security import get_env_value
|
|
9
|
+
import psycopg2,time
|
|
10
|
+
|
|
11
|
+
env_path = '/home/solcatcher/.env'
|
|
12
|
+
|
|
13
|
+
def get_env_val(key):
|
|
14
|
+
return get_env_value(key=key, path=env_path)
|
|
15
|
+
|
|
16
|
+
RABBITMQ_HOST = get_env_val("SOLCATCHER_AMQP_HOST") or 'solcatcher.io'
|
|
17
|
+
RABBITMQ_PORT = get_env_val("SOLCATCHER_AMQP_PORT") or '5672'
|
|
18
|
+
RABBITMQ_USER = get_env_val("SOLCATCHER_AMQP_USER") or 'solcatcher'
|
|
19
|
+
RABBITMQ_NAME = get_env_val("SOLCATCHER_AMQP_NAME") or 'solcatcher'
|
|
20
|
+
RABBITMQ_PASSWORD = get_env_val("SOLCATCHER_AMQP_PASSWORD") or 'solcatcher123'
|
|
21
|
+
|
|
22
|
+
POSTGRESQL_HOST = get_env_val("SOLCATCHER_POSTGRESQL_HOST") or 'solcatcher.io'
|
|
23
|
+
POSTGRESQL_PORT = get_env_val("SOLCATCHER_POSTGRESQL_PORT") or '5432'
|
|
24
|
+
POSTGRESQL_USER = get_env_val("SOLCATCHER_POSTGRESQL_USER") or 'solcatcher'
|
|
25
|
+
POSTGRESQL_NAME = get_env_val("SOLCATCHER_POSTGRESQL_NAME") or 'solcatcher'
|
|
26
|
+
POSTGRESQL_PASSWORD = get_env_val("SOLCATCHER_POSTGRESQL_PASSWORD") or 'solcatcher123!!!456'
|
|
27
|
+
|
|
28
|
+
DB_URL = f"postgresql://{POSTGRESQL_USER}:{POSTGRESQL_PASSWORD}@{POSTGRESQL_HOST}:{POSTGRESQL_PORT}/{POSTGRESQL_NAME}"
|
|
29
|
+
|
|
30
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
31
|
+
|
|
32
|
+
def get_connection():
|
|
33
|
+
"""Establish a database connection."""
|
|
34
|
+
return psycopg2.connect(
|
|
35
|
+
dbname=POSTGRESQL_NAME,
|
|
36
|
+
user=POSTGRESQL_USER,
|
|
37
|
+
password=POSTGRESQL_PASSWORD,
|
|
38
|
+
host=POSTGRESQL_HOST,
|
|
39
|
+
port=POSTGRESQL_PORT
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def get_all_table_names(schema='public'):
|
|
43
|
+
"""Fetch all table names from a specified schema."""
|
|
44
|
+
conn = get_connection()
|
|
45
|
+
query = """
|
|
46
|
+
SELECT table_name
|
|
47
|
+
FROM information_schema.tables
|
|
48
|
+
WHERE table_schema = %s AND table_type = 'BASE TABLE';
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
with conn.cursor() as cursor:
|
|
52
|
+
cursor.execute(query, (schema,))
|
|
53
|
+
table_names = [row[0] for row in cursor.fetchall()]
|
|
54
|
+
return table_names
|
|
55
|
+
except Exception as e:
|
|
56
|
+
print(f"An error occurred in get all tyables: {e}")
|
|
57
|
+
finally:
|
|
58
|
+
conn.close()
|
|
59
|
+
def get_time_interval(seconds =0,minutes=0, hours=0, days=0, weeks=0,months=0, years=0,*args,**kwargs):
|
|
60
|
+
"""Calculate a UNIX timestamp for the given time offset."""
|
|
61
|
+
secs = 1
|
|
62
|
+
mins = secs*60
|
|
63
|
+
hr = 60 * mins
|
|
64
|
+
day = 24 * hr
|
|
65
|
+
week = 7 * day
|
|
66
|
+
year = 365 * day
|
|
67
|
+
month = year/12
|
|
68
|
+
return ((secs * seconds) + (mins * minutes) + (hr * hours) + (day * days) + (week * weeks) + (month * months) + (year * years))
|
|
69
|
+
|
|
70
|
+
def get_time(time_interval,*args,**kwargs):
|
|
71
|
+
timeStamp = time.time() - time_interval
|
|
72
|
+
return int(timeStamp) # Return integer timestamp
|
|
73
|
+
|
|
74
|
+
def derive_timestamp(timestamp=None,seconds =0,minutes=0, hours=0, days=0, weeks=0,months=0, years=0,*args,**kwargs):
|
|
75
|
+
time_interval = get_time_interval(seconds=seconds,minutes=minutes, hours=hours, days=days, weeks=weeks,months=months, years=years)
|
|
76
|
+
if time_interval:
|
|
77
|
+
timestamp = get_time(time_interval)
|
|
78
|
+
return timestamp
|
|
79
|
+
def get_timestamp_from_data(data,*args,**kwargs):
|
|
80
|
+
years = data.get('years',0)
|
|
81
|
+
months = data.get('months',0)
|
|
82
|
+
weeks = data.get('weeks',0)
|
|
83
|
+
days = data.get('days',0)
|
|
84
|
+
hours = data.get('hours',0)
|
|
85
|
+
minutes = data.get('minutes',0)
|
|
86
|
+
seconds = data.get('seconds',0)
|
|
87
|
+
timestamp = data.get('timestamp')
|
|
88
|
+
time_interval = get_time_interval(seconds=seconds,minutes=minutes, hours=hours, days=days, weeks=weeks,months=months, years=years)
|
|
89
|
+
if time_interval:
|
|
90
|
+
timestamp = get_time(time_interval)
|
|
91
|
+
return timestamp
|
|
92
|
+
|
|
93
|
+
def get_query_result(query, conn):
|
|
94
|
+
"""Executes a query and returns the results or commits the transaction."""
|
|
95
|
+
with conn.cursor() as cursor:
|
|
96
|
+
cursor.execute(query)
|
|
97
|
+
if query.strip().lower().startswith("select"):
|
|
98
|
+
return cursor.fetchall() # Return data for SELECT queries
|
|
99
|
+
conn.commit()
|
|
100
|
+
|
|
101
|
+
def query_data(query, values=None, error="Error executing query:", zipRows=True):
|
|
102
|
+
"""Execute a query and handle transactions with error management."""
|
|
103
|
+
with get_connection() as conn:
|
|
104
|
+
# Choose the cursor type based on whether you want to zip rows with column names
|
|
105
|
+
cursor_factory = DictCursor if zipRows else None
|
|
106
|
+
with conn.cursor(cursor_factory=cursor_factory) as cursor:
|
|
107
|
+
try:
|
|
108
|
+
cursor.execute(query, values)
|
|
109
|
+
result = cursor.fetchall()
|
|
110
|
+
# Log the first row to see its structure
|
|
111
|
+
if result:
|
|
112
|
+
logging.info("First row data structure: %s", result[0])
|
|
113
|
+
return result
|
|
114
|
+
except Exception as e:
|
|
115
|
+
conn.rollback()
|
|
116
|
+
logging.error("%s %s\nValues: %s\n%s", error, query, values, e)
|
|
117
|
+
|
|
118
|
+
#####################################
|
|
119
|
+
# Fix #1: Correct the usage in fetch_any_combo
|
|
120
|
+
#####################################
|
|
121
|
+
def fetch_any_combo(columnNames='*',
|
|
122
|
+
tableName=None,
|
|
123
|
+
searchColumn=None,
|
|
124
|
+
searchValue=None,
|
|
125
|
+
anyValue=False,
|
|
126
|
+
zipit=True,
|
|
127
|
+
schema='public'):
|
|
128
|
+
"""
|
|
129
|
+
Fetch rows based on dynamic SQL built from parameters.
|
|
130
|
+
|
|
131
|
+
:param columnNames: Comma separated columns or '*' for all.
|
|
132
|
+
:param tableName: The table to query. Must not be None or '*'.
|
|
133
|
+
:param searchColumn: The column on which to filter.
|
|
134
|
+
:param searchValue: The value to match in searchColumn.
|
|
135
|
+
:param anyValue: If True, uses = ANY(%s) for arrays.
|
|
136
|
+
:param zipit: If True, uses DictCursor in query_data.
|
|
137
|
+
:param schema: The DB schema.
|
|
138
|
+
"""
|
|
139
|
+
if not tableName or tableName == '*':
|
|
140
|
+
logging.error("Invalid tableName provided to fetch_any_combo: %s", tableName)
|
|
141
|
+
return [] # or raise an Exception
|
|
142
|
+
|
|
143
|
+
# Build the SELECT list
|
|
144
|
+
if columnNames == '*':
|
|
145
|
+
select_cols = sql.SQL('*')
|
|
146
|
+
else:
|
|
147
|
+
# Convert "col1,col2" -> [col1, col2]
|
|
148
|
+
col_list = [c.strip() for c in columnNames.split(',')]
|
|
149
|
+
select_cols = sql.SQL(", ").join(sql.Identifier(col) for col in col_list)
|
|
150
|
+
|
|
151
|
+
# Build the base query: SELECT ... FROM schema.tableName
|
|
152
|
+
base_query = sql.SQL("SELECT {} FROM {}.{}").format(
|
|
153
|
+
select_cols,
|
|
154
|
+
sql.Identifier(schema),
|
|
155
|
+
sql.Identifier(tableName)
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Build the WHERE clause if needed
|
|
159
|
+
params = []
|
|
160
|
+
if searchColumn and searchValue is not None:
|
|
161
|
+
if anyValue:
|
|
162
|
+
base_query += sql.SQL(" WHERE {} = ANY(%s)").format(sql.Identifier(searchColumn))
|
|
163
|
+
params.append(searchValue if isinstance(searchValue, list) else [searchValue])
|
|
164
|
+
else:
|
|
165
|
+
base_query += sql.SQL(" WHERE {} = %s").format(sql.Identifier(searchColumn))
|
|
166
|
+
params.append(searchValue)
|
|
167
|
+
|
|
168
|
+
return query_data(base_query, values=params, zipRows=zipit)
|
|
169
|
+
|
|
170
|
+
def get_anything(*args, **kwargs):
|
|
171
|
+
if args:
|
|
172
|
+
for arg in args:
|
|
173
|
+
if 'tableName' not in kwargs:
|
|
174
|
+
kwargs['tableName'] = arg
|
|
175
|
+
response = fetch_any_combo(**kwargs)
|
|
176
|
+
logging.info("Received data: %s", response) # Log to see the data
|
|
177
|
+
if isinstance(response, list) and len(response) == 1:
|
|
178
|
+
response = response[0]
|
|
179
|
+
return response
|
|
180
|
+
|
|
181
|
+
def get_table_name_from_query(query):
|
|
182
|
+
"""Extract table name from SQL query."""
|
|
183
|
+
if isinstance(query, sql.Composed):
|
|
184
|
+
query = query.as_string(get_connection()) # Convert to string
|
|
185
|
+
parts = query.split()
|
|
186
|
+
if 'from' in parts:
|
|
187
|
+
return parts[parts.index('from') + 1]
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
def get_all_pair_info(**kwargs):
|
|
191
|
+
pair = get_pair(**kwargs)
|
|
192
|
+
if not pair:
|
|
193
|
+
return {}
|
|
194
|
+
pair = pair[0]
|
|
195
|
+
pair['meta_data'] = get_meta_data_from_meta_id(pair.get('meta_id'))
|
|
196
|
+
pair['txns'] = get_all_txns_for_pair_id(pair.get('id'))
|
|
197
|
+
return pair
|
|
198
|
+
|
|
199
|
+
class columnNamesManager(metaclass=SingletonMeta):
|
|
200
|
+
def __init__(self):
|
|
201
|
+
if not hasattr(self, 'initialized'):
|
|
202
|
+
self.initialized = True
|
|
203
|
+
self.columnNames = {}
|
|
204
|
+
|
|
205
|
+
def get_column_names(self, tableName, schema='public'):
|
|
206
|
+
if tableName not in self.columnNames:
|
|
207
|
+
self.columnNames[tableName] = self.fetch_column_names(tableName, schema)
|
|
208
|
+
return self.columnNames[tableName]
|
|
209
|
+
|
|
210
|
+
def fetch_column_names(self, tableName, schema='public'):
|
|
211
|
+
query = """
|
|
212
|
+
SELECT column_name
|
|
213
|
+
FROM information_schema.columns
|
|
214
|
+
WHERE table_name = %s AND table_schema = %s
|
|
215
|
+
ORDER BY ordinal_position;
|
|
216
|
+
"""
|
|
217
|
+
results = query_data(query, [tableName, schema], error='Error fetching column names')
|
|
218
|
+
if not results:
|
|
219
|
+
logging.warning(f"No columns found for table {tableName} in schema {schema}")
|
|
220
|
+
return []
|
|
221
|
+
return [row[0] for row in results]
|
|
222
|
+
|
|
223
|
+
def zip_rows(self, tableName, rows, schema='public'):
|
|
224
|
+
column_names = self.get_column_names(tableName, schema)
|
|
225
|
+
return [dict(zip(column_names, make_list(row))) for row in rows]
|
|
226
|
+
|
|
227
|
+
def get_pair_id(*args, **kwargs):
|
|
228
|
+
"""
|
|
229
|
+
Example function that tries to retrieve a pair's ID by various criteria.
|
|
230
|
+
There's a potential bug with 'is_mint(obj)' references, so watch out for that.
|
|
231
|
+
"""
|
|
232
|
+
id_val = None
|
|
233
|
+
for key,value in kwargs.items():
|
|
234
|
+
if key == 'pair_id':
|
|
235
|
+
id_val = value
|
|
236
|
+
if key == 'pair' and isinstance(value, dict):
|
|
237
|
+
id_val = value.get('id')
|
|
238
|
+
if key == 'mint':
|
|
239
|
+
rows = fetch_any_combo(tableName='transactions', searchColumn='mint', searchValue=value)
|
|
240
|
+
if rows:
|
|
241
|
+
id_val = rows[0].get('id')
|
|
242
|
+
if key == 'signature':
|
|
243
|
+
rows = fetch_any_combo(tableName='transactions', searchColumn='signature', searchValue=value)
|
|
244
|
+
if rows:
|
|
245
|
+
id_val = rows[0].get('id')
|
|
246
|
+
|
|
247
|
+
for arg in args:
|
|
248
|
+
if is_number(arg):
|
|
249
|
+
id_val = arg
|
|
250
|
+
elif isinstance(arg, dict):
|
|
251
|
+
id_val = arg.get('id')
|
|
252
|
+
# Potential bug fix #2: changed 'obj' -> 'arg'
|
|
253
|
+
# elif is_signature(arg):
|
|
254
|
+
# ...
|
|
255
|
+
# elif is_mint(arg):
|
|
256
|
+
# ...
|
|
257
|
+
# etc.
|
|
258
|
+
|
|
259
|
+
if id_val:
|
|
260
|
+
return id_val
|
|
261
|
+
def get_max_for_row(tableName):
|
|
262
|
+
return query_data(f"SELECT COUNT(*) FROM {tableName};")
|
|
263
|
+
#####################################
|
|
264
|
+
# Fix #2: Correctly use get_pair_from_* in get_pair
|
|
265
|
+
#####################################
|
|
266
|
+
def get_pair(mint=None, signature=None, pair_id=None):
|
|
267
|
+
"""Retrieve row(s) from 'pairs' table by mint, signature, or pair_id."""
|
|
268
|
+
if pair_id:
|
|
269
|
+
return get_pair_from_pair_id(pair_id)
|
|
270
|
+
elif mint:
|
|
271
|
+
return get_pair_from_mint(mint)
|
|
272
|
+
elif signature:
|
|
273
|
+
return get_pair_from_signature(signature)
|
|
274
|
+
return []
|
|
275
|
+
|
|
276
|
+
def get_pair_from_mint(mint):
|
|
277
|
+
response = fetch_any_combo(tableName='pairs', searchColumn="mint", searchValue=mint, zipit=True)
|
|
278
|
+
return getZipRows('pairs', response)
|
|
279
|
+
|
|
280
|
+
def get_pair_from_signature(signature):
|
|
281
|
+
response = fetch_any_combo(tableName='pairs', searchColumn="signature", searchValue=signature, zipit=True)
|
|
282
|
+
return getZipRows('pairs', response)
|
|
283
|
+
|
|
284
|
+
def get_pair_from_pair_id(pair_id):
|
|
285
|
+
response = fetch_any_combo(tableName='pairs', searchColumn="id", searchValue=pair_id, zipit=True)
|
|
286
|
+
return getZipRows('pairs', response)
|
|
287
|
+
|
|
288
|
+
def getZipRows(tableName, rows, schema='public'):
|
|
289
|
+
column_names = get_column_names(tableName, schema)
|
|
290
|
+
return [dict(zip(column_names, row)) for row in make_list(rows) if row]
|
|
291
|
+
|
|
292
|
+
def get_column_names(tableName, schema='public'):
|
|
293
|
+
return columnNamesManager().get_column_names(tableName, schema)
|
|
294
|
+
|
|
295
|
+
def get_transaction_from_txn_id(txn_id):
|
|
296
|
+
response = fetch_any_combo(tableName='transactions', searchColumn="id", searchValue=txn_id, zipit=True)
|
|
297
|
+
return getZipRows('transactions', response)
|
|
298
|
+
def get_transaction_from_signature(signature):
|
|
299
|
+
response = fetch_any_combo(tableName='transactions', searchColumn="signature", searchValue=signature, zipit=True)
|
|
300
|
+
return getZipRows('transactions', response)
|
|
301
|
+
|
|
302
|
+
def get_transaction_from_log_id(log_id):
|
|
303
|
+
"""
|
|
304
|
+
Example that first fetches signature from logdata, then fetches transactions by signature.
|
|
305
|
+
"""
|
|
306
|
+
signature_rows = fetch_any_combo(columnNames='signature',
|
|
307
|
+
tableName='logdata',
|
|
308
|
+
searchColumn='id',
|
|
309
|
+
searchValue=log_id,
|
|
310
|
+
zipit=True)
|
|
311
|
+
if signature_rows and len(signature_rows) == 1:
|
|
312
|
+
signature = signature_rows[0].get('signature')
|
|
313
|
+
tx_rows = fetch_any_combo(tableName='transactions',
|
|
314
|
+
searchColumn='signature',
|
|
315
|
+
searchValue=signature,
|
|
316
|
+
zipit=True)
|
|
317
|
+
return getZipRows('transactions', tx_rows)
|
|
318
|
+
return []
|
|
319
|
+
|
|
320
|
+
def get_all_txns_for_pair_id(pair_id):
|
|
321
|
+
response = fetch_any_combo(tableName='transactions', searchColumn='pair_id', searchValue=pair_id, zipit=True)
|
|
322
|
+
return getZipRows('transactions', response)
|
|
323
|
+
|
|
324
|
+
#####################################
|
|
325
|
+
# Fix #3: Add missing signature clause in get_transactions
|
|
326
|
+
#####################################
|
|
327
|
+
def get_transactions(txn_id=None, log_id=None, signature=None, pair_id=None):
|
|
328
|
+
if pair_id:
|
|
329
|
+
return get_all_txns_for_pair_id(pair_id)
|
|
330
|
+
if txn_id:
|
|
331
|
+
return get_transaction_from_txn_id(txn_id)
|
|
332
|
+
if log_id:
|
|
333
|
+
return get_transaction_from_log_id(log_id)
|
|
334
|
+
if signature:
|
|
335
|
+
return get_transaction_from_signature(signature)
|
|
336
|
+
return []
|
|
337
|
+
|
|
338
|
+
def get_meta_data_from_mint(mint):
|
|
339
|
+
response = fetch_any_combo(tableName='metadata', searchColumn='mint', searchValue=mint, zipit=True)
|
|
340
|
+
return getZipRows('metadata', response)
|
|
341
|
+
|
|
342
|
+
def get_meta_data_from_meta_id(meta_id):
|
|
343
|
+
response = fetch_any_combo(tableName='metadata', searchColumn='id', searchValue=meta_id, zipit=True)
|
|
344
|
+
return getZipRows('metadata', response)
|
|
345
|
+
|
|
346
|
+
def get_meta_data_from_pair_id(pair_id):
|
|
347
|
+
pair_rows = get_pair_from_pair_id(pair_id)
|
|
348
|
+
if not pair_rows:
|
|
349
|
+
return []
|
|
350
|
+
pair = pair_rows[0]
|
|
351
|
+
meta_id = pair.get('meta_id')
|
|
352
|
+
if not meta_id:
|
|
353
|
+
return []
|
|
354
|
+
return get_meta_data_from_meta_id(meta_id)
|
|
355
|
+
|
|
356
|
+
#####################################
|
|
357
|
+
# Fix #4: get_meta_data uses correct references
|
|
358
|
+
#####################################
|
|
359
|
+
def get_meta_data(pair_id=None, meta_id=None, mint=None):
|
|
360
|
+
if mint:
|
|
361
|
+
return get_meta_data_from_mint(mint)
|
|
362
|
+
if pair_id:
|
|
363
|
+
return get_meta_data_from_pair_id(pair_id)
|
|
364
|
+
if meta_id:
|
|
365
|
+
return get_meta_data_from_meta_id(meta_id)
|
|
366
|
+
return []
|
|
367
|
+
|
|
368
|
+
#####################################
|
|
369
|
+
# Fix #5: Ensure get_all_pair_data aligns with the fixed get_pair
|
|
370
|
+
#####################################
|
|
371
|
+
def get_all_pair_data(mint=None, signature=None, pair_id=None):
|
|
372
|
+
"""
|
|
373
|
+
Gathers pair info, associated transactions, and metadata.
|
|
374
|
+
"""
|
|
375
|
+
pair_rows = get_pair(mint=mint, signature=signature, pair_id=pair_id)
|
|
376
|
+
if not pair_rows:
|
|
377
|
+
return {}
|
|
378
|
+
pair = pair_rows[0]
|
|
379
|
+
pair_id_val = pair.get('id')
|
|
380
|
+
mint_val = pair.get('mint')
|
|
381
|
+
|
|
382
|
+
pair_data = {}
|
|
383
|
+
pair_data["pair"] = pair
|
|
384
|
+
pair_data["txns"] = get_transactions(pair_id=pair_id_val)
|
|
385
|
+
pair_data["metadata"] = get_meta_data(mint=mint_val)
|
|
386
|
+
return pair_data
|
|
387
|
+
|
|
388
|
+
def fetch_filtered_transactions_paginated(
|
|
389
|
+
sol_amount,
|
|
390
|
+
operator=">",
|
|
391
|
+
timestamp_operator=None,
|
|
392
|
+
limit=50,
|
|
393
|
+
offset=0,
|
|
394
|
+
years=0,
|
|
395
|
+
months=0,
|
|
396
|
+
weeks=0,
|
|
397
|
+
days=0,
|
|
398
|
+
hours=0,
|
|
399
|
+
minutes=0,
|
|
400
|
+
seconds=0,
|
|
401
|
+
timestamp=None,
|
|
402
|
+
*args,
|
|
403
|
+
**kwargs
|
|
404
|
+
):
|
|
405
|
+
# Build the base SQL query.
|
|
406
|
+
# Note that we're filtering on the first element of the JSON array in t.tcns
|
|
407
|
+
query = f"""
|
|
408
|
+
SELECT
|
|
409
|
+
p.*,
|
|
410
|
+
t.*,
|
|
411
|
+
m.*,
|
|
412
|
+
t2.id AS related_txn
|
|
413
|
+
FROM
|
|
414
|
+
transactions t
|
|
415
|
+
JOIN
|
|
416
|
+
pairs p ON t.pair_id = p.id
|
|
417
|
+
LEFT JOIN
|
|
418
|
+
metadata m ON p.meta_id = m.id
|
|
419
|
+
LEFT JOIN
|
|
420
|
+
transactions t2 ON t.pair_id = t2.pair_id AND t.signature != t2.signature
|
|
421
|
+
WHERE
|
|
422
|
+
t.signature IN (
|
|
423
|
+
SELECT signature FROM pairs WHERE signature IS NOT NULL
|
|
424
|
+
)
|
|
425
|
+
AND
|
|
426
|
+
t.program_id = p.program_id
|
|
427
|
+
AND
|
|
428
|
+
EXISTS (
|
|
429
|
+
SELECT 1
|
|
430
|
+
FROM jsonb_array_elements(t.tcns) AS elem
|
|
431
|
+
WHERE (elem ->> 'sol_amount')::numeric {operator} %s
|
|
432
|
+
"""
|
|
433
|
+
params = [sol_amount]
|
|
434
|
+
|
|
435
|
+
# Derive a timestamp cutoff if provided (assuming derive_timestamp is implemented)
|
|
436
|
+
ts = derive_timestamp(
|
|
437
|
+
years=years,
|
|
438
|
+
months=months,
|
|
439
|
+
weeks=weeks,
|
|
440
|
+
days=days,
|
|
441
|
+
hours=hours,
|
|
442
|
+
minutes=minutes,
|
|
443
|
+
seconds=seconds,
|
|
444
|
+
timestamp=timestamp)
|
|
445
|
+
|
|
446
|
+
if ts and timestamp_operator:
|
|
447
|
+
query += f" AND (elem ->> 'timestamp')::bigint {timestamp_operator} %s"
|
|
448
|
+
params.append(int(ts))
|
|
449
|
+
|
|
450
|
+
# Close the EXISTS clause and add ordering/pagination
|
|
451
|
+
query += ") ORDER BY t.updated_at DESC LIMIT %s OFFSET %s;"
|
|
452
|
+
params.extend([limit, offset])
|
|
453
|
+
|
|
454
|
+
return query_data(query, params)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .meta_data_manager import *
|
abstract_solcatcher_database-0.0.0.1/src/abstract_solcatcher_database/managers/meta_data_manager.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from abstract_utilities import get_any_value,SingletonMeta
|
|
2
|
+
import logging,os,json,requests
|
|
3
|
+
from abstract_pandas import pd
|
|
4
|
+
from abstract_apis import getRequest
|
|
5
|
+
from ..query_functions.call_functions.call_meta_data import get_meta_data
|
|
6
|
+
from ..utils.image_utils import *
|
|
7
|
+
|
|
8
|
+
metaDataLowerKeys = {
|
|
9
|
+
'image': 'image',
|
|
10
|
+
'uri': 'uri',
|
|
11
|
+
'supply': 'supply',
|
|
12
|
+
'symbol': 'symbol',
|
|
13
|
+
'mintauthority': 'mintAuthority',
|
|
14
|
+
'freezeauthority': 'freezeAuthority',
|
|
15
|
+
'twitter': 'twitter',
|
|
16
|
+
'website': 'website',
|
|
17
|
+
'ismutable': 'isMutable',
|
|
18
|
+
'creators': 'creators',
|
|
19
|
+
'updateauthority': 'updateAuthority',
|
|
20
|
+
'createdon': 'createdOn',
|
|
21
|
+
'primarysalehappened': 'createdOn',
|
|
22
|
+
'description': 'description'
|
|
23
|
+
}
|
|
24
|
+
metaDataTypeKeys = {
|
|
25
|
+
'image': str,
|
|
26
|
+
'uri': str,
|
|
27
|
+
'supply': int,
|
|
28
|
+
'name':str,
|
|
29
|
+
'symbol': str,
|
|
30
|
+
'mintAuthority': str,
|
|
31
|
+
'freezeAuthority': str,
|
|
32
|
+
'twitter': str,
|
|
33
|
+
'website': str,
|
|
34
|
+
'isMutable': bool,
|
|
35
|
+
'creators': str,
|
|
36
|
+
'updateAuthority': str,
|
|
37
|
+
'createdOn': int,
|
|
38
|
+
'primarySaleHappened': bool,
|
|
39
|
+
'description': str
|
|
40
|
+
}
|
|
41
|
+
metaDataCheckKeys = {
|
|
42
|
+
'image': True,
|
|
43
|
+
'uri': True,
|
|
44
|
+
'supply': True,
|
|
45
|
+
'symbol': True,
|
|
46
|
+
'name':True,
|
|
47
|
+
'mintAuthority': True,
|
|
48
|
+
'freezeAuthority': True,
|
|
49
|
+
'twitter': True,
|
|
50
|
+
'website': True,
|
|
51
|
+
'isMutable': True,
|
|
52
|
+
'creators': True,
|
|
53
|
+
'updateAuthority': True,
|
|
54
|
+
'createdOn': True,
|
|
55
|
+
'primarySaleHappened': True,
|
|
56
|
+
'description': True
|
|
57
|
+
}
|
|
58
|
+
metaKeys = list(metaDataTypeKeys.keys())
|
|
59
|
+
|
|
60
|
+
def make_insert(key):
|
|
61
|
+
key = key.replace(' ', '_').upper()
|
|
62
|
+
return f"-{key}-"
|
|
63
|
+
def make_insert_bool(key):
|
|
64
|
+
key = key.replace(' ', '_').upper()
|
|
65
|
+
return f"-{key}_BOOL-"
|
|
66
|
+
def make_check_bool(key):
|
|
67
|
+
key = key.replace(' ', '_').upper()
|
|
68
|
+
return f"-{key}_CHECK-"
|
|
69
|
+
def deKeyKey(key):
|
|
70
|
+
return key.lower()[1:-1].replace('_',' ')
|
|
71
|
+
def getBool(key, value):
|
|
72
|
+
return isinstance(value, metaDataTypeKeys.get(key))
|
|
73
|
+
class metaDataManager(metaclass=SingletonMeta):
|
|
74
|
+
def __init__(self):
|
|
75
|
+
self.allMetaData = {}
|
|
76
|
+
self.metaDataCheckKeys=metaDataCheckKeys
|
|
77
|
+
self.metaDataTypeKeys = metaDataTypeKeys
|
|
78
|
+
def changeTally(self,event,values):
|
|
79
|
+
lower_key = deKeyKey(event)
|
|
80
|
+
regularMetakey = metaDataLowerKeys.get(lower_key)
|
|
81
|
+
self.metaDataCheckKeys[regularMetakey] = values[event]
|
|
82
|
+
def processMetaData(self,mint,imageData=True):
|
|
83
|
+
if not self.allMetaData.get(mint):
|
|
84
|
+
metaData = get_meta_data(mint=mint)
|
|
85
|
+
self.get_meta_vars(metaData,imageData)
|
|
86
|
+
self.allMetaData[mint] = {"processed":self.datas_js,'bools':self.bool_js}
|
|
87
|
+
return self.allMetaData[mint]
|
|
88
|
+
def filter_meta_data(self,mint,filterMetaChecks={}):
|
|
89
|
+
metaData_js = self.processMetaData(mint)
|
|
90
|
+
bools = metaData_js['bools']
|
|
91
|
+
self.allMetaData[mint]['filtered']=True
|
|
92
|
+
for key,check in self.metaDataCheckKeys.items():
|
|
93
|
+
if check and not bools.get(key):
|
|
94
|
+
self.allMetaData[mint]['filtered']=False
|
|
95
|
+
break
|
|
96
|
+
return self.allMetaData[mint]['filtered']
|
|
97
|
+
def get_meta_vars(self,metaData,imageData=True):
|
|
98
|
+
self.datas_js = {}
|
|
99
|
+
self.bool_js = {}
|
|
100
|
+
self.metaData = self.get_uri(metaData,imageData)
|
|
101
|
+
for key in metaKeys:
|
|
102
|
+
self.get_from_any_meta(key,metaData)
|
|
103
|
+
def get_from_any_meta(self,key,metaData):
|
|
104
|
+
values = get_any_value(metaData, key) or None
|
|
105
|
+
value = values[0] if values and isinstance(values, list) else values
|
|
106
|
+
self.bool_js[key] =getBool(key, value)
|
|
107
|
+
self.datas_js[key] = value
|
|
108
|
+
return value
|
|
109
|
+
def get_uri(self,metaData,imageData=True):
|
|
110
|
+
key = 'uri'
|
|
111
|
+
self.get_from_any_meta(key,metaData)
|
|
112
|
+
|
|
113
|
+
if self.bool_js[key] and imageData:
|
|
114
|
+
try:
|
|
115
|
+
metaData[0].update(get_image_vars(self.datas_js[key]) or {})
|
|
116
|
+
except:
|
|
117
|
+
pass
|
|
118
|
+
return metaData
|
|
119
|
+
|
|
120
|
+
|