mcli-framework 7.5.1__py3-none-any.whl → 7.6.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.

Potentially problematic release.


This version of mcli-framework might be problematic. Click here for more details.

Files changed (56) hide show
  1. mcli/app/commands_cmd.py +51 -39
  2. mcli/app/completion_helpers.py +4 -13
  3. mcli/app/main.py +21 -25
  4. mcli/app/model_cmd.py +119 -9
  5. mcli/lib/custom_commands.py +16 -11
  6. mcli/ml/api/app.py +1 -5
  7. mcli/ml/dashboard/app.py +2 -2
  8. mcli/ml/dashboard/app_integrated.py +168 -116
  9. mcli/ml/dashboard/app_supabase.py +7 -3
  10. mcli/ml/dashboard/app_training.py +3 -6
  11. mcli/ml/dashboard/components/charts.py +74 -115
  12. mcli/ml/dashboard/components/metrics.py +24 -44
  13. mcli/ml/dashboard/components/tables.py +32 -40
  14. mcli/ml/dashboard/overview.py +102 -78
  15. mcli/ml/dashboard/pages/cicd.py +103 -56
  16. mcli/ml/dashboard/pages/debug_dependencies.py +35 -28
  17. mcli/ml/dashboard/pages/gravity_viz.py +374 -313
  18. mcli/ml/dashboard/pages/monte_carlo_predictions.py +50 -48
  19. mcli/ml/dashboard/pages/predictions_enhanced.py +396 -248
  20. mcli/ml/dashboard/pages/scrapers_and_logs.py +299 -273
  21. mcli/ml/dashboard/pages/test_portfolio.py +153 -121
  22. mcli/ml/dashboard/pages/trading.py +238 -169
  23. mcli/ml/dashboard/pages/workflows.py +129 -84
  24. mcli/ml/dashboard/streamlit_extras_utils.py +70 -79
  25. mcli/ml/dashboard/utils.py +24 -21
  26. mcli/ml/dashboard/warning_suppression.py +6 -4
  27. mcli/ml/database/session.py +16 -5
  28. mcli/ml/mlops/pipeline_orchestrator.py +1 -3
  29. mcli/ml/predictions/monte_carlo.py +6 -18
  30. mcli/ml/trading/alpaca_client.py +95 -96
  31. mcli/ml/trading/migrations.py +76 -40
  32. mcli/ml/trading/models.py +78 -60
  33. mcli/ml/trading/paper_trading.py +92 -74
  34. mcli/ml/trading/risk_management.py +106 -85
  35. mcli/ml/trading/trading_service.py +155 -110
  36. mcli/ml/training/train_model.py +1 -3
  37. mcli/{app → self}/completion_cmd.py +6 -6
  38. mcli/self/self_cmd.py +100 -57
  39. mcli/test/test_cmd.py +30 -0
  40. mcli/workflow/daemon/daemon.py +2 -0
  41. mcli/workflow/model_service/openai_adapter.py +347 -0
  42. mcli/workflow/politician_trading/models.py +6 -2
  43. mcli/workflow/politician_trading/scrapers_corporate_registry.py +39 -88
  44. mcli/workflow/politician_trading/scrapers_free_sources.py +32 -39
  45. mcli/workflow/politician_trading/scrapers_third_party.py +21 -39
  46. mcli/workflow/politician_trading/seed_database.py +70 -89
  47. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/METADATA +1 -1
  48. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/RECORD +56 -54
  49. /mcli/{app → self}/logs_cmd.py +0 -0
  50. /mcli/{app → self}/redis_cmd.py +0 -0
  51. /mcli/{app → self}/visual_cmd.py +0 -0
  52. /mcli/{app → test}/cron_test_cmd.py +0 -0
  53. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/WHEEL +0 -0
  54. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/entry_points.txt +0 -0
  55. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/licenses/LICENSE +0 -0
  56. {mcli_framework-7.5.1.dist-info → mcli_framework-7.6.1.dist-info}/top_level.txt +0 -0
@@ -7,17 +7,16 @@ This module contains scrapers for free, publicly available politician trading da
7
7
  - SEC Edgar Insider Trading API
8
8
  """
9
9
 
10
+ import logging
10
11
  import os
11
12
  import time
12
13
  from datetime import datetime, timedelta
13
14
  from typing import Dict, List, Optional
14
- import logging
15
15
 
16
16
  import requests
17
17
 
18
18
  from .models import Politician, TradingDisclosure
19
19
 
20
-
21
20
  logger = logging.getLogger(__name__)
22
21
 
23
22
 
@@ -36,9 +35,7 @@ class SenateStockWatcherScraper:
36
35
 
37
36
  def __init__(self):
38
37
  self.session = requests.Session()
39
- self.session.headers.update({
40
- "User-Agent": "PoliticianTradingTracker/1.0"
41
- })
38
+ self.session.headers.update({"User-Agent": "PoliticianTradingTracker/1.0"})
42
39
 
43
40
  def fetch_all_transactions(self) -> List[Dict]:
44
41
  """
@@ -156,9 +153,7 @@ class SenateStockWatcherScraper:
156
153
  return politicians
157
154
 
158
155
  def convert_to_disclosures(
159
- self,
160
- transactions: List[Dict],
161
- politician_lookup: Optional[Dict[str, str]] = None
156
+ self, transactions: List[Dict], politician_lookup: Optional[Dict[str, str]] = None
162
157
  ) -> List[TradingDisclosure]:
163
158
  """
164
159
  Convert transaction data to TradingDisclosure objects
@@ -187,7 +182,11 @@ class SenateStockWatcherScraper:
187
182
  transaction_date = datetime.now()
188
183
 
189
184
  try:
190
- disclosure_date = datetime.strptime(disclosure_date_str, "%m/%d/%Y") if disclosure_date_str else transaction_date
185
+ disclosure_date = (
186
+ datetime.strptime(disclosure_date_str, "%m/%d/%Y")
187
+ if disclosure_date_str
188
+ else transaction_date
189
+ )
191
190
  except ValueError:
192
191
  disclosure_date = transaction_date
193
192
 
@@ -285,10 +284,7 @@ class FinnhubCongressionalAPI:
285
284
  self.session = requests.Session()
286
285
 
287
286
  def get_congressional_trading(
288
- self,
289
- symbol: str,
290
- from_date: Optional[str] = None,
291
- to_date: Optional[str] = None
287
+ self, symbol: str, from_date: Optional[str] = None, to_date: Optional[str] = None
292
288
  ) -> List[Dict]:
293
289
  """
294
290
  Get congressional trading for a specific stock symbol
@@ -303,10 +299,7 @@ class FinnhubCongressionalAPI:
303
299
  """
304
300
  try:
305
301
  url = f"{self.BASE_URL}/stock/congressional-trading"
306
- params = {
307
- "symbol": symbol,
308
- "token": self.api_key
309
- }
302
+ params = {"symbol": symbol, "token": self.api_key}
310
303
 
311
304
  if from_date:
312
305
  params["from"] = from_date
@@ -349,11 +342,13 @@ class SECEdgarInsiderAPI:
349
342
  def __init__(self):
350
343
  self.session = requests.Session()
351
344
  # SEC requires a User-Agent header
352
- self.session.headers.update({
353
- "User-Agent": "PoliticianTradingTracker/1.0 (contact@example.com)",
354
- "Accept-Encoding": "gzip, deflate",
355
- "Host": "data.sec.gov"
356
- })
345
+ self.session.headers.update(
346
+ {
347
+ "User-Agent": "PoliticianTradingTracker/1.0 (contact@example.com)",
348
+ "Accept-Encoding": "gzip, deflate",
349
+ "Host": "data.sec.gov",
350
+ }
351
+ )
357
352
 
358
353
  def get_company_submissions(self, cik: str) -> Dict:
359
354
  """
@@ -419,12 +414,18 @@ class SECEdgarInsiderAPI:
419
414
 
420
415
  for i, form in enumerate(forms):
421
416
  if form == "4": # Form 4 is insider transaction report
