/
urfu_itis_tasks
/
debug_101-Patsyuk
Обзор
Документация
Войти
/
urfu_itis_tasks
/
debug_101-Patsyuk
Код
Запросы
1
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
scripts/check_advanced.py
317 строк
12 KB
urfu_itis_tasks
Initial commit
27 окт 2025, 23:59
27 окт 2025, 23:59
c1603f3
Код
Авторство
О чём код?
#!/usr/bin/env python3 """ Расширенный чекер для средних и сложных заданий Добавлены проверки на замену на среднее, медиану, нормализацию и т.д. """ import sys import os import re import ast import subprocess import pandas as pd class AdvancedChecker: """Строгая проверка для средних и сложных уровней""" def __init__(self, filename, level): self.filename = filename self.level = level self.content = self._read_file() self.original_code = self._extract_section('ИСПРАВЬТЕ КОД НИЖЕ') self.task_id = self._extract_task_id() self.expected_errors = self._get_errors_from_csv() self.errors = [] self.warnings = [] def _read_file(self): try: with open(self.filename, 'r', encoding='utf-8') as f: return f.read() except FileNotFoundError: raise Exception(f"Файл {self.filename} не найден") def _extract_section(self, section_name): pattern = f'# {section_name}.*?# =+\s*\n(.*?)(?:# КОНЕЦ|$)' match = re.search(pattern, self.content, re.DOTALL) return match.group(1).strip() if match else None def _extract_task_id(self): match = re.search(r'Задание #(\d+)', self.content) return int(match.group(1)) if match else None def _get_errors_from_csv(self): possible_paths = [ os.path.join(os.path.dirname(__file__), '100_vibe_coding_tasks.csv'), os.path.join(os.path.dirname(__file__), '..', 'scripts', '100_vibe_coding_tasks.csv'), os.path.join(os.getcwd(), '100_vibe_coding_tasks.csv'), os.path.join(os.getcwd(), 'scripts', '100_vibe_coding_tasks.csv'), ] for path in possible_paths: if os.path.exists(path): try: df = pd.read_csv(path) if self.task_id and self.task_id in df['id'].values: task = df[df['id'] == self.task_id].iloc[0] errors_str = str(task.get('errors', '')) return [e.strip() for e in errors_str.split(';') if e.strip()] except: pass return [] def check_code_exists(self): if not self.original_code: self.errors.append("❌ Секция 'ИСПРАВЬТЕ КОД НИЖЕ' не найдена") return False if len(self.original_code.strip()) < 10: self.errors.append("❌ Код пустой") return False return True def check_syntax(self): if not self.original_code: return False try: ast.parse(self.original_code) self.warnings.append("✅ Синтаксис корректен") return True except SyntaxError as e: self.errors.append(f"❌ Синтаксис ошибка: {e.msg}") return False def check_medium_level(self): """Проверки для среднего уровня""" if self.level != 'medium': return True issues = [] code_lower = self.original_code.lower() # 1. Цепное индексирование if re.search(r'\[.*\]\[.*\]\s*=', self.original_code): issues.append("❌ Цепное индексирование - используйте .loc[]") # 2. Логические операторы wrong_logic = re.findall(r'\]\s+(and|or)\s+\w+\[', self.original_code) if wrong_logic: issues.append("❌ Неправильные логические операторы (используйте & и |)") # 3. inplace с присваиванием if re.search(r'\w+\s*=.*inplace=True', self.original_code): issues.append("❌ inplace=True возвращает None") # 4. Замена на среднее/медиану (НОВОЕ!) has_mean_replacement = 'mean()' in self.original_code or '.fillna(' in self.original_code has_median_replacement = 'median()' in self.original_code has_forward_fill = '.fillna(' in self.original_code and 'method=' in self.original_code has_interpolate = '.interpolate(' in self.original_code # Проверяем что если есть NaN, то есть замена has_missing_check = 'isna()' in code_lower or 'isnull()' in code_lower has_any_replacement = has_mean_replacement or has_median_replacement or has_forward_fill or has_interpolate if has_missing_check and not has_any_replacement: issues.append("⚠️ Найдены пропущенные значения, но нет замены (mean/median/interpolate)") if has_mean_replacement: self.warnings.append("✅ Использована замена на среднее") elif has_median_replacement: self.warnings.append("✅ Использована замена на медиану") elif has_interpolate: self.warnings.append("✅ Использована интерполяция") # 5. Нормализация (НОВОЕ!) has_scaling = 'StandardScaler' in self.original_code or 'MinMaxScaler' in self.original_code or '.norm' in code_lower has_normalization = 'normalize' in code_lower or 'scale' in code_lower if has_scaling or has_normalization: self.warnings.append("✅ Используется нормализация/масштабирование") # 6. Проверка на SettingWithCopy if re.search(r'df\[.*\]\s*=\s*[^;]*\[', self.original_code): if '.loc' not in self.original_code: issues.append("⚠️ Возможно SettingWithCopyWarning - используйте .loc[]") for issue in issues: if issue.startswith('❌'): self.errors.append(issue) else: self.warnings.append(issue) return len([i for i in issues if i.startswith('❌')]) == 0 def check_hard_level(self): """Проверки для сложного уровня""" if self.level != 'hard': return True critical_issues = [] code_lower = self.original_code.lower() # 1. Data leakage - shift if re.search(r'\.shift\s*\(\s*-', self.original_code): critical_issues.append("❌ CRITICAL: shift(-N) создает lead (утечка данных!)") # 2. fit на тестовых данных if re.search(r'test.*\.fit|fit\([^)]*test', self.original_code, re.IGNORECASE): critical_issues.append("❌ CRITICAL: fit() на тестовых данных!") # 3. transform до fit lines = self.original_code.split('\n') fit_lines = [] transform_lines = [] for i, line in enumerate(lines): if '.fit' in line or '.fit_transform' in line: fit_lines.append(i) if '.transform' in line and '.fit_transform' not in line: transform_lines.append(i) if transform_lines and fit_lines: if min(transform_lines) < min(fit_lines): critical_issues.append("❌ CRITICAL: transform() до fit()!") # 4. Cross-validation (НОВОЕ!) has_cv = 'cross_val' in code_lower or 'KFold' in self.original_code or 'StratifiedKFold' in self.original_code if has_cv: self.warnings.append("✅ Используется cross-validation") # 5. Правильное использование pipeline (НОВОЕ!) has_pipeline = 'Pipeline' in self.original_code if has_pipeline: self.warnings.append("✅ Используется Pipeline") # 6. Feature scaling перед fit (НОВОЕ!) has_scaler = 'StandardScaler' in self.original_code or 'MinMaxScaler' in self.original_code if has_scaler and not has_pipeline: # Проверяем порядок scaler_line = None fit_line = None for i, line in enumerate(lines): if 'Scaler' in line or 'fit_transform' in line: if not scaler_line: scaler_line = i if '.fit' in line and scaler_line: fit_line = i break if scaler_line and fit_line and scaler_line > fit_line: critical_issues.append("⚠️ Масштабирование после fit (порядок важен)") # 7. Метрики (НОВОЕ!) has_metrics = 'precision' in code_lower or 'recall' in code_lower or 'f1' in code_lower or 'accuracy' in code_lower if has_metrics and 'sklearn.metrics' not in self.original_code: critical_issues.append("⚠️ Используются метрики без явного импорта sklearn.metrics") for issue in critical_issues: if issue.startswith('❌'): self.errors.append(issue) else: self.warnings.append(issue) return len([i for i in critical_issues if i.startswith('❌')]) == 0 def check_execution(self): if not self.original_code: return False try: namespace = {} exec(self.original_code, namespace) self.warnings.append("✅ Код выполняется") return True except Exception as e: self.errors.append(f"❌ Ошибка выполнения: {type(e).__name__}") return False def generate_report(self): report = [] report.append("\n" + "="*80) report.append(f"ПРОВЕРКА: {self.filename} (Задание #{self.task_id})") report.append("="*80) report.append("\n1️⃣ КОД:") if self.check_code_exists(): report.append(" ✅ Найден") else: return "\n".join(report + self.errors) report.append("\n2️⃣ СИНТАКСИС:") if self.check_syntax(): report.append(" ✅ OK") report.append("\n3️⃣ ЛОГИКА:") if self.level == 'medium': report.append(" 📘 СРЕДНИЙ УРОВЕНЬ:") if self.check_medium_level(): report.append(" ✅ Логика корректна") elif self.level == 'hard': report.append(" 📕 СЛОЖНЫЙ УРОВЕНЬ:") if self.check_hard_level(): report.append(" ✅ Логика корректна") report.append("\n4️⃣ ВЫПОЛНЕНИЕ:") if self.check_execution(): report.append(" ✅ OK") if self.errors: report.append("\n❌ ПРОБЛЕМЫ:") for error in self.errors: report.append(f" {error}") if self.warnings: report.append("\n✅ РЕЗУЛЬТАТЫ:") for warning in self.warnings: report.append(f" {warning}") report.append("\n" + "="*80) return "\n".join(report) def check_all(self): self.check_code_exists() self.check_syntax() if self.level == 'medium': self.check_medium_level() elif self.level == 'hard': self.check_hard_level() self.check_execution() return len(self.errors) == 0 def main(): print("\n" + "="*80) print("РАСШИРЕННАЯ ПРОВЕРКА (со всеми типами ошибок)") print("="*80) checks = [ ('python_medium.py', 'medium'), ('python_hard.py', 'hard') ] all_passed = True for filename, level in checks: if os.path.exists(filename): try: checker = AdvancedChecker(filename, level) checker.check_all() print(checker.generate_report()) if checker.errors: all_passed = False except Exception as e: print(f"\n❌ Ошибка: {e}") all_passed = False sys.exit(0 if all_passed else 1) if __name__ == "__main__": main()