Skip to content

Commit f0f5a87

Browse files
author
VaitaR
committed
remove some ui elements
1 parent f4f197d commit f0f5a87

1 file changed

Lines changed: 54 additions & 307 deletions

File tree

app.py

Lines changed: 54 additions & 307 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,58 @@ def main():
767767
st.markdown("🔄 **Ready for Analysis**")
768768
st.markdown("Performance monitoring will appear after first calculation.")
769769

770+
# Configuration Management
771+
st.markdown("---")
772+
st.markdown("### 💾 Configuration Management")
773+
774+
config_col1, config_col2 = st.columns(2)
775+
776+
with config_col1:
777+
if st.button("💾 Save Config", help="Save current funnel configuration"):
778+
if st.session_state.funnel_steps:
779+
config_name = f"Funnel_{len(st.session_state.saved_configurations) + 1}"
780+
config_json = FunnelConfigManager.save_config(
781+
st.session_state.funnel_config,
782+
st.session_state.funnel_steps,
783+
config_name,
784+
)
785+
st.session_state.saved_configurations.append((config_name, config_json))
786+
st.toast(f"💾 Saved as {config_name}!", icon="💾")
787+
788+
with config_col2:
789+
uploaded_config = st.file_uploader(
790+
"📁 Load Config",
791+
type=["json"],
792+
help="Upload saved configuration",
793+
key="sidebar_config_upload"
794+
)
795+
796+
if uploaded_config is not None:
797+
try:
798+
config_json = uploaded_config.read().decode()
799+
config, steps, name = FunnelConfigManager.load_config(config_json)
800+
st.session_state.funnel_config = config
801+
st.session_state.funnel_steps = steps
802+
st.toast(f"📁 Loaded {name}!", icon="📁")
803+
st.rerun()
804+
except Exception as e:
805+
st.error(f"Error: {str(e)}")
806+
807+
# Download saved configurations
808+
if st.session_state.saved_configurations:
809+
st.markdown("**Saved Configs:**")
810+
for i, (name, config_json) in enumerate(st.session_state.saved_configurations):
811+
config_col1, config_col2 = st.columns([2, 1])
812+
config_col1.caption(name)
813+
config_col2.download_button(
814+
"⬇️",
815+
config_json,
816+
file_name=f"{name}.json",
817+
mime="application/json",
818+
key=f"sidebar_download_{i}",
819+
help="Download",
820+
)
821+
770822
# Cache Management
771823
st.markdown("---")
772824
st.markdown("### 💾 Cache Management")
@@ -1017,57 +1069,7 @@ def main():
10171069
else:
10181070
st.toast("⚠️ Please add at least 2 steps to create a funnel", icon="⚠️")
10191071

1020-
st.markdown("---")
1021-
1022-
# Configuration Management - перенесено в основную область
1023-
st.markdown("### 💾 Save & Load Configurations")
1024-
1025-
col1, col2 = st.columns(2)
1026-
1027-
with col1:
1028-
if st.button("💾 Save Current Config", use_container_width=True):
1029-
if st.session_state.funnel_steps:
1030-
config_name = f"Funnel_{len(st.session_state.saved_configurations) + 1}"
1031-
config_json = FunnelConfigManager.save_config(
1032-
st.session_state.funnel_config,
1033-
st.session_state.funnel_steps,
1034-
config_name,
1035-
)
1036-
st.session_state.saved_configurations.append((config_name, config_json))
1037-
st.success(f"Configuration saved as {config_name}")
1038-
1039-
with col2:
1040-
uploaded_config = st.file_uploader(
1041-
"📁 Load Configuration File",
1042-
type=["json"],
1043-
help="Upload a previously saved funnel configuration",
1044-
)
1045-
1046-
if uploaded_config is not None:
1047-
try:
1048-
config_json = uploaded_config.read().decode()
1049-
config, steps, name = FunnelConfigManager.load_config(config_json)
1050-
st.session_state.funnel_config = config
1051-
st.session_state.funnel_steps = steps
1052-
st.success(f"Loaded configuration: {name}")
1053-
st.rerun()
1054-
except Exception as e:
1055-
st.error(f"Error loading configuration: {str(e)}")
10561072