422
- form4_transactions.append({
423
- "accessionNumber": accession_numbers[i] if i < len(accession_numbers) else None,
424
- "filingDate": filing_dates[i] if i < len(filing_dates) else None,
425
- "primaryDocument": primary_documents[i] if i < len(primary_documents) else None,
426
- "cik": cik
427
- })
417
+ form4_transactions.append(
418
+ {
419
+ "accessionNumber": (
420
+ accession_numbers[i] if i < len(accession_numbers) else None
421
+ ),
422
+ "filingDate": filing_dates[i] if i < len(filing_dates) else None,
423
+ "primaryDocument": (
424
+ primary_documents[i] if i < len(primary_documents) else None
425
+ ),
426
+ "cik": cik,
427
+ }
428
+ )
428
429
 
429
430
  logger.info(f"Found {len(form4_transactions)} Form 4 filings for CIK {cik}")
430
431
  return form4_transactions
@@ -440,10 +441,7 @@ class FreeDataFetcher:
440
441
  Unified interface for fetching politician trading data from free sources
441
442
  """
442
443
 
443
- def __init__(
444
- self,
445
- finnhub_api_key: Optional[str] = None
446
- ):
444
+ def __init__(self, finnhub_api_key: Optional[str] = None):
447
445
  """
448
446
  Initialize fetcher with optional API keys
449
447
 
@@ -461,9 +459,7 @@ class FreeDataFetcher:
461
459
  logger.warning(f"Finnhub API not initialized: {e}")
462
460
 
463
461
  def fetch_from_senate_watcher(
464
- self,
465
- recent_only: bool = False,
466
- days: int = 90
462
+ self, recent_only: bool = False, days: int = 90
467
463
  ) -> Dict[str, List]:
468
464
  """
469
465
  Fetch data from Senate Stock Watcher GitHub dataset
@@ -498,10 +494,7 @@ class FreeDataFetcher:
498
494
  f"{len(disclosures)} disclosures from Senate Stock Watcher"
499
495
  )
500
496
 
501
- return {
502
- "politicians": politicians,
503
- "disclosures": disclosures
504
- }
497
+ return {"politicians": politicians, "disclosures": disclosures}
505
498
 
506
499
 
507
500
  # =============================================================================
@@ -9,18 +9,17 @@ politician trading activity:
9
9
  - ProPublica Congress API
10
10
  """
11
11
 
12
+ import logging
12
13
  import os
13
14
  import time
14
15
  from datetime import datetime, timedelta
15
16
  from typing import Dict, List, Optional
16
- import logging
17
17
 
18
18
  import requests
19
19
  from bs4 import BeautifulSoup
20
20
 
21
21
  from .models import Politician, TradingDisclosure
22
22
 
23
-
24
23
  logger = logging.getLogger(__name__)
25
24
 
26
25
 
@@ -36,9 +35,9 @@ class StockNearScraper:
36
35
 
37
36
  def __init__(self):
38
37
  self.session = requests.Session()
