Spaces:
Sleeping
Sleeping
| """ | |
| Code Review NLP Assistant β Gradio App | |
| Run with: python app.py | |
| """ | |
| import sys | |
| import os | |
| sys.path.insert(0, os.path.dirname(__file__)) | |
| import gradio as gr | |
| import plotly.graph_objects as go | |
| from models.code_analyzer import CodeReviewAnalyzer | |
| from utils.helpers import ( | |
| extract_functions, | |
| extract_classes, | |
| score_to_grade, | |
| score_color, | |
| build_report, | |
| ) | |
| from data.sample_code import SAMPLES | |
| analyzer = CodeReviewAnalyzer(use_gpu=False) | |
| def build_radar(doc_score, name_score, comp_score, overall): | |
| fig = go.Figure(go.Scatterpolar( | |
| r=[doc_score, name_score, comp_score, overall, doc_score], | |
| theta=["Documentation", "Naming", "Complexity", "Overall", "Documentation"], | |
| fill="toself", | |
| fillcolor="rgba(99,102,241,0.2)", | |
| line=dict(color="#6366f1", width=2), | |
| )) | |
| fig.update_layout( | |
| polar=dict(radialaxis=dict(visible=True, range=[0, 100])), | |
| showlegend=False, | |
| margin=dict(l=40, r=40, t=40, b=40), | |
| height=320, | |
| ) | |
| return fig | |
| def build_bars(doc_score, name_score, comp_score): | |
| fig = go.Figure(go.Bar( | |
| x=["Documentation", "Naming", "Complexity"], | |
| y=[doc_score, name_score, comp_score], | |
| marker_color=[ | |
| score_color(doc_score), | |
| score_color(name_score), | |
| score_color(comp_score), | |
| ], | |
| text=[str(doc_score), str(name_score), str(comp_score)], | |
| textposition="outside", | |
| )) | |
| fig.update_layout( | |
| yaxis=dict(range=[0, 115]), | |
| margin=dict(l=20, r=20, t=20, b=20), | |
| height=300, | |
| ) | |
| return fig | |
| def analyze_code(code, sample_choice, generate_doc, get_embed): | |
| if sample_choice != "None" and not code.strip(): | |
| code = SAMPLES[sample_choice] | |
| if not code.strip(): | |
| return ( | |
| "<p>β οΈ Please paste some code or pick a sample.</p>", | |
| None, None, "", "", "", "", "" | |
| ) | |
| result = analyzer.analyze( | |
| code, | |
| generate_doc=generate_doc, | |
| get_embedding=get_embed, | |
| ) | |
| functions = extract_functions(code) | |
| classes = extract_classes(code) | |
| grade, label = score_to_grade(result.overall_score) | |
| color = score_color(result.overall_score) | |
| score_html = f""" | |
| <div style="text-align:center; padding:1.5rem; | |
| background:#0f172a; border-radius:16px; | |
| border:1px solid #1e293b; color:white;"> | |
| <div style="font-size:0.8rem; color:#94a3b8; | |
| text-transform:uppercase; letter-spacing:0.1em;"> | |
| Overall Score | |
| </div> | |
| <div style="font-size:3rem; font-weight:700; color:{color}; margin:0.3rem 0;"> | |
| {result.overall_score} | |
| </div> | |
| <div style="font-size:1rem; color:#e2e8f0;"> | |
| Grade {grade} β {label} | |
| </div> | |
| <div style="display:flex; justify-content:center; | |
| gap:2rem; margin-top:1rem; flex-wrap:wrap;"> | |
| <div> | |
| <div style="color:#94a3b8; font-size:0.75rem;">Docs</div> | |
| <div style="color:{score_color(result.documentation_score)}; | |
| font-weight:600; font-size:1.1rem;"> | |
| {result.documentation_score} | |
| </div> | |
| </div> | |
| <div> | |
| <div style="color:#94a3b8; font-size:0.75rem;">Naming</div> | |
| <div style="color:{score_color(result.naming_score)}; | |
| font-weight:600; font-size:1.1rem;"> | |
| {result.naming_score} | |
| </div> | |
| </div> | |
| <div> | |
| <div style="color:#94a3b8; font-size:0.75rem;">Complexity</div> | |
| <div style="color:{score_color(result.complexity_score)}; | |
| font-weight:600; font-size:1.1rem;"> | |
| {result.complexity_score} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| issues_md = "\n".join(f"β οΈ {i}" for i in result.issues) \ | |
| if result.issues else "β No critical issues found!" | |
| suggestions_md = "\n".join(f"π‘ {s}" for s in result.suggestions) | |
| func_lines = [] | |
| for fn in functions: | |
| doc = "β docstring" if fn["has_docstring"] else "β no docstring" | |
| args = ", ".join(fn["args"]) if fn["args"] else "none" | |
| func_lines.append( | |
| f"**def {fn['name']}()** β args: `{args}` | " | |
| f"returns: `{fn['returns'] or 'not annotated'}` | {doc}" | |
| ) | |
| funcs_md = "\n\n".join(func_lines) if func_lines else "No functions found." | |
| class_lines = [] | |
| for cls in classes: | |
| doc = "β docstring" if cls["has_docstring"] else "β no docstring" | |
| methods = ", ".join(cls["methods"][:5]) | |
| class_lines.append( | |
| f"**class {cls['name']}** β methods: `{methods}` | {doc}" | |
| ) | |
| classes_md = "\n\n".join(class_lines) if class_lines else "No classes found." | |
| docstring_md = f"```python\n{result.generated_docstring}\n```" \ | |
| if result.generated_docstring else "Docstring generation was disabled." | |
| report = build_report(result) | |
| radar = build_radar( | |
| result.documentation_score, | |
| result.naming_score, | |
| result.complexity_score, | |
| result.overall_score, | |
| ) | |
| bars = build_bars( | |
| result.documentation_score, | |
| result.naming_score, | |
| result.complexity_score, | |
| ) | |
| return ( | |
| score_html, radar, bars, | |
| issues_md, suggestions_md, | |
| funcs_md, classes_md, | |
| docstring_md, report, | |
| ) | |
| def load_sample(sample_choice): | |
| if sample_choice == "None": | |
| return "" | |
| return SAMPLES[sample_choice] | |
| with gr.Blocks(title="Code Review NLP Assistant") as demo: | |
| gr.Markdown("# π¬ Code Review NLP Assistant") | |
| gr.Markdown("Powered by **CodeBERT** Β· **CodeT5** Β· **AST Analysis** β 100% free & open source") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π Input") | |
| sample_dropdown = gr.Dropdown( | |
| choices=["None"] + list(SAMPLES.keys()), | |
| value="None", | |
| label="Load a sample", | |
| ) | |
| code_input = gr.Code( | |
| language="python", | |
| label="Paste your Python code here", | |
| lines=20, | |
| ) | |
| sample_dropdown.change( | |
| fn=load_sample, | |
| inputs=sample_dropdown, | |
| outputs=code_input, | |
| ) | |
| with gr.Row(): | |
| generate_doc = gr.Checkbox(value=True, label="Generate docstring (CodeT5)") | |
| get_embed = gr.Checkbox(value=False, label="Get embedding (CodeBERT)") | |
| analyze_btn = gr.Button("π Analyze Code", variant="primary", size="lg") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π Results") | |
| score_html = gr.HTML() | |
| with gr.Row(): | |
| radar_chart = gr.Plot(label="Quality Radar") | |
| bar_chart = gr.Plot(label="Score Breakdown") | |
| with gr.Tabs(): | |
| with gr.Tab("β οΈ Issues"): | |
| issues_out = gr.Markdown() | |
| with gr.Tab("π‘ Suggestions"): | |
| suggestions_out = gr.Markdown() | |
| with gr.Tab("π§ Functions"): | |
| funcs_out = gr.Markdown() | |
| with gr.Tab("ποΈ Classes"): | |
| classes_out = gr.Markdown() | |
| with gr.Tab("π€ Docstring"): | |
| docstring_out = gr.Markdown() | |
| with gr.Tab("π Full Report"): | |
| report_out = gr.Markdown() | |
| analyze_btn.click( | |
| fn=analyze_code, | |
| inputs=[code_input, sample_dropdown, generate_doc, get_embed], | |
| outputs=[ | |
| score_html, radar_chart, bar_chart, | |
| issues_out, suggestions_out, | |
| funcs_out, classes_out, | |
| docstring_out, report_out, | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(share=True) |