1057-
# Download saved configurations
1058-
if st.session_state.saved_configurations:
1059-
st.markdown("**Saved Configurations:**")
1060-
for i, (name, config_json) in enumerate(st.session_state.saved_configurations):
1061-
col1, col2 = st.columns([3, 1])
1062-
col1.write(name)
1063-
col2.download_button(
1064-
"⬇️",
1065-
config_json,
1066-
file_name=f"{name}.json",
1067-
mime="application/json",
1068-
key=f"download_{i}",
1069-
help="Download configuration",
1070-
)
10711073

10721074
# STEP 4: Results - показываем только если есть результаты
10731075
if st.session_state.analysis_results:
@@ -2539,20 +2541,7 @@ def format_time(minutes):
25392541

25402542
tab_idx += 1
25412543

2542-
# Test visualizations button
2543-
if "analysis_results" in st.session_state and st.session_state.analysis_results:
2544-
st.markdown("---")
2545-
test_col1, test_col2, test_col3 = st.columns([1, 1, 1])
2546-
with test_col2:
2547-
if st.button("🧪 Test Visualizations", use_container_width=True):
2548-
with st.spinner("Testing all visualizations..."):
2549-
test_results = test_visualizations()
2550-
2551-
if test_results["success"]:
2552-
st.success("✅ All visualizations passed!")
2553-
else:
2554-
failed_tests = [name for name, _ in test_results["failed"]]
2555-
st.error(f"❌ Failed tests: {', '.join(failed_tests)}")
2544+
25562545

25572546
# Footer
25582547
st.markdown("---")
@@ -2567,250 +2556,8 @@ def format_time(minutes):
25672556
)
25682557

25692558

