Spaces:
Sleeping
Sleeping
File size: 5,138 Bytes
fc2d789 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | """
Utility helpers for the Code Review NLP Assistant.
"""
from __future__ import annotations
import ast
import re
from typing import Any
# ββ Code parsing helpers ββββββββββββββββββββββββββββββββββββββββββββββββββ
def extract_functions(code: str) -> list[dict[str, Any]]:
"""
Parse Python source and return metadata for each function/method.
Returns a list of dicts with keys:
name, args, returns, has_docstring, lineno, end_lineno, source
"""
results = []
try:
tree = ast.parse(code)
except SyntaxError:
return results
lines = code.splitlines()
for node in ast.walk(tree):
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
continue
# Argument names
args = [a.arg for a in node.args.args]
# Return annotation
returns = ""
if node.returns:
try:
returns = ast.unparse(node.returns)
except Exception:
returns = "?"
# Docstring check
has_doc = (
isinstance(node.body[0], ast.Expr)
and isinstance(node.body[0].value, ast.Constant)
and isinstance(node.body[0].value.value, str)
) if node.body else False
end = getattr(node, "end_lineno", node.lineno)
src_lines = lines[node.lineno - 1 : end]
source = "\n".join(src_lines)
results.append({
"name": node.name,
"args": args,
"returns": returns,
"has_docstring": has_doc,
"lineno": node.lineno,
"end_lineno": end,
"source": source,
})
return results
def extract_classes(code: str) -> list[dict[str, Any]]:
"""Return a list of class metadata dicts."""
results = []
try:
tree = ast.parse(code)
except SyntaxError:
return results
for node in ast.walk(tree):
if not isinstance(node, ast.ClassDef):
continue
methods = [
n.name for n in ast.walk(node)
if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
]
has_doc = (
isinstance(node.body[0], ast.Expr)
and isinstance(node.body[0].value, ast.Constant)
) if node.body else False
results.append({
"name": node.name,
"methods": methods,
"has_docstring": has_doc,
"lineno": node.lineno,
})
return results
def detect_language(code: str) -> str:
"""Very simple language heuristic."""
if re.search(r'\bdef\b.*:\s*$', code, re.MULTILINE):
return "python"
if re.search(r'\bfunction\b|\bconst\b|\blet\b|\bvar\b', code):
return "javascript"
if re.search(r'\bpublic\b.*\bclass\b', code):
return "java"
return "unknown"
# ββ Reporting helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββ
GRADE_MAP = [
(90, "A", "Excellent"),
(75, "B", "Good"),
(60, "C", "Needs work"),
(40, "D", "Poor"),
(0, "F", "Critical"),
]
def score_to_grade(score: float) -> tuple[str, str]:
"""Return (letter_grade, label) for a 0-100 score."""
for threshold, letter, label in GRADE_MAP:
if score >= threshold:
return letter, label
return "F", "Critical"
def score_color(score: float) -> str:
"""Return a hex colour representing the score quality."""
if score >= 80: return "#22c55e"
if score >= 60: return "#f59e0b"
if score >= 40: return "#f97316"
return "#ef4444"
def build_report(result: Any, filename: str = "code_review") -> str:
"""Generate a Markdown report from a CodeQualityResult."""
grade, label = score_to_grade(result.overall_score)
lines = [
f"# Code Review Report β `{filename}`\n",
f"## Overall Score: {result.overall_score}/100 ({grade} β {label})\n",
"### Sub-scores\n",
"| Metric | Score |",
"|--------|-------|",
f"| Documentation | {result.documentation_score}/100 |",
f"| Naming Quality | {result.naming_score}/100 |",
f"| Complexity | {result.complexity_score}/100 |",
"",
]
if result.issues:
lines.append("### Issues Found\n")
for issue in result.issues:
lines.append(f"- {issue}")
lines.append("")
if result.suggestions:
lines.append("### Suggestions\n")
for s in result.suggestions:
lines.append(f"- {s}")
lines.append("")
if result.generated_docstring:
lines.append("### Generated Docstring (CodeT5)\n")
lines.append("```python")
lines.append(result.generated_docstring)
lines.append("```\n")
lines.append("---")
lines.append("*Generated by Code Review NLP Assistant using CodeBERT + CodeT5*")
return "\n".join(lines) |