NajmiHassan1 commited on
Commit
53ad34b
Β·
verified Β·
1 Parent(s): e0720e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +100 -32
app.py CHANGED
@@ -2,79 +2,147 @@ import streamlit as st
2
  import ast
3
  import hashlib
4
  from io import StringIO
 
 
5
 
 
6
  st.title("🐍 Code Smell Detector")
7
  st.write("Upload a Python file to detect basic code smells like long functions, too many parameters, and duplicate code blocks.")
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  # Upload section
10
  uploaded_file = st.file_uploader("Choose a Python file", type=["py"])
11
 
12
  if uploaded_file:
13
  code = uploaded_file.read().decode("utf-8")
14
  st.code(code, language="python")
15
-
16
- st.subheader("βš™οΈ Duplicate Detection Settings")
17
- block_size = st.slider("Block size for duplicate detection (lines)", min_value=2, max_value=10, value=3)
18
-
19
  # ---------- Code Smell Detector Class ----------
20
  class CodeSmellDetector(ast.NodeVisitor):
21
  def __init__(self):
22
  self.smells = []
23
-
24
  def visit_FunctionDef(self, node):
25
  start_line = node.lineno
26
  end_line = node.body[-1].lineno
27
  length = end_line - start_line + 1
28
-
29
- if length > 10:
30
  self.smells.append(
31
  f"πŸ”΄ Long function '{node.name}' ({length} lines) at line {start_line}"
32
  )
33
-
34
- if len(node.args.args) > 5:
35
  self.smells.append(
36
  f"🟠 Function '{node.name}' has too many parameters ({len(node.args.args)}) at line {start_line}"
37
  )
38
-
39
  self.generic_visit(node)
40
-
41
  # ---------- Duplicate Code Block Detector ----------
42
  def find_duplicate_blocks(source_code, block_size):
43
  lines = [line.strip() for line in source_code.splitlines()]
44
  hashes = {}
45
  duplicates = []
46
-
47
  for i in range(len(lines) - block_size + 1):
48
  block = "\n".join(lines[i:i+block_size])
49
  h = hashlib.md5(block.encode()).hexdigest()
50
-
51
  if h in hashes:
52
  duplicates.append((i + 1, hashes[h] + 1, block))
53
  else:
54
  hashes[h] = i
55
-
56
  return duplicates
57
-
58
  # ---------- Run Analyzers ----------
59
  tree = ast.parse(code)
60
  detector = CodeSmellDetector()
61
  detector.visit(tree)
62
-
63
  duplicate_blocks = find_duplicate_blocks(code, block_size)
64
-
65
  # ---------- Results ----------