39
- self.session.headers.update({
40
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
41
- })
38
+ self.session.headers.update(
39
+ {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"}
40
+ )
42
41
 
43
42
  def fetch_politicians_list(self) -> List[Dict]:
44
43
  """Fetch list of politicians tracked by StockNear"""
@@ -46,7 +45,7 @@ class StockNearScraper:
46
45
  response = self.session.get(self.BASE_URL, timeout=30)
47
46
  response.raise_for_status()
48
47
 
49
- soup = BeautifulSoup(response.content, 'html.parser')
48
+ soup = BeautifulSoup(response.content, "html.parser")
50
49
 
51
50
  # StockNear loads data via JavaScript - would need Selenium or API access
52
51
  # For now, return structure for manual data entry or API integration
@@ -80,18 +79,17 @@ class ProPublicaAPI:
80
79
  def __init__(self, api_key: Optional[str] = None):
81
80
  self.api_key = api_key or os.getenv("PROPUBLICA_API_KEY")
82
81
  if not self.api_key:
83
- raise ValueError("ProPublica API key required. Set PROPUBLICA_API_KEY environment variable.")
82
+ raise ValueError(
83
+ "ProPublica API key required. Set PROPUBLICA_API_KEY environment variable."
84
+ )
84
85
 
85
86
  self.session = requests.Session()
86
- self.session.headers.update({
87
- "X-API-Key": self.api_key,
88
- "User-Agent": "PoliticianTradingTracker/1.0"
89
- })
87
+ self.session.headers.update(
88
+ {"X-API-Key": self.api_key, "User-Agent": "PoliticianTradingTracker/1.0"}
89
+ )
90
90
 
91
91
  def get_member_financial_disclosures(
92
- self,
93
- member_id: str,
94
- congress: int = 118 # 118th Congress (2023-2025)
92
+ self, member_id: str, congress: int = 118 # 118th Congress (2023-2025)
95
93
  ) -> List[Dict]:
96
94
  """
97
95
  Get financial disclosures for a specific member of Congress
@@ -128,11 +126,7 @@ class ProPublicaAPI:
128
126
  logger.error(f"Error fetching ProPublica financial disclosures: {e}")
129
127
  return []
130
128
 
131
- def get_recent_stock_transactions(
132
- self,
133
- congress: int = 118,
134
- offset: int = 0
135
- ) -> List[Dict]:
129
+ def get_recent_stock_transactions(self, congress: int = 118, offset: int = 0) -> List[Dict]:
136
130
  """
137
131
  Get recent stock transactions by members of Congress
138
132
 
@@ -144,7 +138,9 @@ class ProPublicaAPI:
144
138
  List of stock transactions
145
139
  """
146
140
  try:
147
- url = f"{self.BASE_URL}/{congress}/house/members/financial-disclosures/transactions.json"
141
+ url = (
142
+ f"{self.BASE_URL}/{congress}/house/members/financial-disclosures/transactions.json"
143
+ )
148
144
  params = {"offset": offset}
149
145
 
150
146
  response = self.session.get(url, params=params, timeout=30)
@@ -160,9 +156,7 @@ class ProPublicaAPI:
160
156
  return []
161
157
 
162
158
  def list_current_members(
163
- self,
164
- chamber: str = "house", # "house" or "senate"
165
- congress: int = 118
159
+ self, chamber: str = "house", congress: int = 118 # "house" or "senate"
166
160
  ) -> List[Dict]:
167
161
  """
168
162
  Get list of current members of Congress
@@ -220,9 +214,7 @@ class ThirdPartyDataFetcher:
220
214
  self.stocknear = StockNearScraper()
221
215
 
222
216
  def fetch_from_propublica(
223
- self,
224
- fetch_members: bool = True,
225
- fetch_transactions: bool = True
217
+ self, fetch_members: bool = True, fetch_transactions: bool = True
226
218
  ) -> Dict[str, List]:
227
219
  """
228
220
  Fetch data from ProPublica Congress API
@@ -262,16 +254,9 @@ class ThirdPartyDataFetcher:
262
254
  f"{len(disclosures)} disclosures from ProPublica"
263
255
  )
264
256
 
265
- return {
266
- "politicians": politicians,
267
- "disclosures": disclosures
268
- }
257
+ return {"politicians": politicians, "disclosures": disclosures}
269
258
 
270
- def _convert_propublica_members(
271
- self,
272
- members: List[Dict],
273
- chamber: str
274
- ) -> List[Politician]:
259
+ def _convert_propublica_members(self, members: List[Dict], chamber: str) -> List[Politician]:
275
260
  """Convert ProPublica member data to Politician objects"""
276
261
  politicians = []
277
262
 
@@ -294,10 +279,7 @@ class ThirdPartyDataFetcher:
294
279
 
295
280
  return politicians
296
281
 
297
- def _convert_propublica_transactions(
298
- self,
299
- transactions: List[Dict]
300
- ) -> List[TradingDisclosure]:
282
+ def _convert_propublica_transactions(self, transactions: List[Dict]) -> List[TradingDisclosure]:
301
283
  """Convert ProPublica transaction data to TradingDisclosure objects"""
302
284
  disclosures = []
303
285
 
@@ -20,11 +20,12 @@ from pathlib import Path
20
20
  from typing import Dict, List, Optional
21
21
  from uuid import UUID
22
22
 
23
- from supabase import create_client, Client
23
+ from supabase import Client, create_client
24
24
 
25
25
  # Load environment variables from .env file
26
26
  try:
27
27
  from dotenv import load_dotenv
28
+
28
29
  # Look for .env in project root
29
30
  env_path = Path(__file__).parent.parent.parent.parent.parent / ".env"
30
31
  if env_path.exists():
@@ -39,15 +40,11 @@ from .data_sources import ALL_DATA_SOURCES, AccessMethod, DataSource
39
40
  from .models import Politician, TradingDisclosure
40
41
  from .scrapers_free_sources import FreeDataFetcher
41
42
 
42
-
43
43
  # Configure logging
44
44
  logging.basicConfig(
45
45
  level=logging.INFO,
46
46
  format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
47
- handlers=[
48
- logging.StreamHandler(sys.stdout),
49
- logging.FileHandler("/tmp/seed_database.log")
50
- ]
47
+ handlers=[logging.StreamHandler(sys.stdout), logging.FileHandler("/tmp/seed_database.log")],
51
48
  )
52
49
  logger = logging.getLogger(__name__)
53
50
 
@@ -76,11 +73,7 @@ def get_supabase_client() -> Client:
76
73
  # =============================================================================
77
74
 
78
75
 
79
- def create_data_pull_job(
80
- client: Client,
81
- job_type: str,
82
- config: Optional[Dict] = None
83
- ) -> UUID:
76
+ def create_data_pull_job(client: Client, job_type: str, config: Optional[Dict] = None) -> UUID:
84
77
  """
85
78
  Create a new data pull job record
86
79
 
@@ -93,12 +86,18 @@ def create_data_pull_job(
93
86
  Job ID
94
87
  """
95
88
  try:
96
- result = client.table("data_pull_jobs").insert({
97
- "job_type": job_type,
98
- "status": "running",
99
- "started_at": datetime.now().isoformat(),
100
- "config_snapshot": config or {}
101
- }).execute()
89
+ result = (
90
+ client.table("data_pull_jobs")
91
+ .insert(
92
+ {
93
+ "job_type": job_type,
94
+ "status": "running",
95
+ "started_at": datetime.now().isoformat(),
96
+ "config_snapshot": config or {},
97
+ }
98
+ )
99
+ .execute()
100
+ )
102
101
 
103
102
  job_id = result.data[0]["id"]
104
103
  logger.info(f"Created data pull job: {job_id} (type: {job_type})")
@@ -114,7 +113,7 @@ def update_data_pull_job(
114
113
  job_id: UUID,
115
114
  status: str,
116
115
  stats: Optional[Dict] = None,
117
- error: Optional[str] = None
116
+ error: Optional[str] = None,
118
117
  ):
119
118
  """
120
119
  Update data pull job with results
@@ -127,10 +126,7 @@ def update_data_pull_job(
127
126
  error: Optional error message if failed
128
127
  """
129
128
  try:
130
- update_data = {
131
- "status": status,
132
- "completed_at": datetime.now().isoformat()
133
- }
129
+ update_data = {"status": status, "completed_at": datetime.now().isoformat()}
134
130
 
135
131
  if stats:
136
132
  update_data.update(stats)
@@ -151,10 +147,7 @@ def update_data_pull_job(
151
147
  # =============================================================================
152
148
 
153
149
 
154
- def upsert_politicians(
155
- client: Client,
156
- politicians: List[Politician]
157
- ) -> Dict[str, UUID]:
150
+ def upsert_politicians(client: Client, politicians: List[Politician]) -> Dict[str, UUID]:
158
151
  """
159
152
  Upsert politicians to database, returning mapping of bioguide_id -> UUID
160
153
 
@@ -186,20 +179,23 @@ def upsert_politicians(
186
179
  # Try to find existing politician
187
180
  if politician.bioguide_id:
188
181
  # Query by bioguide_id if available
189
- existing = client.table("politicians").select("id").eq(
190
- "bioguide_id", politician.bioguide_id
191
- ).execute()
182
+ existing = (
183
+ client.table("politicians")
184
+ .select("id")
185
+ .eq("bioguide_id", politician.bioguide_id)
186
+ .execute()
187
+ )
192
188
  else:
193
189
  # Query by unique constraint fields (first_name, last_name, role, state_or_country)
194
- existing = client.table("politicians").select("id").eq(
195
- "first_name", politician.first_name
196
- ).eq(
197
- "last_name", politician.last_name
198
- ).eq(
199
- "role", politician.role
200
- ).eq(
201
- "state_or_country", politician.state_or_country
202
- ).execute()
190
+ existing = (
191
+ client.table("politicians")
192
+ .select("id")
193
+ .eq("first_name", politician.first_name)
194
+ .eq("last_name", politician.last_name)
195
+ .eq("role", politician.role)
196
+ .eq("state_or_country", politician.state_or_country)
197
+ .execute()
198
+ )
203
199
 
204
200
  if existing.data:
205
201
  # Update existing
@@ -223,7 +219,9 @@ def upsert_politicians(
223
219
  logger.error(f"Error upserting politician {politician.full_name}: {e}")
224
220
  continue
225
221
 
226
- logger.info(f"Upserted {len(politicians)} politicians ({new_count} new, {updated_count} updated)")
222
+ logger.info(
223
+ f"Upserted {len(politicians)} politicians ({new_count} new, {updated_count} updated)"
224
+ )
227
225
 
228
226
  return politician_map
229
227
 
@@ -234,9 +232,7 @@ def upsert_politicians(
234
232
 
235
233
 
236
234
  def upsert_trading_disclosures(
237
- client: Client,
238
- disclosures: List[TradingDisclosure],
239
- politician_map: Dict[str, UUID]
235
+ client: Client, disclosures: List[TradingDisclosure], politician_map: Dict[str, UUID]
240
236
  ) -> Dict[str, int]:
241
237
  """
242
238
  Upsert trading disclosures to database
@@ -283,17 +279,16 @@ def upsert_trading_disclosures(
283
279
  }
284
280
 
285
281
  # Check for existing disclosure (using unique constraint)
286
- existing = client.table("trading_disclosures").select("id").eq(
287
- "politician_id", str(pol_id)
288
- ).eq(
289
- "transaction_date", disclosure.transaction_date.isoformat()
290
- ).eq(
291
- "asset_name", disclosure.asset_name
292
- ).eq(
293
- "transaction_type", disclosure.transaction_type
294
- ).eq(
295
- "disclosure_date", disclosure.disclosure_date.isoformat()
296
- ).execute()
282
+ existing = (
283
+ client.table("trading_disclosures")
284
+ .select("id")
285
+ .eq("politician_id", str(pol_id))
286
+ .eq("transaction_date", disclosure.transaction_date.isoformat())
287
+ .eq("asset_name", disclosure.asset_name)
288
+ .eq("transaction_type", disclosure.transaction_type)
289
+ .eq("disclosure_date", disclosure.disclosure_date.isoformat())
290
+ .execute()
291
+ )
297
292
 
298
293
  if existing.data:
299
294
  # Update existing
@@ -331,10 +326,7 @@ def upsert_trading_disclosures(
331
326
 
332
327
 
333
328
  def seed_from_senate_watcher(
334
- client: Client,
335
- test_run: bool = False,
336
- recent_only: bool = False,
337
- days: int = 90
329
+ client: Client, test_run: bool = False, recent_only: bool = False, days: int = 90
338
330
  ) -> Dict[str, int]:
339
331
  """
340
332
  Seed database from Senate Stock Watcher GitHub dataset
@@ -353,20 +345,16 @@ def seed_from_senate_watcher(
353
345
  logger.info("=" * 80)
354
346
 
355
347
  # Create job record
356
- job_id = create_data_pull_job(client, "senate_watcher_seed", {
357
- "recent_only": recent_only,
358
- "days": days
359
- })
348
+ job_id = create_data_pull_job(
349
+ client, "senate_watcher_seed", {"recent_only": recent_only, "days": days}
350
+ )
360
351
 
361
352
  try:
362
353
  # Initialize fetcher
363
354
  fetcher = FreeDataFetcher()
364
355
 
365
356
  # Fetch data
366
- data = fetcher.fetch_from_senate_watcher(
367
- recent_only=recent_only,
368
- days=days
369
- )
357
+ data = fetcher.fetch_from_senate_watcher(recent_only=recent_only, days=days)
370
358
 
371
359
  politicians = data["politicians"]
372
360
  disclosures = data["disclosures"]
@@ -377,11 +365,16 @@ def seed_from_senate_watcher(
377
365
  logger.info("TEST RUN - Not inserting to database")
378
366
  logger.info(f"Sample politician: {politicians[0] if politicians else 'None'}")
379
367
  logger.info(f"Sample disclosure: {disclosures[0] if disclosures else 'None'}")
380
- update_data_pull_job(client, job_id, "completed", {
381
- "records_found": len(politicians) + len(disclosures),
382
- "records_new": 0,
383
- "records_updated": 0,
384
- })
368
+ update_data_pull_job(
369
+ client,
370
+ job_id,
371
+ "completed",
372
+ {
373
+ "records_found": len(politicians) + len(disclosures),
374
+ "records_new": 0,
375
+ "records_updated": 0,
376
+ },
377
+ )
385
378
  return {"records_found": len(politicians) + len(disclosures)}
386
379
 
387
380
  # Upsert politicians
@@ -401,10 +394,7 @@ def seed_from_senate_watcher(
401
394
  raise
402
395
 
403
396
 
404
- def seed_from_all_sources(
405
- client: Client,
406
- test_run: bool = False
407
- ) -> Dict[str, Dict[str, int]]:
397
+ def seed_from_all_sources(client: Client, test_run: bool = False) -> Dict[str, Dict[str, int]]:
408
398
  """
409
399
  Seed database from all available sources
410
400
 
@@ -467,33 +457,27 @@ def main():
467
457
  "--sources",
468
458
  choices=["all", "senate", "finnhub", "sec-edgar"],
469
459
  default="all",
470
- help="Which data sources to seed from (default: all)"
460
+ help="Which data sources to seed from (default: all)",
471
461
  )
472
462
 
473
463
  parser.add_argument(
474
- "--recent-only",
475
- action="store_true",
476
- help="Only fetch recent transactions (last 90 days)"
464
+ "--recent-only", action="store_true", help="Only fetch recent transactions (last 90 days)"
477
465
  )
478
466
 
479
467
  parser.add_argument(
480
468
  "--days",
481
469
  type=int,
482
470
  default=90,
483
- help="Number of days to look back when using --recent-only (default: 90)"
471
+ help="Number of days to look back when using --recent-only (default: 90)",
484
472
  )
485
473
 
486
474
  parser.add_argument(
487
475
  "--test-run",
488
476
  action="store_true",
489
- help="Fetch data but don't insert to database (for testing)"
477
+ help="Fetch data but don't insert to database (for testing)",
490
478
  )
491
479
 
492
- parser.add_argument(
493
- "--verbose",
494
- action="store_true",
495
- help="Enable verbose logging"
496
- )
480
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
497
481
 
498
482
  args = parser.parse_args()
499
483
 
@@ -512,10 +496,7 @@ def main():
512
496
  try:
513
497
  if args.sources == "senate":
514
498
  seed_from_senate_watcher(
515
- client,
516
- test_run=args.test_run,
517
- recent_only=args.recent_only,
518
- days=args.days
499
+ client, test_run=args.test_run, recent_only=args.recent_only, days=args.days
519
500
  )
520
501
  elif args.sources == "all":
521
502
  seed_from_all_sources(client, args.test_run)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcli-framework
3
- Version: 7.5.1
3
+ Version: 7.6.1
4
4
  Summary: 🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals
5
5
  Author-email: Luis Fernandez de la Vara <luis@lefv.io>
6
6
  Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>