codeshift 0.5.0__py3-none-any.whl → 0.7.3__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.3
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
@@ -44,7 +44,10 @@ Dynamic: license-file
44
44
 
45
45
  # Codeshift
46
46
 
47
- [![PyPI version](https://badge.fury.io/py/codeshift.svg)](https://pypi.org/project/codeshift/)
47
+ [![CI](https://github.com/Ragab-Technologies/codeshift/actions/workflows/ci.yml/badge.svg)](https://github.com/Ragab-Technologies/codeshift/actions/workflows/ci.yml)
48
+ [![codecov](https://codecov.io/gh/Ragab-Technologies/codeshift/branch/main/graph/badge.svg)](https://codecov.io/gh/Ragab-Technologies/codeshift)
49
+ [![PyPI version](https://img.shields.io/pypi/v/codeshift)](https://pypi.org/project/codeshift/)
50
+ [![Downloads](https://static.pepy.tech/badge/codeshift/month)](https://pepy.tech/project/codeshift)
48
51
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
49
52
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
50
53
 
@@ -118,25 +121,29 @@ codeshift --help
118
121
  ## Quick Start
119
122
 
120
123
  ```bash
121
- # 1. Scan your project for outdated dependencies
122
- codeshift scan
123
-
124
- # 2. Upgrade a specific library
124
+ pip install codeshift
125
125
  codeshift upgrade pydantic --target 2.5.0
126
+ codeshift diff && codeshift apply
127
+ ```
126
128
 
127
- # 3. Review the proposed changes
128
- codeshift diff
129
+ That's it! Codeshift scans your code, transforms it, and shows exactly what changed.
129
130
 
130
- # 4. Apply the changes
131
- codeshift apply
131
+ ### Before / After
132
132
 
133
- # 5. Run your tests to verify
134
- pytest
135
- ```
133
+ | Pydantic v1 | Pydantic v2 |
134
+ |-------------|-------------|
135
+ | `class Config:` | `model_config = ConfigDict()` |
136
+ | `@validator` | `@field_validator` |
137
+ | `.dict()` | `.model_dump()` |
138
+ | `parse_obj()` | `model_validate()` |
136
139
 
137
- Or upgrade everything at once:
140
+ ### More Options
138
141
 
139
142
  ```bash
143
+ # Scan your project for all outdated dependencies
144
+ codeshift scan
145
+
146
+ # Or upgrade everything at once
140
147
  codeshift upgrade-all
141
148
  ```
142
149
 
@@ -1,17 +1,28 @@
1
- codeshift/__init__.py,sha256=L-ImO-zi7CvCuKCl_PYICUgTDppPXgYDaUoIQWukRzU,202
1
+ codeshift/__init__.py,sha256=IgqGM-QFk67tCA5P_i62lIvKs_YT8iumBgJ4GUZfSyg,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.3.dist-info/licenses/LICENSE,sha256=mHKnse9JK19WRK76lYEwKB9nJWyzMzRpG4gkUtbTyac,1066
83
+ codeshift-0.7.3.dist-info/METADATA,sha256=-e486E0pgZdy_uuRZ5rXoeRgMZm3QonjQxdPJXSGsCE,17497
84
+ codeshift-0.7.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
85
+ codeshift-0.7.3.dist-info/entry_points.txt,sha256=AlJ8V7a2pNyu-9UiRKUWiTMIJtaYAUnlg53Y-wFHiK0,53
86
+ codeshift-0.7.3.dist-info/top_level.txt,sha256=Ct42mtGs5foZ4MyYSksd5rXP0qFhWSZz8Y8mON0EEds,10
87
+ codeshift-0.7.3.dist-info/RECORD,,