sovai 0.2.44__tar.gz → 0.2.46__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.
Files changed (101) hide show
  1. {sovai-0.2.44 → sovai-0.2.46}/PKG-INFO +1 -1
  2. sovai-0.2.46/data/tickers.parq +0 -0
  3. {sovai-0.2.44 → sovai-0.2.46}/pyproject.toml +1 -1
  4. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_data.py +105 -5
  5. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side_s3_part_high.py +198 -70
  6. sovai-0.2.44/data/tickers.parq +0 -0
  7. {sovai-0.2.44 → sovai-0.2.46}/LICENSE +0 -0
  8. {sovai-0.2.44 → sovai-0.2.46}/README.md +0 -0
  9. {sovai-0.2.44 → sovai-0.2.46}/data/codes.parq +0 -0
  10. {sovai-0.2.44 → sovai-0.2.46}/sovai/.DS_Store +0 -0
  11. {sovai-0.2.44 → sovai-0.2.46}/sovai/__init__.py +0 -0
  12. {sovai-0.2.44 → sovai-0.2.46}/sovai/api_config.py +0 -0
  13. {sovai-0.2.44 → sovai-0.2.46}/sovai/assets/dropdown.css +0 -0
  14. {sovai-0.2.44 → sovai-0.2.46}/sovai/assets/features_mapping.parq +0 -0
  15. {sovai-0.2.44 → sovai-0.2.46}/sovai/basic_auth.py +0 -0
  16. {sovai-0.2.44 → sovai-0.2.46}/sovai/computations/__init__.py +0 -0
  17. {sovai-0.2.44 → sovai-0.2.46}/sovai/computations/functions.py +0 -0
  18. {sovai-0.2.44 → sovai-0.2.46}/sovai/errors/__init__.py +0 -0
  19. {sovai-0.2.44 → sovai-0.2.46}/sovai/errors/sovai_errors.py +0 -0
  20. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/__init__.py +0 -0
  21. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/anomalies.py +0 -0
  22. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/ask_df_llm.py +0 -0
  23. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/change_point_generator.py +0 -0
  24. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/clustering.py +0 -0
  25. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/core_kshape.py +0 -0
  26. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/cum_sum_torch.py +0 -0
  27. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/cusum.py +0 -0
  28. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/dimensionality_reduction.py +0 -0
  29. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_extraction.py +0 -0
  30. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_importance.py +0 -0
  31. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/feature_neutralizer.py +0 -0
  32. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/filter_df.py +0 -0
  33. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/fractional_differencing.py +0 -0
  34. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/nowcasting.py +0 -0
  35. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pairwise.py +0 -0
  36. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pandas_extensions.py +0 -0
  37. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/parallel_functions.py +0 -0
  38. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/pfa_feature_selector.py +0 -0
  39. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/regime_change.py +0 -0
  40. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/regime_change_pca.py +0 -0
  41. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/shapley_global_importance.py +0 -0
  42. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/shapley_importance.py +0 -0
  43. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/signal_evaluation.py +0 -0
  44. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/technical_indicators.py +0 -0
  45. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/time_decomposition.py +0 -0
  46. {sovai-0.2.44 → sovai-0.2.46}/sovai/extensions/weight_optimization.py +0 -0
  47. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_compute.py +0 -0
  48. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_data copy.py +0 -0
  49. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_plots.py +0 -0
  50. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_reports.py +0 -0
  51. {sovai-0.2.44 → sovai-0.2.46}/sovai/get_tools.py +0 -0
  52. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/__init__.py +0 -0
  53. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/accounting/__init__.py +0 -0
  54. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/accounting/accounting_plots.py +0 -0
  55. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/allocation/__init__.py +0 -0
  56. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/allocation/allocation_plots.py +0 -0
  57. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/bankruptcy/__init__.py +0 -0
  58. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/bankruptcy/bankruptcy_plots.py +0 -0
  59. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/breakout/__init__.py +0 -0
  60. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/breakout/breakout_plots.py +0 -0
  61. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/corp_risk/__init__.py +0 -0
  62. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/corp_risk/corp_risk_plots.py +0 -0
  63. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/earnings_surprise/__init__.py +0 -0
  64. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/earnings_surprise/earnings_surprise_plots.py +0 -0
  65. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/insider/__init__.py +0 -0
  66. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/insider/insider_plots.py +0 -0
  67. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/institutional/__init__.py +0 -0
  68. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/institutional/institutional_plots.py +0 -0
  69. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/news/__init__.py +0 -0
  70. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/news/news_plots.py +0 -0
  71. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/ratios/__init__.py +0 -0
  72. {sovai-0.2.44 → sovai-0.2.46}/sovai/plots/ratios/ratios_plots.py +0 -0
  73. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/accounting/__init__.py +0 -0
  74. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/accounting/accounting_balance_sheet.py +0 -0
  75. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/bankruptcy/__init__.py +0 -0
  76. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/bankruptcy/bankruptcy_monthly_top.py +0 -0
  77. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/general/__init__.py +0 -0
  78. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/general/general_plots.py +0 -0
  79. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/news/__inti__.py +0 -0
  80. {sovai-0.2.44 → sovai-0.2.46}/sovai/reports/news/news_econometric_analysis.py +0 -0
  81. {sovai-0.2.44 → sovai-0.2.46}/sovai/studies/nowcasting.py +0 -0
  82. {sovai-0.2.44 → sovai-0.2.46}/sovai/token_auth.py +0 -0
  83. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/authentication/__init__.py +0 -0
  84. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/authentication/authentication.py +0 -0
  85. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/__init__.py +0 -0
  86. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/graphs.py +0 -0
  87. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/llm_code_generator.py +0 -0
  88. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/sec_10_k_8_k_filings.py +0 -0
  89. {sovai-0.2.44 → sovai-0.2.46}/sovai/tools/sec/sec_edgar_search.py +0 -0
  90. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/__init__.py +0 -0
  91. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side.py +0 -0
  92. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/client_side_s3.py +0 -0
  93. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/converter.py +0 -0
  94. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/datetime_formats.py +0 -0
  95. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/file_management.py +0 -0
  96. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/get_tickers.py +0 -0
  97. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/helpers.py +0 -0
  98. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/pandas_extensions copy.py +0 -0
  99. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/plot.py +0 -0
  100. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/port_manager.py +0 -0
  101. {sovai-0.2.44 → sovai-0.2.46}/sovai/utils/stream.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sovai
