/
aastray
/
StudentPart
Обзор
Документация
Войти
/
aastray
/
StudentPart
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
service/src/routers/practice.py
361 строка
15 KB
aastray
Реализован функционал загрузки файлов и просмотра документов, удалены базовые эндпоинты, обновлена документация
10 янв 2026, 15:38
10 янв 2026, 15:38
4d47c55
Код
Авторство
О чём код?
""" Роутер для работы с практиками студентов. Содержит эндпоинты для работы с анкетами, документами и файлами. """ from fastapi import APIRouter, HTTPException, Body, Query, UploadFile, File, Form, Depends from typing import Dict, Any, Optional, List from sqlalchemy.ext.asyncio import AsyncSession from src.database.db import get_session from src.schemas.PracticeSchemas.application_schema import ( PracticeApplicationCreate, PracticeApplicationResponse, PracticeApplicationListResponse ) from src.services.practice_service import ( submit_practice_application, get_practice_application, get_all_practice_applications, get_practice_applications_by_student, get_document_template, upload_document_scans, upload_practice_report, get_practice_grade, get_application_files, get_student_files, get_all_students_files ) from src.schemas.PracticeSchemas.file_response_schema import ( ApplicationFilesResponse, StudentFilesResponse, AllStudentsFilesResponse ) router = APIRouter( prefix="/practice", tags=["Practice"], responses={404: {"description": "Not found"}}, ) @router.post("/application", summary="Отправить анкету на практику и сгенерировать документы", response_model=Dict[str, Any]) async def submit_application( application_data: PracticeApplicationCreate, db: AsyncSession = Depends(get_session) ): """ Принимает анкету студента, сохраняет её в БД, генерирует все необходимые документы (договор, задание, инструктаж, направление) и возвращает их в ответе. Возвращает: - Сохраненная анкета из БД - Сгенерированные документы (base64) - Статус обработки """ try: result = await submit_practice_application(application_data, db) return result except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при обработке анкеты: {str(e)}" ) @router.get("/application/{application_id}", summary="Получить анкету по ID", response_model=PracticeApplicationResponse) async def get_application( application_id: str, db: AsyncSession = Depends(get_session) ): """ Возвращает анкету на практику по ID. Args: application_id: ID анкеты Returns: Данные анкеты """ try: application = await get_practice_application(application_id, db) if not application: raise HTTPException(status_code=404, detail=f"Анкета с ID {application_id} не найдена") return application except HTTPException: raise except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении анкеты: {str(e)}" ) @router.get("/applications", summary="Получить список всех анкет", response_model=List[PracticeApplicationResponse]) async def get_all_applications( skip: int = Query(0, ge=0, description="Количество записей для пропуска"), limit: int = Query(100, ge=1, le=1000, description="Максимальное количество записей"), status: Optional[str] = Query(None, description="Фильтр по статусу (draft, submitted, approved, printed, completed)"), practice_type: Optional[str] = Query(None, description="Фильтр по виду практики (учебная, производственная, преддипломная)"), student_id: Optional[str] = Query(None, description="Фильтр по ID студента (опционально)"), db: AsyncSession = Depends(get_session) ): """ Возвращает список всех анкет с возможностью фильтрации. Args: skip: Количество записей для пропуска (для пагинации) limit: Максимальное количество записей status: Фильтр по статусу (опционально) practice_type: Фильтр по виду практики (опционально) student_id: Фильтр по ID студента (опционально). Если указан, возвращает только анкеты студента. Returns: Список анкет """ try: # Если указан student_id, используем старую функцию if student_id: applications = await get_practice_applications_by_student(student_id, db, skip, limit) else: # Иначе получаем все анкеты с фильтрами applications = await get_all_practice_applications(db, skip, limit, status, practice_type) return applications except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении списка анкет: {str(e)}" ) @router.get("/template", summary="Получить шаблон документа") async def get_template( template_type: str = Query(..., description="Тип шаблона: договор, индивидуальное_задание, лист_учета_инструктажа, направление"), practice_type: Optional[str] = Query(None, description="Вид практики (опционально)") ): """ Возвращает шаблон документа по типу и виду практики. Args: template_type: Тип шаблона документа practice_type: Вид практики (опционально) Returns: Шаблон документа в base64 и метаданные """ try: result = await get_document_template(template_type, practice_type) return result except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении шаблона: {str(e)}" ) @router.post("/upload-document", summary="Загрузить сканы документов с печатями (4 файла)") async def upload_documents( application_id: str = Form(..., description="ID анкеты, к которой привязываются документы"), contract: UploadFile = File(..., description="Скан договора на практику (PDF, DOCX)"), assignment: UploadFile = File(..., description="Скан индивидуального задания (PDF, DOCX)"), instruction: UploadFile = File(..., description="Скан листа учета инструктажа (PDF, DOCX)"), direction: UploadFile = File(..., description="Скан направления на практику (PDF, DOCX)"), db: AsyncSession = Depends(get_session) ): """ Принимает 4 скана документов с печатями: 1. Договор на практику 2. Индивидуальное задание 3. Лист учета инструктажа 4. Направление на практику Файлы сохраняются в локальное файловое хранилище, метаданные (пути к файлам) сохраняются в БД. Args: application_id: ID анкеты (передается через Form) contract: Скан договора assignment: Скан индивидуального задания instruction: Скан листа учета инструктажа direction: Скан направления db: Сессия базы данных Returns: Информация о загруженных файлах и ссылки для скачивания """ try: # Собираем файлы в список в правильном порядке files = [contract, assignment, instruction, direction] # Метаданные пока пустые, можно расширить в будущем metadata = {} # Вызываем сервис для загрузки всех файлов result = await upload_document_scans( files=files, application_id=application_id, metadata=metadata, db=db ) return result except ValueError as e: raise HTTPException( status_code=400, detail=str(e) ) except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при загрузке документов: {str(e)}" ) @router.post("/upload-report", summary="Загрузить отчет о практике") async def upload_report( application_id: str = Form(..., description="ID анкеты, к которой привязывается отчет"), file: UploadFile = File(..., description="Файл отчета (DOCX, PDF)"), db: AsyncSession = Depends(get_session) ): """ Принимает отчет о практике, сохраняет его в файловое хранилище и сохраняет метаданные в БД. Args: application_id: ID анкеты (передается через Form) file: Загружаемый файл отчета db: Сессия базы данных Returns: Информация о загруженном файле и ссылка для скачивания """ try: # Метаданные пока пустые, можно расширить в будущем metadata = {} result = await upload_practice_report( file=file, application_id=application_id, metadata=metadata, db=db ) return result except ValueError as e: raise HTTPException( status_code=400, detail=str(e) ) except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при загрузке отчета: {str(e)}" ) @router.get("/grade", summary="Получить оценку за практику") async def get_grade( practice_id: Optional[str] = Query(None, description="ID практики"), student_id: Optional[str] = Query(None, description="ID студента") ): """ Возвращает оценку за практику (получается из внешнего сервиса через оркестратор). Args: practice_id: ID практики student_id: ID студента Returns: Данные об оценке """ try: result = await get_practice_grade(practice_id, student_id) return result except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении оценки: {str(e)}" ) @router.get("/files/all", summary="Получить всех студентов с их файлами", response_model=AllStudentsFilesResponse) async def get_all_students_with_files( skip: int = Query(0, ge=0, description="Количество записей для пропуска"), limit: int = Query(100, ge=1, le=1000, description="Максимальное количество записей"), db: AsyncSession = Depends(get_session) ): """ Возвращает список всех студентов с их анкетами и прикрепленными файлами. Полезно для административного просмотра всех загруженных документов и отчетов. Args: skip: Количество записей для пропуска (для пагинации) limit: Максимальное количество записей Returns: Список всех студентов с их файлами (документы и отчеты) """ try: result = await get_all_students_files(db, skip, limit) return result except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении файлов студентов: {str(e)}" ) @router.get("/files/student/{student_id}", summary="Получить файлы студента", response_model=StudentFilesResponse) async def get_student_files_endpoint( student_id: str, db: AsyncSession = Depends(get_session) ): """ Возвращает все файлы конкретного студента по всем его анкетам. Показывает все загруженные документы (сканы) и отчеты студента. Args: student_id: ID студента Returns: Информация о файлах студента (документы и отчеты по всем анкетам) """ try: result = await get_student_files(student_id, db) return result except ValueError as e: raise HTTPException( status_code=404, detail=str(e) ) except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении файлов студента: {str(e)}" ) @router.get("/files/application/{application_id}", summary="Получить файлы анкеты", response_model=ApplicationFilesResponse) async def get_application_files_endpoint( application_id: str, db: AsyncSession = Depends(get_session) ): """ Возвращает все файлы конкретной анкеты (сканы документов и отчет). Позволяет просмотреть все прикрепленные файлы к конкретной заявке. Args: application_id: ID анкеты Returns: Информация о файлах анкеты (документы и отчеты) """ try: result = await get_application_files(application_id, db) return result except ValueError as e: raise HTTPException( status_code=404, detail=str(e) ) except Exception as e: raise HTTPException( status_code=500, detail=f"Ошибка при получении файлов анкеты: {str(e)}" )