diff --git a/backend/app/api/routes/missions.py b/backend/app/api/routes/missions.py index 0f95102..57efe95 100644 --- a/backend/app/api/routes/missions.py +++ b/backend/app/api/routes/missions.py @@ -307,23 +307,9 @@ def list_missions( dto.is_completed = False dto.is_available = is_available dto.locked_reasons = reasons - - total_python = ( - db.query(PythonChallenge) - .filter(PythonChallenge.mission_id == mission.id) - .count() - ) - if total_python: - progress = ( - db.query(PythonUserProgress) - .filter( - PythonUserProgress.user_id == current_user.id, - PythonUserProgress.mission_id == mission.id, - ) - .first() - ) - dto.python_challenges_total = total_python - dto.python_completed_challenges = progress.current_order if progress else 0 + dto.has_coding_challenges = bool(mission.coding_challenges) + dto.coding_challenge_count = len(mission.coding_challenges) + dto.completed_coding_challenges = coding_progress.get(mission.id, 0) response.append(dto) return response @@ -395,28 +381,9 @@ def get_mission( updated_at=mission.updated_at, ) data.requires_documents = mission.id in REQUIRED_DOCUMENT_MISSIONS - - total_python = ( - db.query(PythonChallenge) - .filter(PythonChallenge.mission_id == mission.id) - .count() - ) - if total_python: - progress = ( - db.query(PythonUserProgress) - .filter( - PythonUserProgress.user_id == current_user.id, - PythonUserProgress.mission_id == mission.id, - ) - .first() - ) - data.python_challenges_total = total_python - data.python_completed_challenges = progress.current_order if progress else 0 - if progress and progress.current_order >= total_python: - data.is_completed = True - data.is_available = False - data.locked_reasons = ["Миссия уже завершена"] - + data.has_coding_challenges = bool(mission.coding_challenges) + data.coding_challenge_count = len(mission.coding_challenges) + data.completed_coding_challenges = coding_progress.get(mission.id, 0) if mission.id in completed_missions: data.is_completed = True data.is_available = False diff --git a/backend/app/schemas/mission.py b/backend/app/schemas/mission.py index 1ae62cc..01e7f02 100644 --- a/backend/app/schemas/mission.py +++ b/backend/app/schemas/mission.py @@ -24,10 +24,9 @@ class MissionBase(BaseModel): locked_reasons: list[str] = Field(default_factory=list) is_completed: bool = False requires_documents: bool = False - python_challenges_total: Optional[int] = None - python_completed_challenges: Optional[int] = None - requires_documents: bool = False - is_completed: bool = False + has_coding_challenges: bool = False + coding_challenge_count: int = 0 + completed_coding_challenges: int = 0 class Config: from_attributes = True diff --git a/scripts/seed_data.py b/scripts/seed_data.py index e4e0b62..c109c86 100644 --- a/scripts/seed_data.py +++ b/scripts/seed_data.py @@ -164,8 +164,8 @@ def seed() -> None: category="quest", ) python_branch = Branch( - title="Галактокод", - description="Практика языка Python по мотивам путеводителя.", + title="Основы Python", + description="Мини-курс из 10 задач для проверки синтаксиса и базовой логики.", category="training", ) session.add_all([branch, python_branch]) @@ -206,16 +206,21 @@ def seed() -> None: difficulty=MissionDifficulty.HARD, minimum_rank_id=ranks[1].id, ) - mission_python = Mission( - title="Основы Python", - description="Решите последовательность задач, чтобы доказать владение базовыми конструкциями языка.", + mission_python_basics = Mission( + title="Основные знания Python", + description="Решите 10 небольших задач и докажите, что уверенно чувствуете себя в языке программирования.", xp_reward=250, mana_reward=120, difficulty=MissionDifficulty.MEDIUM, minimum_rank_id=ranks[0].id, - artifact_id=artifact_by_name["Путеводитель по Галактике"].id, ) - session.add_all([mission_documents, mission_resume, mission_interview, mission_onboarding, mission_python]) + session.add_all([ + mission_documents, + mission_resume, + mission_interview, + mission_onboarding, + mission_python_basics, + ]) session.flush() session.add_all( @@ -241,7 +246,7 @@ def seed() -> None: level_delta=1, ), MissionCompetencyReward( - mission_id=mission_python.id, + mission_id=mission_python_basics.id, competency_id=competencies[2].id, level_delta=1, ), @@ -254,7 +259,94 @@ def seed() -> None: BranchMission(branch_id=branch.id, mission_id=mission_resume.id, order=2), BranchMission(branch_id=branch.id, mission_id=mission_interview.id, order=3), BranchMission(branch_id=branch.id, mission_id=mission_onboarding.id, order=4), - BranchMission(branch_id=python_branch.id, mission_id=mission_python.id, order=1), + BranchMission(branch_id=python_branch.id, mission_id=mission_python_basics.id, order=1), + ] + ) + + python_challenges_specs = [ + { + "order": 1, + "title": "Приветствие пилота", + "prompt": "Выведите в консоль точную фразу «Привет, Python!». Без дополнительных символов или пробелов.", + "starter_code": "# Напишите одну строку с функцией print\n", + "expected_output": "Привет, Python!", + }, + { + "order": 2, + "title": "Сложение топлива", + "prompt": "Создайте переменные a и b, найдите их сумму и выведите результат в формате «Сумма: 12».", + "starter_code": "a = 7\nb = 5\n# Напечатайте строку в формате: Сумма: 12\n", + "expected_output": "Сумма: 12", + }, + { + "order": 3, + "title": "Площадь отсека", + "prompt": "Перемножьте длину и ширину отсека и выведите строку «Площадь: 24».", + "starter_code": "length = 8\nwidth = 3\n# Вычислите площадь и выведите результат\n", + "expected_output": "Площадь: 24", + }, + { + "order": 4, + "title": "Обратный отсчёт", + "prompt": "С помощью цикла for выведите числа от 1 до 5, каждое на новой строке.", + "starter_code": "for number in range(1, 6):\n # Напечатайте текущее число\n pass\n", + "expected_output": "1\n2\n3\n4\n5", + }, + { + "order": 5, + "title": "Квадраты сигналов", + "prompt": "Создайте список квадратов чисел от 1 до 5 и выведите строку «Список квадратов: [1, 4, 9, 16, 25]».", + "starter_code": "levels = [1, 2, 3, 4, 5]\n# Соберите список квадратов и напечатайте его\n", + "expected_output": "Список квадратов: [1, 4, 9, 16, 25]", + }, + { + "order": 6, + "title": "Длина сообщения", + "prompt": "Определите длину строки message и выведите её как «Количество символов: 25».", + "starter_code": "message = \"Пангалактический экспресс\"\n# Посчитайте длину и выведите результат\n", + "expected_output": "Количество символов: 25", + }, + { + "order": 7, + "title": "Запасы склада", + "prompt": "Пройдитесь по словарю storage и выведите информацию в формате «мануал: 3» и «датчик: 5».", + "starter_code": "storage = {\"мануал\": 3, \"датчик\": 5}\n# Выведите данные из словаря построчно\n", + "expected_output": "мануал: 3\nдатчик: 5", + }, + { + "order": 8, + "title": "Проверка чётности", + "prompt": "Для чисел 2 и 7 напечатайте на отдельных строках True, если число чётное, иначе False.", + "starter_code": "numbers = [2, 7]\nfor number in numbers:\n # Напечатайте True или False в зависимости от чётности\n pass\n", + "expected_output": "True\nFalse", + }, + { + "order": 9, + "title": "Сумма диапазона", + "prompt": "Посчитайте сумму чисел от 1 до 10 и выведите строку «Сумма от 1 до 10: 55».", + "starter_code": "total = 0\nfor number in range(1, 11):\n # Добавляйте число к сумме\n pass\n# После цикла выведите итог\n", + "expected_output": "Сумма от 1 до 10: 55", + }, + { + "order": 10, + "title": "Факториал 5", + "prompt": "Вычислите факториал числа 5 и выведите строку «Факториал 5: 120».", + "starter_code": "result = 1\nfor number in range(1, 6):\n # Умножайте result на текущее число\n pass\n# Выведите итоговое значение\n", + "expected_output": "Факториал 5: 120", + }, + ] + + session.add_all( + [ + CodingChallenge( + mission_id=mission_python_basics.id, + order=spec["order"], + title=spec["title"], + prompt=spec["prompt"], + starter_code=spec["starter_code"], + expected_output=spec["expected_output"], + ) + for spec in python_challenges_specs ] )