3
- Version: 0.2.44
3
+ Version: 0.2.46
4
4
  Summary: python for asset management
5
5
  License: Proprietary License
6
6
  Author: Derek Snow
Binary file
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sovai"
3
- version = "0.2.44"
3
+ version = "0.2.46"
4
4
  description = "python for asset management"
5
5
  authors = ["Derek Snow <d.snow@outlook.com>"]
6
6
  license = "Proprietary License"
@@ -308,7 +308,9 @@ ENDPOINT_TO_TICKER = {
308
308
  "/news/pronouns": "",
309
309
  "/news/word_count": "",
310
310
  "/news/sentiment_score": "", # was None before?
311
+
311
312
  "/insider/trading": "",
313
+
312
314
  "/wikipedia/views": "",
313
315
  "/accounting/weekly": "",
314
316
  "/visas/h1b": "",
@@ -346,6 +348,8 @@ ENDPOINT_TO_TICKER = {
346
348
 
347
349
  "/clinical/trials": "",
348
350
 
351
+
352
+
349
353
  "/spending/awards": "",
350
354
  "/spending/compensation": "",
351
355
  "/spending/competition": "",
@@ -394,11 +398,13 @@ CLIENT_SIDE_ENDPOINTS_S3_PART_HIGH = {
394
398
  "spending/product",
395
399
 
396
400
  "spending/transactions",
397
-
401
+
398
402
  "lobbying",
399
403
 
400
404
  "accounting/weekly",
401
405
 
406
+ "insider/trading",
407
+
402
408
  "ratios/normal",
403
409
  "ratios/relative",
404
410
 
@@ -414,9 +420,39 @@ CLIENT_SIDE_ENDPOINTS_S3_PART_HIGH = {
414
420
 
415
421
  "breakout",
416
422
 
417
- "corprisk/risks",
423
+ "corprisk/risks",
424
+ "corprisk/accounting",
425
+ "corprisk/events",
426
+ "corprisk/misstatements",
427
+
428
+ "visas/h1b",
429
+
430
+ "wikipedia/views",
431
+
432
+ "short/volume",
433
+ "short/maker",
434
+ "short/over_shorted",
435
+
436
+ "institutional/trading"
437
+
438
+ }
439
+
440
+
418
441
 
419
- "visas/h1b"
442
+ ENDPOINT_ALIASES = {
443
+ "clinical/predict": {
444
+ "target_endpoint": "clinical/trials",
445
+ "columns": [
446
+ 'ticker', 'date', 'source', 'subsidiary', 'sponsor',
447
+ 'trial_id', 'official_title', 'success_prediction',
448
+ 'economic_effect', 'duration_prediction', 'success_composite'
449
+ ]
450
+ }
451
+ # --- Add other aliases here in the future ---
452
+ # "some/alias": {
453
+ # "target_endpoint": "real/endpoint",
454
+ # "columns": ["col1", "col2", "date", "ticker"] # Ensure essential columns are included
455
+ # }
420
456
  }
421
457
 
422
458
 
@@ -1092,6 +1128,38 @@ def data(
1092
1128
  params = params or {}
1093
1129
  params = map_synonyms(params)
1094
1130
 
1131
+ # --- MODIFIED SECTION: Alias Handling ---
1132
+ original_endpoint_request = endpoint # Keep track for logging
1133
+ forced_columns = None # Will store columns if an alias forces them
1134
+ normalized_req_endpoint = normalize_endpoint(endpoint)
1135
+
1136
+ if normalized_req_endpoint in ENDPOINT_ALIASES:
1137
+ alias_config = ENDPOINT_ALIASES[normalized_req_endpoint]
1138
+ target_endpoint = alias_config['target_endpoint']
1139
+ forced_columns_list = alias_config['columns']
1140
+ verbose_mode.log(f"Endpoint alias detected: '{original_endpoint_request}' maps to '{target_endpoint}' with predefined columns.")
1141
+
1142
+ endpoint = target_endpoint # *Overwrite* endpoint for the rest of the function
1143
+ columns = forced_columns_list # *Overwrite* columns parameter
1144
+
1145
+ # Ensure the 'columns' value in the main 'params' dict is also updated
1146
+ # This is crucial for cache key generation and potentially for client-side handlers
1147
+ # Convert list to string if needed for _prepare_params consistency
1148
+ params['columns'] = ",".join(forced_columns_list)
1149
+
1150
+ else:
1151
+ # If not an alias, handle the columns parameter normally
1152
+ # Convert list to string if provided as list initially
1153
+ if isinstance(columns, list):
1154
+ columns_str = ",".join(columns)
1155
+ else:
1156
+ columns_str = columns # It's already a string or None
1157
+ # Update params dict if columns were passed directly to the function
1158
+ if columns is not None:
1159
+ params['columns'] = columns_str
1160
+ # --- END MODIFIED SECTION ---
1161
+
1162
+
1095
1163
  # Force full_history if date range is specified
1096
1164
  if start_date is not None or end_date is not None:
1097
1165
  full_history = True
@@ -1099,6 +1167,14 @@ def data(
1099
1167
 
1100
1168
  # Update params with prepared values
1101
1169
  try:
1170
+
1171
+ # Make sure to use the potentially updated 'columns' value from params dict
1172
+ # if it exists, otherwise use the function argument (which might be None or overridden)
1173
+ current_columns = params.get("columns", columns)
1174
+ # Convert back to string if it was forced as a list earlier
1175
+ if isinstance(current_columns, list):
1176
+ current_columns = ",".join(current_columns)
1177
+
1102
1178
  params.update(
1103
1179
  _prepare_params(
1104
1180
  tickers=params.get("tickers", tickers),
@@ -1147,6 +1223,10 @@ def data(
1147
1223
 
1148
1224
  # Try client-side handlers for specific endpoints
1149
1225
  handler_result = None
1226
+
1227
+ # Get the columns value to pass to handlers (use the string version from params)
1228
+ handler_columns_arg = params.get('columns')
1229
+
1150
1230
  for endpoint_set, handler_func, message in ENDPOINT_HANDLERS:
1151
1231
  if (
1152
1232
  normalized_endpoint in endpoint_set and
@@ -1157,7 +1237,7 @@ def data(
1157
1237
  verbose_mode.log(f"Calling client-side handler with tickers={tickers}, start_date={start_date}, end_date={end_date}")
1158
1238
  try:
1159
1239
  handler_result = handler_func(
1160
- normalized_endpoint, tickers, columns, start_date, end_date
1240
+ normalized_endpoint, tickers, handler_columns_arg, start_date, end_date
1161
1241
  )
1162
1242
  _query_cache[cache_key] = handler_result
1163
1243
  result_data = handler_result
@@ -1238,11 +1318,15 @@ def data(
1238
1318
  logger.error(error_msg)
1239
1319
  raise Exception(error_msg)
1240
1320
 
1321
+ filter_columns_arg = columns
1322
+ if isinstance(columns, str): # filter_data might prefer list
1323
+ filter_columns_arg = [c.strip() for c in columns.split(',')] if columns else None
1324
+
1241
1325
  # Apply filters
1242
1326
  verbose_mode.log("Applying filters to data")
1243
1327
  data_result = filter_data(
1244
1328
  data_result,
1245
- columns=columns,
1329
+ columns=filter_columns_arg,
1246
1330
  start_date=start_date,
1247
1331
  end_date=end_date,
1248
1332
  use_polars=use_polars
@@ -1328,6 +1412,22 @@ def data(
1328
1412
 
1329
1413
  result_data = process_dataframe(result_data)
1330
1414
 
1415
+
1416
+ # --- Final Check: Ensure alias columns are applied if data wasn't filtered before ---
1417
+ # This is a safety net in case a data path (e.g., direct API return without 'all tickers')
1418
+ # didn't explicitly filter columns based on the overridden parameter.
1419
+ if forced_columns is not None and result_data is not None and isinstance(result_data, (pd.DataFrame, pl.DataFrame)):
1420
+ # Check if columns need filtering (some might be missing if already filtered)
1421
+ current_cols = list(result_data.columns)
1422
+ cols_to_keep = [col for col in forced_columns_list if col in current_cols]
1423
+ if set(cols_to_keep) != set(current_cols):
1424
+ verbose_mode.log(f"Applying final column filter for alias: {cols_to_keep}")
1425
+ if isinstance(result_data, pd.DataFrame):
1426
+ result_data = result_data[cols_to_keep]
1427
+ elif isinstance(result_data, pl.DataFrame):
1428
+ result_data = result_data.select(cols_to_keep)
1429
+
1430
+
1331
1431
  return result_data
1332
1432
 
1333
1433
 
@@ -32,16 +32,23 @@ from sovai.tools.authentication import authentication
32
32
  # Logging Configuration
33
33
  # =========================================================================
34
34
 
35
+ # Near top of load_frame_s3_partitioned_high.py
35
36
  logger = logging.getLogger(__name__)
36
37
  if not logger.handlers:
37
- logging.basicConfig(
38
- level=logging.INFO,
39
- format='%(asctime)s - %(levelname)s - %(message)s',
40
- handlers=[
41
- logging.FileHandler("data_operations.log"),
42
- logging.StreamHandler()
43
- ]
44
- )
38
+ log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - [%(funcName)s] - %(message)s') # Added function name
39
+
40
+ # File Handler
41
+ file_handler = logging.FileHandler("data_operations.log")
42
+ file_handler.setFormatter(log_formatter)
43
+
44
+ # Console Handler (add this)
45
+ console_handler = logging.StreamHandler()
46
+ console_handler.setFormatter(log_formatter)
47
+
48
+ logger.addHandler(file_handler)
49
+ logger.addHandler(console_handler) # Add console output
50
+ logger.setLevel(logging.INFO) # Set level (INFO or DEBUG)
51
+ # To see DEBUG level messages, set logger.setLevel(logging.DEBUG)
45
52
 
46
53
  # =========================================================================
47
54
  # Filesystem Access
@@ -295,10 +302,13 @@ class PartitionFinder:
295
302
  # =========================================================================
296
303
  # Data Loading
297
304
  # =========================================================================
305
+ # =========================================================================
306
+ # Data Loading
307
+ # =========================================================================
298
308
 
299
309
  class DataLoader:
300
310
  """Core data loading functionality with filtering and parallelism."""
301
-
311
+
302
312
  @staticmethod
303
313
  def load_partition(
304
314
  path: str,
@@ -308,77 +318,127 @@ class DataLoader:
308
318
  end_date: Optional[str] = None
309
319
  ) -> pd.DataFrame:
310
320
  """
311
- Load and filter data from a single partition.
312
-
321
+ Load and filter data from a single partition. Added logging for debugging column selection.
322
+
313
323
  Args:
314
324
  path: S3 path to the partition
315
325
  ticker_filter: Optional list of tickers to filter by
316
326
  columns: Optional list of columns to load
317
327
  start_date: Optional start date for filtering
318
328
  end_date: Optional end date for filtering
319
-
329
+
320
330
  Returns:
321
331
  Filtered pandas DataFrame
322
332
  """
323
333
  fs = get_s3_filesystem()
324
-
334
+ logger.info(f"--- Loading Partition Start: {path} ---")
335
+ logger.debug(f"Parameters received: ticker_filter={ticker_filter}, columns={columns}, start_date={start_date}, end_date={end_date}")
336
+
325
337
  try:
326
338
  # Create dataset
327
- dataset = ds.dataset(path, filesystem=fs, format='parquet')
328
-
339
+ logger.debug(f"[{path}] Creating dataset object...")
340
+ dataset = ds.dataset(path, filesystem=fs, format='parquet', partitioning="hive") # Assuming Hive partitioning
341
+ logger.info(f"[{path}] Initial dataset schema: {dataset.schema}")
342
+
329
343
  # Build filter expressions
330
344
  filters = DataLoader._build_date_filters(start_date, end_date)
331
-
332
- # Apply filters to dataset
345
+ logger.debug(f"[{path}] Built date filters: {filters}")
346
+
347
+ # Apply filters to dataset before reading columns
333
348
  if filters:
334
349
  filter_expr = filters[0]
335
350
  for f in filters[1:]:
336
351
  filter_expr = filter_expr & f
337
- dataset = dataset.filter(filter_expr)
338
-
339
- # Load data
340
- table = dataset.to_table(columns=columns, use_threads=True)
352
+ try:
353
+ logger.info(f"[{path}] Applying filter expression: {filter_expr}")
354
+ dataset = dataset.filter(filter_expr)
355
+ # Note: Checking row count here might be slow as it forces some reading
356
+ # fragment_count = len(list(dataset.get_fragments()))
357
+ # logger.info(f"[{path}] Filter applied. Found {fragment_count} fragments. Schema after filtering: {dataset.schema}")
358
+ logger.info(f"[{path}] Filter applied. Schema after filtering: {dataset.schema}")
359
+ except Exception as filter_e:
360
+ logger.error(f"[{path}] Error applying filters: {filter_e}", exc_info=True)
361
+ logger.info(f"--- Loading Partition Failed (Filter Error): {path} ---")
362
+ return pd.DataFrame()
363
+
364
+ # Load data using to_table
365
+ logger.info(f"[{path}] Calling dataset.to_table() with columns: {columns}")
366
+ try:
367
+ # Explicitly handle None columns case for logging clarity
368
+ cols_to_load = columns if columns else "ALL"
369
+ table = dataset.to_table(columns=columns, use_threads=True) # Pass None or list
370
+ logger.info(f"[{path}] dataset.to_table(columns={cols_to_load}) successful.")
371
+ logger.info(f"[{path}] Resulting Arrow Table shape: {table.shape}")
372
+ logger.debug(f"[{path}] Resulting Arrow Table columns: {table.column_names}")
373
+
374
+ # Check if table is empty AFTER the potentially selective read
375
+ if table.num_rows == 0:
376
+ logger.warning(f"[{path}] Arrow Table is empty after to_table(columns={cols_to_load}). "
377
+ f"This might be expected if date filter removed all rows, "
378
+ f"or problematic if columns were requested but not found/read.")
379
+ # Proceed to convert empty table to empty DataFrame
380
+
381
+ except Exception as to_table_e:
382
+ logger.error(f"[{path}] Error during dataset.to_table(columns={columns}): {to_table_e}", exc_info=True)
383
+ logger.error(f"[{path}] Dataset schema at time of error: {dataset.schema}")
384
+ logger.info(f"--- Loading Partition Failed (to_table Error): {path} ---")
385
+ return pd.DataFrame()
386
+
387
+ # Convert to pandas
388
+ logger.debug(f"[{path}] Converting Arrow Table to pandas DataFrame...")
341
389
  df = table.to_pandas()
342
-
343
- # Apply ticker filter if needed
390
+ logger.info(f"[{path}] Converted to pandas DataFrame. Shape: {df.shape}")
391
+ if df.empty and table.num_rows > 0:
392
+ logger.warning(f"[{path}] Conversion to pandas resulted in empty DataFrame, but Arrow table had {table.num_rows} rows.")
393
+ logger.debug(f"[{path}] Pandas DataFrame columns: {list(df.columns)}")
394
+
395
+ # Apply ticker filter if needed (post-load)
344
396
  if ticker_filter and 'ticker' in df.columns:
397
+ logger.info(f"[{path}] Applying post-load ticker filter: {ticker_filter}")
398
+ initial_rows = len(df)
345
399
  df = df[df['ticker'].isin(ticker_filter)]
346
-
400
+ logger.info(f"[{path}] Ticker filter applied. Rows reduced from {initial_rows} to {len(df)}")
401
+ elif ticker_filter:
402
+ logger.warning(f"[{path}] Ticker filter {ticker_filter} requested but 'ticker' column not found in loaded columns: {list(df.columns)}.")
403
+
404
+ logger.info(f"--- Loading Partition End: {path} --- Returning DataFrame shape: {df.shape}")
347
405
  return df
406
+
348
407
  except Exception as e:
349
- logger.error(f"Error loading data from {path}: {e}")
408
+ # Catch-all for other errors (e.g., ds.dataset creation failed)
409
+ logger.error(f"Unhandled error processing partition {path}: {e}", exc_info=True)
410
+ logger.info(f"--- Loading Partition Failed (Unhandled Error): {path} ---")
350
411
  return pd.DataFrame()
351
-
412
+
413
+
352
414
  @staticmethod
353
415
  def _build_date_filters(
354
- start_date: Optional[str],
416
+ start_date: Optional[str],
355
417
  end_date: Optional[str]
356
418
  ) -> List[ds.Expression]:
357
419
  """
358
- Build PyArrow date filter expressions.
359
-
360
- Args:
361
- start_date: Optional start date string
362
- end_date: Optional end date string
363
-
364
- Returns:
365
- List of PyArrow filter expressions
420
+ Build PyArrow date filter expressions. (Ensure 'date' column exists)
366
421
  """
367
422
  filters = []
368
-
369
- # Date filters
370
- if start_date:
371
- start_date_obj = pd.to_datetime(start_date).date()
372
- start_date_pa = pa.scalar(start_date_obj, type=pa.date32())
373
- filters.append(ds.field('date') >= start_date_pa)
374
-
375
- if end_date:
376
- end_date_obj = pd.to_datetime(end_date).date()
377
- end_date_pa = pa.scalar(end_date_obj, type=pa.date32())
378
- filters.append(ds.field('date') <= end_date_pa)
379
-
423
+ try:
424
+ # Date filters - Assuming 'date' column exists and is date32 or similar
425
+ if start_date:
426
+ start_date_obj = pd.to_datetime(start_date).date()
427
+ # Use pa.date32() explicitly assuming the Parquet schema uses it
428
+ start_date_pa = pa.scalar(start_date_obj, type=pa.date32())
429
+ filters.append(ds.field('date') >= start_date_pa)
430
+
431
+ if end_date:
432
+ end_date_obj = pd.to_datetime(end_date).date()
433
+ end_date_pa = pa.scalar(end_date_obj, type=pa.date32())
434
+ filters.append(ds.field('date') <= end_date_pa)
435
+ except Exception as e:
436
+ logger.error(f"Failed to build date filters (start={start_date}, end={end_date}): {e}", exc_info=True)
437
+ # Decide if you want to raise or return empty filters
438
+ # return [] # Return empty filters if parsing fails?
439
+
380
440
  return filters
381
-
441
+
382
442
  @staticmethod
383
443
  def load_data_parallel(
384
444
  tasks: List[Tuple],
@@ -386,34 +446,42 @@ class DataLoader:
386
446
  ) -> List[pd.DataFrame]:
387
447
  """
388
448
  Process loading tasks in parallel with progress tracking.
389
-
390
- Args:
391
- tasks: List of (path, ticker_filter, columns, start_date, end_date) tuples
392
- max_workers: Maximum number of concurrent loading threads
393
-
394
- Returns:
395
- List of loaded DataFrames
396
449
  """
397
450
  results = []
398
-
451
+ if not tasks:
452
+ logger.warning("load_data_parallel received no tasks.")
453
+ return results
454
+
455
+ logger.info(f"Starting parallel load of {len(tasks)} tasks with max_workers={max_workers}")
456
+
399
457
  # Process tasks in parallel
400
458
  with ThreadPoolExecutor(max_workers=max_workers) as executor:
401
- future_to_task = {
402
- executor.submit(DataLoader.load_partition, path, ticker_filter, columns, start_date, end_date): path
403
- for path, ticker_filter, columns, start_date, end_date in tasks
459
+ # Prepare arguments properly for submit
460
+ future_to_path = {
461
+ executor.submit(DataLoader.load_partition, path, ticker_filter, task_columns, task_start_date, task_end_date): path
462
+ for path, ticker_filter, task_columns, task_start_date, task_end_date in tasks # Unpack task tuple
404
463
  }
405
-
406
- for future in tqdm(as_completed(future_to_task), total=len(tasks), desc="Loading data"):
464
+
465
+ # Use tqdm for progress bar
466
+ for future in tqdm(as_completed(future_to_path), total=len(tasks), desc="Loading partitions", unit="partition"):
467
+ path = future_to_path[future]
407
468
  try:
408
- df = future.result()
409
- if not df.empty:
410
- results.append(df)
469
+ df_partition = future.result() # Get result from load_partition
470
+ if df_partition is not None and not df_partition.empty:
471
+ logger.debug(f"Successfully loaded non-empty DataFrame from {path}, shape: {df_partition.shape}")
472
+ results.append(df_partition)
473
+ elif df_partition is not None:
474
+ logger.info(f"Loaded empty DataFrame from {path}")
475
+ else:
476
+ # Should not happen if load_partition always returns a DataFrame
477
+ logger.warning(f"Load task for {path} returned None.")
478
+
411
479
  except Exception as e:
412
- path = future_to_task[future]
413
- logger.error(f"Task failed for {path}: {e}")
414
-
415
- return results
480
+ # This catches errors raised *after* submit, e.g., during future.result() processing if not caught inside load_partition
481
+ logger.error(f"Task failed for partition {path} during result retrieval: {e}", exc_info=True)
416
482
 
483
+ logger.info(f"Parallel loading finished. Collected results from {len(results)} non-empty partitions.")
484
+ return results
417
485
  # =========================================================================
418
486
  # Main Loading Interface
419
487
  # =========================================================================
@@ -692,7 +760,62 @@ ENDPOINT_CONFIG = {
692
760
  "ticker_path": "sovai/sovai-employment/partitioned/ticker",
693
761
  "date_path": "sovai/sovai-employment/partitioned/date",
694
762
  "has_year": False
695
- }
763
+ },
764
+
765
+ "wikipedia/views": {
766
+ "ticker_path": "sovai/sovai-wiki/views/partitioned/ticker",
767
+ "date_path": "sovai/sovai-wiki/views/partitioned/date",
768
+ "has_year": False
769
+ },
770
+
771
+ "insider/trading": {
772
+ "ticker_path": "sovai/sovai-insider/partitioned/ticker",
773
+ "date_path": "sovai/sovai-insider/partitioned/date",
774
+ "has_year": False
775
+ },
776
+
777
+ "corprisk/risks": {
778
+ "ticker_path": "sovai/sovai-flags/risks/partitioned/ticker",
779
+ "date_path": "sovai/sovai-flags/risks/partitioned/date",
780
+ "has_year": False
781
+ },
782
+ "corprisk/accounting": {
783
+ "ticker_path": "sovai/sovai-flags/accounting/partitioned/ticker",
784
+ "date_path": "sovai/sovai-flags/accounting/partitioned/date",
785
+ "has_year": False
786
+ },
787
+ "corprisk/events": {
788
+ "ticker_path": "sovai/sovai-flags/events/partitioned/ticker",
789
+ "date_path": "sovai/sovai-flags/events/partitioned/date",
790
+ "has_year": False
791
+ },
792
+ "corprisk/misstatements": {
793
+ "ticker_path": "sovai/sovai-flags/misstatements/partitioned/ticker",
794
+ "date_path": "sovai/sovai-flags/misstatements/partitioned/date",
795
+ "has_year": False
796
+ },
797
+ "short/volume": {
798
+ "ticker_path": "sovai/sovai-short/short_volume_weekly/partitioned/ticker",
799
+ "date_path": "sovai/sovai-short/short_volume_weekly/partitioned/date",
800
+ "has_year": False
801
+ },
802
+ ## probably more to do with short later.
803
+ "short/maker": {
804
+ "ticker_path": "sovai/sovai-short/over_shorted/partitioned/ticker",
805
+ "date_path": "sovai/sovai-short/over_shorted/partitioned/date",
806
+ "has_year": False
807
+ },
808
+ "short/over_shorted": {
809
+ "ticker_path": "sovai/sovai-short/over_shorted/partitioned/ticker",
810
+ "date_path": "sovai/sovai-short/over_shorted/partitioned/date",
811
+ "has_year": False
812
+ },
813
+ "institutional/trading": {
814
+ "ticker_path": "sovai/sovai-institutional/trading/partitioned/ticker",
815
+ "date_path": "sovai/sovai-institutional/trading/partitioned/date",
816
+ "has_year": False
817
+ },
818
+
696
819
  }
697
820
 
698
821
 
@@ -781,10 +904,15 @@ def load_frame_s3_partitioned_high(
781
904
  tickers=tickers,
782
905
  start_date=start_date,
783
906
  end_date=end_date,
784
- columns=columns,
907
+ columns=[col.strip() for col in columns.split(',')] ,
785
908
  max_workers=8,
786
909
  post_process=post_process
787
910
  )
911
+
912
+ # print(columns)
913
+ # columns = [col.strip() for col in columns.split(',')]
914
+
915
+ # df = df[columns] if columns else df
788
916
 
789
917
  # Convert to CustomDataFrame if available
790
918
  if HAS_CUSTOM_DATAFRAME:
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes