/
serkatrinaa
/
final_project_base
Обзор
Документация
Войти
/
serkatrinaa
/
final_project_base
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/grader.py
347 строк
12 KB
Katya Mashnina
project done
28 дек 2025, 23:22
28 дек 2025, 23:22
cc88ff0
Код
Авторство
О чём код?
""" Essay Grader module. Contains the main EssayGrader class that orchestrates all analyzers and produces the final grade with feedback. """ from dataclasses import dataclass, field from typing import Dict, List, Optional from .analyzers import ( AnalysisResult, GrammarAnalyzer, PlagiarismAnalyzer, ReadabilityAnalyzer, SentimentAnalyzer, StructureAnalyzer, ) @dataclass class GradingCriteria: """ Configuration for grading criteria weights. All weights should sum to 1.0 for proper scoring. """ readability_weight: float = 0.20 grammar_weight: float = 0.25 plagiarism_weight: float = 0.20 sentiment_weight: float = 0.15 structure_weight: float = 0.20 def __post_init__(self): """Validate that weights sum to 1.0.""" total = ( self.readability_weight + self.grammar_weight + self.plagiarism_weight + self.sentiment_weight + self.structure_weight ) if abs(total - 1.0) > 0.01: raise ValueError( f"Weights must sum to 1.0, got {total:.2f}" ) @dataclass class GradingResult: """ Complete grading result with all analysis details. """ total_score: float max_score: float grade: str letter_grade: str summary: str criteria_results: Dict[str, AnalysisResult] = field(default_factory=dict) recommendations: List[str] = field(default_factory=list) def to_dict(self) -> dict: """Convert result to dictionary format.""" return { "total_score": self.total_score, "max_score": self.max_score, "grade": self.grade, "letter_grade": self.letter_grade, "summary": self.summary, "criteria_results": { name: { "score": result.score, "max_score": result.max_score, "feedback": result.feedback, "details": result.details } for name, result in self.criteria_results.items() }, "recommendations": self.recommendations } class EssayGrader: """ Main essay grading class. Orchestrates multiple analyzers to produce a comprehensive grade and feedback for essays. Usage: grader = EssayGrader() result = grader.grade(essay_text) print(result.summary) """ GRADE_SCALE = [ (90, "Отлично", "A"), (80, "Хорошо", "B"), (70, "Удовлетворительно", "C"), (60, "Достаточно", "D"), (0, "Неудовлетворительно", "F"), ] def __init__( self, criteria: Optional[GradingCriteria] = None, language: str = "ru", reference_texts: Optional[List[str]] = None ): """ Initialize the essay grader. Args: criteria: Custom grading criteria weights. language: Language for grammar checking. reference_texts: Reference texts for plagiarism checking. """ self.criteria = criteria or GradingCriteria() self.language = language # Initialize analyzers self._readability_analyzer = ReadabilityAnalyzer() self._grammar_analyzer = GrammarAnalyzer(language=language) self._plagiarism_analyzer = PlagiarismAnalyzer( reference_texts=reference_texts ) self._sentiment_analyzer = SentimentAnalyzer() self._structure_analyzer = StructureAnalyzer() def add_reference_text(self, text: str): """ Add a reference text for plagiarism checking. Args: text: Reference text to add. """ self._plagiarism_analyzer.add_reference(text) def grade(self, essay: str) -> GradingResult: """ Grade an essay and return comprehensive results. Args: essay: The essay text to grade. Returns: GradingResult with scores, grade, and feedback. """ if not essay or not essay.strip(): return GradingResult( total_score=0, max_score=100, grade="Неудовлетворительно", letter_grade="F", summary="Эссе пустое или содержит только пробелы.", recommendations=["Напишите содержательное эссе."] ) # Run all analyzers results = {} results["readability"] = self._readability_analyzer.analyze(essay) results["grammar"] = self._grammar_analyzer.analyze(essay) results["plagiarism"] = self._plagiarism_analyzer.analyze(essay) results["sentiment"] = self._sentiment_analyzer.analyze(essay) results["structure"] = self._structure_analyzer.analyze(essay) # Calculate weighted total score weighted_scores = { "readability": ( results["readability"].score / results["readability"].max_score * self.criteria.readability_weight * 100 ), "grammar": ( results["grammar"].score / results["grammar"].max_score * self.criteria.grammar_weight * 100 ), "plagiarism": ( results["plagiarism"].score / results["plagiarism"].max_score * self.criteria.plagiarism_weight * 100 ), "sentiment": ( results["sentiment"].score / results["sentiment"].max_score * self.criteria.sentiment_weight * 100 ), "structure": ( results["structure"].score / results["structure"].max_score * self.criteria.structure_weight * 100 ), } total_score = sum(weighted_scores.values()) # Determine grade grade, letter_grade = self._calculate_grade(total_score) # Generate recommendations recommendations = self._generate_recommendations(results) # Generate summary summary = self._generate_summary( total_score, grade, results, recommendations ) return GradingResult( total_score=round(total_score, 2), max_score=100, grade=grade, letter_grade=letter_grade, summary=summary, criteria_results=results, recommendations=recommendations ) def _calculate_grade(self, score: float) -> tuple: """Calculate grade based on score.""" for threshold, grade_name, letter in self.GRADE_SCALE: if score >= threshold: return grade_name, letter return "Неудовлетворительно", "F" def _generate_recommendations( self, results: Dict[str, AnalysisResult] ) -> List[str]: """Generate specific recommendations based on analysis.""" recommendations = [] # Check readability readability = results["readability"] if readability.score < readability.max_score * 0.7: if readability.details: flesch = readability.details.get("flesch_reading_ease", 50) if flesch > 70: recommendations.append( "Усложните лексику и структуру предложений " "для академического стиля." ) elif flesch < 30: recommendations.append( "Упростите предложения для лучшей читаемости." ) # Check grammar grammar = results["grammar"] if grammar.score < grammar.max_score * 0.8: error_count = ( grammar.details.get("total_errors", 0) if grammar.details else 0 ) if error_count > 5: recommendations.append( f"Исправьте грамматические ошибки ({error_count} найдено). " "Рекомендуется вычитка текста." ) # Check plagiarism plagiarism = results["plagiarism"] if plagiarism.score < plagiarism.max_score * 0.7: originality = ( plagiarism.details.get("originality_percent", 100) if plagiarism.details else 100 ) recommendations.append( f"Повысьте оригинальность текста (текущая: {originality:.1f}%). " "Перефразируйте заимствованные фрагменты." ) # Check sentiment sentiment = results["sentiment"] if sentiment.details: subjectivity = sentiment.details.get("subjectivity", 0) if subjectivity > 0.6: recommendations.append( "Сделайте текст более объективным. " "Избегайте личных оценочных суждений." ) # Check structure structure = results["structure"] if structure.score < structure.max_score * 0.7: if structure.details: para_count = structure.details.get("paragraph_count", 0) if para_count < 3: recommendations.append( "Добавьте больше абзацев. Эссе должно иметь " "чёткую структуру: вступление, основную часть, заключение." ) if not recommendations: recommendations.append( "Хорошая работа! Продолжайте в том же духе." ) return recommendations def _generate_summary( self, total_score: float, grade: str, results: Dict[str, AnalysisResult], recommendations: List[str] ) -> str: """Generate a summary of the grading.""" summary_parts = [ f"Итоговая оценка: {total_score:.1f}/100 ({grade})", "", "Результаты по критериям:" ] criteria_names = { "readability": "Читаемость", "grammar": "Грамматика", "plagiarism": "Оригинальность", "sentiment": "Тональность", "structure": "Структура" } for key, name in criteria_names.items(): result = results[key] summary_parts.append( f" • {name}: {result.score:.1f}/{result.max_score:.1f}" ) summary_parts.extend(["", "Обратная связь:"]) for result in results.values(): summary_parts.append(f" - {result.feedback}") if recommendations: summary_parts.extend(["", "Рекомендации:"]) for rec in recommendations: summary_parts.append(f" → {rec}") return "\n".join(summary_parts) def close(self): """Clean up resources.""" self._grammar_analyzer.close() def __enter__(self): """Context manager entry.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Context manager exit.""" self.close() return False