/
mvidy
/
Programming_Tutor_AI
Обзор
Документация
Войти
/
mvidy
/
Programming_Tutor_AI
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
main.py
511 строк
22 KB
Mihail
v 0.1.1
20 окт 2025, 19:21
20 окт 2025, 19:21
1b42dae
Код
Авторство
О чём код?
# app.py from flask import Flask, render_template, request, redirect, url_for, session, jsonify import json import random from datetime import datetime from typing import Dict, List, Optional import os app = Flask(__name__) app.secret_key = 'supersecretkey' PROGRESS_FILE = 'tutor_progress.json' class ProgrammingTutorAI: """ Персональный AI-тренер по программированию Адаптивная система для изучения программирования с генерацией задач, проверкой решений и персонализированными рекомендациями. """ def __init__(self): self.user_levels = { "beginner": 1, "intermediate": 2, "advanced": 3 } self.problems_db = self._initialize_problems() self.user_progress = {} self.load_progress() def _initialize_problems(self) -> Dict: """Инициализация базы задач по уровням сложности""" return { "beginner": [ { "id": 1, "title": "Сумма двух чисел", "description": "Напишите функцию, которая принимает два числа и возвращает их сумму.", "function_name": "sum_two_numbers", "parameters": ["a", "b"], "test_cases": [ {"input": (1, 2), "output": 3}, {"input": (-1, 1), "output": 0}, {"input": (10, -5), "output": 5} ], "hint": "Просто сложите два числа и верните результат.", "difficulty": 1, "tags": ["functions", "arithmetic"] }, { "id": 2, "title": "Проверка четности", "description": "Напишите функцию, которая проверяет, является ли число четным.", "function_name": "is_even", "parameters": ["number"], "test_cases": [ {"input": (4,), "output": True}, {"input": (7,), "output": False}, {"input": (0,), "output": True} ], "hint": "Используйте оператор % для проверки остатка от деления.", "difficulty": 1, "tags": ["functions", "conditions"] } ], "intermediate": [ { "id": 3, "title": "Палиндром", "description": "Напишите функцию, которая проверяет, является ли строка палиндромом.", "function_name": "is_palindrome", "parameters": ["s"], "test_cases": [ {"input": ("радар",), "output": True}, {"input": ("привет",), "output": False}, {"input": ("А роза упала на лапу Азора",), "output": True} ], "hint": "Уберите пробелы и приведите к нижнему регистру перед проверкой.", "difficulty": 2, "tags": ["strings", "algorithms"] } ], "advanced": [ { "id": 4, "title": "Бинарный поиск", "description": "Реализуйте алгоритм бинарного поиска в отсортированном массиве.", "function_name": "binary_search", "parameters": ["arr", "target"], "test_cases": [ {"input": ([1, 2, 3, 4, 5], 3), "output": 2}, {"input": ([1, 2, 3, 4, 5], 6), "output": -1} ], "hint": "Используйте два указателя и постоянно уменьшайте область поиска.", "difficulty": 3, "tags": ["algorithms", "search"] } ] } def register_user(self, username: str, initial_level: str = "beginner"): """Регистрация нового пользователя""" if initial_level not in self.user_levels: raise ValueError(f"Доступные уровни: {list(self.user_levels.keys())}") self.user_progress[username] = { "level": initial_level, "score": 0, "problems_solved": 0, "current_problem": None, "attempts": {}, "registration_date": datetime.now().isoformat(), "recent_performance": [], "weak_areas": [], "strength_areas": [] } print(f"🎯 Пользователь {username} зарегистрирован с уровнем {initial_level}") self.save_progress() def assess_initial_level(self, answers: Dict[str, bool]) -> str: """ Оценка начального уровня на основе ответов на вопросы answers: словарь с ответами True/False на вопросы по темам """ correct_answers = sum(answers.values()) total_questions = len(answers) score_percentage = (correct_answers / total_questions) * 100 if score_percentage < 40: return "beginner" elif score_percentage < 70: return "intermediate" else: return "advanced" def generate_problem(self, username: str) -> Dict: """Генерация задачи подходящей сложности на основе прогресса""" if username not in self.user_progress: raise ValueError("Пользователь не зарегистрирован") user_data = self.user_progress[username] user_level = user_data["level"] available_problems = self.problems_db[user_level] # Исключаем недавно решенные задачи solved_problems = set(user_data["attempts"].keys()) unsolved_problems = [p for p in available_problems if str(p["id"]) not in solved_problems] if not unsolved_problems: # Если все задачи решены, берем из более сложного уровня next_level = self._get_next_level(user_level) if next_level and next_level in self.problems_db: unsolved_problems = self.problems_db[next_level] else: # Или повторяем случайную задачу unsolved_problems = available_problems # Выбираем задачу с учетом слабых мест пользователя problem = self._select_problem_based_on_weaknesses(unsolved_problems, user_data) user_data["current_problem"] = problem self.save_progress() return problem def _select_problem_based_on_weaknesses(self, problems: List[Dict], user_data: Dict) -> Dict: """Выбор задачи на основе слабых мест пользователя""" weak_areas = user_data.get("weak_areas", []) if weak_areas: # Пытаемся найти задачу, соответствующую слабым местам for problem in problems: if any(tag in weak_areas for tag in problem.get("tags", [])): return problem # Или возвращаем случайную задачу return random.choice(problems) def check_solution(self, username: str, code: str) -> Dict: """Проверка решения пользователя""" if username not in self.user_progress: return {"success": False, "error": "Пользователь не найден"} user_data = self.user_progress[username] current_problem = user_data.get("current_problem") if not current_problem: return {"success": False, "error": "Нет активной задачи"} result = { "success": False, "test_results": [], "errors": [], "hints": [], "improvements": [] } try: # Безопасное выполнение кода exec_globals = {} exec(code, exec_globals) function_name = current_problem["function_name"] if function_name not in exec_globals: result["errors"].append(f"Функция {function_name} не найдена в коде") return result user_function = exec_globals[function_name] # Запуск тестов all_passed = True for i, test_case in enumerate(current_problem["test_cases"]): try: output = user_function(*test_case["input"]) expected = test_case["output"] test_passed = output == expected all_passed = all_passed and test_passed result["test_results"].append({ "test_case": i + 1, "input": test_case["input"], "expected": expected, "actual": output, "passed": test_passed }) if not test_passed: result["hints"].append(self._generate_hint(current_problem, test_case, output)) except Exception as e: result["test_results"].append({ "test_case": i + 1, "input": test_case["input"], "error": str(e), "passed": False }) all_passed = False result["success"] = all_passed if all_passed: self._update_progress(username, True) result["message"] = "✅ Отлично! Задача решена правильно!" result["improvements"] = self._suggest_improvements(code, current_problem) else: self._update_progress(username, False) result["message"] = "❌ Есть ошибки. Посмотрите детали тестов." except Exception as e: result["success"] = False result["errors"].append(f"Ошибка выполнения: {str(e)}") result["hints"].append("Проверьте синтаксис и логику вашего кода") return result def _generate_hint(self, problem: Dict, test_case: Dict, actual_output) -> str: """Генерация подсказки на основе ошибки""" function_name = problem["function_name"] if function_name == "is_even": return "Проверьте правильность использования оператора %. Четное число делится на 2 без остатка." elif function_name == "is_palindrome": return "Не забудьте удалить пробелы и привести строку к одному регистру перед сравнением." elif function_name == "binary_search": return "Убедитесь, что массив отсортирован и вы правильно обновляете границы поиска." return f"Ожидалось: {test_case['output']}, получено: {actual_output}. Проверьте логику функции." def _suggest_improvements(self, code: str, problem: Dict) -> List[str]: """Предложение улучшений кода""" improvements = [] # Анализ кода try: tree = ast.parse(code) # Проверка на наличие комментариев if not any(isinstance(node, ast.Expr) for node in ast.walk(tree)): improvements.append("Добавьте комментарии для лучшей читаемости кода") # Проверка на слишком длинные функции for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): if len(node.body) > 10: improvements.append("Функция слишком длинная - consider разбить на более мелкие функции") except: pass # Общие рекомендации improvements.extend([ "Проверьте обработку граничных случаев", "Убедитесь в читаемости имен переменных", "Consider возможность оптимизации алгоритма" ]) return improvements def _update_progress(self, username: str, success: bool): """Обновление прогресса пользователя""" user_data = self.user_progress[username] problem_id = str(user_data["current_problem"]["id"]) # Обновление попыток if problem_id not in user_data["attempts"]: user_data["attempts"][problem_id] = [] user_data["attempts"][problem_id].append({ "timestamp": datetime.now().isoformat(), "success": success }) # Обновление счета и решенных задач if success: user_data["problems_solved"] += 1 user_data["score"] += user_data["current_problem"]["difficulty"] * 10 # Адаптация сложности self._adapt_difficulty(username) # Анализ слабых мест self._analyze_weak_areas(username) self.save_progress() def _adapt_difficulty(self, username: str): """Адаптация уровня сложности на основе прогресса""" user_data = self.user_progress[username] recent_attempts = user_data["recent_performance"] # Добавляем последний результат current_success = any(attempt["success"] for attempt in user_data["attempts"].get(str(user_data["current_problem"]["id"]), [])) recent_attempts.append(current_success) # Ограничиваем историю последними 10 попытками if len(recent_attempts) > 10: recent_attempts.pop(0) success_rate = sum(recent_attempts) / len(recent_attempts) if recent_attempts else 0 current_level = user_data["level"] if success_rate > 0.8 and current_level != "advanced": # Повышаем уровень new_level = self._get_next_level(current_level) if new_level: user_data["level"] = new_level print(f"🎉 Уровень повышен до {new_level}!") elif success_rate < 0.4 and current_level != "beginner": # Понижаем уровень new_level = self._get_previous_level(current_level) if new_level: user_data["level"] = new_level print(f"🔧 Уровень понижен до {new_level}. Нужно больше практики!") def _get_next_level(self, current_level: str) -> Optional[str]: """Получить следующий уровень""" levels = list(self.user_levels.keys()) current_index = levels.index(current_level) return levels[current_index + 1] if current_index + 1 < len(levels) else None def _get_previous_level(self, current_level: str) -> Optional[str]: """Получить предыдущий уровень""" levels = list(self.user_levels.keys()) current_index = levels.index(current_level) return levels[current_index - 1] if current_index - 1 >= 0 else None def _analyze_weak_areas(self, username: str): """Анализ слабых мест пользователя""" user_data = self.user_progress[username] failed_problems = [] for problem_id, attempts in user_data["attempts"].items(): success_rate = sum(1 for attempt in attempts if attempt["success"]) / len(attempts) if success_rate < 0.5: # Находим проблему по ID for level_problems in self.problems_db.values(): for problem in level_problems: if str(problem["id"]) == problem_id: failed_problems.append(problem) break # Анализ тегов проблемных задач weak_tags = [] for problem in failed_problems: weak_tags.extend(problem.get("tags", [])) # Находим наиболее частые слабые темы from collections import Counter tag_counts = Counter(weak_tags) user_data["weak_areas"] = [tag for tag, count in tag_counts.most_common(3)] def get_progress_report(self, username: str) -> Dict: """Получение отчета о прогрессе""" if username not in self.user_progress: return {"error": "Пользователь не найден"} user_data = self.user_progress[username] return { "username": username, "level": user_data["level"], "score": user_data["score"], "problems_solved": user_data["problems_solved"], "total_attempts": sum(len(attempts) for attempts in user_data["attempts"].values()), "success_rate": self._calculate_success_rate(user_data), "weak_areas": user_data.get("weak_areas", []), "registration_date": user_data["registration_date"] } def _calculate_success_rate(self, user_data: Dict) -> float: """Расчет общего процента успешных решений""" total_attempts = 0 successful_attempts = 0 for attempts in user_data["attempts"].values(): total_attempts += len(attempts) successful_attempts += sum(1 for attempt in attempts if attempt["success"]) return successful_attempts / total_attempts if total_attempts > 0 else 0 def save_progress(self): """Сохранение прогресса в файл""" try: with open(PROGRESS_FILE, "w", encoding="utf-8") as f: json.dump(self.user_progress, f, ensure_ascii=False, indent=2) except Exception as e: print(f"Ошибка сохранения прогресса: {e}") def load_progress(self): """Загрузка прогресса из файла""" try: if os.path.exists(PROGRESS_FILE): with open(PROGRESS_FILE, "r", encoding="utf-8") as f: self.user_progress = json.load(f) except Exception as e: print(f"Ошибка загрузки прогресса: {e}") tutor_ai = ProgrammingTutorAI() @app.route('/') def index(): return render_template('index.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] answers = { "Знакомы с основами Python?": request.form.get('q1') == 'on', "Умеете работать с функциями?": request.form.get('q2') == 'on', "Знаете что такое ООП?": request.form.get('q3') == 'on', "Работали с алгоритмами поиска?": request.form.get('q4') == 'on', "Знаете структуры данных?": request.form.get('q5') == 'on' } level = tutor_ai.assess_initial_level(answers) tutor_ai.register_user(username, level) session['username'] = username return redirect(url_for('problem')) return render_template('login.html') @app.route('/problem') def problem(): if 'username' not in session: return redirect(url_for('login')) problem = tutor_ai.generate_problem(session['username']) return render_template('problem.html', problem=problem) @app.route('/solution', methods=['GET', 'POST']) def solution(): if 'username' not in session: return redirect(url_for('login')) if request.method == 'POST': code = request.form['code'] result = tutor_ai.check_solution(session['username'], code) return render_template('solution.html', result=result) return render_template('solution.html') @app.route('/progress') def progress(): if 'username' not in session: return redirect(url_for('login')) report = tutor_ai.get_progress_report(session['username']) return render_template('progress.html', report=report) @app.route('/logout') def logout(): session.pop('username', None) return redirect(url_for('index')) if __name__ == '__main__': app.run(debug=True)