codeguardian-cli 1.0.0__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.
- CODEGUARDIAN/__init__.py +0 -0
- CODEGUARDIAN/analyzers/__init__.py +0 -0
- CODEGUARDIAN/analyzers/circular_dependency_analyzer.py +101 -0
- CODEGUARDIAN/analyzers/db_access_analyzer.py +55 -0
- CODEGUARDIAN/analyzers/dependency_analyzer.py +259 -0
- CODEGUARDIAN/analyzers/file_analyzer.py +96 -0
- CODEGUARDIAN/analyzers/function_analyzer.py +199 -0
- CODEGUARDIAN/analyzers/source_code_analyzer.py +169 -0
- CODEGUARDIAN/cli/__init__.py +0 -0
- CODEGUARDIAN/cli/commands.py +619 -0
- CODEGUARDIAN/config/__init__.py +0 -0
- CODEGUARDIAN/config/config_loader.py +76 -0
- CODEGUARDIAN/config/settings.py +2 -0
- CODEGUARDIAN/main.py +4 -0
- CODEGUARDIAN/reports/__init__.py +0 -0
- CODEGUARDIAN/reports/architecture_score.py +68 -0
- CODEGUARDIAN/reports/console_reporter.py +487 -0
- CODEGUARDIAN/reports/html_reporter.py +293 -0
- CODEGUARDIAN/reports/json_reporter.py +69 -0
- CODEGUARDIAN/reports/markdown_reporter.py +130 -0
- CODEGUARDIAN/reports/severity.py +15 -0
- CODEGUARDIAN/reports/statistics_reporter.py +183 -0
- CODEGUARDIAN/rules/__init__.py +0 -0
- CODEGUARDIAN/rules/architecture_rules.py +5 -0
- CODEGUARDIAN/rules/architecture_validator.py +69 -0
- CODEGUARDIAN/rules/controllers/user_controller.py +16 -0
- CODEGUARDIAN/src/__init__.py +0 -0
- CODEGUARDIAN/src/discovery/__init__.py +0 -0
- CODEGUARDIAN/src/discovery/finder.py +39 -0
- CODEGUARDIAN/src/extractor/__init__.py +0 -0
- CODEGUARDIAN/src/extractor/class_extractor.py +32 -0
- CODEGUARDIAN/src/extractor/function_extractor.py +17 -0
- CODEGUARDIAN/src/extractor/import_extractor.py +23 -0
- CODEGUARDIAN/src/extractor/python_class_extractor.py +13 -0
- CODEGUARDIAN/src/extractor/python_function_extractor.py +16 -0
- CODEGUARDIAN/src/extractor/python_imports_extractor.py +24 -0
- CODEGUARDIAN/src/main.py +65 -0
- CODEGUARDIAN/src/parser/__init__.py +0 -0
- CODEGUARDIAN/src/parser/ts_parser.py +14 -0
- CODEGUARDIAN/src/tree/Walker.py +10 -0
- CODEGUARDIAN/src/tree/__init__.py +0 -0
- CODEGUARDIAN/utils/__init__.py +0 -0
- CODEGUARDIAN/utils/ast_cache.py +47 -0
- CODEGUARDIAN/utils/file_cache.py +30 -0
- CODEGUARDIAN/utils/layer_utils.py +19 -0
- codeguardian_cli-1.0.0.dist-info/METADATA +43 -0
- codeguardian_cli-1.0.0.dist-info/RECORD +50 -0
- codeguardian_cli-1.0.0.dist-info/WHEEL +5 -0
- codeguardian_cli-1.0.0.dist-info/entry_points.txt +2 -0
- codeguardian_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class HtmlReporter:
|
|
5
|
+
|
|
6
|
+
def generate(
|
|
7
|
+
self,
|
|
8
|
+
file_violations,
|
|
9
|
+
function_violations,
|
|
10
|
+
architecture_violations,
|
|
11
|
+
circular_violations,
|
|
12
|
+
db_access_violations,
|
|
13
|
+
source_analysis,
|
|
14
|
+
score
|
|
15
|
+
):
|
|
16
|
+
|
|
17
|
+
if score >= 90:
|
|
18
|
+
score_color = "#28a745"
|
|
19
|
+
status = "🟢 Excellent"
|
|
20
|
+
elif score >= 70:
|
|
21
|
+
score_color = "#ffc107"
|
|
22
|
+
status = "🟡 Good"
|
|
23
|
+
else:
|
|
24
|
+
score_color = "#dc3545"
|
|
25
|
+
status = "🔴 Needs Improvement"
|
|
26
|
+
|
|
27
|
+
total_files = len(source_analysis)
|
|
28
|
+
|
|
29
|
+
total_classes = sum(
|
|
30
|
+
len(item["classes"])
|
|
31
|
+
for item in source_analysis
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
total_functions = sum(
|
|
35
|
+
len(item["functions"])
|
|
36
|
+
for item in source_analysis
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
total_imports = sum(
|
|
40
|
+
len(item["imports"])
|
|
41
|
+
for item in source_analysis
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
html = f"""<!DOCTYPE html>
|
|
45
|
+
|
|
46
|
+
<html>
|
|
47
|
+
|
|
48
|
+
<head>
|
|
49
|
+
|
|
50
|
+
<meta charset="UTF-8">
|
|
51
|
+
|
|
52
|
+
<title>CodeGuardian Report</title>
|
|
53
|
+
|
|
54
|
+
<link rel="stylesheet" href="reports/style.css">
|
|
55
|
+
|
|
56
|
+
</head>
|
|
57
|
+
|
|
58
|
+
<body>
|
|
59
|
+
|
|
60
|
+
<div class="container">
|
|
61
|
+
|
|
62
|
+
<h1>CodeGuardian Report</h1>
|
|
63
|
+
|
|
64
|
+
<p>
|
|
65
|
+
|
|
66
|
+
<b>Generated:</b>
|
|
67
|
+
|
|
68
|
+
{datetime.now().strftime("%d %B %Y %H:%M")}
|
|
69
|
+
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<div class="dashboard">
|
|
73
|
+
|
|
74
|
+
<div
|
|
75
|
+
class="score"
|
|
76
|
+
style="color:{score_color};">
|
|
77
|
+
|
|
78
|
+
Architecture Score
|
|
79
|
+
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<div class="progress">
|
|
83
|
+
|
|
84
|
+
<div
|
|
85
|
+
class="progress-bar"
|
|
86
|
+
style="width:{score}%; background:{score_color};">
|
|
87
|
+
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<h2>{score}/100</h2>
|
|
93
|
+
|
|
94
|
+
<p class="status">
|
|
95
|
+
|
|
96
|
+
{status}
|
|
97
|
+
|
|
98
|
+
</p>
|
|
99
|
+
|
|
100
|
+
<div class="grid">
|
|
101
|
+
|
|
102
|
+
<div class="card blue">
|
|
103
|
+
<h3>📄 Files</h3>
|
|
104
|
+
<p>{total_files}</p>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="card green">
|
|
108
|
+
<h3>🏛 Classes</h3>
|
|
109
|
+
<p>{total_classes}</p>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<div class="card green">
|
|
113
|
+
<h3>⚙ Functions</h3>
|
|
114
|
+
<p>{total_functions}</p>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div class="card blue">
|
|
118
|
+
<h3>📦 Imports</h3>
|
|
119
|
+
<p>{total_imports}</p>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div class="card orange">
|
|
123
|
+
<h3>⚠ Oversized Files</h3>
|
|
124
|
+
<p>{len(file_violations)}</p>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div class="card orange">
|
|
128
|
+
<h3>⚠ Oversized Functions</h3>
|
|
129
|
+
<p>{len(function_violations)}</p>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div class="card red">
|
|
133
|
+
<h3>❌ Architecture Issues</h3>
|
|
134
|
+
<p>{len(architecture_violations)}</p>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="card red">
|
|
138
|
+
<h3>🔁 Circular Dependencies</h3>
|
|
139
|
+
<p>{len(circular_violations)}</p>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
</div>
|
|
145
|
+
"""
|
|
146
|
+
def section(
|
|
147
|
+
title,
|
|
148
|
+
headers,
|
|
149
|
+
rows
|
|
150
|
+
):
|
|
151
|
+
|
|
152
|
+
nonlocal html
|
|
153
|
+
|
|
154
|
+
html += f"<h2>{title}</h2>"
|
|
155
|
+
|
|
156
|
+
if not rows:
|
|
157
|
+
|
|
158
|
+
html += """
|
|
159
|
+
<p class="ok">
|
|
160
|
+
✔ No issues found.
|
|
161
|
+
</p>
|
|
162
|
+
"""
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
html += "<table>"
|
|
166
|
+
|
|
167
|
+
html += "<tr>"
|
|
168
|
+
|
|
169
|
+
for header in headers:
|
|
170
|
+
|
|
171
|
+
html += f"<th>{header}</th>"
|
|
172
|
+
|
|
173
|
+
html += "</tr>"
|
|
174
|
+
|
|
175
|
+
for row in rows:
|
|
176
|
+
|
|
177
|
+
html += "<tr>"
|
|
178
|
+
|
|
179
|
+
for cell in row:
|
|
180
|
+
|
|
181
|
+
html += f"<td>{cell}</td>"
|
|
182
|
+
|
|
183
|
+
html += "</tr>"
|
|
184
|
+
|
|
185
|
+
html += "</table>"
|
|
186
|
+
section(
|
|
187
|
+
"Oversized Files",
|
|
188
|
+
["File", "Lines"],
|
|
189
|
+
[
|
|
190
|
+
[
|
|
191
|
+
violation.file_path,
|
|
192
|
+
violation.line_count
|
|
193
|
+
]
|
|
194
|
+
for violation in file_violations
|
|
195
|
+
]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
section(
|
|
199
|
+
"Oversized Functions",
|
|
200
|
+
["Function", "Lines"],
|
|
201
|
+
[
|
|
202
|
+
[
|
|
203
|
+
violation.function_name,
|
|
204
|
+
violation.line_count
|
|
205
|
+
]
|
|
206
|
+
for violation in function_violations
|
|
207
|
+
]
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
section(
|
|
211
|
+
"Architecture Violations",
|
|
212
|
+
["File", "Rule Broken"],
|
|
213
|
+
[
|
|
214
|
+
[
|
|
215
|
+
violation.source_file,
|
|
216
|
+
f"{violation.source_layer} → {violation.target_layer}"
|
|
217
|
+
]
|
|
218
|
+
for violation in architecture_violations
|
|
219
|
+
]
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
section(
|
|
223
|
+
"Circular Dependencies",
|
|
224
|
+
["Dependency Cycle"],
|
|
225
|
+
[
|
|
226
|
+
[
|
|
227
|
+
" → ".join(
|
|
228
|
+
violation.cycle
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
for violation in circular_violations
|
|
232
|
+
]
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
html += """
|
|
236
|
+
<h2>Source Code Analysis</h2>
|
|
237
|
+
|
|
238
|
+
<table>
|
|
239
|
+
|
|
240
|
+
<tr>
|
|
241
|
+
|
|
242
|
+
<th>File</th>
|
|
243
|
+
|
|
244
|
+
<th>Classes</th>
|
|
245
|
+
|
|
246
|
+
<th>Functions</th>
|
|
247
|
+
|
|
248
|
+
<th>Imports</th>
|
|
249
|
+
|
|
250
|
+
</tr>
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
for item in source_analysis:
|
|
254
|
+
|
|
255
|
+
html += f"""
|
|
256
|
+
<tr>
|
|
257
|
+
|
|
258
|
+
<td>{item["file"]}</td>
|
|
259
|
+
|
|
260
|
+
<td>{len(item["classes"])}</td>
|
|
261
|
+
|
|
262
|
+
<td>{len(item["functions"])}</td>
|
|
263
|
+
|
|
264
|
+
<td>{len(item["imports"])}</td>
|
|
265
|
+
|
|
266
|
+
</tr>
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
html += """
|
|
270
|
+
</table>
|
|
271
|
+
|
|
272
|
+
<div class="footer">
|
|
273
|
+
|
|
274
|
+
Generated by <b>CodeGuardian</b><br>
|
|
275
|
+
|
|
276
|
+
Version 1.0
|
|
277
|
+
|
|
278
|
+
</div>
|
|
279
|
+
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
</body>
|
|
283
|
+
|
|
284
|
+
</html>
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
with open(
|
|
288
|
+
"report.html",
|
|
289
|
+
"w",
|
|
290
|
+
encoding="utf-8"
|
|
291
|
+
) as file:
|
|
292
|
+
|
|
293
|
+
file.write(html)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class JsonReporter:
|
|
5
|
+
|
|
6
|
+
def generate(
|
|
7
|
+
self,
|
|
8
|
+
file_violations,
|
|
9
|
+
function_violations,
|
|
10
|
+
architecture_violations,
|
|
11
|
+
circular_violations,
|
|
12
|
+
db_access_violations,
|
|
13
|
+
source_analysis,
|
|
14
|
+
score
|
|
15
|
+
):
|
|
16
|
+
report = {
|
|
17
|
+
|
|
18
|
+
"architecture_score": score,
|
|
19
|
+
|
|
20
|
+
"oversized_files": [
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
"file": v.file_path,
|
|
24
|
+
"lines": v.line_count
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for v in file_violations
|
|
28
|
+
],
|
|
29
|
+
|
|
30
|
+
"oversized_functions": [
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
"function": v.function_name,
|
|
34
|
+
"lines": v.line_count
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for v in function_violations
|
|
38
|
+
],
|
|
39
|
+
|
|
40
|
+
"architecture_violations": [
|
|
41
|
+
|
|
42
|
+
{
|
|
43
|
+
"source_file": v.source_file,
|
|
44
|
+
"rule": (
|
|
45
|
+
f"{v.source_layer}"
|
|
46
|
+
f" -> "
|
|
47
|
+
f"{v.target_layer}"
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for v in architecture_violations
|
|
52
|
+
],
|
|
53
|
+
|
|
54
|
+
"circular_dependencies": [
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
"cycle": c.cycle
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for c in circular_violations
|
|
61
|
+
],
|
|
62
|
+
|
|
63
|
+
"source_analysis": source_analysis
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return json.dumps(
|
|
67
|
+
report,
|
|
68
|
+
indent=4
|
|
69
|
+
)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
class MarkdownReporter:
|
|
2
|
+
|
|
3
|
+
def generate(
|
|
4
|
+
self,
|
|
5
|
+
file_violations,
|
|
6
|
+
function_violations,
|
|
7
|
+
architecture_violations,
|
|
8
|
+
circular_violations,
|
|
9
|
+
db_access_violations,
|
|
10
|
+
source_analysis,
|
|
11
|
+
score
|
|
12
|
+
):
|
|
13
|
+
|
|
14
|
+
lines = []
|
|
15
|
+
|
|
16
|
+
lines.append("# CodeGuardian Report\n")
|
|
17
|
+
|
|
18
|
+
lines.append(f"## Architecture Score\n")
|
|
19
|
+
lines.append(f"**{score}/100**\n")
|
|
20
|
+
|
|
21
|
+
# --------------------------------------------------
|
|
22
|
+
|
|
23
|
+
lines.append("## Oversized Files\n")
|
|
24
|
+
|
|
25
|
+
if file_violations:
|
|
26
|
+
|
|
27
|
+
lines.append("| File | Lines |")
|
|
28
|
+
lines.append("|------|------:|")
|
|
29
|
+
|
|
30
|
+
for violation in file_violations:
|
|
31
|
+
|
|
32
|
+
lines.append(
|
|
33
|
+
f"| {violation.file_path} | {violation.line_count} |"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
else:
|
|
37
|
+
|
|
38
|
+
lines.append("No oversized files found.")
|
|
39
|
+
|
|
40
|
+
lines.append("")
|
|
41
|
+
|
|
42
|
+
# --------------------------------------------------
|
|
43
|
+
|
|
44
|
+
lines.append("## Oversized Functions\n")
|
|
45
|
+
|
|
46
|
+
if function_violations:
|
|
47
|
+
|
|
48
|
+
lines.append("| Function | Lines |")
|
|
49
|
+
lines.append("|----------|------:|")
|
|
50
|
+
|
|
51
|
+
for violation in function_violations:
|
|
52
|
+
|
|
53
|
+
lines.append(
|
|
54
|
+
f"| {violation.function_name} | {violation.line_count} |"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
|
|
59
|
+
lines.append("No oversized functions found.")
|
|
60
|
+
|
|
61
|
+
lines.append("")
|
|
62
|
+
|
|
63
|
+
# --------------------------------------------------
|
|
64
|
+
|
|
65
|
+
lines.append("## Architecture Violations\n")
|
|
66
|
+
|
|
67
|
+
if architecture_violations:
|
|
68
|
+
|
|
69
|
+
lines.append("| File | Rule |")
|
|
70
|
+
lines.append("|------|------|")
|
|
71
|
+
|
|
72
|
+
for violation in architecture_violations:
|
|
73
|
+
|
|
74
|
+
lines.append(
|
|
75
|
+
f"| {violation.source_file} | "
|
|
76
|
+
f"{violation.source_layer} → "
|
|
77
|
+
f"{violation.target_layer} |"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
|
|
82
|
+
lines.append("No architecture violations found.")
|
|
83
|
+
|
|
84
|
+
lines.append("")
|
|
85
|
+
|
|
86
|
+
# --------------------------------------------------
|
|
87
|
+
|
|
88
|
+
lines.append("## Circular Dependencies\n")
|
|
89
|
+
|
|
90
|
+
if circular_violations:
|
|
91
|
+
|
|
92
|
+
for violation in circular_violations:
|
|
93
|
+
|
|
94
|
+
cycle = " → ".join(
|
|
95
|
+
violation.cycle
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
lines.append(f"- {cycle}")
|
|
99
|
+
|
|
100
|
+
else:
|
|
101
|
+
|
|
102
|
+
lines.append("No circular dependencies found.")
|
|
103
|
+
|
|
104
|
+
lines.append("")
|
|
105
|
+
|
|
106
|
+
# --------------------------------------------------
|
|
107
|
+
|
|
108
|
+
lines.append("## Source Code Analysis\n")
|
|
109
|
+
|
|
110
|
+
lines.append("| File | Classes | Functions | Imports |")
|
|
111
|
+
lines.append("|------|---------:|----------:|--------:|")
|
|
112
|
+
|
|
113
|
+
for item in source_analysis:
|
|
114
|
+
|
|
115
|
+
lines.append(
|
|
116
|
+
f"| {item['file']} | "
|
|
117
|
+
f"{len(item['classes'])} | "
|
|
118
|
+
f"{len(item['functions'])} | "
|
|
119
|
+
f"{len(item['imports'])} |"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
with open(
|
|
123
|
+
"report.md",
|
|
124
|
+
"w",
|
|
125
|
+
encoding="utf-8"
|
|
126
|
+
) as file:
|
|
127
|
+
|
|
128
|
+
file.write(
|
|
129
|
+
"\n".join(lines)
|
|
130
|
+
)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StatisticsReporter:
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.console = Console()
|
|
11
|
+
|
|
12
|
+
def show_statistics(
|
|
13
|
+
self,
|
|
14
|
+
source_analysis,
|
|
15
|
+
file_violations,
|
|
16
|
+
function_violations,
|
|
17
|
+
architecture_violations,
|
|
18
|
+
circular_violations,
|
|
19
|
+
db_access_violations,
|
|
20
|
+
score
|
|
21
|
+
):
|
|
22
|
+
|
|
23
|
+
total_files = len(source_analysis)
|
|
24
|
+
|
|
25
|
+
total_classes = sum(
|
|
26
|
+
len(item["classes"])
|
|
27
|
+
for item in source_analysis
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
total_functions = sum(
|
|
31
|
+
len(item["functions"])
|
|
32
|
+
for item in source_analysis
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
total_imports = sum(
|
|
36
|
+
len(item["imports"])
|
|
37
|
+
for item in source_analysis
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
py_files = 0
|
|
41
|
+
ts_files = 0
|
|
42
|
+
js_files = 0
|
|
43
|
+
|
|
44
|
+
total_lines = 0
|
|
45
|
+
largest_file = "-"
|
|
46
|
+
largest_line_count = 0
|
|
47
|
+
|
|
48
|
+
for item in source_analysis:
|
|
49
|
+
|
|
50
|
+
file_name = item["file"]
|
|
51
|
+
|
|
52
|
+
if file_name.endswith(".py"):
|
|
53
|
+
py_files += 1
|
|
54
|
+
|
|
55
|
+
elif file_name.endswith(".ts"):
|
|
56
|
+
ts_files += 1
|
|
57
|
+
|
|
58
|
+
elif file_name.endswith(".js"):
|
|
59
|
+
js_files += 1
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
|
|
63
|
+
with open(
|
|
64
|
+
file_name,
|
|
65
|
+
"r",
|
|
66
|
+
encoding="utf-8"
|
|
67
|
+
) as file:
|
|
68
|
+
|
|
69
|
+
line_count = len(
|
|
70
|
+
file.readlines()
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
total_lines += line_count
|
|
74
|
+
|
|
75
|
+
if line_count > largest_line_count:
|
|
76
|
+
|
|
77
|
+
largest_line_count = line_count
|
|
78
|
+
largest_file = file_name
|
|
79
|
+
|
|
80
|
+
except Exception:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
average_lines = (
|
|
84
|
+
total_lines // total_files
|
|
85
|
+
if total_files > 0
|
|
86
|
+
else 0
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
table = Table(
|
|
90
|
+
title="Project Statistics"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
table.add_column(
|
|
94
|
+
"Metric",
|
|
95
|
+
style="cyan"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
table.add_column(
|
|
99
|
+
"Value",
|
|
100
|
+
style="green"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
table.add_row(
|
|
104
|
+
"Files Scanned",
|
|
105
|
+
str(total_files)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
table.add_row(
|
|
109
|
+
"Classes Found",
|
|
110
|
+
str(total_classes)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
table.add_row(
|
|
114
|
+
"Functions Found",
|
|
115
|
+
str(total_functions)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
table.add_row(
|
|
119
|
+
"Imports Found",
|
|
120
|
+
str(total_imports)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
table.add_row(
|
|
124
|
+
"Python Files",
|
|
125
|
+
str(py_files)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
table.add_row(
|
|
129
|
+
"TypeScript Files",
|
|
130
|
+
str(ts_files)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
table.add_row(
|
|
134
|
+
"JavaScript Files",
|
|
135
|
+
str(js_files)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
table.add_row(
|
|
139
|
+
"Total Lines of Code",
|
|
140
|
+
str(total_lines)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
table.add_row(
|
|
144
|
+
"Average File Size",
|
|
145
|
+
f"{average_lines} lines"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
table.add_row(
|
|
149
|
+
"Largest File",
|
|
150
|
+
f"{largest_file} ({largest_line_count} lines)"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
table.add_row(
|
|
154
|
+
"Oversized Files",
|
|
155
|
+
str(len(file_violations))
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
table.add_row(
|
|
159
|
+
"Oversized Functions",
|
|
160
|
+
str(len(function_violations))
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
table.add_row(
|
|
164
|
+
"Architecture Violations",
|
|
165
|
+
str(len(architecture_violations))
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
table.add_row(
|
|
169
|
+
"Circular Dependencies",
|
|
170
|
+
str(len(circular_violations))
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
table.add_row(
|
|
174
|
+
"Direct DB Access Issues",
|
|
175
|
+
str(len(db_access_violations))
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
table.add_row(
|
|
179
|
+
"Architecture Score",
|
|
180
|
+
f"{score}/100"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.console.print(table)
|
|
File without changes
|