66
- st.subheader("🚨 Code Smells Detected:")
67
- if detector.smells:
68
- for smell in detector.smells:
69
- st.write(smell)
70
- else:
71
- st.success("βœ… No function-level code smells detected!")
72
-
73
- st.subheader("πŸ“¦ Duplicate Code Blocks:")
74
- if duplicate_blocks:
75
- for dup in duplicate_blocks:
76
- st.error(f"πŸ” Duplicate block found at lines {dup[0]} and {dup[1]}")
77
- with st.expander("Show Duplicate Block"):
78
- st.code(dup[2])
79
- else:
80
- st.success("βœ… No duplicate blocks detected!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import ast
3
  import hashlib
4
  from io import StringIO
5
+ import base64
6
+ import datetime
7
 
8
+ st.set_page_config(layout="wide")
9
  st.title("🐍 Code Smell Detector")
10
  st.write("Upload a Python file to detect basic code smells like long functions, too many parameters, and duplicate code blocks.")
11
 
12
+ # Move settings to sidebar
13
+ with st.sidebar:
14
+ st.header("Settings")
15
+
16
+ st.subheader("βš™οΈ Duplicate Detection Settings")
17
+ block_size = st.slider("Block size for duplicate detection (lines)",
18
+ min_value=2, max_value=10, value=3)
19
+
20
+ function_length_limit = st.slider("Max function length (lines)",
21
+ min_value=5, max_value=50, value=10)
22
+
23
+ param_limit = st.slider("Max parameters per function",
24
+ min_value=2, max_value=10, value=5)
25
+
26
  # Upload section
27
  uploaded_file = st.file_uploader("Choose a Python file", type=["py"])
28
 
29
  if uploaded_file:
30
  code = uploaded_file.read().decode("utf-8")
31
  st.code(code, language="python")
32
+
 
 
 
33
  # ---------- Code Smell Detector Class ----------
34
  class CodeSmellDetector(ast.NodeVisitor):
35
  def __init__(self):
36
  self.smells = []
37
+
38
  def visit_FunctionDef(self, node):
39
  start_line = node.lineno
40
  end_line = node.body[-1].lineno
41
  length = end_line - start_line + 1
42
+
43
+ if length > function_length_limit:
44
  self.smells.append(
45
  f"πŸ”΄ Long function '{node.name}' ({length} lines) at line {start_line}"
46
  )
47
+
48
+ if len(node.args.args) > param_limit:
49
  self.smells.append(
50
  f"🟠 Function '{node.name}' has too many parameters ({len(node.args.args)}) at line {start_line}"
51
  )
52
+
53
  self.generic_visit(node)
54
+
55
  # ---------- Duplicate Code Block Detector ----------
56
  def find_duplicate_blocks(source_code, block_size):
57
  lines = [line.strip() for line in source_code.splitlines()]
58
  hashes = {}
59
  duplicates = []
60
+
61
  for i in range(len(lines) - block_size + 1):
62
  block = "\n".join(lines[i:i+block_size])
63
  h = hashlib.md5(block.encode()).hexdigest()
64
+
65
  if h in hashes:
66
  duplicates.append((i + 1, hashes[h] + 1, block))
67
  else:
68
  hashes[h] = i
69
+
70
  return duplicates
71
+
72
  # ---------- Run Analyzers ----------
73
  tree = ast.parse(code)
74
  detector = CodeSmellDetector()
75
  detector.visit(tree)
76
+
77
  duplicate_blocks = find_duplicate_blocks(code, block_size)
78
+
79
  # ---------- Results ----------
80
+ col1, col2 = st.columns(2)
81
+
82
+ with col1:
83
+ st.subheader("🚨 Code Smells Detected:")
84
+ if detector.smells:
85
+ for smell in detector.smells:
86
+ st.write(smell)
87
+ else:
88
+ st.success("βœ… No function-level code smells detected!")
89
+
90
+ with col2:
91
+ st.subheader("πŸ“¦ Duplicate Code Blocks:")
92
+ if duplicate_blocks:
93
+ for dup in duplicate_blocks:
94
+ st.error(f"πŸ” Duplicate block found at lines {dup[0]} and {dup[1]}")
95
+ with st.expander("Show Duplicate Block"):
96
+ st.code(dup[2])
97
+ else:
98
+ st.success("βœ… No duplicate blocks detected!")
99
+
100
+ # ---------- Generate Report ----------
101
+ def generate_report():
102
+ now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
103
+ filename = uploaded_file.name
104
+
105
+ report = f"""# Code Smell Analysis Report
106
+ Generated: {now}
107
+ File: {filename}
108
+
109
+ ## Analysis Settings
110
+ - Function length limit: {function_length_limit} lines
111
+ - Parameter limit: {param_limit}
112
+ - Duplicate block size: {block_size} lines
113
+
114
+ ## Function-level Code Smells
115
+ """
116
+ if detector.smells:
117
+ for smell in detector.smells:
118
+ report += f"- {smell}\n"
119
+ else:
120
+ report += "No function-level code smells detected.\n"
121
+
122
+ report += "\n## Duplicate Code Blocks\n"
123
+ if duplicate_blocks:
124
+ for idx, dup in enumerate(duplicate_blocks):
125
+ report += f"### Duplicate Block #{idx+1}\n"
126
+ report += f"- Found at lines {dup[0]} and {dup[1]}\n"
127
+ report += "```python\n"
128
+ report += dup[2] + "\n"
129
+ report += "```\n\n"
130
+ else:
131
+ report += "No duplicate blocks detected.\n"
132
+
133
+ return report
134
+
135
+ # Add download button to sidebar
136
+ with st.sidebar:
137
+ st.subheader("πŸ“Š Report")
138
+
139
+ if st.button("Generate Download Report"):
140
+ report_content = generate_report()
141
+ b64 = base64.b64encode(report_content.encode()).decode()
142
+ filename = f"code_smell_report_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.md"
143
+ href = f'<a href="data:text/markdown;base64,{b64}" download="{filename}">Download Report (Markdown)</a>'
144
+ st.markdown(href, unsafe_allow_html=True)
145
+ st.success("Report generated! Click the link above to download.")
146
+ else:
147
+ with st.sidebar:
148
+ st.info("Upload a Python file to see analysis options and download report.")