Skip to content

Commit 8059c8d

Browse files
authored
Merge pull request #27 from VaitaR/ui-perfomance-fixes
UI perfomance fixes
2 parents 01e8f08 + f0f5a87 commit 8059c8d

17 files changed

Lines changed: 221 additions & 372 deletions

FALLBACK_REPORT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,4 @@
5353
| unordered | optimized_reentry | unique_users | time_to_convert | ✗ No | |
5454
| unordered | optimized_reentry | unique_users | cohort_analysis | ✗ No | |
5555

56-
## Recommendations
56+
## Recommendations

app.py

Lines changed: 190 additions & 332 deletions
Large diffs are not rendered by default.

core/calculator.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ def calculate_funnel_metrics(
10061006
Returns:
10071007
FunnelResults object with all calculated metrics
10081008
"""
1009-
start_time = time.time()
1009+
time.time()
10101010

10111011
if len(funnel_steps) < 2:
10121012
return FunnelResults(
@@ -1088,7 +1088,7 @@ def _calculate_funnel_metrics_polars(
10881088
existing_events_in_data = set(
10891089
polars_df.select("event_name").unique().to_series().to_list()
10901090
)
1091-
funnel_steps_in_data = set(funnel_steps) & existing_events_in_data
1091+
set(funnel_steps) & existing_events_in_data
10921092

10931093
zero_counts = [0] * len(funnel_steps)
10941094
drop_offs = [0] * len(funnel_steps)
@@ -1167,7 +1167,6 @@ def _calculate_funnel_metrics_polars(
11671167

11681168
# Ensure consistent data types between DataFrames
11691169
# Get the schema of segment_polars_df
1170-
segment_schema = segment_polars_df.schema
11711170

11721171
# Cast user_id in both DataFrames to string to ensure consistent types
11731172
segment_polars_df = segment_polars_df.with_columns(
@@ -1272,7 +1271,7 @@ def _calculate_funnel_metrics_pandas(
12721271
return FunnelResults([], [], [], [], [])
12731272
# Events exist but none match funnel steps - check if any of the funnel steps exist in the data at all
12741273
existing_events_in_data = set(events_df["event_name"].unique())
1275-
funnel_steps_in_data = set(funnel_steps) & existing_events_in_data
1274+
set(funnel_steps) & existing_events_in_data
12761275

12771276
zero_counts = [0] * len(funnel_steps)
12781277
drop_offs = [0] * len(funnel_steps)
@@ -2013,7 +2012,7 @@ def _calculate_timeseries_metrics_pandas(
20132012

20142013
# Define first and last steps
20152014
first_step = funnel_steps[0]
2016-
last_step = funnel_steps[-1]
2015+
funnel_steps[-1]
20172016
conversion_window_hours = self.config.conversion_window_hours
20182017

20192018
try:
@@ -3250,7 +3249,7 @@ def _calculate_path_analysis_polars_optimized(
32503249
# Fully vectorized approach for between-steps analysis
32513250
try:
32523251
# Get unique user IDs with valid conversion pairs
3253-
user_ids = conversion_pairs.select("user_id").unique()
3252+
conversion_pairs.select("user_id").unique()
32543253

32553254
# Create a lazy frame with step pairs information
32563255
step_pairs_lazy = conversion_pairs.lazy().select(
@@ -3785,7 +3784,7 @@ def _analyze_between_steps_polars(
37853784
continue
37863785

37873786
step_A_time = user_A[0, "step_A_time"]
3788-
conversion_window = timedelta(hours=self.config.conversion_window_hours)
3787+
timedelta(hours=self.config.conversion_window_hours)
37893788

37903789
# Find first B after A within conversion window
37913790
potential_Bs = user_B.filter(
@@ -4576,7 +4575,7 @@ def _calculate_unique_pairs_funnel_polars(
45764575
users_count.append(count)
45774576

45784577
# For unique pairs, conversion rate is step-to-step
4579-
step_conversion_rate = (
4578+
(
45804579
(count / len(prev_step_users) * 100) if len(prev_step_users) > 0 else 0
45814580
)
45824581
# But we also track overall conversion rate from first step for consistency
@@ -5694,7 +5693,7 @@ def _user_did_later_steps_before_current(
56945693
try:
56955694
# Get the funnel sequence from the order that steps appear in the overall dataset
56965695
# This is a heuristic but works for most cases
5697-
all_funnel_events = all_events_df["event_name"].unique()
5696+
all_events_df["event_name"].unique()
56985697

56995698
# For the test case, we know the sequence should be: Sign Up -> Email Verification -> First Login
57005699
# When checking Email Verification after Sign Up, we should see if First Login happened before Email Verification
@@ -5888,7 +5887,7 @@ def _calculate_unique_pairs_funnel(
58885887
users_count.append(count)
58895888

58905889
# For unique pairs, conversion rate is step-to-step
5891-
step_conversion_rate = (
5890+
(
58925891
(count / len(prev_step_users) * 100) if len(prev_step_users) > 0 else 0
58935892
)
58945893
# But we also track overall conversion rate from first step
@@ -5929,7 +5928,7 @@ def _user_did_later_steps_before_current_polars(
59295928
try:
59305929
# Find the indices of steps in the funnel
59315930
try:
5932-
prev_step_idx = funnel_steps.index(prev_step)
5931+
funnel_steps.index(prev_step)
59335932
current_step_idx = funnel_steps.index(current_step)
59345933
except ValueError:
59355934
# If the steps aren't in the funnel, we can't determine order

core/data_source.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def _load_from_file_pandas_fallback(self, uploaded_file) -> pd.DataFrame:
322322

323323
return df
324324

325-
except Exception as e:
325+
except Exception:
326326
return pd.DataFrame()
327327

328328
def connect_clickhouse(
@@ -338,7 +338,7 @@ def connect_clickhouse(
338338
database=database,
339339
)
340340
# Test connection
341-
result = self.clickhouse_client.query("SELECT 1")
341+
self.clickhouse_client.query("SELECT 1")
342342
return True
343343
except Exception as e:
344344
st.error(f"ClickHouse connection failed: {str(e)}")

quick_benchmark.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def quick_benchmark():
106106
# Test statistics (optimized)
107107
print(" Testing _calculate_process_statistics_optimized...")
108108
start_time = time.time()
109-
statistics = analyzer._calculate_process_statistics_optimized(
109+
analyzer._calculate_process_statistics_optimized(
110110
journey_df, activities, transitions
111111
)
112112
stats_time = time.time() - start_time
@@ -141,7 +141,7 @@ def quick_benchmark():
141141

142142
print(" Without cycles:")
143143
start_time = time.time()
144-
result_no_cycles = analyzer.discover_process_mining_structure(
144+
analyzer.discover_process_mining_structure(
145145
df, min_frequency=1, include_cycles=False
146146
)
147147
time_no_cycles = time.time() - start_time
@@ -151,7 +151,7 @@ def quick_benchmark():
151151

152152
print(" With cycles:")
153153
start_time = time.time()
154-
result_with_cycles = analyzer.discover_process_mining_structure(
154+
analyzer.discover_process_mining_structure(
155155
df, min_frequency=1, include_cycles=True
156156
)
157157
time_with_cycles = time.time() - start_time

run_tests.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -405,12 +405,10 @@ def generate_test_report() -> TestResult:
405405
stdout = result.stdout
406406
stderr = result.stderr
407407
# For pytest, exit code 1 with fixture errors is acceptable
408-
success = result.returncode in [0, 1] # Accept both success and fixture errors
409408
except Exception as e:
410409
print(f"❌ Error running pytest: {e}")
411410
stdout = ""
412411
stderr = str(e)
413-
success = False
414412

415413
# Parse the output to extract test statistics
416414
passed = 0
@@ -428,10 +426,9 @@ def generate_test_report() -> TestResult:
428426
summary_matches = re.findall(summary_pattern, combined_output)
429427

430428
# Look for the final summary line (usually the last one)
431-
main_summary = ""
432429
for match in summary_matches:
433430
if any(word in match.lower() for word in ["passed", "failed", "error", "skipped"]):
434-
main_summary = match
431+
pass
435432

436433
# Extract individual numbers using separate patterns from the entire output
437434
passed_match = re.search(r"(\d+) passed", combined_output)

scalability_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,15 @@ def scalability_test():
125125

126126
# Test without cycles
127127
start_time = time.time()
128-
result_no_cycles = analyzer.discover_process_mining_structure(
128+
analyzer.discover_process_mining_structure(
129129
df, min_frequency=10, include_cycles=False
130130
)
131131
time_no_cycles = time.time() - start_time
132132
print(f"{time_no_cycles:>8.2f}s", end=" ")
133133

134134
# Test with cycles
135135
start_time = time.time()
136-
result_with_cycles = analyzer.discover_process_mining_structure(
136+
analyzer.discover_process_mining_structure(
137137
df, min_frequency=10, include_cycles=True
138138
)
139139
time_with_cycles = time.time() - start_time

tests/test_app_ui.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def load_sample_data(self) -> None:
5757
if attempt < max_retries - 1: # Don't run on last attempt
5858
self.at.run(timeout=10) # Re-run to refresh state
5959

60-
except Exception as e:
60+
except Exception:
6161
# If button interaction fails, manually load sample data for testing
6262
from datetime import datetime, timedelta
6363

@@ -151,7 +151,7 @@ def build_funnel(self, steps: List[str]) -> None:
151151
try:
152152
# Check the checkbox for this event
153153
self.at.checkbox(key=checkbox_key).check().run()
154-
except Exception as e:
154+
except Exception:
155155
# If checkbox interaction fails, manually add to session state for testing
156156
if step not in self.at.session_state.funnel_steps:
157157
self.at.session_state.funnel_steps.append(step)
@@ -195,7 +195,7 @@ def analyze_funnel(self) -> None:
195195
except KeyError:
196196
# If analyze button not available, skip this test (button might not be rendered yet)
197197
pytest.skip("Analyze button not available - UI might not be fully rendered")
198-
except Exception as e:
198+
except Exception:
199199
# If analysis fails, create mock results for testing UI flow
200200
from models import FunnelResults
201201

tests/test_conversion_logic_debug.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_debug_conversion_rate_calculation(self):
6565

6666
overall_started = overall_results.users_count[0]
6767
overall_completed = overall_results.users_count[1]
68-
overall_conversion = overall_results.conversion_rates[1]
68+
overall_results.conversion_rates[1]
6969

7070
print(
7171
f" Manual verification: {overall_completed}/{overall_started} = {(overall_completed / overall_started * 100):.2f}%"

tests/test_data_source_advanced.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,6 @@ def test_load_unsupported_file_format(self, data_manager):
426426

427427
def test_load_corrupted_csv_file(self, data_manager):
428428
"""Test loading corrupted CSV file."""
429-
corrupted_csv = """user_id,event_name,timestamp
430-
user_001,Sign Up,2024-01-01
431-
user_002,"Incomplete quote
432-
user_003,Login,""" # Corrupted CSV
433429

434430
# Create mock file
435431
mock_file = Mock()

0 commit comments

Comments
 (0)