2570-
def test_visualizations():
2571-
"""
2572-
Universal test function to verify all visualizations render correctly.
2573-
Can be run with:
2574-
1. python app.py test_vis - to run in standalone mode with dummy data
2575-
2. Called from within the app with actual data
2576-
"""
2577-
2578-
import numpy as np
2579-
import streamlit as st
2580-
2581-
# Function to create minimal dummy data for testing
2582-
def create_dummy_data():
2583-
# Minimal FunnelResults
2584-
class DummyFunnelResults:
2585-
def __init__(self):
2586-
self.steps = ["Step 1", "Step 2", "Step 3"]
2587-
self.users_count = [1000, 700, 400]
2588-
self.drop_offs = [0, 300, 300]
2589-
self.drop_off_rates = [0, 30.0, 42.9]
2590-
self.conversion_rates = [100.0, 70.0, 40.0]
2591-
self.segment_data = {
2592-
"Segment A": [600, 400, 250],
2593-
"Segment B": [400, 300, 150],
2594-
}
2595-
2596-
# Minimal TimeToConvertStats
2597-
class DummyTimeStats:
2598-
def __init__(self, step_from, step_to):
2599-
self.step_from = step_from
2600-
self.step_to = step_to
2601-
self.conversion_times = np.random.exponential(scale=2.0, size=10)
2602-
self.mean_hours = np.mean(self.conversion_times)
2603-
self.median_hours = np.median(self.conversion_times)
2604-
self.p25_hours = np.percentile(self.conversion_times, 25)
2605-
self.p75_hours = np.percentile(self.conversion_times, 75)
2606-
self.p90_hours = np.percentile(self.conversion_times, 90)
2607-
self.std_hours = np.std(self.conversion_times)
2608-
2609-
# Minimal CohortData
2610-
class DummyCohortData:
2611-
def __init__(self):
2612-
self.cohort_labels = ["Cohort 1", "Cohort 2"]
2613-
self.cohort_sizes = {"Cohort 1": 500, "Cohort 2": 400}
2614-
self.conversion_rates = {
2615-
"Cohort 1": [100.0, 75.0, 50.0],
2616-
"Cohort 2": [100.0, 70.0, 45.0],
2617-
}
2618-
2619-
# Minimal PathAnalysisData
2620-
class DummyPathData:
2621-
def __init__(self):
2622-
self.dropoff_paths = {
2623-
"Step 1": {"Other Path 1": 150, "Other Path 2": 100},
2624-
"Step 2": {"Other Path 3": 200, "Other Path 4": 100},
2625-
}
2626-
self.between_steps_events = {
2627-
"Step 1 → Step 2": {"Event 1": 700},
2628-
"Step 2 → Step 3": {"Event 2": 400},
2629-
}
2630-
2631-
# Minimal StatSignificanceResult
2632-
class DummyStatTest:
2633-
def __init__(self):
2634-
self.segment_a = "Segment A"
2635-
self.segment_b = "Segment B"
2636-
self.conversion_a = 40.0
2637-
self.conversion_b = 25.0
2638-
self.p_value = 0.03
2639-
self.is_significant = True
2640-
self.z_score = 2.5
2641-
self.confidence_interval = (0.05, 0.15)
2642-
2643-
return {
2644-
"funnel_results": DummyFunnelResults(),
2645-
"time_stats": [
2646-
DummyTimeStats("Step 1", "Step 2"),
2647-
DummyTimeStats("Step 2", "Step 3"),
2648-
],
2649-
"cohort_data": DummyCohortData(),
2650-
"path_data": DummyPathData(),
2651-
"stat_tests": [DummyStatTest(), DummyStatTest()],
2652-
}
2653-
2654-
# Function to get real data if available, otherwise use dummy data
2655-
def get_test_data():
2656-
# Try to get real data from session state if exists
2657-
data = {}
2658-
2659-
try:
2660-
# Check if we have session state and if we're in the Streamlit context
2661-
has_session = (
2662-
"session_state" in globals() or "st" in globals() and hasattr(st, "session_state")
2663-
)
2664-
2665-
if has_session and hasattr(st.session_state, "analysis_results"):
2666-
results = st.session_state.analysis_results
2667-
if results:
2668-
data["funnel_results"] = results
2669-
if hasattr(results, "time_to_convert"):
2670-
data["time_stats"] = results.time_to_convert
2671-
if hasattr(results, "cohort_data"):
2672-
data["cohort_data"] = results.cohort_data
2673-
if hasattr(results, "path_analysis"):
2674-
data["path_data"] = results.path_analysis
2675-
if hasattr(results, "stat_significance"):
2676-
data["stat_tests"] = results.stat_significance
2677-
except Exception:
2678-
pass # If we can't access session state or it's not properly initialized
2679-
2680-
# For any missing data, fill with dummy data
2681-
dummy_data = create_dummy_data()
2682-
for key in dummy_data:
2683-
if key not in data or not data[key]:
2684-
data[key] = dummy_data[key]
2685-
2686-
return data
2687-
2688-
# Track test results
2689-
test_results = {"passed": [], "failed": []}
2690-
2691-
# Get test data (real or dummy)
2692-
data = get_test_data()
2693-
2694-
# Set up Streamlit page
2695-
st.title("Visualization Tests")
2696-
st.markdown(
2697-
"This test page verifies that all visualizations render correctly with dark theme."
2698-
)
2699-
2700-
# Run tests for each visualization
2701-
with st.expander("Test Details", expanded=True):
2702-
# Test 1: Funnel Chart
2703-
try:
2704-
funnel_chart = FunnelVisualizer.create_funnel_chart(data["funnel_results"])
2705-
test_results["passed"].append("Funnel Chart")
2706-
st.success("✅ Funnel Chart")
2707-
except Exception as e:
2708-
test_results["failed"].append(("Funnel Chart", str(e)))
2709-
st.error(f"❌ Funnel Chart: {str(e)}")
2710-
2711-
# Test 2: Segmented Funnel Chart
2712-
try:
2713-
segmented_funnel = FunnelVisualizer.create_funnel_chart(
2714-
data["funnel_results"], show_segments=True
2715-
)
2716-
test_results["passed"].append("Segmented Funnel")
2717-
st.success("✅ Segmented Funnel")
2718-
except Exception as e:
2719-
test_results["failed"].append(("Segmented Funnel", str(e)))
2720-
st.error(f"❌ Segmented Funnel: {str(e)}")
2721-
2722-
# Test 3: Conversion Flow Sankey
2723-
try:
2724-
flow_chart = FunnelVisualizer.create_conversion_flow_sankey(data["funnel_results"])
2725-
test_results["passed"].append("Conversion Flow Sankey")
2726-
st.success("✅ Conversion Flow Sankey")
2727-
except Exception as e:
2728-
test_results["failed"].append(("Conversion Flow Sankey", str(e)))
2729-
st.error(f"❌ Conversion Flow Sankey: {str(e)}")
2730-
2731-
# Test 4: Time to Convert Chart
2732-
try:
2733-
time_chart = FunnelVisualizer.create_time_to_convert_chart(data["time_stats"])
2734-
test_results["passed"].append("Time to Convert Chart")
2735-
st.success("✅ Time to Convert Chart")
2736-
except Exception as e:
2737-
test_results["failed"].append(("Time to Convert Chart", str(e)))
2738-
st.error(f"❌ Time to Convert Chart: {str(e)}")
2739-
2740-
# Test 5: Cohort Heatmap
2741-
try:
2742-
cohort_chart = FunnelVisualizer.create_cohort_heatmap(data["cohort_data"])
2743-
test_results["passed"].append("Cohort Heatmap")
2744-
st.success("✅ Cohort Heatmap")
2745-
except Exception as e:
2746-
test_results["failed"].append(("Cohort Heatmap", str(e)))
2747-
st.error(f"❌ Cohort Heatmap: {str(e)}")
2748-
2749-
# Test 6: Path Analysis Chart
2750-
try:
2751-
path_chart = FunnelVisualizer.create_path_analysis_chart(data["path_data"])
2752-
test_results["passed"].append("Path Analysis Chart")
2753-
st.success("✅ Path Analysis Chart")
2754-
except Exception as e:
2755-
test_results["failed"].append(("Path Analysis Chart", str(e)))
2756-
st.error(f"❌ Path Analysis Chart: {str(e)}")
2757-
2758-
# Test 7: Statistical Significance Table
2759-
try:
2760-
stat_table = FunnelVisualizer.create_statistical_significance_table(data["stat_tests"])
2761-
test_results["passed"].append("Statistical Significance Table")
2762-
st.success("✅ Statistical Significance Table")
2763-
except Exception as e:
2764-
test_results["failed"].append(("Statistical Significance Table", str(e)))
2765-
st.error(f"❌ Statistical Significance Table: {str(e)}")
2766-
2767-
# Show overall test result
2768-
if not test_results["failed"]:
2769-
st.success(f"✅ All {len(test_results['passed'])} visualizations passed!")
2770-
else:
2771-
st.error(
2772-
f"❌ {len(test_results['failed'])} of {len(test_results['passed']) + len(test_results['failed'])} tests failed."
2773-
)
27742559

