codeshift 0.5.0__py3-none-any.whl → 0.7.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.
@@ -0,0 +1,310 @@
1
+ """Report generation for health scores (JSON and HTML)."""
2
+
3
+ import html
4
+ import json
5
+ from datetime import datetime
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from codeshift.health.models import HealthReport, HealthScore
10
+
11
+
12
+ def generate_json_report(report: HealthReport | HealthScore, pretty: bool = True) -> str:
13
+ """Generate a JSON report.
14
+
15
+ Args:
16
+ report: HealthReport or HealthScore to serialize
17
+ pretty: Whether to pretty-print the JSON
18
+
19
+ Returns:
20
+ JSON string
21
+ """
22
+ if isinstance(report, HealthScore):
23
+ data = report.to_dict()
24
+ else:
25
+ data = report.to_dict()
26
+
27
+ if pretty:
28
+ return json.dumps(data, indent=2, default=_json_serializer)
29
+ return json.dumps(data, default=_json_serializer)
30
+
31
+
32
+ def save_json_report(report: HealthReport | HealthScore, output_path: Path) -> None:
33
+ """Save a JSON report to a file.
34
+
35
+ Args:
36
+ report: HealthReport or HealthScore to serialize
37
+ output_path: Path to save the report
38
+ """
39
+ json_content = generate_json_report(report)
40
+ output_path.write_text(json_content)
41
+
42
+
43
+ def generate_html_report(report: HealthReport | HealthScore) -> str:
44
+ """Generate an HTML report.
45
+
46
+ Args:
47
+ report: HealthReport or HealthScore to render
48
+
49
+ Returns:
50
+ HTML string
51
+ """
52
+ if isinstance(report, HealthScore):
53
+ score = report
54
+ trend_info = ""
55
+ else:
56
+ score = report.current
57
+ if report.previous:
58
+ delta = report.score_delta or 0
59
+ sign = "+" if delta >= 0 else ""
60
+ trend_info = (
61
+ f'<span class="trend {report.trend}">{report.trend_emoji} {sign}{delta:.1f}</span>'
62
+ )
63
+ else:
64
+ trend_info = '<span class="trend new">New baseline</span>'
65
+
66
+ # Build metrics rows
67
+ metrics_rows = ""
68
+ for metric in score.metrics:
69
+ grade_class = _get_score_class(metric.score)
70
+ metrics_rows += f"""
71
+ <tr>
72
+ <td>{_format_category(metric.category.value)}</td>
73
+ <td class="{grade_class}">{metric.score:.1f}</td>
74
+ <td>{metric.weight * 100:.0f}%</td>
75
+ <td>{html.escape(metric.description)}</td>
76
+ </tr>
77
+ """
78
+
79
+ # Build recommendations list
80
+ recs_html = ""
81
+ for rec in score.top_recommendations:
82
+ recs_html += f"<li>{html.escape(rec)}</li>\n"
83
+
84
+ # Build dependencies table
85
+ deps_rows = ""
86
+ for dep in score.dependencies:
87
+ status = "✓" if not dep.is_outdated else "↑"
88
+ status_class = "up-to-date" if not dep.is_outdated else "outdated"
89
+ tier = "Tier 1" if dep.has_tier1_support else ("Tier 2" if dep.has_tier2_support else "-")
90
+ vuln_count = len(dep.vulnerabilities)
91
+ vuln_class = "vuln-none" if vuln_count == 0 else "vuln-some"
92
+
93
+ deps_rows += f"""
94
+ <tr>
95
+ <td>{html.escape(dep.name)}</td>
96
+ <td>{html.escape(dep.current_version or 'unknown')}</td>
97
+ <td>{html.escape(dep.latest_version or 'unknown')}</td>
98
+ <td class="{status_class}">{status}</td>
99
+ <td>{tier}</td>
100
+ <td class="{vuln_class}">{vuln_count}</td>
101
+ </tr>
102
+ """
103
+
104
+ # Build vulnerabilities section
105
+ vulns_html = ""
106
+ if score.vulnerabilities:
107
+ vulns_rows = ""
108
+ for vuln in score.vulnerabilities:
109
+ vulns_rows += f"""
110
+ <tr class="severity-{vuln.severity.value}">
111
+ <td>{html.escape(vuln.package)}</td>
112
+ <td>{html.escape(vuln.vulnerability_id)}</td>
113
+ <td>{vuln.severity.value.upper()}</td>
114
+ <td>{html.escape(vuln.description[:100])}...</td>
115
+ <td>{html.escape(vuln.fixed_in or '-')}</td>
116
+ </tr>
117
+ """
118
+
119
+ vulns_html = f"""
120
+ <section class="vulnerabilities">
121
+ <h2>Security Vulnerabilities</h2>
122
+ <table>
123
+ <thead>
124
+ <tr>
125
+ <th>Package</th>
126
+ <th>ID</th>
127
+ <th>Severity</th>
128
+ <th>Description</th>
129
+ <th>Fixed In</th>
130
+ </tr>
131
+ </thead>
132
+ <tbody>
133
+ {vulns_rows}
134
+ </tbody>
135
+ </table>
136
+ </section>
137
+ """
138
+
139
+ grade_class = score.grade.value.lower()
140
+
141
+ return f"""<!DOCTYPE html>
142
+ <html lang="en">
143
+ <head>
144
+ <meta charset="UTF-8">
145
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
146
+ <title>Codeshift Health Report</title>
147
+ <style>
148
+ :root {{
149
+ --color-a: #22c55e;
150
+ --color-b: #06b6d4;
151
+ --color-c: #eab308;
152
+ --color-d: #f97316;
153
+ --color-f: #ef4444;
154
+ }}
155
+ * {{ box-sizing: border-box; margin: 0; padding: 0; }}
156
+ body {{
157
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
158
+ line-height: 1.6;
159
+ color: #1f2937;
160
+ background: #f9fafb;
161
+ padding: 2rem;
162
+ }}
163
+ .container {{ max-width: 1200px; margin: 0 auto; }}
164
+ h1 {{ margin-bottom: 0.5rem; }}
165
+ h2 {{ margin: 2rem 0 1rem; border-bottom: 2px solid #e5e7eb; padding-bottom: 0.5rem; }}
166
+ .header {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }}
167
+ .score-card {{
168
+ background: white;
169
+ border-radius: 12px;
170
+ padding: 2rem;
171
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
172
+ text-align: center;
173
+ }}
174
+ .grade {{ font-size: 4rem; font-weight: bold; }}
175
+ .grade.a {{ color: var(--color-a); }}
176
+ .grade.b {{ color: var(--color-b); }}
177
+ .grade.c {{ color: var(--color-c); }}
178
+ .grade.d {{ color: var(--color-d); }}
179
+ .grade.f {{ color: var(--color-f); }}
180
+ .overall-score {{ font-size: 1.5rem; color: #6b7280; }}
181
+ .trend {{ font-size: 1rem; margin-top: 0.5rem; display: block; }}
182
+ .trend.improving {{ color: var(--color-a); }}
183
+ .trend.declining {{ color: var(--color-f); }}
184
+ .trend.stable {{ color: #6b7280; }}
185
+ table {{ width: 100%; border-collapse: collapse; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
186
+ th, td {{ padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid #e5e7eb; }}
187
+ th {{ background: #f3f4f6; font-weight: 600; }}
188
+ tr:last-child td {{ border-bottom: none; }}
189
+ .excellent {{ color: var(--color-a); font-weight: 600; }}
190
+ .good {{ color: var(--color-b); font-weight: 600; }}
191
+ .fair {{ color: var(--color-c); font-weight: 600; }}
192
+ .poor {{ color: var(--color-d); font-weight: 600; }}
193
+ .critical {{ color: var(--color-f); font-weight: 600; }}
194
+ .up-to-date {{ color: var(--color-a); }}
195
+ .outdated {{ color: var(--color-d); }}
196
+ .vuln-none {{ color: var(--color-a); }}
197
+ .vuln-some {{ color: var(--color-f); font-weight: 600; }}
198
+ .severity-critical td {{ background: #fef2f2; }}
199
+ .severity-high td {{ background: #fff7ed; }}
200
+ .recommendations {{ background: white; border-radius: 8px; padding: 1.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
201
+ .recommendations ul {{ padding-left: 1.5rem; }}
202
+ .recommendations li {{ margin: 0.5rem 0; }}
203
+ .meta {{ color: #6b7280; font-size: 0.875rem; margin-top: 2rem; }}
204
+ </style>
205
+ </head>
206
+ <body>
207
+ <div class="container">
208
+ <div class="header">
209
+ <div>
210
+ <h1>Codeshift Health Report</h1>
211
+ <p>{html.escape(str(score.project_path))}</p>
212
+ </div>
213
+ <div class="score-card">
214
+ <div class="grade {grade_class}">{score.grade.value}</div>
215
+ <div class="overall-score">{score.overall_score:.1f}/100</div>
216
+ {trend_info}
217
+ </div>
218
+ </div>
219
+
220
+ <section>
221
+ <h2>Metrics Breakdown</h2>
222
+ <table>
223
+ <thead>
224
+ <tr>
225
+ <th>Category</th>
226
+ <th>Score</th>
227
+ <th>Weight</th>
228
+ <th>Details</th>
229
+ </tr>
230
+ </thead>
231
+ <tbody>
232
+ {metrics_rows}
233
+ </tbody>
234
+ </table>
235
+ </section>
236
+
237
+ <section class="recommendations">
238
+ <h2>Recommendations</h2>
239
+ <ul>
240
+ {recs_html if recs_html else "<li>No recommendations - your project is in great shape!</li>"}
241
+ </ul>
242
+ </section>
243
+
244
+ <section>
245
+ <h2>Dependencies ({len(score.dependencies)})</h2>
246
+ <table>
247
+ <thead>
248
+ <tr>
249
+ <th>Package</th>
250
+ <th>Current</th>
251
+ <th>Latest</th>
252
+ <th>Status</th>
253
+ <th>Migration Support</th>
254
+ <th>Vulnerabilities</th>
255
+ </tr>
256
+ </thead>
257
+ <tbody>
258
+ {deps_rows if deps_rows else "<tr><td colspan='6'>No dependencies found</td></tr>"}
259
+ </tbody>
260
+ </table>
261
+ </section>
262
+
263
+ {vulns_html}
264
+
265
+ <p class="meta">
266
+ Generated by Codeshift on {score.calculated_at.strftime('%Y-%m-%d %H:%M:%S')}
267
+ </p>
268
+ </div>
269
+ </body>
270
+ </html>
271
+ """
272
+
273
+
274
+ def save_html_report(report: HealthReport | HealthScore, output_path: Path) -> None:
275
+ """Save an HTML report to a file.
276
+
277
+ Args:
278
+ report: HealthReport or HealthScore to render
279
+ output_path: Path to save the report
280
+ """
281
+ html_content = generate_html_report(report)
282
+ output_path.write_text(html_content)
283
+
284
+
285
+ def _json_serializer(obj: Any) -> Any:
286
+ """Custom JSON serializer for non-standard types."""
287
+ if isinstance(obj, datetime):
288
+ return obj.isoformat()
289
+ if isinstance(obj, Path):
290
+ return str(obj)
291
+ raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
292
+
293
+
294
+ def _get_score_class(score: float) -> str:
295
+ """Get CSS class based on score."""
296
+ if score >= 90:
297
+ return "excellent"
298
+ elif score >= 80:
299
+ return "good"
300
+ elif score >= 70:
301
+ return "fair"
302
+ elif score >= 60:
303
+ return "poor"
304
+ else:
305
+ return "critical"
306
+
307
+
308
+ def _format_category(category: str) -> str:
309
+ """Format category name for display."""
310
+ return category.replace("_", " ").title()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeshift
3
- Version: 0.5.0
3
+ Version: 0.7.0
4
4
  Summary: AI-powered CLI tool that migrates Python code to handle breaking dependency changes
5
5
  Author: Ragab Technologies
6
6
  License: MIT
@@ -1,17 +1,28 @@
1
- codeshift/__init__.py,sha256=L-ImO-zi7CvCuKCl_PYICUgTDppPXgYDaUoIQWukRzU,202
1
+ codeshift/__init__.py,sha256=PqYe0Tu6RgBALNREdCA84JuTWQPa29eB4_cBTueBalc,202
2
2
  codeshift/analyzer/__init__.py,sha256=m61k8rtOyHQNoPLDeoe0S9WSy9syvCyMgLZ07ojpoNU,188
3
3
  codeshift/analyzer/risk_assessor.py,sha256=nKplyymbsqcbsZyDLVZ2zukpQlPD5KIvO-FBJ4AZFYc,13422
4
4
  codeshift/cli/__init__.py,sha256=rf0c8oMqzYktdLH8ga2sA7rS5GsJse8dGfIbfW4DDXo,87
5
- codeshift/cli/main.py,sha256=iLXy2QMLXoiM88sn-XEHk9vJncB-WeKfEq0eps3ZHig,6983
5
+ codeshift/cli/main.py,sha256=clTP_TFu1x6T0TLRvI-g2WbO4l3pbMZckQQ3AwnsIy8,7056
6
6
  codeshift/cli/package_manager.py,sha256=K7spYHSQ6VoHlrzM3L9B2JoxBTe_9gLd57P6ME0PsSI,2807
7
7
  codeshift/cli/quota.py,sha256=zBiY3zqCGEyxbS3vnoQdgBld1emMQzLc0_5cUOWy9U8,5989
8
8
  codeshift/cli/commands/__init__.py,sha256=Kbs7DFUWOXkw5-9jiiR03cuUJeo5byusLr_Y3cKFE3E,218
9
9
  codeshift/cli/commands/apply.py,sha256=JqUiu6I5WD25677SXpOejKxLWNIUQUKrbOQ_JbpoEak,11213
10
10
  codeshift/cli/commands/auth.py,sha256=bCGF9aEz-5PLsp1rOkHZ8A3GRtd9NtpC3tePlJ3ZO6M,28638
11
11
  codeshift/cli/commands/diff.py,sha256=4LjrVRu4lhj-aOBvClOnEnN0l2nZEU5S1_qzYoXL4dQ,6357
12
+ codeshift/cli/commands/health.py,sha256=aH2QMIr8ucf1WVSZHH4Bjux1WZosz18dtdA95TH_eX0,7701
12
13
  codeshift/cli/commands/scan.py,sha256=Eia3xW6sVZSiKtxd7JXyjIcOUsxduLGOuFjBhhp1ut0,11373
13
14
  codeshift/cli/commands/upgrade.py,sha256=15SPmu09oi7Gu7KLvyDLbcF-YndkEoa0sPhlMZ-zsuo,15853
