Spaces:
Sleeping
Sleeping
| import javalang | |
| from typing import Dict, List, Tuple | |
| from dataclasses import dataclass | |
| import gradio as gr | |
| class RubricCriterion: | |
| name: str | |
| description: str | |
| weight: int | |
| is_essential: bool | |
| levels: Dict[str, Dict[str, float]] | |
| class EnhancedJavaPOOEvaluator: | |
| """Avaliador POO com rubrica detalhada""" | |
| def __init__(self): | |
| self.rubric = { | |
| "classes_objects": RubricCriterion( | |
| name="Classes e Objetos", | |
| description="Avalia a definição e uso de classes e objetos", | |
| weight=20, | |
| is_essential=True, | |
| levels={ | |
| "Fraco": {"threshold": 0, "description": "Nenhuma ou poucas classes/objetos"}, | |
| "Regular": {"threshold": 10, "description": "Classes básicas sem organização clara"}, | |
| "Bom": {"threshold": 15, "description": "Classes bem estruturadas e objetos adequados"}, | |
| "Excelente": {"threshold": 20, "description": "Excelente uso de classes e objetos"} | |
| } | |
| ), | |
| "methods": RubricCriterion( | |
| name="Métodos", | |
| description="Avalia métodos e sua organização", | |
| weight=20, | |
| is_essential=True, | |
| levels={ | |
| "Fraco": {"threshold": 0, "description": "Poucos métodos ou mal estruturados"}, | |
| "Regular": {"threshold": 10, "description": "Métodos básicos sem sobrecarga"}, | |
| "Bom": {"threshold": 15, "description": "Boa organização e alguns métodos sobrecarregados"}, | |
| "Excelente": {"threshold": 20, "description": "Excelente organização e uso de sobrecarga"} | |
| } | |
| ), | |
| "attributes": RubricCriterion( | |
| name="Atributos", | |
| description="Avalia atributos e sua organização", | |
| weight=20, | |
| is_essential=True, | |
| levels={ | |
| "Fraco": {"threshold": 0, "description": "Poucos atributos ou mal organizados"}, | |
| "Regular": {"threshold": 10, "description": "Atributos básicos sem encapsulamento"}, | |
| "Bom": {"threshold": 15, "description": "Boa organização de atributos"}, | |
| "Excelente": {"threshold": 20, "description": "Excelente organização e encapsulamento"} | |
| } | |
| ), | |
| "encapsulation": RubricCriterion( | |
| name="Encapsulamento", | |
| description="Avalia uso de modificadores e getters/setters", | |
| weight=10, | |
| is_essential=False, | |
| levels={ | |
| "Ausente": {"threshold": 0, "description": "Sem encapsulamento"}, | |
| "Parcial": {"threshold": 5, "description": "Encapsulamento básico"}, | |
| "Bom": {"threshold": 7.5, "description": "Bom uso de encapsulamento"}, | |
| "Excelente": {"threshold": 10, "description": "Encapsulamento completo e correto"} | |
| } | |
| ), | |
| "inheritance": RubricCriterion( | |
| name="Herança", | |
| description="Avalia uso de herança", | |
| weight=10, | |
| is_essential=False, | |
| levels={ | |
| "Ausente": {"threshold": 0, "description": "Sem uso de herança"}, | |
| "Parcial": {"threshold": 5, "description": "Uso básico de herança"}, | |
| "Bom": {"threshold": 7.5, "description": "Bom uso de herança"}, | |
| "Excelente": {"threshold": 10, "description": "Uso avançado e apropriado de herança"} | |
| } | |
| ), | |
| "polymorphism": RubricCriterion( | |
| name="Polimorfismo", | |
| description="Avalia uso de polimorfismo", | |
| weight=10, | |
| is_essential=False, | |
| levels={ | |
| "Ausente": {"threshold": 0, "description": "Sem uso de polimorfismo"}, | |
| "Parcial": {"threshold": 5, "description": "Uso básico de sobrescrita"}, | |
| "Bom": {"threshold": 7.5, "description": "Bom uso de polimorfismo"}, | |
| "Excelente": {"threshold": 10, "description": "Uso avançado de polimorfismo"} | |
| } | |
| ), | |
| "abstraction": RubricCriterion( | |
| name="Abstração", | |
| description="Avalia uso de abstrações", | |
| weight=10, | |
| is_essential=False, | |
| levels={ | |
| "Ausente": {"threshold": 0, "description": "Sem uso de abstração"}, | |
| "Parcial": {"threshold": 5, "description": "Uso básico de interfaces/classes abstratas"}, | |
| "Bom": {"threshold": 7.5, "description": "Bom uso de abstração"}, | |
| "Excelente": {"threshold": 10, "description": "Uso completo de abstrações"} | |
| } | |
| ) | |
| } | |
| def evaluate_criterion(self, criterion: RubricCriterion, analysis_result: Dict) -> Tuple[float, str, str]: | |
| """Avalia um critério específico baseado nos resultados da análise""" | |
| score = 0 | |
| level = list(criterion.levels.keys())[0] # Nível mais baixo por padrão | |
| feedback = [] | |
| if criterion.name == "Classes e Objetos": | |
| num_classes = len(analysis_result.get("classes", [])) | |
| num_objects = len(analysis_result.get("objects", [])) | |
| if num_classes >= 3 and num_objects >= 5: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif num_classes >= 2 and num_objects >= 3: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif num_classes >= 1 and num_objects >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Regular" | |
| feedback.append(f"Encontradas {num_classes} classes e {num_objects} objetos") | |
| elif criterion.name == "Métodos": | |
| methods = analysis_result.get("methods", []) | |
| method_names = [m.name for m in methods] | |
| overloaded = len([name for name in method_names if method_names.count(name) > 1]) | |
| if len(methods) >= 5 and overloaded >= 2: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif len(methods) >= 3 and overloaded >= 1: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif len(methods) >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Regular" | |
| feedback.append(f"Encontrados {len(methods)} métodos, sendo {overloaded} sobrecarregados") | |
| elif criterion.name == "Atributos": | |
| attributes = analysis_result.get("attributes", []) | |
| num_private = analysis_result["encapsulation"]["private_count"] | |
| if len(attributes) >= 5 and num_private >= 3: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif len(attributes) >= 3 and num_private >= 1: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif len(attributes) >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Regular" | |
| feedback.append(f"Encontrados {len(attributes)} atributos, sendo {num_private} privados") | |
| elif criterion.name == "Encapsulamento": | |
| num_private = analysis_result["encapsulation"]["private_count"] | |
| num_getters_setters = analysis_result["encapsulation"]["getters_setters"] | |
| if num_private >= 3 and num_getters_setters >= 4: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif num_private >= 2 and num_getters_setters >= 3: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif num_private >= 1 and num_getters_setters >= 2: | |
| score = criterion.weight * 0.5 | |
| level = "Parcial" | |
| feedback.append(f"Encontrados {num_private} atributos privados e {num_getters_setters} getters/setters") | |
| elif criterion.name == "Herança": | |
| subclasses = analysis_result["inheritance"]["subclasses"] | |
| if len(subclasses) >= 3: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif len(subclasses) >= 2: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif len(subclasses) >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Parcial" | |
| feedback.append(f"Encontradas {len(subclasses)} classes que usam herança") | |
| elif criterion.name == "Polimorfismo": | |
| overridden = len(analysis_result["polymorphism"]["overridden_methods"]) | |
| if overridden >= 3: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif overridden >= 2: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif overridden >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Parcial" | |
| feedback.append(f"Encontrados {overridden} métodos sobrescritos") | |
| elif criterion.name == "Abstração": | |
| abstract_classes = len(analysis_result["abstraction"]["abstract_classes"]) | |
| interfaces = len(analysis_result["abstraction"]["interfaces"]) | |
| if abstract_classes >= 1 and interfaces >= 1: | |
| score = criterion.weight | |
| level = "Excelente" | |
| elif abstract_classes >= 1 and interfaces >= 0: | |
| score = criterion.weight * 0.75 | |
| level = "Bom" | |
| elif abstract_classes >= 1 or interfaces >= 1: | |
| score = criterion.weight * 0.5 | |
| level = "Parcial" | |
| feedback.append(f"Encontradas {abstract_classes} classes abstratas e {interfaces} interfaces") | |
| return score, level, ". ".join(feedback) | |
| def analyze_code(self, code: str) -> Dict: | |
| """Analisa o código Java e retorna dados brutos""" | |
| analysis = { | |
| "classes": [], | |
| "objects": [], | |
| "methods": [], | |
| "attributes": [], | |
| "encapsulation": {"private_count": 0, "getters_setters": 0}, | |
| "inheritance": {"subclasses": []}, | |
| "polymorphism": {"overridden_methods": []}, | |
| "abstraction": {"abstract_classes": [], "interfaces": []} | |
| } | |
| try: | |
| tree = javalang.parse.parse(code) | |
| # Análise de classes e objetos check | |
| analysis["classes"] = [node for _, node in tree.filter(javalang.tree.ClassDeclaration)] | |
| analysis["objects"] = [node for _, node in tree.filter(javalang.tree.VariableDeclarator) | |
| if isinstance(node.initializer, javalang.tree.ClassCreator)] | |
| # Análise de métodos | |
| analysis["methods"] = [node for _, node in tree.filter(javalang.tree.MethodDeclaration)] | |
| # Análise de atributos e encapsulamento | |
| fields = [node for _, node in tree.filter(javalang.tree.FieldDeclaration)] | |
| analysis["attributes"] = fields | |
| analysis["encapsulation"]["private_count"] = sum(1 for field in fields | |
| if "private" in field.modifiers) | |
| # Contagem de getters e setters | |
| methods = analysis["methods"] | |
| getters_setters = sum(1 for method in methods | |
| if method.name.startswith('get') or method.name.startswith('set')) | |
| analysis["encapsulation"]["getters_setters"] = getters_setters | |
| # Análise de herança | |
| analysis["inheritance"]["subclasses"] = [cls for cls in analysis["classes"] | |
| if cls.extends is not None] | |
| # Análise de polimorfismo | |
| analysis["polymorphism"]["overridden_methods"] = [method for method in methods | |
| if any(ann.name == "Override" | |
| for ann in (method.annotations or []))] | |
| # Análise de abstração | |
| analysis["abstraction"]["abstract_classes"] = [cls for cls in analysis["classes"] | |
| if "abstract" in cls.modifiers] | |
| analysis["abstraction"]["interfaces"] = [node for _, node in tree.filter(javalang.tree.InterfaceDeclaration)] | |
| except Exception as e: | |
| print(f"Erro na análise: {str(e)}") | |
| return analysis | |
| def evaluate_code(self, code: str) -> Dict: | |
| """Avalia o código Java usando a rubrica detalhada""" | |
| analysis = self.analyze_code(code) | |
| evaluation = { | |
| "scores": {}, | |
| "levels": {}, | |
| "feedback": {}, | |
| "summary": { | |
| "essential_score": 0, | |
| "bonus_score": 0, | |
| "total_score": 0 | |
| } | |
| } | |
| # Avalia cada critério | |
| for criterion_key, criterion in self.rubric.items(): | |
| score, level, feedback = self.evaluate_criterion(criterion, analysis) | |
| evaluation["scores"][criterion_key] = score | |
| evaluation["levels"][criterion_key] = level | |
| evaluation["feedback"][criterion_key] = feedback | |
| if criterion.is_essential: | |
| evaluation["summary"]["essential_score"] += score | |
| else: | |
| evaluation["summary"]["bonus_score"] += score | |
| evaluation["summary"]["total_score"] = min(100, | |
| evaluation["summary"]["essential_score"] + | |
| evaluation["summary"]["bonus_score"]) | |
| # Determina nível geral | |
| if evaluation["summary"]["total_score"] >= 90: | |
| evaluation["summary"]["proficiency"] = "Excelente" | |
| elif evaluation["summary"]["total_score"] >= 75: | |
| evaluation["summary"]["proficiency"] = "Bom" | |
| elif evaluation["summary"]["total_score"] >= 60: | |
| evaluation["summary"]["proficiency"] = "Satisfatório" | |
| else: | |
| evaluation["summary"]["proficiency"] = "Necessita Melhorias" | |
| return evaluation | |
| # Interface Gradio | |
| with gr.Blocks(title="Java-Judge: Avaliador de POO em Java") as demo: | |
| gr.Markdown("# Java-Judge: Avaliador de POO em Java") | |
| # Descrição do autor e link | |
| gr.HTML(""" | |
| <p>Java-Judge: Avaliador de POO em Java</p> | |
| <p>Ramon Mayor Martins: <a href="https://rmayormartins.github.io/" target="_blank">Website</a> | | |
| <a href="https://huggingface.co/rmayormartins" target="_blank">Spaces</a></p> | |
| """) | |
| gr.Markdown(""" | |
| Este avaliador analisa código Java em relação aos princípios de Programação Orientada a Objetos. | |
| Critérios avaliados: | |
| """) | |
| # Links para a rubrica | |
| gr.HTML(""" | |
| <p> | |
| <a href="https://huggingface.co/spaces/rmayormartins/java-judge-oo/blob/main/assets/rubric.pdf" target="_blank">📄 Visualizar Rubrica PDF</a> | |
| </p> | |
| <p> | |
| <a href="https://huggingface.co/spaces/rmayormartins/java-judge-oo/blob/main/assets/rubric_table.PNG" target="_blank">📊 Visualizar Tabela da Rubrica</a> | |
| </p> | |
| <p> | |
| <a href="https://hackmd.io/@dc5kbfmvTRSDYhAP5VRHUQ/Bk1xG_vByx" target="_blank">🖥️ Visualizar Markdown</a> | |
| </p> | |
| """) | |
| upload = gr.File(label="Carregue arquivos Java para avaliação", file_types=[".java"], file_count="multiple") | |
| evaluate_button = gr.Button("Avaliar Código") | |
| output = gr.Textbox(label="Resultado da Avaliação", lines=25) | |
| def evaluate_code_files(files) -> str: | |
| """Função para avaliar múltiplos arquivos Java""" | |
| evaluator = EnhancedJavaPOOEvaluator() | |
| results = [] | |
| for file in files: | |
| with open(file.name, 'r', encoding='utf-8') as f: | |
| code = f.read() | |
| evaluation = evaluator.evaluate_code(code) | |
| # Formatar resultado por arquivo | |
| result = f"\n{'='*50}\nAvaliação do arquivo: {file.name}\n{'='*50}\n\n" | |
| # Pontuação e nível geral | |
| result += f"Pontuação Total: {evaluation['summary']['total_score']:.1f}/100\n" | |
| result += f"Nível de Proficiência: {evaluation['summary']['proficiency']}\n" | |
| result += f"Pontuação Essencial: {evaluation['summary']['essential_score']:.1f}/60\n" | |
| result += f"Pontuação Bônus: {evaluation['summary']['bonus_score']:.1f}/40\n\n" | |
| # Detalhamento por critério | |
| result += "Avaliação Detalhada por Critério:\n" | |
| result += "-" * 30 + "\n\n" | |
| for criterion_key, criterion in evaluator.rubric.items(): | |
| result += f"• {criterion.name}:\n" | |
| result += f" Nível: {evaluation['levels'][criterion_key]}\n" | |
| result += f" Pontuação: {evaluation['scores'][criterion_key]:.1f}/{criterion.weight}\n" | |
| if evaluation['feedback'][criterion_key]: | |
| result += f" Feedback: {evaluation['feedback'][criterion_key]}\n" | |
| result += "\n" | |
| results.append(result) | |
| return "\n".join(results) | |
| evaluate_button.click(fn=evaluate_code_files, inputs=upload, outputs=output) | |
| if __name__ == "__main__": | |
| demo.launch(debug=True) | |