2775-
# Show successful visualizations
2776-
if test_results["passed"]:
2777-
st.subheader("Successful Visualizations")
2778-
2779-
# Display the charts that passed
2780-
for viz_name in test_results["passed"]:
2781-
if viz_name == "Funnel Chart":
2782-
st.subheader("1. Funnel Chart")
2783-
st.plotly_chart(funnel_chart, use_container_width=True)
2784-
elif viz_name == "Segmented Funnel":
2785-
st.subheader("2. Segmented Funnel Chart")
2786-
st.plotly_chart(segmented_funnel, use_container_width=True)
2787-
elif viz_name == "Conversion Flow Sankey":
2788-
st.subheader("3. Conversion Flow Sankey")
2789-
st.plotly_chart(flow_chart, use_container_width=True)
2790-
elif viz_name == "Time to Convert Chart":
2791-
st.subheader("4. Time to Convert Chart")
2792-
st.plotly_chart(time_chart, use_container_width=True)
2793-
elif viz_name == "Cohort Heatmap":
2794-
st.subheader("5. Cohort Heatmap")
2795-
st.plotly_chart(cohort_chart, use_container_width=True)
2796-
elif viz_name == "Path Analysis Chart":
2797-
st.subheader("6. Path Analysis Chart")
2798-
st.plotly_chart(path_chart, use_container_width=True)
2799-
elif viz_name == "Statistical Significance Table":
2800-
st.subheader("7. Statistical Significance Table")
2801-
st.dataframe(stat_table)
2802-
2803-
return {
2804-
"success": len(test_results["failed"]) == 0,
2805-
"passed": test_results["passed"],
2806-
"failed": test_results["failed"],
2807-
}
28082560

28092561

28102562
if __name__ == "__main__":
2811-
import sys
2812-
2813-
if len(sys.argv) > 1 and sys.argv[1] == "test_vis":
2814-
test_visualizations()
2815-
else:
2816-
main()
2563+
main()

0 commit comments

Comments
 (0)