mcli-framework 7.1.1__py3-none-any.whl → 7.1.2__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.
- mcli/app/completion_cmd.py +59 -49
- mcli/app/completion_helpers.py +60 -138
- mcli/app/logs_cmd.py +6 -2
- mcli/app/main.py +17 -14
- mcli/app/model_cmd.py +19 -4
- mcli/chat/chat.py +3 -2
- mcli/lib/search/cached_vectorizer.py +1 -0
- mcli/lib/services/data_pipeline.py +12 -5
- mcli/lib/services/lsh_client.py +68 -57
- mcli/ml/api/app.py +28 -36
- mcli/ml/api/middleware.py +8 -16
- mcli/ml/api/routers/admin_router.py +3 -1
- mcli/ml/api/routers/auth_router.py +32 -56
- mcli/ml/api/routers/backtest_router.py +3 -1
- mcli/ml/api/routers/data_router.py +3 -1
- mcli/ml/api/routers/model_router.py +35 -74
- mcli/ml/api/routers/monitoring_router.py +3 -1
- mcli/ml/api/routers/portfolio_router.py +3 -1
- mcli/ml/api/routers/prediction_router.py +60 -65
- mcli/ml/api/routers/trade_router.py +6 -2
- mcli/ml/api/routers/websocket_router.py +12 -9
- mcli/ml/api/schemas.py +10 -2
- mcli/ml/auth/auth_manager.py +49 -114
- mcli/ml/auth/models.py +30 -15
- mcli/ml/auth/permissions.py +12 -19
- mcli/ml/backtesting/backtest_engine.py +134 -108
- mcli/ml/backtesting/performance_metrics.py +142 -108
- mcli/ml/cache.py +12 -18
- mcli/ml/cli/main.py +37 -23
- mcli/ml/config/settings.py +29 -12
- mcli/ml/dashboard/app.py +122 -130
- mcli/ml/dashboard/app_integrated.py +216 -150
- mcli/ml/dashboard/app_supabase.py +176 -108
- mcli/ml/dashboard/app_training.py +212 -206
- mcli/ml/dashboard/cli.py +14 -5
- mcli/ml/data_ingestion/api_connectors.py +51 -81
- mcli/ml/data_ingestion/data_pipeline.py +127 -125
- mcli/ml/data_ingestion/stream_processor.py +72 -80
- mcli/ml/database/migrations/env.py +3 -2
- mcli/ml/database/models.py +112 -79
- mcli/ml/database/session.py +6 -5
- mcli/ml/experimentation/ab_testing.py +149 -99
- mcli/ml/features/ensemble_features.py +9 -8
- mcli/ml/features/political_features.py +6 -5
- mcli/ml/features/recommendation_engine.py +15 -14
- mcli/ml/features/stock_features.py +7 -6
- mcli/ml/features/test_feature_engineering.py +8 -7
- mcli/ml/logging.py +10 -15
- mcli/ml/mlops/data_versioning.py +57 -64
- mcli/ml/mlops/experiment_tracker.py +49 -41
- mcli/ml/mlops/model_serving.py +59 -62
- mcli/ml/mlops/pipeline_orchestrator.py +203 -149
- mcli/ml/models/base_models.py +8 -7
- mcli/ml/models/ensemble_models.py +6 -5
- mcli/ml/models/recommendation_models.py +7 -6
- mcli/ml/models/test_models.py +18 -14
- mcli/ml/monitoring/drift_detection.py +95 -74
- mcli/ml/monitoring/metrics.py +10 -22
- mcli/ml/optimization/portfolio_optimizer.py +172 -132
- mcli/ml/predictions/prediction_engine.py +62 -50
- mcli/ml/preprocessing/data_cleaners.py +6 -5
- mcli/ml/preprocessing/feature_extractors.py +7 -6
- mcli/ml/preprocessing/ml_pipeline.py +3 -2
- mcli/ml/preprocessing/politician_trading_preprocessor.py +11 -10
- mcli/ml/preprocessing/test_preprocessing.py +4 -4
- mcli/ml/scripts/populate_sample_data.py +36 -16
- mcli/ml/tasks.py +82 -83
- mcli/ml/tests/test_integration.py +86 -76
- mcli/ml/tests/test_training_dashboard.py +169 -142
- mcli/mygroup/test_cmd.py +2 -1
- mcli/self/self_cmd.py +31 -16
- mcli/self/test_cmd.py +2 -1
- mcli/workflow/dashboard/dashboard_cmd.py +13 -6
- mcli/workflow/lsh_integration.py +46 -58
- mcli/workflow/politician_trading/commands.py +576 -427
- mcli/workflow/politician_trading/config.py +7 -7
- mcli/workflow/politician_trading/connectivity.py +35 -33
- mcli/workflow/politician_trading/data_sources.py +72 -71
- mcli/workflow/politician_trading/database.py +18 -16
- mcli/workflow/politician_trading/demo.py +4 -3
- mcli/workflow/politician_trading/models.py +5 -5
- mcli/workflow/politician_trading/monitoring.py +13 -13
- mcli/workflow/politician_trading/scrapers.py +332 -224
- mcli/workflow/politician_trading/scrapers_california.py +116 -94
- mcli/workflow/politician_trading/scrapers_eu.py +70 -71
- mcli/workflow/politician_trading/scrapers_uk.py +118 -90
- mcli/workflow/politician_trading/scrapers_us_states.py +125 -92
- mcli/workflow/politician_trading/workflow.py +98 -71
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/METADATA +1 -1
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/RECORD +94 -94
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/WHEEL +0 -0
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/top_level.txt +0 -0
|
@@ -6,19 +6,19 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
import uuid
|
|
8
8
|
from datetime import datetime, timedelta
|
|
9
|
-
from typing import
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
10
|
|
|
11
11
|
from .config import WorkflowConfig
|
|
12
12
|
from .database import PoliticianTradingDB
|
|
13
|
-
from .models import DataPullJob, Politician,
|
|
13
|
+
from .models import DataPullJob, Politician, PoliticianRole, TradingDisclosure
|
|
14
14
|
from .scrapers import (
|
|
15
15
|
CongressTradingScraper,
|
|
16
|
-
QuiverQuantScraper,
|
|
17
16
|
EUParliamentScraper,
|
|
18
17
|
PoliticianMatcher,
|
|
19
|
-
|
|
18
|
+
QuiverQuantScraper,
|
|
20
19
|
run_california_workflow,
|
|
21
20
|
run_eu_member_states_workflow,
|
|
21
|
+
run_uk_parliament_workflow,
|
|
22
22
|
run_us_states_workflow,
|
|
23
23
|
)
|
|
24
24
|
|
|
@@ -79,7 +79,9 @@ class PoliticianTradingWorkflow:
|
|
|
79
79
|
# Run EU member states collection
|
|
80
80
|
eu_states_results = await self._collect_eu_member_states_data()
|
|
81
81
|
results["jobs"]["eu_member_states"] = eu_states_results
|
|
82
|
-
results["summary"]["total_new_disclosures"] += eu_states_results.get(
|
|
82
|
+
results["summary"]["total_new_disclosures"] += eu_states_results.get(
|
|
83
|
+
"new_disclosures", 0
|
|
84
|
+
)
|
|
83
85
|
results["summary"]["total_updated_disclosures"] += eu_states_results.get(
|
|
84
86
|
"updated_disclosures", 0
|
|
85
87
|
)
|
|
@@ -87,7 +89,9 @@ class PoliticianTradingWorkflow:
|
|
|
87
89
|
# Run US states collection
|
|
88
90
|
us_states_results = await self._collect_us_states_data()
|
|
89
91
|
results["jobs"]["us_states"] = us_states_results
|
|
90
|
-
results["summary"]["total_new_disclosures"] += us_states_results.get(
|
|
92
|
+
results["summary"]["total_new_disclosures"] += us_states_results.get(
|
|
93
|
+
"new_disclosures", 0
|
|
94
|
+
)
|
|
91
95
|
results["summary"]["total_updated_disclosures"] += us_states_results.get(
|
|
92
96
|
"updated_disclosures", 0
|
|
93
97
|
)
|
|
@@ -147,7 +151,9 @@ class PoliticianTradingWorkflow:
|
|
|
147
151
|
|
|
148
152
|
async def _collect_us_congress_data(self) -> Dict[str, Any]:
|
|
149
153
|
"""Collect US Congress trading data"""
|
|
150
|
-
job_id = await self.db.create_data_pull_job(
|
|
154
|
+
job_id = await self.db.create_data_pull_job(
|
|
155
|
+
"us_congress", self.config.to_serializable_dict()
|
|
156
|
+
)
|
|
151
157
|
|
|
152
158
|
job_result = {
|
|
153
159
|
"job_id": job_id,
|
|
@@ -198,19 +204,21 @@ class PoliticianTradingWorkflow:
|
|
|
198
204
|
logger.warning("Skipping disclosure with empty politician name")
|
|
199
205
|
job.records_failed += 1
|
|
200
206
|
continue
|
|
201
|
-
|
|
207
|
+
|
|
202
208
|
# Filter out obviously invalid politician names
|
|
203
209
|
if self._is_invalid_politician_name(politician_name):
|
|
204
|
-
logger.warning(
|
|
210
|
+
logger.warning(
|
|
211
|
+
f"Skipping disclosure with invalid politician name: {politician_name}"
|
|
212
|
+
)
|
|
205
213
|
job.records_failed += 1
|
|
206
214
|
continue
|
|
207
|
-
|
|
215
|
+
|
|
208
216
|
politician = matcher.find_politician(politician_name)
|
|
209
217
|
|
|
210
218
|
if not politician:
|
|
211
219
|
# Create new politician with real name from scraper
|
|
212
220
|
logger.info(f"Creating new politician for: {politician_name}")
|
|
213
|
-
|
|
221
|
+
|
|
214
222
|
# Parse real name into first/last components
|
|
215
223
|
name_parts = politician_name.strip().split()
|
|
216
224
|
if len(name_parts) >= 2:
|
|
@@ -219,7 +227,7 @@ class PoliticianTradingWorkflow:
|
|
|
219
227
|
else:
|
|
220
228
|
first_name = politician_name.strip()
|
|
221
229
|
last_name = ""
|
|
222
|
-
|
|
230
|
+
|
|
223
231
|
# Create politician with real name - use generic role for now
|
|
224
232
|
new_politician = Politician(
|
|
225
233
|
first_name=first_name,
|
|
@@ -283,7 +291,9 @@ class PoliticianTradingWorkflow:
|
|
|
283
291
|
|
|
284
292
|
async def _collect_eu_parliament_data(self) -> Dict[str, Any]:
|
|
285
293
|
"""Collect EU Parliament trading/financial data"""
|
|
286
|
-
job_id = await self.db.create_data_pull_job(
|
|
294
|
+
job_id = await self.db.create_data_pull_job(
|
|
295
|
+
"eu_parliament", self.config.to_serializable_dict()
|
|
296
|
+
)
|
|
287
297
|
|
|
288
298
|
job_result = {
|
|
289
299
|
"job_id": job_id,
|
|
@@ -358,7 +368,9 @@ class PoliticianTradingWorkflow:
|
|
|
358
368
|
|
|
359
369
|
async def _collect_uk_parliament_data(self) -> Dict[str, Any]:
|
|
360
370
|
"""Collect UK Parliament financial interests data"""
|
|
361
|
-
job_id = await self.db.create_data_pull_job(
|
|
371
|
+
job_id = await self.db.create_data_pull_job(
|
|
372
|
+
"uk_parliament", self.config.to_serializable_dict()
|
|
373
|
+
)
|
|
362
374
|
|
|
363
375
|
job_result = {
|
|
364
376
|
"job_id": job_id,
|
|
@@ -379,9 +391,9 @@ class PoliticianTradingWorkflow:
|
|
|
379
391
|
# Collect UK Parliament financial interests
|
|
380
392
|
logger.info("Starting UK Parliament financial interests collection")
|
|
381
393
|
uk_disclosures = await run_uk_parliament_workflow(self.config.scraping)
|
|
382
|
-
|
|
394
|
+
|
|
383
395
|
job.records_found = len(uk_disclosures)
|
|
384
|
-
|
|
396
|
+
|
|
385
397
|
# Process each disclosure
|
|
386
398
|
matcher = PoliticianMatcher(self.politicians)
|
|
387
399
|
|
|
@@ -391,11 +403,13 @@ class PoliticianTradingWorkflow:
|
|
|
391
403
|
if not disclosure.politician_id:
|
|
392
404
|
# Extract real politician name from raw data
|
|
393
405
|
politician_name = disclosure.raw_data.get("politician_name", "")
|
|
394
|
-
|
|
406
|
+
|
|
395
407
|
if not politician_name or politician_name.strip() == "":
|
|
396
408
|
# Fallback to using member ID if no name available
|
|
397
409
|
if disclosure.raw_data.get("uk_member_id"):
|
|
398
|
-
logger.warning(
|
|
410
|
+
logger.warning(
|
|
411
|
+
f"Using member ID as fallback for UK disclosure: {disclosure.raw_data.get('uk_member_id')}"
|
|
412
|
+
)
|
|
399
413
|
uk_politician = Politician(
|
|
400
414
|
first_name="UK",
|
|
401
415
|
last_name="MP",
|
|
@@ -406,19 +420,23 @@ class PoliticianTradingWorkflow:
|
|
|
406
420
|
politician_id = await self.db.upsert_politician(uk_politician)
|
|
407
421
|
disclosure.politician_id = politician_id
|
|
408
422
|
else:
|
|
409
|
-
logger.warning(
|
|
423
|
+
logger.warning(
|
|
424
|
+
"Skipping UK disclosure with no politician name or member ID"
|
|
425
|
+
)
|
|
410
426
|
job.records_failed += 1
|
|
411
427
|
continue
|
|
412
428
|
else:
|
|
413
429
|
# Filter out obviously invalid politician names
|
|
414
430
|
if self._is_invalid_politician_name(politician_name):
|
|
415
|
-
logger.warning(
|
|
431
|
+
logger.warning(
|
|
432
|
+
f"Skipping UK disclosure with invalid politician name: {politician_name}"
|
|
433
|
+
)
|
|
416
434
|
job.records_failed += 1
|
|
417
435
|
continue
|
|
418
|
-
|
|
436
|
+
|
|
419
437
|
# Try to find existing politician
|
|
420
438
|
politician = matcher.find_politician(politician_name)
|
|
421
|
-
|
|
439
|
+
|
|
422
440
|
if not politician:
|
|
423
441
|
# Create new politician with real name from scraper
|
|
424
442
|
# Parse real name into first/last components
|
|
@@ -429,7 +447,7 @@ class PoliticianTradingWorkflow:
|
|
|
429
447
|
else:
|
|
430
448
|
first_name = politician_name.strip()
|
|
431
449
|
last_name = ""
|
|
432
|
-
|
|
450
|
+
|
|
433
451
|
# Create politician with REAL name
|
|
434
452
|
uk_politician = Politician(
|
|
435
453
|
first_name=first_name,
|
|
@@ -478,7 +496,9 @@ class PoliticianTradingWorkflow:
|
|
|
478
496
|
|
|
479
497
|
async def _collect_california_data(self) -> Dict[str, Any]:
|
|
480
498
|
"""Collect California NetFile and state disclosure data"""
|
|
481
|
-
job_id = await self.db.create_data_pull_job(
|
|
499
|
+
job_id = await self.db.create_data_pull_job(
|
|
500
|
+
"california", self.config.to_serializable_dict()
|
|
501
|
+
)
|
|
482
502
|
|
|
483
503
|
job_result = {
|
|
484
504
|
"job_id": job_id,
|
|
@@ -499,9 +519,9 @@ class PoliticianTradingWorkflow:
|
|
|
499
519
|
# Collect California financial disclosures
|
|
500
520
|
logger.info("Starting California financial disclosures collection")
|
|
501
521
|
california_disclosures = await run_california_workflow(self.config.scraping)
|
|
502
|
-
|
|
522
|
+
|
|
503
523
|
job.records_found = len(california_disclosures)
|
|
504
|
-
|
|
524
|
+
|
|
505
525
|
# Process each disclosure
|
|
506
526
|
matcher = PoliticianMatcher(self.politicians)
|
|
507
527
|
|
|
@@ -557,11 +577,13 @@ class PoliticianTradingWorkflow:
|
|
|
557
577
|
|
|
558
578
|
async def _collect_eu_member_states_data(self) -> Dict[str, Any]:
|
|
559
579
|
"""Collect EU member states financial disclosure data"""
|
|
560
|
-
job_id = await self.db.create_data_pull_job(
|
|
580
|
+
job_id = await self.db.create_data_pull_job(
|
|
581
|
+
"eu_member_states", self.config.to_serializable_dict()
|
|
582
|
+
)
|
|
561
583
|
|
|
562
584
|
job_result = {
|
|
563
585
|
"job_id": job_id,
|
|
564
|
-
"status": "running",
|
|
586
|
+
"status": "running",
|
|
565
587
|
"new_disclosures": 0,
|
|
566
588
|
"updated_disclosures": 0,
|
|
567
589
|
"errors": [],
|
|
@@ -578,9 +600,9 @@ class PoliticianTradingWorkflow:
|
|
|
578
600
|
# Collect EU member states financial disclosures
|
|
579
601
|
logger.info("Starting EU member states financial disclosures collection")
|
|
580
602
|
eu_states_disclosures = await run_eu_member_states_workflow(self.config.scraping)
|
|
581
|
-
|
|
603
|
+
|
|
582
604
|
job.records_found = len(eu_states_disclosures)
|
|
583
|
-
|
|
605
|
+
|
|
584
606
|
# Process each disclosure
|
|
585
607
|
matcher = PoliticianMatcher(self.politicians)
|
|
586
608
|
|
|
@@ -591,18 +613,18 @@ class PoliticianTradingWorkflow:
|
|
|
591
613
|
# Extract politician details from raw data
|
|
592
614
|
country = disclosure.raw_data.get("country", "Unknown")
|
|
593
615
|
source = disclosure.raw_data.get("source", "unknown")
|
|
594
|
-
|
|
616
|
+
|
|
595
617
|
# Map country to appropriate role
|
|
596
618
|
role_map = {
|
|
597
619
|
"Germany": PoliticianRole.GERMAN_BUNDESTAG,
|
|
598
620
|
"France": PoliticianRole.FRENCH_DEPUTY,
|
|
599
621
|
"Italy": PoliticianRole.ITALIAN_DEPUTY,
|
|
600
622
|
"Spain": PoliticianRole.SPANISH_DEPUTY,
|
|
601
|
-
"Netherlands": PoliticianRole.DUTCH_MP
|
|
623
|
+
"Netherlands": PoliticianRole.DUTCH_MP,
|
|
602
624
|
}
|
|
603
|
-
|
|
625
|
+
|
|
604
626
|
politician_role = role_map.get(country, PoliticianRole.EU_MEP)
|
|
605
|
-
|
|
627
|
+
|
|
606
628
|
# Create placeholder politician
|
|
607
629
|
eu_politician = Politician(
|
|
608
630
|
first_name=country,
|
|
@@ -652,7 +674,7 @@ class PoliticianTradingWorkflow:
|
|
|
652
674
|
|
|
653
675
|
job_result = {
|
|
654
676
|
"job_id": job_id,
|
|
655
|
-
"status": "running",
|
|
677
|
+
"status": "running",
|
|
656
678
|
"new_disclosures": 0,
|
|
657
679
|
"updated_disclosures": 0,
|
|
658
680
|
"errors": [],
|
|
@@ -669,9 +691,9 @@ class PoliticianTradingWorkflow:
|
|
|
669
691
|
# Collect US states financial disclosures
|
|
670
692
|
logger.info("Starting US states financial disclosures collection")
|
|
671
693
|
us_states_disclosures = await run_us_states_workflow(self.config.scraping)
|
|
672
|
-
|
|
694
|
+
|
|
673
695
|
job.records_found = len(us_states_disclosures)
|
|
674
|
-
|
|
696
|
+
|
|
675
697
|
# Process each disclosure
|
|
676
698
|
matcher = PoliticianMatcher(self.politicians)
|
|
677
699
|
|
|
@@ -682,24 +704,28 @@ class PoliticianTradingWorkflow:
|
|
|
682
704
|
# Extract real politician name from raw data
|
|
683
705
|
politician_name = disclosure.raw_data.get("politician_name", "")
|
|
684
706
|
if not politician_name or politician_name.strip() == "":
|
|
685
|
-
logger.warning(
|
|
707
|
+
logger.warning(
|
|
708
|
+
"Skipping US states disclosure with empty politician name"
|
|
709
|
+
)
|
|
686
710
|
job.records_failed += 1
|
|
687
711
|
continue
|
|
688
|
-
|
|
712
|
+
|
|
689
713
|
# Filter out obviously invalid politician names
|
|
690
714
|
if self._is_invalid_politician_name(politician_name):
|
|
691
|
-
logger.warning(
|
|
715
|
+
logger.warning(
|
|
716
|
+
f"Skipping US states disclosure with invalid politician name: {politician_name}"
|
|
717
|
+
)
|
|
692
718
|
job.records_failed += 1
|
|
693
719
|
continue
|
|
694
|
-
|
|
720
|
+
|
|
695
721
|
# Try to find existing politician
|
|
696
722
|
politician = matcher.find_politician(politician_name)
|
|
697
|
-
|
|
723
|
+
|
|
698
724
|
if not politician:
|
|
699
725
|
# Create new politician with real name from scraper
|
|
700
726
|
state = disclosure.raw_data.get("state", "Unknown")
|
|
701
727
|
source = disclosure.raw_data.get("source", "unknown")
|
|
702
|
-
|
|
728
|
+
|
|
703
729
|
# Map state to appropriate role
|
|
704
730
|
role_map = {
|
|
705
731
|
"Texas": PoliticianRole.TEXAS_STATE_OFFICIAL,
|
|
@@ -708,11 +734,11 @@ class PoliticianTradingWorkflow:
|
|
|
708
734
|
"Illinois": PoliticianRole.ILLINOIS_STATE_OFFICIAL,
|
|
709
735
|
"Pennsylvania": PoliticianRole.PENNSYLVANIA_STATE_OFFICIAL,
|
|
710
736
|
"Massachusetts": PoliticianRole.MASSACHUSETTS_STATE_OFFICIAL,
|
|
711
|
-
"California": PoliticianRole.CALIFORNIA_STATE_OFFICIAL
|
|
737
|
+
"California": PoliticianRole.CALIFORNIA_STATE_OFFICIAL,
|
|
712
738
|
}
|
|
713
|
-
|
|
739
|
+
|
|
714
740
|
politician_role = role_map.get(state, PoliticianRole.US_HOUSE_REP)
|
|
715
|
-
|
|
741
|
+
|
|
716
742
|
# Parse real name into first/last components
|
|
717
743
|
name_parts = politician_name.strip().split()
|
|
718
744
|
if len(name_parts) >= 2:
|
|
@@ -721,7 +747,7 @@ class PoliticianTradingWorkflow:
|
|
|
721
747
|
else:
|
|
722
748
|
first_name = politician_name.strip()
|
|
723
749
|
last_name = ""
|
|
724
|
-
|
|
750
|
+
|
|
725
751
|
# Create politician with REAL name
|
|
726
752
|
state_politician = Politician(
|
|
727
753
|
first_name=first_name,
|
|
@@ -795,47 +821,48 @@ class PoliticianTradingWorkflow:
|
|
|
795
821
|
"""Check if a name is obviously not a politician name"""
|
|
796
822
|
if not name or len(name.strip()) < 2:
|
|
797
823
|
return True
|
|
798
|
-
|
|
824
|
+
|
|
799
825
|
# Check for proper name structure first (before converting to uppercase)
|
|
800
826
|
original_name = name.strip()
|
|
801
827
|
import re
|
|
802
|
-
|
|
828
|
+
|
|
829
|
+
if not re.search(r"[A-Za-z]", original_name): # Should have at least one letter
|
|
803
830
|
return True
|
|
804
|
-
if re.search(r
|
|
831
|
+
if re.search(r"^\d+", original_name): # Starting with numbers
|
|
805
832
|
return True
|
|
806
|
-
|
|
833
|
+
|
|
807
834
|
# Now convert to uppercase for pattern matching
|
|
808
835
|
name = original_name.upper()
|
|
809
|
-
|
|
836
|
+
|
|
810
837
|
# Filter out obvious non-names
|
|
811
838
|
invalid_patterns = [
|
|
812
839
|
# Asset tickers and financial instruments
|
|
813
|
-
r
|
|
814
|
-
r
|
|
815
|
-
r
|
|
840
|
+
r"^-.*CT$", # -ETHEREUMCT, -DOGCT patterns
|
|
841
|
+
r"^[A-Z]{2,5}$", # Short all-caps (likely tickers)
|
|
842
|
+
r"^\$", # Starting with $
|
|
816
843
|
# Municipal and financial terms
|
|
817
|
-
r
|
|
818
|
-
r
|
|
819
|
-
r
|
|
820
|
-
r
|
|
821
|
-
r
|
|
822
|
-
r
|
|
823
|
-
r
|
|
824
|
-
r
|
|
844
|
+
r"MUNICIPAL",
|
|
845
|
+
r"BOND",
|
|
846
|
+
r"TRUST",
|
|
847
|
+
r"FUND",
|
|
848
|
+
r"CORP",
|
|
849
|
+
r"INC\.$",
|
|
850
|
+
r"LLC$",
|
|
851
|
+
r"LP$",
|
|
825
852
|
# Common non-name patterns
|
|
826
|
-
r
|
|
827
|
-
r
|
|
828
|
-
r
|
|
853
|
+
r"^UNKNOWN",
|
|
854
|
+
r"^TEST",
|
|
855
|
+
r"^SAMPLE",
|
|
829
856
|
# Crypto/financial asset patterns
|
|
830
|
-
r
|
|
831
|
-
r
|
|
832
|
-
r
|
|
857
|
+
r"ETHEREUM",
|
|
858
|
+
r"BITCOIN",
|
|
859
|
+
r"CRYPTO",
|
|
833
860
|
]
|
|
834
|
-
|
|
861
|
+
|
|
835
862
|
for pattern in invalid_patterns:
|
|
836
863
|
if re.search(pattern, name):
|
|
837
864
|
return True
|
|
838
|
-
|
|
865
|
+
|
|
839
866
|
return False
|
|
840
867
|
|
|
841
868
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcli-framework
|
|
3
|
-
Version: 7.1.
|
|
3
|
+
Version: 7.1.2
|
|
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>
|