14
15
  codeshift/cli/commands/upgrade_all.py,sha256=FimS7SYJeYkQvhrWACLQG4QiyyynCgi9SapUb8Ax0Ik,18964
16
+ codeshift/health/__init__.py,sha256=UwyuKdvFJnqhZSUHFQ2wgtxpXol9AHelDiUaOnrKYGc,1207
17
+ codeshift/health/calculator.py,sha256=JD5UTVqHKxcJc8mUaWF3V2fy6EpyPO7VIbHGBrONDcI,8032
18
+ codeshift/health/models.py,sha256=7yTthtQyQNIEDlggg8JjXb_G4qfqYOvg5a87kuBvVis,8672
19
+ codeshift/health/report.py,sha256=fEolswSGjL8pETowqCPnmBbDIMPXazG-SliGFNbBfJU,10551
20
+ codeshift/health/metrics/__init__.py,sha256=9qS6nUPjbO99DICqJiUWuhYfMAn-Fxqxw3e3grks8IA,1748
21
+ codeshift/health/metrics/documentation.py,sha256=3dJ_oR6HcV-bFpIF2FCT1FT38Hh0_vWsMXabkh-oXLk,6375
22
+ codeshift/health/metrics/freshness.py,sha256=xzDYOq3_K_F2dYVF6W1Y5MSBW864FQKInv7rhgMb6VA,6096
23
+ codeshift/health/metrics/migration_readiness.py,sha256=mhXo_1yEmxe0kwW-0e6c4Dqx7mqbPZVL9mpfmgL2E14,5000
24
+ codeshift/health/metrics/security.py,sha256=rUco4Or_hXLY8ZSuNMT0DCyPJDfUHTrtDzIrsMJrjWk,7447
25
+ codeshift/health/metrics/test_coverage.py,sha256=0jsPcNGfhEnGDRB2LhReQDsiir5U9kVi2MKJ_6zCt-k,6046
15
26
  codeshift/knowledge/__init__.py,sha256=_YwrLgjvsJQuYajfnIhUQqFeircF0MfkI9zJBPZTupc,1221
16
27
  codeshift/knowledge/cache.py,sha256=aEx9aNDfrzCYMzlPRBzBOYiFIAGDcZJfQvcXroa5vsA,4837
17
28
  codeshift/knowledge/generator.py,sha256=t30Rf8ByFxjz3wUk8Dq5fWGcL2MLS_rP4Xik4OspAUs,7386
@@ -68,9 +79,9 @@ codeshift/utils/llm_client.py,sha256=NNVBJIl0dbxU9PMOJuSdDCTvRZFNetgJIkmjVSjEM0c
68
79
  codeshift/validator/__init__.py,sha256=WRQSfJ7eLJdjR2_f_dXSaBtfawkvu1Dlu20Gh76D12c,280
69
80
  codeshift/validator/syntax_checker.py,sha256=FJeLIqhNhV7_Xj2RskHScJZks6A9fybaqv5Z1-MGDfo,5343
70
81
  codeshift/validator/test_runner.py,sha256=VX0OqkuI3AJxOUzRW2_BEjdDsMw1N4a0od-pPbSF6O8,6760
71
- codeshift-0.5.0.dist-info/licenses/LICENSE,sha256=mHKnse9JK19WRK76lYEwKB9nJWyzMzRpG4gkUtbTyac,1066
72
- codeshift-0.5.0.dist-info/METADATA,sha256=xzJZhOOKl7VZMhw-2qQBXfVhSAVmGJIcbpWoZFt_xOw,16841
73
- codeshift-0.5.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
74
- codeshift-0.5.0.dist-info/entry_points.txt,sha256=AlJ8V7a2pNyu-9UiRKUWiTMIJtaYAUnlg53Y-wFHiK0,53
75
- codeshift-0.5.0.dist-info/top_level.txt,sha256=Ct42mtGs5foZ4MyYSksd5rXP0qFhWSZz8Y8mON0EEds,10
76
- codeshift-0.5.0.dist-info/RECORD,,
82
+ codeshift-0.7.0.dist-info/licenses/LICENSE,sha256=mHKnse9JK19WRK76lYEwKB9nJWyzMzRpG4gkUtbTyac,1066
83
+ codeshift-0.7.0.dist-info/METADATA,sha256=CYqRP2CSoxpilkxYx8yxz3ceoofvLFsOW29YtSvTNkw,16841
84
+ codeshift-0.7.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
85
+ codeshift-0.7.0.dist-info/entry_points.txt,sha256=AlJ8V7a2pNyu-9UiRKUWiTMIJtaYAUnlg53Y-wFHiK0,53
86
+ codeshift-0.7.0.dist-info/top_level.txt,sha256=Ct42mtGs5foZ4MyYSksd5rXP0qFhWSZz8Y8mON0EEds,10
87
+ codeshift-0.7.0.dist-info/RECORD,,