Created
October 15, 2025 09:45
-
-
Save git-ethan-mars/1bf7ebaf9e8ea79fcf45bd612b6c3695 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import requests | |
| # === Конфигурация === | |
| MOODLE_URL = "https://elearn.urfu.ru" | |
| TOKEN = os.getenv('moodle_token') # ← ЗАМЕНИТЕ на ваш токен! | |
| DOWNLOAD_DIR = "downloads" # Папка для скачанных файлов | |
| os.makedirs(DOWNLOAD_DIR, exist_ok=True) | |
| def moodle_request(wsfunction: str, params: dict) -> dict: | |
| """Универсальный запрос к Moodle REST API.""" | |
| base_params = { | |
| 'wstoken': TOKEN, | |
| 'wsfunction': wsfunction, | |
| 'moodlewsrestformat': 'json' | |
| } | |
| base_params.update(params) | |
| try: | |
| resp = requests.post(f"{MOODLE_URL}/webservice/rest/server.php", data=base_params) | |
| except requests.RequestException as e: | |
| raise Exception(f"Ошибка сети: {e}") | |
| if resp.status_code != 200: | |
| raise Exception(f"HTTP {resp.status_code}: {resp.text[:500]}") | |
| try: | |
| data = resp.json() | |
| except requests.JSONDecodeError: | |
| raise Exception(f"Ответ не в формате JSON. Ответ: {resp.text[:500]}") | |
| if isinstance(data, dict) and 'exception' in data: | |
| raise Exception(f"Moodle error: {data.get('message', 'Unknown')}") | |
| return data | |
| def get_assign_id_by_cmid(cmid: int) -> int: | |
| """Получает assign id по cmid.""" | |
| data = moodle_request('core_course_get_course_module', {'cmid': cmid}) | |
| return data['cm']['instance'] | |
| def get_student_submission(assign_id: int, user_id: int): | |
| """Получает сабмит студента.""" | |
| data = moodle_request('mod_assign_get_submissions', {'assignmentids[0]': assign_id}) | |
| assignments = data.get('assignments', []) | |
| if not assignments: | |
| return None | |
| submissions = assignments[0].get('submissions', []) | |
| return next((s for s in submissions if s.get('userid') == user_id), None) | |
| def extract_student_answer(submission: dict) -> str: | |
| """Извлекает текстовый ответ студента.""" | |
| for plugin in submission.get('plugins', []): | |
| if plugin.get('type') == 'onlinetext': | |
| for field in plugin.get('editorfields', []): | |
| if field.get('name') == 'onlinetext': | |
| return field.get('text', '') | |
| return "" | |
| def extract_teacher_comment(submission: dict) -> str: | |
| """Извлекает комментарий преподавателя.""" | |
| for plugin in submission.get('plugins', []): | |
| if plugin.get('type') == 'comments': | |
| for field in plugin.get('editorfields', []): | |
| if field.get('name') == 'text': | |
| return field.get('text', '') | |
| return "" | |
| def download_student_files(submission: dict, user_id: int): | |
| """ | |
| Скачивает все файлы, прикреплённые студентом к заданию. | |
| Файлы сохраняются в папку downloads/. | |
| """ | |
| files = [] | |
| for plugin in submission.get('plugins', []): | |
| if plugin.get('type') == 'file': | |
| files.extend(plugin.get('fileareas', [{}])[0].get('files', [])) | |
| if not files: | |
| print("📎 Файлы не прикреплены.") | |
| return | |
| print(f"📎 Найдено файлов: {len(files)}") | |
| for f in files: | |
| filename = f['filename'] | |
| file_url = f['fileurl'] | |
| # Добавляем токен к URL для доступа | |
| if '?' in file_url: | |
| download_url = f"{file_url}&token={TOKEN}" | |
| else: | |
| download_url = f"{file_url}?token={TOKEN}" | |
| safe_filename = f"{user_id}_{filename}" | |
| filepath = os.path.join(DOWNLOAD_DIR, safe_filename) | |
| print(f" ↓ Скачивание: {safe_filename}") | |
| try: | |
| r = requests.get(download_url) | |
| r.raise_for_status() | |
| with open(filepath, 'wb') as out: | |
| out.write(r.content) | |
| print(f" ✅ Сохранён: {filepath}") | |
| except Exception as e: | |
| print(f" ❌ Ошибка при скачивании {filename}: {e}") | |
| def save_grade(assign_id: int, user_id: int, grade: float, feedback: str, format_type: int = 1): | |
| """Выставляет оценку и комментарий.""" | |
| params = { | |
| 'assignmentid': assign_id, | |
| 'applytoall': 0, | |
| 'grades[0][userid]': user_id, | |
| 'grades[0][grade]': grade, | |
| 'grades[0][attemptnumber]': -1, | |
| 'grades[0][addattempt]': 0, | |
| 'grades[0][workflowstate]': 'graded', | |
| 'grades[0][plugindata][assignfeedbackcomments_editor][text]': feedback, | |
| 'grades[0][plugindata][assignfeedbackcomments_editor][format]': format_type, | |
| } | |
| moodle_request('mod_assign_save_grades', params) | |
| print("✅ Оценка успешно выставлена!") | |
| def main(): | |
| CMID = 177171 # из .../view.php?id=177171 | |
| STUDENT_ID = 55838 # из ...&userid=13267 | |
| try: | |
| assign_id = get_assign_id_by_cmid(CMID) | |
| submission = get_student_submission(assign_id, STUDENT_ID) | |
| if not submission: | |
| print("⚠️ Сабмит студента не найден.") | |
| return | |
| # Текстовый ответ | |
| ans = extract_student_answer(submission) | |
| print("\n📝 Текстовый ответ студента:") | |
| print(ans or "[отсутствует]") | |
| # Комментарий преподавателя | |
| comm = extract_teacher_comment(submission) | |
| print("\n💬 Текущий комментарий преподавателя:") | |
| print(comm or "[отсутствует]") | |
| # Скачивание файлов | |
| print("\n📁 Работа с прикреплёнными файлами:") | |
| download_student_files(submission, STUDENT_ID) | |
| # Пример выставления оценки (раскомментируйте, если нужно) | |
| # save_grade(assign_id, STUDENT_ID, grade=88.0, feedback="Хорошо, но есть замечания.") | |
| except Exception as e: | |
| print(f"❌ Ошибка: {e}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment