Boray commited on
Commit
7422a2a
·
1 Parent(s): 7013de7

sample report changes

Browse files
Files changed (2) hide show
  1. app.py +0 -680
  2. src/streamlit_app.py +66 -52
app.py DELETED
@@ -1,680 +0,0 @@
1
- import json
2
- from pathlib import Path
3
- from typing import Any, Dict, List
4
-
5
- import pandas as pd
6
- import plotly.express as px
7
- import plotly.graph_objects as go
8
- import streamlit as st
9
-
10
- st.set_page_config(
11
- page_title="Radon Complexity Analyzer",
12
- page_icon="📊",
13
- layout="wide",
14
- initial_sidebar_state="expanded",
15
- )
16
-
17
- # Custom CSS for better styling
18
- st.markdown(
19
- """
20
- <style>
21
- .grade-A { color: #2ecc71; font-weight: bold; }
22
- .grade-B { color: #f39c12; font-weight: bold; }
23
- .grade-C { color: #e74c3c; font-weight: bold; }
24
- .grade-D { color: #e67e22; font-weight: bold; }
25
- .grade-F { color: #c0392b; font-weight: bold; }
26
- .metric-high { background-color: #ffe6e6; }
27
- .metric-medium { background-color: #fff3cd; }
28
- .metric-low { background-color: #d4edda; }
29
- </style>
30
- """,
31
- unsafe_allow_html=True,
32
- )
33
-
34
-
35
- def get_grade_color(grade: str) -> str:
36
- """Get color for grade"""
37
- colors = {
38
- "A": "#2ecc71", # Green
39
- "B": "#f39c12", # Orange
40
- "C": "#e74c3c", # Red
41
- "D": "#e67e22", # Dark Orange
42
- "E": "#d35400", # Darker Orange
43
- "F": "#c0392b", # Dark Red
44
- }
45
- return colors.get(grade, "#95a5a6")
46
-
47
-
48
- def get_complexity_color(complexity: int, high_threshold: int = 10) -> str:
49
- """Get color based on complexity value"""
50
- if complexity <= 3:
51
- return "#2ecc71" # Green - Simple
52
- elif complexity <= 7:
53
- return "#f39c12" # Orange - Moderate
54
- elif complexity <= high_threshold:
55
- return "#e74c3c" # Red - Complex
56
- else:
57
- return "#c0392b" # Dark Red - Very Complex
58
-
59
-
60
- def flatten_report(report: Dict[str, List[Dict]]) -> pd.DataFrame:
61
- """Convert nested JSON report to flattened DataFrame"""
62
- rows = []
63
-
64
- for filepath, items in report.items():
65
- if not isinstance(items, list):
66
- continue
67
-
68
- for item in items:
69
- row = {
70
- "filepath": filepath,
71
- "type": item.get("type", "N/A"),
72
- "name": item.get("name", "N/A"),
73
- "classname": item.get("classname", ""),
74
- "complexity": item.get("complexity", 0),
75
- "rank": item.get("rank", "N/A"),
76
- "lineno": item.get("lineno", 0),
77
- "endline": item.get("endline", 0),
78
- "col_offset": item.get("col_offset", 0),
79
- }
80
- rows.append(row)
81
-
82
- # Add nested methods/closures
83
- if item.get("methods"):
84
- for method in item["methods"]:
85
- method_row = row.copy()
86
- method_row.update(
87
- {
88
- "type": method.get("type", "method"),
89
- "name": method.get("name", "N/A"),
90
- "complexity": method.get("complexity", 0),
91
- "rank": method.get("rank", "N/A"),
92
- "lineno": method.get("lineno", 0),
93
- "endline": method.get("endline", 0),
94
- "col_offset": method.get("col_offset", 0),
95
- "parent_name": item.get("name", ""),
96
- }
97
- )
98
- rows.append(method_row)
99
-
100
- if item.get("closures"):
101
- for closure in item["closures"]:
102
- closure_row = row.copy()
103
- closure_row.update(
104
- {
105
- "type": closure.get("type", "closure"),
106
- "name": closure.get("name", "N/A"),
107
- "complexity": closure.get("complexity", 0),
108
- "rank": closure.get("rank", "N/A"),
109
- "lineno": closure.get("lineno", 0),
110
- "endline": closure.get("endline", 0),
111
- "col_offset": closure.get("col_offset", 0),
112
- "parent_name": item.get("name", ""),
113
- }
114
- )
115
- rows.append(closure_row)
116
-
117
- return pd.DataFrame(rows)
118
-
119
-
120
- def display_grade_badge(grade: str) -> str:
121
- """Create colored grade badge"""
122
- color = get_grade_color(grade)
123
- return f'<span style="background-color: {color}; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;">{grade}</span>'
124
-
125
-
126
- def identify_risky_items(
127
- df: pd.DataFrame, complexity_threshold: int = 10, risky_grades: List[str] = None
128
- ) -> pd.DataFrame:
129
- """Identify items that need investigation"""
130
- if risky_grades is None:
131
- risky_grades = ["D", "E", "F"]
132
-
133
- risky = df[
134
- (df["complexity"] >= complexity_threshold) | (df["rank"].isin(risky_grades))
135
- ]
136
- return risky.sort_values("complexity", ascending=False)
137
-
138
-
139
- def create_complexity_chart(df: pd.DataFrame):
140
- """Create a chart showing complexity distribution"""
141
- complexity_dist = df["complexity"].value_counts().sort_index()
142
- fig = go.Figure(data=[go.Bar(x=complexity_dist.index, y=complexity_dist.values)])
143
- fig.update_layout(
144
- title="Complexity Distribution",
145
- xaxis_title="Complexity Level",
146
- yaxis_title="Count",
147
- hovermode="x unified",
148
- )
149
- return fig
150
-
151
-
152
- def create_grade_chart(df: pd.DataFrame):
153
- """Create a chart showing grade distribution"""
154
- grade_dist = df["rank"].value_counts()
155
- grade_order = ["A", "B", "C", "D", "E", "F"]
156
- grade_dist = grade_dist.reindex(
157
- [g for g in grade_order if g in grade_dist.index], fill_value=0
158
- )
159
-
160
- colors = [get_grade_color(g) for g in grade_dist.index]
161
- fig = go.Figure(
162
- data=[go.Bar(x=grade_dist.index, y=grade_dist.values, marker_color=colors)]
163
- )
164
- fig.update_layout(
165
- title="Grade Distribution",
166
- xaxis_title="Grade",
167
- yaxis_title="Count",
168
- hovermode="x unified",
169
- )
170
- return fig
171
-
172
-
173
- def create_scatter_plot(df: pd.DataFrame):
174
- """Create scatter plot of complexity vs files"""
175
- # Add a column with just the filename for display
176
- df_plot = df.copy()
177
- df_plot["filename"] = df_plot["filepath"].apply(lambda x: x.split("/")[-1])
178
-
179
- fig = px.scatter(
180
- df_plot,
181
- x="filename",
182
- y="complexity",
183
- color="rank",
184
- hover_data=["name", "type", "lineno", "filepath"],
185
- title="Complexity by File and Grade",
186
- color_discrete_map={g: get_grade_color(g) for g in df_plot["rank"].unique()},
187
- height=600,
188
- )
189
- fig.update_layout(xaxis_tickangle=-45, xaxis_title="File")
190
- return fig
191
-
192
-
193
- # Initialize session state
194
- if "report_data" not in st.session_state:
195
- st.session_state.report_data = None
196
- if "df" not in st.session_state:
197
- st.session_state.df = None
198
-
199
- # Sidebar for file upload
200
- st.sidebar.title("📊 Radon Report Analyzer")
201
-
202
- # File upload
203
- uploaded_file = st.sidebar.file_uploader(
204
- "Upload JSON Report",
205
- type=["json"],
206
- help="Upload the cyclomatic complexity report from radon library",
207
- )
208
-
209
- if uploaded_file:
210
- try:
211
- report_data = json.load(uploaded_file)
212
- st.session_state.report_data = report_data
213
- st.session_state.df = flatten_report(report_data)
214
- st.sidebar.success("✅ Report loaded successfully!")
215
- except json.JSONDecodeError:
216
- st.sidebar.error("❌ Invalid JSON file")
217
- except Exception as e:
218
- st.sidebar.error(f"❌ Error loading file: {str(e)}")
219
-
220
- # Main app logic
221
- if st.session_state.df is not None and len(st.session_state.df) > 0:
222
- df = st.session_state.df.copy()
223
-
224
- # drop duplicate rows by name (drop the one with NaN parrent)
225
- df = df.drop_duplicates(subset=["name", "filepath", "lineno"], keep="first")
226
-
227
- # Create tabs
228
- tab1, tab2, tab3, tab4 = st.tabs(
229
- ["📈 Overview", "🔍 Analysis", "⚠️ Warnings", "📋 Details"]
230
- )
231
-
232
- # ===== TAB 1: OVERVIEW =====
233
- with tab1:
234
- col1, col2, col3, col4 = st.columns(4)
235
-
236
- with col1:
237
- st.metric("Total Items", len(df))
238
- with col2:
239
- st.metric("Total Files", df["filepath"].nunique())
240
- with col3:
241
- avg_complexity = df["complexity"].mean()
242
- st.metric("Avg Complexity", f"{avg_complexity:.2f}")
243
- with col4:
244
- max_complexity = df["complexity"].max()
245
- st.metric("Max Complexity", max_complexity)
246
-
247
- st.divider()
248
-
249
- col1, col2 = st.columns(2)
250
- with col1:
251
- st.plotly_chart(create_complexity_chart(df), use_container_width=True)
252
- with col2:
253
- st.plotly_chart(create_grade_chart(df), use_container_width=True)
254
-
255
- st.divider()
256
- st.subheader("📍 Complexity by File and Grade")
257
-
258
- # File filter for scatter plot - show only filename, not full path
259
- filepath_to_filename = {fp: fp.split("/")[-1] for fp in df["filepath"].unique()}
260
- filename_to_filepath = {v: k for k, v in filepath_to_filename.items()}
261
-
262
- # Initialize selected files in session state if not exists
263
- if "selected_scatter_files" not in st.session_state:
264
- st.session_state.selected_scatter_files = sorted(
265
- filepath_to_filename.values()
266
- )
267
-
268
- # Select all / Remove all buttons
269
- col_btn1, col_btn2, col_spacer = st.columns([1, 1, 6])
270
- with col_btn1:
271
- if st.button("Select All", use_container_width=True):
272
- st.session_state.selected_scatter_files = sorted(
273
- filepath_to_filename.values()
274
- )
275
- st.rerun()
276
- with col_btn2:
277
- if st.button("Remove All", use_container_width=True):
278
- st.session_state.selected_scatter_files = []
279
- st.rerun()
280
-
281
- st.write("**Select files to display:**")
282
- scatter_file_filter_display = st.pills(
283
- "Filter files",
284
- options=sorted(filepath_to_filename.values()),
285
- selection_mode="multi",
286
- default=st.session_state.selected_scatter_files,
287
- label_visibility="collapsed",
288
- key="scatter_plot_file_filter",
289
- )
290
-
291
- # Update session state
292
- st.session_state.selected_scatter_files = (
293
- scatter_file_filter_display if scatter_file_filter_display else []
294
- )
295
-
296
- # Convert selected filenames back to full paths
297
- scatter_file_filter = [
298
- filename_to_filepath[fn] for fn in (scatter_file_filter_display or [])
299
- ]
300
-
301
- # Apply file filter for scatter plot
302
- if scatter_file_filter:
303
- scatter_df = df[df["filepath"].isin(scatter_file_filter)]
304
- else:
305
- scatter_df = pd.DataFrame() # Empty dataframe when no files selected
306
-
307
- if len(scatter_df) > 0:
308
- st.plotly_chart(create_scatter_plot(scatter_df), use_container_width=True)
309
- else:
310
- st.info("No data to display. Please select at least one file.")
311
-
312
- # ===== TAB 2: ANALYSIS WITH FILTERS =====
313
- with tab2:
314
- st.subheader("Filter & Sort Data")
315
-
316
- col1, col2, col3, col4 = st.columns(4)
317
-
318
- with col1:
319
- type_filter = st.multiselect(
320
- "Type",
321
- options=df["type"].unique(),
322
- default=df["type"].unique(),
323
- help="Filter by item type",
324
- )
325
-
326
- with col2:
327
- grade_filter = st.multiselect(
328
- "Grade",
329
- options=sorted(df["rank"].unique()),
330
- default=sorted(df["rank"].unique()),
331
- help="Filter by grade",
332
- )
333
-
334
- with col3:
335
- complexity_range = st.slider(
336
- "Complexity Range",
337
- min_value=int(df["complexity"].min()),
338
- max_value=int(df["complexity"].max()),
339
- value=(int(df["complexity"].min()), int(df["complexity"].max())),
340
- help="Filter by complexity level",
341
- )
342
-
343
- with col4:
344
- filepath_filter = st.multiselect(
345
- "Files",
346
- options=sorted(df["filepath"].unique()),
347
- default=sorted(df["filepath"].unique()),
348
- help="Filter by file",
349
- )
350
-
351
- # Apply filters
352
- filtered_df = df[
353
- (df["type"].isin(type_filter))
354
- & (df["rank"].isin(grade_filter))
355
- & (df["complexity"] >= complexity_range[0])
356
- & (df["complexity"] <= complexity_range[1])
357
- & (df["filepath"].isin(filepath_filter))
358
- ]
359
-
360
- col1, col2 = st.columns(2)
361
- with col1:
362
- sort_by = st.selectbox(
363
- "Sort by",
364
- options=[
365
- "Complexity (High→Low)",
366
- "Complexity (Low→High)",
367
- "Grade (Best→Worst)",
368
- "Name (A→Z)",
369
- "File Path",
370
- "Line Number",
371
- ],
372
- help="Sort the filtered results",
373
- )
374
-
375
- with col2:
376
- search_term = st.text_input(
377
- "Search by name", help="Search for specific function/class names"
378
- )
379
-
380
- # Apply sorting
381
- if sort_by == "Complexity (High→Low)":
382
- filtered_df = filtered_df.sort_values("complexity", ascending=False)
383
- elif sort_by == "Complexity (Low→High)":
384
- filtered_df = filtered_df.sort_values("complexity", ascending=True)
385
- elif sort_by == "Grade (Best→Worst)":
386
- grade_order = {"A": 1, "B": 2, "C": 3, "D": 4, "F": 5}
387
- filtered_df = filtered_df.sort_values(
388
- "rank", key=lambda x: x.map(grade_order)
389
- )
390
- elif sort_by == "Name (A→Z)":
391
- filtered_df = filtered_df.sort_values("name")
392
- elif sort_by == "File Path":
393
- filtered_df = filtered_df.sort_values("filepath")
394
- elif sort_by == "Line Number":
395
- filtered_df = filtered_df.sort_values("lineno")
396
-
397
- # Apply search
398
- if search_term:
399
- filtered_df = filtered_df[
400
- filtered_df["name"].str.contains(search_term, case=False, na=False)
401
- ]
402
-
403
- st.info(f"Showing {len(filtered_df)} of {len(df)} items")
404
-
405
- # Display table with color coding
406
- def style_dataframe(val, column):
407
- if column == "rank":
408
- color = get_grade_color(val)
409
- return f"background-color: {color}; color: white; font-weight: bold;"
410
- elif column == "complexity":
411
- color = get_complexity_color(int(val))
412
- return f"background-color: {color}; color: white;"
413
- return ""
414
-
415
- display_df = filtered_df[
416
- ["filepath", "type", "name", "complexity", "rank", "lineno", "endline"]
417
- ].copy()
418
- display_df = display_df.reset_index(drop=True)
419
-
420
- st.dataframe(
421
- display_df,
422
- use_container_width=True,
423
- column_config={
424
- "complexity": st.column_config.NumberColumn(width="small"),
425
- "rank": st.column_config.TextColumn(width="small"),
426
- "lineno": st.column_config.NumberColumn(width="small"),
427
- "endline": st.column_config.NumberColumn(width="small"),
428
- "type": st.column_config.TextColumn(width="small"),
429
- },
430
- )
431
-
432
- # ===== TAB 3: WARNINGS =====
433
- with tab3:
434
- st.subheader("⚠️ Items Requiring Investigation")
435
-
436
- col1, col2 = st.columns(2)
437
- with col1:
438
- complexity_threshold = st.slider(
439
- "Complexity Threshold",
440
- min_value=1,
441
- max_value=int(df["complexity"].max()),
442
- value=10,
443
- help="Items with complexity >= this value will be flagged",
444
- )
445
-
446
- with col2:
447
- risky_grades = st.multiselect(
448
- "Risky Grades",
449
- options=["A", "B", "C", "D", "E", "F"],
450
- default=["D", "E", "F"],
451
- help="Grades considered risky",
452
- )
453
-
454
- risky_df = identify_risky_items(df, complexity_threshold, risky_grades)
455
-
456
- if len(risky_df) > 0:
457
- st.warning(f"⚠️ Found {len(risky_df)} items that need investigation")
458
-
459
- # Group by severity
460
- col1, col2 = st.columns(2)
461
- with col1:
462
- high_risk = risky_df[risky_df["complexity"] >= complexity_threshold + 5]
463
- st.metric("High Risk (Very High Complexity)", len(high_risk))
464
-
465
- with col2:
466
- bad_grade = risky_df[risky_df["rank"].isin(["D", "F"])]
467
- st.metric("Bad Grade Items", len(bad_grade))
468
-
469
- st.divider()
470
-
471
- # Detailed view of risky items
472
- for idx, (_, row) in enumerate(risky_df.head(20).iterrows(), 1):
473
- with st.expander(
474
- f"🚨 {row['name']} (Complexity: {row['complexity']}, Grade: {row['rank']})",
475
- expanded=(idx == 1),
476
- ):
477
- col1, col2, col3, col4 = st.columns(4)
478
- with col1:
479
- st.metric("Complexity", row["complexity"])
480
- with col2:
481
- st.write(f"**Grade:** {row['rank']}")
482
- with col3:
483
- st.write(f"**Type:** {row['type']}")
484
- with col4:
485
- st.write(f"**Lines:** {row['lineno']}-{row['endline']}")
486
-
487
- st.write(f"**File:** `{row['filepath']}`")
488
- full_name = (
489
- f"{row['classname']}.{row['name']}"
490
- if row["type"] == "method"
491
- else row["name"]
492
- )
493
- st.write(f"**Full Name:** `{full_name}`")
494
-
495
- # Recommendation
496
- if row["complexity"] >= complexity_threshold + 5:
497
- st.error("🔴 **CRITICAL:** This needs immediate refactoring")
498
- elif row["complexity"] >= complexity_threshold:
499
- st.warning(
500
- "🟠 **HIGH:** Consider breaking this into smaller functions"
501
- )
502
-
503
- if row["rank"] in ["D", "E", "F"]:
504
- st.warning(
505
- f"**Grade {row['rank']}:** Code quality is poor, refactoring recommended"
506
- )
507
- else:
508
- st.success("✅ No risky items found! Your code looks good.")
509
-
510
- # ===== TAB 4: DETAILED VIEW =====
511
- with tab4:
512
- st.subheader("Detailed Item Analysis")
513
-
514
- # Select item to analyze
515
- df_display = df.copy()
516
- df_display["display_name"] = df_display.apply(
517
- lambda x: f"{x['name']} ({x['type']}) - {x['filepath'].split('/')[-1]}",
518
- axis=1,
519
- )
520
-
521
- selected_item = st.selectbox(
522
- "Select an item to analyze",
523
- options=df_display.index,
524
- format_func=lambda x: df_display.loc[x, "display_name"],
525
- )
526
-
527
- if selected_item is not None:
528
- item = df.iloc[selected_item]
529
-
530
- # Header with grade badge
531
- col1, col2 = st.columns([3, 1])
532
- with col1:
533
- st.title(item["name"])
534
- with col2:
535
- grade_html = display_grade_badge(item["rank"])
536
- st.markdown(grade_html, unsafe_allow_html=True)
537
-
538
- st.divider()
539
-
540
- # Detailed metrics
541
- col1, col2, col3, col4, col5 = st.columns(5)
542
- with col1:
543
- st.metric("Complexity", item["complexity"])
544
- with col2:
545
- st.metric("Type", item["type"])
546
- with col3:
547
- st.metric("Start Line", int(item["lineno"]))
548
- with col4:
549
- st.metric("End Line", int(item["endline"]))
550
- with col5:
551
- st.metric("Lines of Code", int(item["endline"] - item["lineno"] + 1))
552
-
553
- st.divider()
554
-
555
- # File and location info
556
- col1, col2 = st.columns(2)
557
- with col1:
558
- st.write("**File Path:**")
559
- st.code(item["filepath"], language="text")
560
- with col2:
561
- st.write("**Location:**")
562
- st.code(
563
- f"Line {int(item['lineno'])} to {int(item['endline'])}, Column {int(item['col_offset'])}",
564
- language="text",
565
- )
566
-
567
- if item["classname"]:
568
- st.write("**Class Name:**")
569
- st.code(item["classname"], language="text")
570
-
571
- st.divider()
572
-
573
- # Recommendations
574
- st.subheader("💡 Recommendations")
575
-
576
- complexity = int(item["complexity"])
577
- if complexity <= 3:
578
- st.success(
579
- "✅ **Simple:** This code is easy to understand and maintain."
580
- )
581
- elif complexity <= 7:
582
- st.info(
583
- "ℹ️ **Moderate:** Code is reasonably complex. Consider breaking into smaller functions if it exceeds 7."
584
- )
585
- elif complexity <= 10:
586
- st.warning(
587
- "⚠️ **Complex:** This code is complex and may be difficult to maintain. Consider refactoring."
588
- )
589
- else:
590
- st.error(
591
- "🔴 **Very Complex:** This code needs immediate refactoring. Break it into smaller, testable units."
592
- )
593
-
594
- if item["rank"] in ["D", "E", "F"]:
595
- st.error(
596
- f"📉 **Grade {item['rank']}:** Code quality needs improvement."
597
- )
598
-
599
- else:
600
- # Landing page
601
- st.title("📊 Radon Complexity Analyzer")
602
- st.markdown(
603
- """
604
- Welcome to the Radon Cyclomatic Complexity Analyzer!
605
-
606
- This tool helps you analyze and visualize Python code complexity reports from the **radon** library.
607
-
608
- ### Features:
609
- - 📈 **Overview:** See complexity distribution across your codebase
610
- - 🔍 **Analysis:** Filter, sort, and search for specific functions/classes
611
- - ⚠️ **Warnings:** Identify items that need immediate attention
612
- - 📋 **Details:** Get detailed analysis and recommendations for each item
613
-
614
- ### How to use:
615
- 1. Generate a radon complexity report as JSON:
616
- ```bash
617
- radon cc your_project/ -j > report.json
618
- ```
619
- 2. Upload the JSON file using the sidebar
620
- 3. Explore and analyze your code complexity!
621
- """
622
- )
623
-
624
- # Create sample data for demonstration
625
- st.divider()
626
- st.subheader("Or try with sample data:")
627
-
628
- if st.button("Load Sample Report"):
629
- sample_report = {
630
- "example/settings.py": [
631
- {
632
- "type": "class",
633
- "rank": "A",
634
- "lineno": 7,
635
- "complexity": 1,
636
- "endline": 8,
637
- "name": "DBSettings",
638
- "col_offset": 0,
639
- "methods": [],
640
- },
641
- {
642
- "type": "class",
643
- "rank": "B",
644
- "lineno": 11,
645
- "complexity": 5,
646
- "endline": 13,
647
- "name": "ComplexSettings",
648
- "col_offset": 0,
649
- "methods": [
650
- {
651
- "type": "method",
652
- "rank": "C",
653
- "lineno": 12,
654
- "classname": "ComplexSettings",
655
- "complexity": 8,
656
- "endline": 13,
657
- "name": "validate",
658
- "col_offset": 4,
659
- "closures": [],
660
- }
661
- ],
662
- },
663
- ],
664
- "example/base.py": [
665
- {
666
- "type": "function",
667
- "rank": "F",
668
- "lineno": 1,
669
- "complexity": 15,
670
- "endline": 50,
671
- "name": "complex_function",
672
- "col_offset": 0,
673
- }
674
- ],
675
- }
676
-
677
- st.session_state.report_data = sample_report
678
- st.session_state.df = flatten_report(sample_report)
679
- st.success("✅ Sample data loaded! Refresh the page to see the analysis.")
680
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/streamlit_app.py CHANGED
@@ -626,55 +626,69 @@ else:
626
  st.subheader("Or try with sample data:")
627
 
628
  if st.button("Load Sample Report"):
629
- sample_report = {
630
- "example/settings.py": [
631
- {
632
- "type": "class",
633
- "rank": "A",
634
- "lineno": 7,
635
- "complexity": 1,
636
- "endline": 8,
637
- "name": "DBSettings",
638
- "col_offset": 0,
639
- "methods": [],
640
- },
641
- {
642
- "type": "class",
643
- "rank": "B",
644
- "lineno": 11,
645
- "complexity": 5,
646
- "endline": 13,
647
- "name": "ComplexSettings",
648
- "col_offset": 0,
649
- "methods": [
650
- {
651
- "type": "method",
652
- "rank": "C",
653
- "lineno": 12,
654
- "classname": "ComplexSettings",
655
- "complexity": 8,
656
- "endline": 13,
657
- "name": "validate",
658
- "col_offset": 4,
659
- "closures": [],
660
- }
661
- ],
662
- },
663
- ],
664
- "example/base.py": [
665
- {
666
- "type": "function",
667
- "rank": "F",
668
- "lineno": 1,
669
- "complexity": 15,
670
- "endline": 50,
671
- "name": "complex_function",
672
- "col_offset": 0,
673
- }
674
- ],
675
- }
676
-
677
- st.session_state.report_data = sample_report
678
- st.session_state.df = flatten_report(sample_report)
679
- st.success("✅ Sample data loaded! Refresh the page to see the analysis.")
680
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
626
  st.subheader("Or try with sample data:")
627
 
628
  if st.button("Load Sample Report"):
629
+ sample_file_path = Path(__file__).parent / "sample_report.json"
630
+
631
+ try:
632
+ with open(sample_file_path, "r") as f:
633
+ sample_report = json.load(f)
634
+
635
+ st.session_state.report_data = sample_report
636
+ st.session_state.df = flatten_report(sample_report)
637
+ st.success(" Sample data loaded! Refresh the page to see the analysis.")
638
+ st.rerun()
639
+ except FileNotFoundError:
640
+ st.error("❌ Sample report file not found. Please upload your own report.")
641
+ except Exception as e:
642
+ st.error(f"❌ Error loading sample data: {str(e)}")
643
+ # sample_report = {
644
+ # "example/settings.py": [
645
+ # {
646
+ # "type": "class",
647
+ # "rank": "A",
648
+ # "lineno": 7,
649
+ # "complexity": 1,
650
+ # "endline": 8,
651
+ # "name": "DBSettings",
652
+ # "col_offset": 0,
653
+ # "methods": [],
654
+ # },
655
+ # {
656
+ # "type": "class",
657
+ # "rank": "B",
658
+ # "lineno": 11,
659
+ # "complexity": 5,
660
+ # "endline": 13,
661
+ # "name": "ComplexSettings",
662
+ # "col_offset": 0,
663
+ # "methods": [
664
+ # {
665
+ # "type": "method",
666
+ # "rank": "C",
667
+ # "lineno": 12,
668
+ # "classname": "ComplexSettings",
669
+ # "complexity": 8,
670
+ # "endline": 13,
671
+ # "name": "validate",
672
+ # "col_offset": 4,
673
+ # "closures": [],
674
+ # }
675
+ # ],
676
+ # },
677
+ # ],
678
+ # "example/base.py": [
679
+ # {
680
+ # "type": "function",
681
+ # "rank": "F",
682
+ # "lineno": 1,
683
+ # "complexity": 15,
684
+ # "endline": 50,
685
+ # "name": "complex_function",
686
+ # "col_offset": 0,
687
+ # }
688
+ # ],
689
+ # }
690
+
691
+ # st.session_state.report_data = sample_report
692
+ # st.session_state.df = flatten_report(sample_report)
693
+ # st.success("✅ Sample data loaded! Refresh the page to see the analysis.")
694
+ # st.rerun()