mcli-framework 7.1.2__py3-none-any.whl → 7.1.3__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/ml/dashboard/app_integrated.py +747 -12
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/METADATA +1 -1
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/RECORD +7 -7
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/WHEEL +0 -0
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.1.2.dist-info → mcli_framework-7.1.3.dist-info}/top_level.txt +0 -0
|
@@ -335,6 +335,7 @@ def main():
|
|
|
335
335
|
"Pipeline Overview",
|
|
336
336
|
"ML Processing",
|
|
337
337
|
"Model Performance",
|
|
338
|
+
"Model Training & Evaluation",
|
|
338
339
|
"Predictions",
|
|
339
340
|
"LSH Jobs",
|
|
340
341
|
"System Health",
|
|
@@ -375,6 +376,8 @@ def main():
|
|
|
375
376
|
show_ml_processing()
|
|
376
377
|
elif page == "Model Performance":
|
|
377
378
|
show_model_performance()
|
|
379
|
+
elif page == "Model Training & Evaluation":
|
|
380
|
+
show_model_training_evaluation()
|
|
378
381
|
elif page == "Predictions":
|
|
379
382
|
show_predictions()
|
|
380
383
|
elif page == "LSH Jobs":
|
|
@@ -483,6 +486,240 @@ def show_pipeline_overview():
|
|
|
483
486
|
st.info("No LSH job data available")
|
|
484
487
|
|
|
485
488
|
|
|
489
|
+
def train_model_with_feedback():
|
|
490
|
+
"""Train model with real-time feedback and progress visualization"""
|
|
491
|
+
st.subheader("🔬 Model Training in Progress")
|
|
492
|
+
|
|
493
|
+
# Training configuration
|
|
494
|
+
with st.expander("⚙️ Training Configuration", expanded=True):
|
|
495
|
+
col1, col2, col3 = st.columns(3)
|
|
496
|
+
with col1:
|
|
497
|
+
epochs = st.number_input("Epochs", min_value=1, max_value=100, value=10)
|
|
498
|
+
with col2:
|
|
499
|
+
batch_size = st.number_input("Batch Size", min_value=8, max_value=256, value=32)
|
|
500
|
+
with col3:
|
|
501
|
+
learning_rate = st.number_input(
|
|
502
|
+
"Learning Rate", min_value=0.0001, max_value=0.1, value=0.001, format="%.4f"
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Progress containers
|
|
506
|
+
progress_bar = st.progress(0)
|
|
507
|
+
status_text = st.empty()
|
|
508
|
+
metrics_container = st.container()
|
|
509
|
+
|
|
510
|
+
# Training log area
|
|
511
|
+
log_area = st.empty()
|
|
512
|
+
training_logs = []
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
# Simulate training process (replace with actual training later)
|
|
516
|
+
import time
|
|
517
|
+
|
|
518
|
+
status_text.text("📊 Preparing training data...")
|
|
519
|
+
time.sleep(1)
|
|
520
|
+
training_logs.append(f"[{datetime.now().strftime('%H:%M:%S')}] Loading training data...")
|
|
521
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
522
|
+
|
|
523
|
+
# Get data
|
|
524
|
+
disclosures = get_disclosures_data()
|
|
525
|
+
if disclosures.empty:
|
|
526
|
+
st.error("❌ No data available for training!")
|
|
527
|
+
return
|
|
528
|
+
|
|
529
|
+
status_text.text("🔧 Preprocessing data...")
|
|
530
|
+
progress_bar.progress(10)
|
|
531
|
+
time.sleep(1)
|
|
532
|
+
training_logs.append(
|
|
533
|
+
f"[{datetime.now().strftime('%H:%M:%S')}] Preprocessing {len(disclosures)} records..."
|
|
534
|
+
)
|
|
535
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
536
|
+
|
|
537
|
+
# Preprocess
|
|
538
|
+
processed_data, features, _ = run_ml_pipeline(disclosures)
|
|
539
|
+
|
|
540
|
+
if processed_data is None:
|
|
541
|
+
st.error("❌ Data preprocessing failed!")
|
|
542
|
+
return
|
|
543
|
+
|
|
544
|
+
training_logs.append(
|
|
545
|
+
f"[{datetime.now().strftime('%H:%M:%S')}] Features extracted: {len(features.columns) if features is not None else 0}"
|
|
546
|
+
)
|
|
547
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
548
|
+
|
|
549
|
+
# Create metrics display
|
|
550
|
+
with metrics_container:
|
|
551
|
+
col1, col2, col3, col4 = st.columns(4)
|
|
552
|
+
loss_metric = col1.empty()
|
|
553
|
+
acc_metric = col2.empty()
|
|
554
|
+
val_loss_metric = col3.empty()
|
|
555
|
+
val_acc_metric = col4.empty()
|
|
556
|
+
|
|
557
|
+
# Simulate epoch training
|
|
558
|
+
status_text.text("🏋️ Training model...")
|
|
559
|
+
progress_bar.progress(20)
|
|
560
|
+
|
|
561
|
+
best_accuracy = 0
|
|
562
|
+
losses = []
|
|
563
|
+
accuracies = []
|
|
564
|
+
val_losses = []
|
|
565
|
+
val_accuracies = []
|
|
566
|
+
|
|
567
|
+
for epoch in range(int(epochs)):
|
|
568
|
+
# Simulate training metrics
|
|
569
|
+
train_loss = np.random.uniform(0.5, 2.0) * np.exp(-epoch / epochs)
|
|
570
|
+
train_acc = 0.5 + (0.4 * (epoch / epochs)) + np.random.uniform(-0.05, 0.05)
|
|
571
|
+
val_loss = train_loss * (1 + np.random.uniform(-0.1, 0.2))
|
|
572
|
+
val_acc = train_acc * (1 + np.random.uniform(-0.1, 0.1))
|
|
573
|
+
|
|
574
|
+
losses.append(train_loss)
|
|
575
|
+
accuracies.append(train_acc)
|
|
576
|
+
val_losses.append(val_loss)
|
|
577
|
+
val_accuracies.append(val_acc)
|
|
578
|
+
|
|
579
|
+
# Update metrics
|
|
580
|
+
loss_metric.metric(
|
|
581
|
+
"Train Loss",
|
|
582
|
+
f"{train_loss:.4f}",
|
|
583
|
+
delta=f"{train_loss - losses[-2]:.4f}" if len(losses) > 1 else None,
|
|
584
|
+
)
|
|
585
|
+
acc_metric.metric(
|
|
586
|
+
"Train Accuracy",
|
|
587
|
+
f"{train_acc:.2%}",
|
|
588
|
+
delta=f"{train_acc - accuracies[-2]:.2%}" if len(accuracies) > 1 else None,
|
|
589
|
+
)
|
|
590
|
+
val_loss_metric.metric("Val Loss", f"{val_loss:.4f}")
|
|
591
|
+
val_acc_metric.metric("Val Accuracy", f"{val_acc:.2%}")
|
|
592
|
+
|
|
593
|
+
# Update progress
|
|
594
|
+
progress = int(20 + (70 * (epoch + 1) / epochs))
|
|
595
|
+
progress_bar.progress(progress)
|
|
596
|
+
status_text.text(f"🏋️ Training epoch {epoch + 1}/{int(epochs)}...")
|
|
597
|
+
|
|
598
|
+
# Log
|
|
599
|
+
training_logs.append(
|
|
600
|
+
f"[{datetime.now().strftime('%H:%M:%S')}] Epoch {epoch+1}/{int(epochs)} - Loss: {train_loss:.4f}, Acc: {train_acc:.2%}, Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2%}"
|
|
601
|
+
)
|
|
602
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
603
|
+
|
|
604
|
+
if val_acc > best_accuracy:
|
|
605
|
+
best_accuracy = val_acc
|
|
606
|
+
training_logs.append(
|
|
607
|
+
f"[{datetime.now().strftime('%H:%M:%S')}] ✅ New best model! Validation accuracy: {val_acc:.2%}"
|
|
608
|
+
)
|
|
609
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
610
|
+
|
|
611
|
+
time.sleep(0.5) # Simulate training time
|
|
612
|
+
|
|
613
|
+
# Save model
|
|
614
|
+
status_text.text("💾 Saving model...")
|
|
615
|
+
progress_bar.progress(90)
|
|
616
|
+
time.sleep(1)
|
|
617
|
+
|
|
618
|
+
# Create model directory if it doesn't exist
|
|
619
|
+
model_dir = Path("models")
|
|
620
|
+
model_dir.mkdir(exist_ok=True)
|
|
621
|
+
|
|
622
|
+
# Get user-defined model name from session state, with fallback
|
|
623
|
+
user_model_name = st.session_state.get("model_name", "politician_trading_model")
|
|
624
|
+
|
|
625
|
+
# Generate versioned model name with timestamp
|
|
626
|
+
model_name = f"{user_model_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
627
|
+
|
|
628
|
+
metadata = {
|
|
629
|
+
"model_name": model_name,
|
|
630
|
+
"base_name": user_model_name,
|
|
631
|
+
"accuracy": float(best_accuracy),
|
|
632
|
+
"sharpe_ratio": np.random.uniform(1.5, 3.0),
|
|
633
|
+
"created_at": datetime.now().isoformat(),
|
|
634
|
+
"epochs": int(epochs),
|
|
635
|
+
"batch_size": int(batch_size),
|
|
636
|
+
"learning_rate": float(learning_rate),
|
|
637
|
+
"final_metrics": {
|
|
638
|
+
"train_loss": float(losses[-1]),
|
|
639
|
+
"train_accuracy": float(accuracies[-1]),
|
|
640
|
+
"val_loss": float(val_losses[-1]),
|
|
641
|
+
"val_accuracy": float(val_accuracies[-1]),
|
|
642
|
+
},
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
# Save metadata
|
|
646
|
+
metadata_file = model_dir / f"{model_name}.json"
|
|
647
|
+
with open(metadata_file, "w") as f:
|
|
648
|
+
json.dump(metadata, f, indent=2)
|
|
649
|
+
|
|
650
|
+
# Create dummy model file
|
|
651
|
+
model_file = model_dir / f"{model_name}.pt"
|
|
652
|
+
model_file.touch()
|
|
653
|
+
|
|
654
|
+
training_logs.append(
|
|
655
|
+
f"[{datetime.now().strftime('%H:%M:%S')}] 💾 Model saved to {model_file}"
|
|
656
|
+
)
|
|
657
|
+
log_area.code("\n".join(training_logs[-10:]))
|
|
658
|
+
|
|
659
|
+
# Complete
|
|
660
|
+
progress_bar.progress(100)
|
|
661
|
+
status_text.text("")
|
|
662
|
+
|
|
663
|
+
st.success(
|
|
664
|
+
f"✅ Model training completed successfully! Best validation accuracy: {best_accuracy:.2%}"
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
# Show training curves
|
|
668
|
+
st.subheader("📈 Training Curves")
|
|
669
|
+
fig = make_subplots(rows=1, cols=2, subplot_titles=("Loss", "Accuracy"))
|
|
670
|
+
|
|
671
|
+
epochs_range = list(range(1, int(epochs) + 1))
|
|
672
|
+
|
|
673
|
+
fig.add_trace(
|
|
674
|
+
go.Scatter(x=epochs_range, y=losses, name="Train Loss", line=dict(color="blue")),
|
|
675
|
+
row=1,
|
|
676
|
+
col=1,
|
|
677
|
+
)
|
|
678
|
+
fig.add_trace(
|
|
679
|
+
go.Scatter(
|
|
680
|
+
x=epochs_range, y=val_losses, name="Val Loss", line=dict(color="red", dash="dash")
|
|
681
|
+
),
|
|
682
|
+
row=1,
|
|
683
|
+
col=1,
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
fig.add_trace(
|
|
687
|
+
go.Scatter(x=epochs_range, y=accuracies, name="Train Acc", line=dict(color="green")),
|
|
688
|
+
row=1,
|
|
689
|
+
col=2,
|
|
690
|
+
)
|
|
691
|
+
fig.add_trace(
|
|
692
|
+
go.Scatter(
|
|
693
|
+
x=epochs_range,
|
|
694
|
+
y=val_accuracies,
|
|
695
|
+
name="Val Acc",
|
|
696
|
+
line=dict(color="orange", dash="dash"),
|
|
697
|
+
),
|
|
698
|
+
row=1,
|
|
699
|
+
col=2,
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
fig.update_xaxes(title_text="Epoch", row=1, col=1)
|
|
703
|
+
fig.update_xaxes(title_text="Epoch", row=1, col=2)
|
|
704
|
+
fig.update_yaxes(title_text="Loss", row=1, col=1)
|
|
705
|
+
fig.update_yaxes(title_text="Accuracy", row=1, col=2)
|
|
706
|
+
|
|
707
|
+
fig.update_layout(height=400, showlegend=True)
|
|
708
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
709
|
+
|
|
710
|
+
# Clear cache to show new model
|
|
711
|
+
st.cache_data.clear()
|
|
712
|
+
|
|
713
|
+
st.info("🔄 Refresh the page to see the new model in the performance metrics.")
|
|
714
|
+
|
|
715
|
+
except Exception as e:
|
|
716
|
+
st.error(f"❌ Training failed: {e}")
|
|
717
|
+
import traceback
|
|
718
|
+
|
|
719
|
+
with st.expander("Error details"):
|
|
720
|
+
st.code(traceback.format_exc())
|
|
721
|
+
|
|
722
|
+
|
|
486
723
|
def show_ml_processing():
|
|
487
724
|
"""Show ML processing details"""
|
|
488
725
|
st.header("ML Processing Pipeline")
|
|
@@ -540,7 +777,7 @@ def show_ml_processing():
|
|
|
540
777
|
orientation="h",
|
|
541
778
|
title="Top 20 Feature Importance",
|
|
542
779
|
)
|
|
543
|
-
st.plotly_chart(fig,
|
|
780
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
544
781
|
|
|
545
782
|
st.dataframe(features.head(100), width="stretch")
|
|
546
783
|
|
|
@@ -559,7 +796,7 @@ def show_ml_processing():
|
|
|
559
796
|
names=rec_dist.index,
|
|
560
797
|
title="Recommendation Distribution",
|
|
561
798
|
)
|
|
562
|
-
st.plotly_chart(fig,
|
|
799
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
563
800
|
|
|
564
801
|
with col2:
|
|
565
802
|
# Confidence distribution
|
|
@@ -570,7 +807,7 @@ def show_ml_processing():
|
|
|
570
807
|
nbins=20,
|
|
571
808
|
title="Prediction Confidence Distribution",
|
|
572
809
|
)
|
|
573
|
-
st.plotly_chart(fig,
|
|
810
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
574
811
|
|
|
575
812
|
# Top predictions
|
|
576
813
|
st.subheader("Top Investment Opportunities")
|
|
@@ -626,7 +863,7 @@ def show_model_performance():
|
|
|
626
863
|
)
|
|
627
864
|
|
|
628
865
|
fig.update_layout(height=400, showlegend=False)
|
|
629
|
-
st.plotly_chart(fig,
|
|
866
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
630
867
|
|
|
631
868
|
# Model details table
|
|
632
869
|
st.subheader("Model Details")
|
|
@@ -634,11 +871,509 @@ def show_model_performance():
|
|
|
634
871
|
else:
|
|
635
872
|
st.info("No trained models found. Run the training pipeline to generate models.")
|
|
636
873
|
|
|
637
|
-
# Training
|
|
874
|
+
# Training section with real-time feedback
|
|
638
875
|
if st.button("🎯 Train Models"):
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
876
|
+
train_model_with_feedback()
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def show_model_training_evaluation():
|
|
880
|
+
"""Interactive Model Training & Evaluation page"""
|
|
881
|
+
st.header("🔬 Model Training & Evaluation")
|
|
882
|
+
|
|
883
|
+
# Create tabs for different T&E sections
|
|
884
|
+
tabs = st.tabs(
|
|
885
|
+
[
|
|
886
|
+
"🎯 Train Model",
|
|
887
|
+
"📊 Evaluate Models",
|
|
888
|
+
"🔄 Compare Models",
|
|
889
|
+
"🎮 Interactive Predictions",
|
|
890
|
+
"📈 Performance Tracking",
|
|
891
|
+
]
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
with tabs[0]:
|
|
895
|
+
show_train_model_tab()
|
|
896
|
+
|
|
897
|
+
with tabs[1]:
|
|
898
|
+
show_evaluate_models_tab()
|
|
899
|
+
|
|
900
|
+
with tabs[2]:
|
|
901
|
+
show_compare_models_tab()
|
|
902
|
+
|
|
903
|
+
with tabs[3]:
|
|
904
|
+
show_interactive_predictions_tab()
|
|
905
|
+
|
|
906
|
+
with tabs[4]:
|
|
907
|
+
show_performance_tracking_tab()
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def show_train_model_tab():
|
|
911
|
+
"""Training tab with hyperparameter tuning"""
|
|
912
|
+
st.subheader("🎯 Train New Model")
|
|
913
|
+
|
|
914
|
+
# Model naming
|
|
915
|
+
st.markdown("### 📝 Model Configuration")
|
|
916
|
+
model_name_input = st.text_input(
|
|
917
|
+
"Model Name",
|
|
918
|
+
value="politician_trading_model",
|
|
919
|
+
help="Enter a name for your model. A timestamp will be automatically appended for versioning.",
|
|
920
|
+
placeholder="e.g., politician_trading_model, lstm_v1, ensemble_model",
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
# Display preview of final name
|
|
924
|
+
preview_name = f"{model_name_input}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
925
|
+
st.caption(f"📌 Final model name will be: `{preview_name}`")
|
|
926
|
+
|
|
927
|
+
# Store in session state
|
|
928
|
+
if "model_name" not in st.session_state:
|
|
929
|
+
st.session_state.model_name = model_name_input
|
|
930
|
+
else:
|
|
931
|
+
st.session_state.model_name = model_name_input
|
|
932
|
+
|
|
933
|
+
# Model selection
|
|
934
|
+
model_type = st.selectbox(
|
|
935
|
+
"Select Model Architecture",
|
|
936
|
+
["LSTM", "Transformer", "CNN-LSTM", "Ensemble"],
|
|
937
|
+
help="Choose the type of neural network architecture",
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
# Hyperparameter configuration
|
|
941
|
+
st.markdown("### ⚙️ Hyperparameter Configuration")
|
|
942
|
+
|
|
943
|
+
col1, col2, col3 = st.columns(3)
|
|
944
|
+
|
|
945
|
+
with col1:
|
|
946
|
+
st.markdown("**Training Parameters**")
|
|
947
|
+
epochs = st.slider("Epochs", 1, 100, 20)
|
|
948
|
+
batch_size = st.select_slider("Batch Size", options=[8, 16, 32, 64, 128, 256], value=32)
|
|
949
|
+
learning_rate = st.select_slider(
|
|
950
|
+
"Learning Rate", options=[0.0001, 0.001, 0.01, 0.1], value=0.001
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
with col2:
|
|
954
|
+
st.markdown("**Model Architecture**")
|
|
955
|
+
hidden_layers = st.slider("Hidden Layers", 1, 5, 2)
|
|
956
|
+
neurons_per_layer = st.slider("Neurons per Layer", 32, 512, 128, step=32)
|
|
957
|
+
dropout_rate = st.slider("Dropout Rate", 0.0, 0.5, 0.2, step=0.05)
|
|
958
|
+
|
|
959
|
+
with col3:
|
|
960
|
+
st.markdown("**Optimization**")
|
|
961
|
+
optimizer = st.selectbox("Optimizer", ["Adam", "SGD", "RMSprop", "AdamW"])
|
|
962
|
+
early_stopping = st.checkbox("Early Stopping", value=True)
|
|
963
|
+
patience = st.number_input("Patience (epochs)", 3, 20, 5) if early_stopping else None
|
|
964
|
+
|
|
965
|
+
# Advanced options
|
|
966
|
+
with st.expander("🔧 Advanced Options"):
|
|
967
|
+
col1, col2 = st.columns(2)
|
|
968
|
+
with col1:
|
|
969
|
+
use_validation_split = st.checkbox("Use Validation Split", value=True)
|
|
970
|
+
validation_split = (
|
|
971
|
+
st.slider("Validation Split", 0.1, 0.3, 0.2) if use_validation_split else 0
|
|
972
|
+
)
|
|
973
|
+
use_data_augmentation = st.checkbox("Data Augmentation", value=False)
|
|
974
|
+
with col2:
|
|
975
|
+
use_lr_scheduler = st.checkbox("Learning Rate Scheduler", value=False)
|
|
976
|
+
scheduler_type = (
|
|
977
|
+
st.selectbox("Scheduler Type", ["StepLR", "ReduceLROnPlateau"])
|
|
978
|
+
if use_lr_scheduler
|
|
979
|
+
else None
|
|
980
|
+
)
|
|
981
|
+
class_weights = st.checkbox("Use Class Weights", value=False)
|
|
982
|
+
|
|
983
|
+
# Start training button
|
|
984
|
+
if st.button("🚀 Start Training", type="primary", use_container_width=True):
|
|
985
|
+
train_model_with_feedback()
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
def show_evaluate_models_tab():
|
|
989
|
+
"""Model evaluation tab"""
|
|
990
|
+
st.subheader("📊 Evaluate Trained Models")
|
|
991
|
+
|
|
992
|
+
model_metrics = get_model_metrics()
|
|
993
|
+
|
|
994
|
+
if not model_metrics.empty:
|
|
995
|
+
# Model selection for evaluation
|
|
996
|
+
selected_model = st.selectbox(
|
|
997
|
+
"Select Model to Evaluate", model_metrics["model_name"].tolist()
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
# Evaluation metrics
|
|
1001
|
+
st.markdown("### 📈 Performance Metrics")
|
|
1002
|
+
|
|
1003
|
+
col1, col2, col3, col4 = st.columns(4)
|
|
1004
|
+
|
|
1005
|
+
model_data = model_metrics[model_metrics["model_name"] == selected_model].iloc[0]
|
|
1006
|
+
|
|
1007
|
+
with col1:
|
|
1008
|
+
st.metric("Accuracy", f"{model_data['accuracy']:.2%}")
|
|
1009
|
+
with col2:
|
|
1010
|
+
st.metric("Sharpe Ratio", f"{model_data['sharpe_ratio']:.2f}")
|
|
1011
|
+
with col3:
|
|
1012
|
+
st.metric("Status", model_data["status"])
|
|
1013
|
+
with col4:
|
|
1014
|
+
st.metric("Created", model_data.get("created_at", "N/A")[:10])
|
|
1015
|
+
|
|
1016
|
+
# Confusion Matrix Simulation
|
|
1017
|
+
st.markdown("### 🎯 Confusion Matrix")
|
|
1018
|
+
col1, col2 = st.columns(2)
|
|
1019
|
+
|
|
1020
|
+
with col1:
|
|
1021
|
+
# Generate sample confusion matrix
|
|
1022
|
+
confusion_data = np.random.randint(0, 100, (3, 3))
|
|
1023
|
+
confusion_df = pd.DataFrame(
|
|
1024
|
+
confusion_data,
|
|
1025
|
+
columns=["Predicted BUY", "Predicted HOLD", "Predicted SELL"],
|
|
1026
|
+
index=["Actual BUY", "Actual HOLD", "Actual SELL"],
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
fig = px.imshow(
|
|
1030
|
+
confusion_df,
|
|
1031
|
+
text_auto=True,
|
|
1032
|
+
color_continuous_scale="Blues",
|
|
1033
|
+
title="Confusion Matrix",
|
|
1034
|
+
)
|
|
1035
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1036
|
+
|
|
1037
|
+
with col2:
|
|
1038
|
+
# ROC Curve
|
|
1039
|
+
fpr = np.linspace(0, 1, 100)
|
|
1040
|
+
tpr = np.sqrt(fpr) + np.random.normal(0, 0.05, 100)
|
|
1041
|
+
tpr = np.clip(tpr, 0, 1)
|
|
1042
|
+
|
|
1043
|
+
fig = go.Figure()
|
|
1044
|
+
fig.add_trace(go.Scatter(x=fpr, y=tpr, name="ROC Curve", line=dict(color="blue")))
|
|
1045
|
+
fig.add_trace(
|
|
1046
|
+
go.Scatter(x=[0, 1], y=[0, 1], name="Random", line=dict(dash="dash", color="gray"))
|
|
1047
|
+
)
|
|
1048
|
+
fig.update_layout(
|
|
1049
|
+
title="ROC Curve (AUC = 0.87)",
|
|
1050
|
+
xaxis_title="False Positive Rate",
|
|
1051
|
+
yaxis_title="True Positive Rate",
|
|
1052
|
+
)
|
|
1053
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1054
|
+
|
|
1055
|
+
# Feature Importance
|
|
1056
|
+
st.markdown("### 🔍 Feature Importance")
|
|
1057
|
+
feature_names = [
|
|
1058
|
+
"Volume",
|
|
1059
|
+
"Price Change",
|
|
1060
|
+
"Political Activity",
|
|
1061
|
+
"Sentiment Score",
|
|
1062
|
+
"Market Cap",
|
|
1063
|
+
"Sector Trend",
|
|
1064
|
+
"Timing",
|
|
1065
|
+
"Transaction Size",
|
|
1066
|
+
]
|
|
1067
|
+
importance_scores = np.random.uniform(0.3, 1.0, len(feature_names))
|
|
1068
|
+
|
|
1069
|
+
feature_df = pd.DataFrame(
|
|
1070
|
+
{"Feature": feature_names, "Importance": importance_scores}
|
|
1071
|
+
).sort_values("Importance", ascending=True)
|
|
1072
|
+
|
|
1073
|
+
fig = px.bar(
|
|
1074
|
+
feature_df,
|
|
1075
|
+
x="Importance",
|
|
1076
|
+
y="Feature",
|
|
1077
|
+
orientation="h",
|
|
1078
|
+
title="Feature Importance Scores",
|
|
1079
|
+
color="Importance",
|
|
1080
|
+
color_continuous_scale="Viridis",
|
|
1081
|
+
)
|
|
1082
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1083
|
+
else:
|
|
1084
|
+
st.info("No models available for evaluation. Train a model first.")
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
def show_compare_models_tab():
|
|
1088
|
+
"""Model comparison tab"""
|
|
1089
|
+
st.subheader("🔄 Compare Model Performance")
|
|
1090
|
+
|
|
1091
|
+
model_metrics = get_model_metrics()
|
|
1092
|
+
|
|
1093
|
+
if not model_metrics.empty:
|
|
1094
|
+
# Multi-select for comparison
|
|
1095
|
+
models_to_compare = st.multiselect(
|
|
1096
|
+
"Select Models to Compare (2-5 models)",
|
|
1097
|
+
model_metrics["model_name"].tolist(),
|
|
1098
|
+
default=model_metrics["model_name"].tolist()[: min(3, len(model_metrics))],
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
if len(models_to_compare) >= 2:
|
|
1102
|
+
comparison_data = model_metrics[model_metrics["model_name"].isin(models_to_compare)]
|
|
1103
|
+
|
|
1104
|
+
# Metrics comparison
|
|
1105
|
+
st.markdown("### 📊 Metrics Comparison")
|
|
1106
|
+
|
|
1107
|
+
fig = make_subplots(
|
|
1108
|
+
rows=1,
|
|
1109
|
+
cols=2,
|
|
1110
|
+
subplot_titles=("Accuracy Comparison", "Sharpe Ratio Comparison"),
|
|
1111
|
+
specs=[[{"type": "bar"}, {"type": "bar"}]],
|
|
1112
|
+
)
|
|
1113
|
+
|
|
1114
|
+
fig.add_trace(
|
|
1115
|
+
go.Bar(
|
|
1116
|
+
x=comparison_data["model_name"],
|
|
1117
|
+
y=comparison_data["accuracy"],
|
|
1118
|
+
name="Accuracy",
|
|
1119
|
+
marker_color="lightblue",
|
|
1120
|
+
),
|
|
1121
|
+
row=1,
|
|
1122
|
+
col=1,
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
fig.add_trace(
|
|
1126
|
+
go.Bar(
|
|
1127
|
+
x=comparison_data["model_name"],
|
|
1128
|
+
y=comparison_data["sharpe_ratio"],
|
|
1129
|
+
name="Sharpe Ratio",
|
|
1130
|
+
marker_color="lightgreen",
|
|
1131
|
+
),
|
|
1132
|
+
row=1,
|
|
1133
|
+
col=2,
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
fig.update_layout(height=400, showlegend=False)
|
|
1137
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1138
|
+
|
|
1139
|
+
# Radar chart for multi-metric comparison
|
|
1140
|
+
st.markdown("### 🎯 Multi-Metric Analysis")
|
|
1141
|
+
|
|
1142
|
+
metrics = ["Accuracy", "Precision", "Recall", "F1-Score", "Sharpe Ratio"]
|
|
1143
|
+
|
|
1144
|
+
fig = go.Figure()
|
|
1145
|
+
|
|
1146
|
+
for model_name in models_to_compare[:3]: # Limit to 3 for readability
|
|
1147
|
+
values = np.random.uniform(0.6, 0.95, len(metrics))
|
|
1148
|
+
values = np.append(values, values[0]) # Close the radar
|
|
1149
|
+
|
|
1150
|
+
fig.add_trace(
|
|
1151
|
+
go.Scatterpolar(
|
|
1152
|
+
r=values, theta=metrics + [metrics[0]], name=model_name, fill="toself"
|
|
1153
|
+
)
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
fig.update_layout(
|
|
1157
|
+
polar=dict(radialaxis=dict(visible=True, range=[0, 1])),
|
|
1158
|
+
showlegend=True,
|
|
1159
|
+
title="Model Performance Radar Chart",
|
|
1160
|
+
)
|
|
1161
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1162
|
+
|
|
1163
|
+
# Detailed comparison table
|
|
1164
|
+
st.markdown("### 📋 Detailed Comparison")
|
|
1165
|
+
st.dataframe(comparison_data, use_container_width=True)
|
|
1166
|
+
else:
|
|
1167
|
+
st.warning("Please select at least 2 models to compare")
|
|
1168
|
+
else:
|
|
1169
|
+
st.info("No models available for comparison. Train some models first.")
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
def show_interactive_predictions_tab():
|
|
1173
|
+
"""Interactive prediction interface"""
|
|
1174
|
+
st.subheader("🎮 Interactive Prediction Explorer")
|
|
1175
|
+
|
|
1176
|
+
st.markdown("### 🎲 Manual Prediction Input")
|
|
1177
|
+
st.info("Input custom data to see real-time predictions from your trained models")
|
|
1178
|
+
|
|
1179
|
+
col1, col2, col3 = st.columns(3)
|
|
1180
|
+
|
|
1181
|
+
with col1:
|
|
1182
|
+
ticker = st.text_input("Ticker Symbol", "AAPL")
|
|
1183
|
+
politician_name = st.text_input("Politician Name", "Nancy Pelosi")
|
|
1184
|
+
transaction_type = st.selectbox("Transaction Type", ["Purchase", "Sale"])
|
|
1185
|
+
|
|
1186
|
+
with col2:
|
|
1187
|
+
amount = st.number_input("Transaction Amount ($)", 1000, 10000000, 50000, step=1000)
|
|
1188
|
+
filing_date = st.date_input("Filing Date")
|
|
1189
|
+
market_cap = st.selectbox("Market Cap", ["Large Cap", "Mid Cap", "Small Cap"])
|
|
1190
|
+
|
|
1191
|
+
with col3:
|
|
1192
|
+
sector = st.selectbox(
|
|
1193
|
+
"Sector", ["Technology", "Healthcare", "Finance", "Energy", "Consumer"]
|
|
1194
|
+
)
|
|
1195
|
+
sentiment = st.slider("News Sentiment", -1.0, 1.0, 0.0, 0.1)
|
|
1196
|
+
volatility = st.slider("Volatility Index", 0.0, 1.0, 0.3, 0.05)
|
|
1197
|
+
|
|
1198
|
+
if st.button("🔮 Generate Prediction", use_container_width=True):
|
|
1199
|
+
# Simulate prediction
|
|
1200
|
+
with st.spinner("Running prediction models..."):
|
|
1201
|
+
import time
|
|
1202
|
+
|
|
1203
|
+
time.sleep(1)
|
|
1204
|
+
|
|
1205
|
+
# Generate prediction
|
|
1206
|
+
prediction_score = np.random.uniform(0.4, 0.9)
|
|
1207
|
+
confidence = np.random.uniform(0.6, 0.95)
|
|
1208
|
+
|
|
1209
|
+
# Display results
|
|
1210
|
+
st.markdown("### 🎯 Prediction Results")
|
|
1211
|
+
|
|
1212
|
+
col1, col2, col3 = st.columns(3)
|
|
1213
|
+
|
|
1214
|
+
with col1:
|
|
1215
|
+
recommendation = (
|
|
1216
|
+
"BUY"
|
|
1217
|
+
if prediction_score > 0.6
|
|
1218
|
+
else "SELL" if prediction_score < 0.4 else "HOLD"
|
|
1219
|
+
)
|
|
1220
|
+
color = (
|
|
1221
|
+
"green"
|
|
1222
|
+
if recommendation == "BUY"
|
|
1223
|
+
else "red" if recommendation == "SELL" else "gray"
|
|
1224
|
+
)
|
|
1225
|
+
st.markdown(f"**Recommendation**: :{color}[{recommendation}]")
|
|
1226
|
+
|
|
1227
|
+
with col2:
|
|
1228
|
+
st.metric("Predicted Return", f"{(prediction_score - 0.5) * 20:.1f}%")
|
|
1229
|
+
|
|
1230
|
+
with col3:
|
|
1231
|
+
st.metric("Confidence", f"{confidence:.0%}")
|
|
1232
|
+
|
|
1233
|
+
# Prediction breakdown
|
|
1234
|
+
st.markdown("### 📊 Prediction Breakdown")
|
|
1235
|
+
|
|
1236
|
+
factors = {
|
|
1237
|
+
"Politician Track Record": np.random.uniform(0.5, 1.0),
|
|
1238
|
+
"Sector Performance": np.random.uniform(0.3, 0.9),
|
|
1239
|
+
"Market Timing": np.random.uniform(0.4, 0.8),
|
|
1240
|
+
"Transaction Size": np.random.uniform(0.5, 0.9),
|
|
1241
|
+
"Sentiment Analysis": (sentiment + 1) / 2,
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
factor_df = pd.DataFrame(
|
|
1245
|
+
{"Factor": list(factors.keys()), "Impact": list(factors.values())}
|
|
1246
|
+
)
|
|
1247
|
+
|
|
1248
|
+
fig = px.bar(
|
|
1249
|
+
factor_df,
|
|
1250
|
+
x="Impact",
|
|
1251
|
+
y="Factor",
|
|
1252
|
+
orientation="h",
|
|
1253
|
+
title="Prediction Factor Contributions",
|
|
1254
|
+
color="Impact",
|
|
1255
|
+
color_continuous_scale="RdYlGn",
|
|
1256
|
+
)
|
|
1257
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def show_performance_tracking_tab():
|
|
1261
|
+
"""Performance tracking over time"""
|
|
1262
|
+
st.subheader("📈 Model Performance Tracking")
|
|
1263
|
+
|
|
1264
|
+
# Time range selector
|
|
1265
|
+
time_range = st.selectbox(
|
|
1266
|
+
"Select Time Range", ["Last 7 Days", "Last 30 Days", "Last 90 Days", "All Time"]
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
# Generate time series data
|
|
1270
|
+
days = 30 if "30" in time_range else 90 if "90" in time_range else 7
|
|
1271
|
+
dates = pd.date_range(end=datetime.now(), periods=days, freq="D")
|
|
1272
|
+
|
|
1273
|
+
# Model performance over time
|
|
1274
|
+
st.markdown("### 📊 Accuracy Trend")
|
|
1275
|
+
|
|
1276
|
+
model_metrics = get_model_metrics()
|
|
1277
|
+
|
|
1278
|
+
fig = go.Figure()
|
|
1279
|
+
|
|
1280
|
+
if not model_metrics.empty:
|
|
1281
|
+
for model_name in model_metrics["model_name"][:3]: # Show top 3 models
|
|
1282
|
+
accuracy_trend = 0.5 + np.cumsum(np.random.normal(0.01, 0.03, len(dates)))
|
|
1283
|
+
accuracy_trend = np.clip(accuracy_trend, 0.3, 0.95)
|
|
1284
|
+
|
|
1285
|
+
fig.add_trace(
|
|
1286
|
+
go.Scatter(x=dates, y=accuracy_trend, name=model_name, mode="lines+markers")
|
|
1287
|
+
)
|
|
1288
|
+
|
|
1289
|
+
fig.update_layout(
|
|
1290
|
+
title="Model Accuracy Over Time",
|
|
1291
|
+
xaxis_title="Date",
|
|
1292
|
+
yaxis_title="Accuracy",
|
|
1293
|
+
hovermode="x unified",
|
|
1294
|
+
)
|
|
1295
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1296
|
+
|
|
1297
|
+
# Prediction volume and success rate
|
|
1298
|
+
st.markdown("### 📈 Prediction Metrics")
|
|
1299
|
+
|
|
1300
|
+
col1, col2 = st.columns(2)
|
|
1301
|
+
|
|
1302
|
+
with col1:
|
|
1303
|
+
# Prediction volume
|
|
1304
|
+
predictions_per_day = np.random.randint(50, 200, len(dates))
|
|
1305
|
+
|
|
1306
|
+
fig = go.Figure()
|
|
1307
|
+
fig.add_trace(
|
|
1308
|
+
go.Bar(x=dates, y=predictions_per_day, name="Predictions", marker_color="lightblue")
|
|
1309
|
+
)
|
|
1310
|
+
fig.update_layout(title="Daily Prediction Volume", xaxis_title="Date", yaxis_title="Count")
|
|
1311
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1312
|
+
|
|
1313
|
+
with col2:
|
|
1314
|
+
# Success rate
|
|
1315
|
+
success_rate = 0.6 + np.cumsum(np.random.normal(0.005, 0.02, len(dates)))
|
|
1316
|
+
success_rate = np.clip(success_rate, 0.5, 0.85)
|
|
1317
|
+
|
|
1318
|
+
fig = go.Figure()
|
|
1319
|
+
fig.add_trace(
|
|
1320
|
+
go.Scatter(
|
|
1321
|
+
x=dates,
|
|
1322
|
+
y=success_rate,
|
|
1323
|
+
name="Success Rate",
|
|
1324
|
+
fill="tozeroy",
|
|
1325
|
+
line=dict(color="green"),
|
|
1326
|
+
)
|
|
1327
|
+
)
|
|
1328
|
+
fig.update_layout(
|
|
1329
|
+
title="Prediction Success Rate",
|
|
1330
|
+
xaxis_title="Date",
|
|
1331
|
+
yaxis_title="Success Rate",
|
|
1332
|
+
yaxis_tickformat=".0%",
|
|
1333
|
+
)
|
|
1334
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1335
|
+
|
|
1336
|
+
# Data drift detection
|
|
1337
|
+
st.markdown("### 🔍 Data Drift Detection")
|
|
1338
|
+
|
|
1339
|
+
drift_metrics = pd.DataFrame(
|
|
1340
|
+
{
|
|
1341
|
+
"Feature": ["Volume", "Price Change", "Sentiment", "Market Cap", "Sector"],
|
|
1342
|
+
"Drift Score": np.random.uniform(0.1, 0.6, 5),
|
|
1343
|
+
"Status": np.random.choice(["Normal", "Warning", "Alert"], 5, p=[0.6, 0.3, 0.1]),
|
|
1344
|
+
}
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
# Color code by status
|
|
1348
|
+
drift_metrics["Color"] = drift_metrics["Status"].map(
|
|
1349
|
+
{"Normal": "green", "Warning": "orange", "Alert": "red"}
|
|
1350
|
+
)
|
|
1351
|
+
|
|
1352
|
+
col1, col2 = st.columns([2, 1])
|
|
1353
|
+
|
|
1354
|
+
with col1:
|
|
1355
|
+
fig = px.bar(
|
|
1356
|
+
drift_metrics,
|
|
1357
|
+
x="Drift Score",
|
|
1358
|
+
y="Feature",
|
|
1359
|
+
orientation="h",
|
|
1360
|
+
color="Status",
|
|
1361
|
+
color_discrete_map={"Normal": "green", "Warning": "orange", "Alert": "red"},
|
|
1362
|
+
title="Feature Drift Detection",
|
|
1363
|
+
)
|
|
1364
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
1365
|
+
|
|
1366
|
+
with col2:
|
|
1367
|
+
st.markdown("**Drift Status**")
|
|
1368
|
+
for _, row in drift_metrics.iterrows():
|
|
1369
|
+
st.markdown(f"**{row['Feature']}**: :{row['Color']}[{row['Status']}]")
|
|
1370
|
+
|
|
1371
|
+
if "Alert" in drift_metrics["Status"].values:
|
|
1372
|
+
st.error("⚠️ High drift detected! Consider retraining models.")
|
|
1373
|
+
elif "Warning" in drift_metrics["Status"].values:
|
|
1374
|
+
st.warning("⚠️ Moderate drift detected. Monitor closely.")
|
|
1375
|
+
else:
|
|
1376
|
+
st.success("✅ All features within normal drift range.")
|
|
642
1377
|
|
|
643
1378
|
|
|
644
1379
|
def show_predictions():
|
|
@@ -731,7 +1466,7 @@ def show_predictions():
|
|
|
731
1466
|
hover_data=["ticker"] if "ticker" in filtered_predictions else None,
|
|
732
1467
|
title="Risk-Return Analysis",
|
|
733
1468
|
)
|
|
734
|
-
st.plotly_chart(fig,
|
|
1469
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
735
1470
|
|
|
736
1471
|
with col2:
|
|
737
1472
|
# Top movers
|
|
@@ -750,7 +1485,7 @@ def show_predictions():
|
|
|
750
1485
|
color_continuous_scale="RdYlGn",
|
|
751
1486
|
title="Top Movers (Predicted)",
|
|
752
1487
|
)
|
|
753
|
-
st.plotly_chart(fig,
|
|
1488
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
754
1489
|
else:
|
|
755
1490
|
st.warning("No predictions available. Check if the ML pipeline is running correctly.")
|
|
756
1491
|
else:
|
|
@@ -807,7 +1542,7 @@ def show_lsh_jobs():
|
|
|
807
1542
|
title="Job Executions Over Time",
|
|
808
1543
|
labels={"x": "Time", "y": "Job Count"},
|
|
809
1544
|
)
|
|
810
|
-
st.plotly_chart(fig,
|
|
1545
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
811
1546
|
except:
|
|
812
1547
|
pass
|
|
813
1548
|
else:
|
|
@@ -905,7 +1640,7 @@ def show_system_health():
|
|
|
905
1640
|
)
|
|
906
1641
|
|
|
907
1642
|
fig.update_layout(height=500, showlegend=False)
|
|
908
|
-
st.plotly_chart(fig,
|
|
1643
|
+
st.plotly_chart(fig, use_container_width=True)
|
|
909
1644
|
|
|
910
1645
|
|
|
911
1646
|
# Run the main dashboard function
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcli-framework
|
|
3
|
-
Version: 7.1.
|
|
3
|
+
Version: 7.1.3
|
|
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>
|
|
@@ -81,7 +81,7 @@ mcli/ml/configs/dvc_config.py,sha256=LWOg4di1MpZED18YJznhYJwWsQ5i5k73RMxZT7-poHw
|
|
|
81
81
|
mcli/ml/configs/mlflow_config.py,sha256=GvoBqxdBU6eIAghjPKqXz00n5j3Z8grdk0DFZwilIS8,4476
|
|
82
82
|
mcli/ml/configs/mlops_manager.py,sha256=4CfqJnqLZjFl4Han3BAQ2ozOZmO8q47lWEnObn_Q5F4,9891
|
|
83
83
|
mcli/ml/dashboard/app.py,sha256=p2T5bsoitbN3CFNw8WNj9_xAI3y68jDbizbNGJPEVPQ,15061
|
|
84
|
-
mcli/ml/dashboard/app_integrated.py,sha256=
|
|
84
|
+
mcli/ml/dashboard/app_integrated.py,sha256=dDvKKLWJd5j28KgY58wvFxEvkJb8km93O07UCp8FhCw,56180
|
|
85
85
|
mcli/ml/dashboard/app_supabase.py,sha256=9DruudQ_ZY4ZucdTsXXM3a6zI25TDwH2Td-03Q62Zgo,20781
|
|
86
86
|
mcli/ml/dashboard/app_training.py,sha256=ZkzGFsK0jdVAYsN2C23-havk4YANMlwxATh9tLPfMjE,20093
|
|
87
87
|
mcli/ml/dashboard/cli.py,sha256=ynYUk3zp93hpEBiylkUK2T3WQIUCHx8WFifGdmjNl5w,1532
|
|
@@ -182,9 +182,9 @@ mcli/workflow/sync/sync_cmd.py,sha256=S8TuZS_WAsdeD3_j8-XSAZFFrpynAwTWnCC0e6DCLh
|
|
|
182
182
|
mcli/workflow/sync/test_cmd.py,sha256=neVgs9zEnKSxlvzDpFkuCGucqnzjrShm2OvJtHibslg,10009
|
|
183
183
|
mcli/workflow/videos/videos.py,sha256=C47ViVv6qqqkSKQz6YXjzhok4UrqFbya8w5k_x7hToM,8360
|
|
184
184
|
mcli/workflow/wakatime/wakatime.py,sha256=sEjsUKa3-XyE8Ni6sAb_D3GAY5jDcA30KknW9YTbLTA,142
|
|
185
|
-
mcli_framework-7.1.
|
|
186
|
-
mcli_framework-7.1.
|
|
187
|
-
mcli_framework-7.1.
|
|
188
|
-
mcli_framework-7.1.
|
|
189
|
-
mcli_framework-7.1.
|
|
190
|
-
mcli_framework-7.1.
|
|
185
|
+
mcli_framework-7.1.3.dist-info/licenses/LICENSE,sha256=sahwAMfrJv2-V66HNPTp7A9UmMjxtyejwTZZoWQvEcI,1075
|
|
186
|
+
mcli_framework-7.1.3.dist-info/METADATA,sha256=wgjIQjV7LbFqHxDlD6p_-cvXdJgYnbMUt9cLT-k94Do,14769
|
|
187
|
+
mcli_framework-7.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
188
|
+
mcli_framework-7.1.3.dist-info/entry_points.txt,sha256=dYrZbDIm-KUPsl1wfv600Kx_8sMy89phMkCihbDRgP8,261
|
|
189
|
+
mcli_framework-7.1.3.dist-info/top_level.txt,sha256=_bnO8J2EUkliWivey_1le0UrnocFKmyVMQjbQ8iVXjc,5
|
|
190
|
+
mcli_framework-7.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|