Brushed up the backend, added writing task 1 academic prompt gen and grading ENCOA-274
This commit is contained in:
@@ -38,4 +38,4 @@ EXPOSE 8000
|
|||||||
# For environments with multiple CPU cores, increase the number of workers
|
# For environments with multiple CPU cores, increase the number of workers
|
||||||
# to be equal to the cores available.
|
# to be equal to the cores available.
|
||||||
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
|
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
|
||||||
ENTRYPOINT ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "1", "--threads", "8", "--timeout", "0", "-k", "uvicorn.workers.UvicornWorker", "app.server:app"]
|
ENTRYPOINT ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "1", "--threads", "8", "--timeout", "0", "-k", "uvicorn.workers.UvicornWorker", "ielts_be:app"]
|
||||||
|
|||||||
2
app.py
2
app.py
@@ -13,7 +13,7 @@ load_dotenv()
|
|||||||
)
|
)
|
||||||
def main(env: str):
|
def main(env: str):
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
app="app.server:app",
|
app="ielts_be:app",
|
||||||
host="localhost",
|
host="localhost",
|
||||||
port=8000,
|
port=8000,
|
||||||
reload=True if env != "production" else False,
|
reload=True if env != "production" else False,
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
from .listening import listening_router
|
|
||||||
from .reading import reading_router
|
|
||||||
from .speaking import speaking_router
|
|
||||||
from .training import training_router
|
|
||||||
from .writing import writing_router
|
|
||||||
from .grade import grade_router
|
|
||||||
from .user import user_router
|
|
||||||
from .level import level_router
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api", tags=["Home"])
|
|
||||||
|
|
||||||
@router.get('/healthcheck')
|
|
||||||
async def healthcheck():
|
|
||||||
return {"healthy": True}
|
|
||||||
|
|
||||||
exercises_router = APIRouter()
|
|
||||||
exercises_router.include_router(listening_router, prefix="/listening", tags=["Listening"])
|
|
||||||
exercises_router.include_router(reading_router, prefix="/reading", tags=["Reading"])
|
|
||||||
exercises_router.include_router(speaking_router, prefix="/speaking", tags=["Speaking"])
|
|
||||||
exercises_router.include_router(writing_router, prefix="/writing", tags=["Writing"])
|
|
||||||
exercises_router.include_router(level_router, prefix="/level", tags=["Level"])
|
|
||||||
|
|
||||||
router.include_router(grade_router, prefix="/grade", tags=["Grade"])
|
|
||||||
router.include_router(training_router, prefix="/training", tags=["Training"])
|
|
||||||
router.include_router(user_router, prefix="/user", tags=["Users"])
|
|
||||||
router.include_router(exercises_router)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import random
|
|
||||||
|
|
||||||
from dependency_injector.wiring import inject, Provide
|
|
||||||
from fastapi import APIRouter, Path, Query, Depends
|
|
||||||
|
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
|
||||||
from app.configs.constants import EducationalContent
|
|
||||||
from app.controllers.abc import IWritingController
|
|
||||||
|
|
||||||
controller = "writing_controller"
|
|
||||||
writing_router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@writing_router.get(
|
|
||||||
'/{task}',
|
|
||||||
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
|
||||||
)
|
|
||||||
@inject
|
|
||||||
async def generate_writing(
|
|
||||||
task: int = Path(..., ge=1, le=2),
|
|
||||||
difficulty: str = Query(default=None),
|
|
||||||
topic: str = Query(default=None),
|
|
||||||
writing_controller: IWritingController = Depends(Provide[controller])
|
|
||||||
):
|
|
||||||
difficulty = random.choice(EducationalContent.DIFFICULTIES) if not difficulty else difficulty
|
|
||||||
topic = random.choice(EducationalContent.MTI_TOPICS) if not topic else topic
|
|
||||||
return await writing_controller.get_writing_task_general_question(task, topic, difficulty)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
from app.dtos.user_batch import BatchUsersDTO
|
|
||||||
|
|
||||||
|
|
||||||
class IUserController(ABC):
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def batch_import(self, batch: BatchUsersDTO):
|
|
||||||
pass
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class IWritingController(ABC):
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def get_writing_task_general_question(self, task: int, topic: str, difficulty: str):
|
|
||||||
pass
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
from app.controllers.abc import IWritingController
|
|
||||||
from app.services.abc import IWritingService
|
|
||||||
|
|
||||||
|
|
||||||
class WritingController(IWritingController):
|
|
||||||
|
|
||||||
def __init__(self, writing_service: IWritingService):
|
|
||||||
self._service = writing_service
|
|
||||||
|
|
||||||
async def get_writing_task_general_question(self, task: int, topic: str, difficulty: str):
|
|
||||||
return await self._service.get_writing_task_general_question(task, topic, difficulty)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from .document_stores import *
|
|
||||||
from app.repositories.impl.file_storage.firebase import FirebaseStorage
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"FirebaseStorage"
|
|
||||||
]
|
|
||||||
|
|
||||||
__all__.extend(document_stores.__all__)
|
|
||||||
@@ -1,156 +1,156 @@
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import logging.config
|
import logging.config
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
|
|
||||||
import aioboto3
|
import aioboto3
|
||||||
import contextlib
|
import contextlib
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import List
|
from typing import List
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.middleware import Middleware
|
from fastapi.middleware import Middleware
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
import nltk
|
import nltk
|
||||||
from starlette import status
|
from starlette import status
|
||||||
|
|
||||||
from app.api import router
|
from ielts_be.api import router
|
||||||
from app.configs import DependencyInjector
|
from ielts_be.configs import DependencyInjector
|
||||||
from app.exceptions import CustomException
|
from ielts_be.exceptions import CustomException
|
||||||
from app.middlewares import AuthenticationMiddleware, AuthBackend
|
from ielts_be.middlewares import AuthenticationMiddleware, AuthBackend
|
||||||
from app.services.impl import OpenAIWhisper
|
from ielts_be.services.impl import OpenAIWhisper
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(_app: FastAPI):
|
async def lifespan(_app: FastAPI):
|
||||||
"""
|
"""
|
||||||
Startup and Shutdown logic is in this lifespan method
|
Startup and Shutdown logic is in this lifespan method
|
||||||
|
|
||||||
https://fastapi.tiangolo.com/advanced/events/
|
https://fastapi.tiangolo.com/advanced/events/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# NLTK required datasets download
|
# NLTK required datasets download
|
||||||
nltk.download('words')
|
nltk.download('words')
|
||||||
nltk.download("punkt")
|
nltk.download("punkt")
|
||||||
|
|
||||||
# AWS Polly client instantiation
|
# AWS Polly client instantiation
|
||||||
context_stack = contextlib.AsyncExitStack()
|
context_stack = contextlib.AsyncExitStack()
|
||||||
session = aioboto3.Session()
|
session = aioboto3.Session()
|
||||||
polly_client = await context_stack.enter_async_context(
|
polly_client = await context_stack.enter_async_context(
|
||||||
session.client(
|
session.client(
|
||||||
'polly',
|
'polly',
|
||||||
region_name='eu-west-1',
|
region_name='eu-west-1',
|
||||||
aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
|
||||||
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID")
|
aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
http_client = httpx.AsyncClient()
|
http_client = httpx.AsyncClient()
|
||||||
stt = OpenAIWhisper()
|
stt = OpenAIWhisper()
|
||||||
|
|
||||||
DependencyInjector(
|
DependencyInjector(
|
||||||
polly_client,
|
polly_client,
|
||||||
http_client,
|
http_client,
|
||||||
stt
|
stt
|
||||||
).inject()
|
).inject()
|
||||||
|
|
||||||
# Setup logging
|
# Setup logging
|
||||||
config_file = pathlib.Path("./app/configs/logging/logging_config.json")
|
config_file = pathlib.Path("./ielts_be/configs/logging/logging_config.json")
|
||||||
with open(config_file) as f_in:
|
with open(config_file) as f_in:
|
||||||
config = json.load(f_in)
|
config = json.load(f_in)
|
||||||
|
|
||||||
logging.config.dictConfig(config)
|
logging.config.dictConfig(config)
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
stt.close()
|
stt.close()
|
||||||
await http_client.aclose()
|
await http_client.aclose()
|
||||||
await polly_client.close()
|
await polly_client.close()
|
||||||
await context_stack.aclose()
|
await context_stack.aclose()
|
||||||
|
|
||||||
|
|
||||||
def setup_listeners(_app: FastAPI) -> None:
|
def setup_listeners(_app: FastAPI) -> None:
|
||||||
@_app.exception_handler(RequestValidationError)
|
@_app.exception_handler(RequestValidationError)
|
||||||
async def custom_form_validation_error(request, exc):
|
async def custom_form_validation_error(request, exc):
|
||||||
"""
|
"""
|
||||||
Don't delete request param
|
Don't delete request param
|
||||||
"""
|
"""
|
||||||
reformatted_message = defaultdict(list)
|
reformatted_message = defaultdict(list)
|
||||||
for pydantic_error in exc.errors():
|
for pydantic_error in exc.errors():
|
||||||
loc, msg = pydantic_error["loc"], pydantic_error["msg"]
|
loc, msg = pydantic_error["loc"], pydantic_error["msg"]
|
||||||
filtered_loc = loc[1:] if loc[0] in ("body", "query", "path") else loc
|
filtered_loc = loc[1:] if loc[0] in ("body", "query", "path") else loc
|
||||||
field_string = ".".join(filtered_loc)
|
field_string = ".".join(filtered_loc)
|
||||||
if field_string == "cookie.refresh_token":
|
if field_string == "cookie.refresh_token":
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=401,
|
status_code=401,
|
||||||
content={"error_code": 401, "message": HTTPStatus.UNAUTHORIZED.description},
|
content={"error_code": 401, "message": HTTPStatus.UNAUTHORIZED.description},
|
||||||
)
|
)
|
||||||
reformatted_message[field_string].append(msg)
|
reformatted_message[field_string].append(msg)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
content=jsonable_encoder(
|
content=jsonable_encoder(
|
||||||
{"details": "Invalid request!", "errors": reformatted_message}
|
{"details": "Invalid request!", "errors": reformatted_message}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@_app.exception_handler(CustomException)
|
@_app.exception_handler(CustomException)
|
||||||
async def custom_exception_handler(request: Request, exc: CustomException):
|
async def custom_exception_handler(request: Request, exc: CustomException):
|
||||||
"""
|
"""
|
||||||
Don't delete request param
|
Don't delete request param
|
||||||
"""
|
"""
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=exc.code,
|
status_code=exc.code,
|
||||||
content={"error_code": exc.error_code, "message": exc.message},
|
content={"error_code": exc.error_code, "message": exc.message},
|
||||||
)
|
)
|
||||||
|
|
||||||
@_app.exception_handler(Exception)
|
@_app.exception_handler(Exception)
|
||||||
async def default_exception_handler(request: Request, exc: Exception):
|
async def default_exception_handler(request: Request, exc: Exception):
|
||||||
"""
|
"""
|
||||||
Don't delete request param
|
Don't delete request param
|
||||||
"""
|
"""
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=500,
|
status_code=500,
|
||||||
content=str(exc),
|
content=str(exc),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_middleware() -> List[Middleware]:
|
def setup_middleware() -> List[Middleware]:
|
||||||
middleware = [
|
middleware = [
|
||||||
Middleware(
|
Middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
),
|
),
|
||||||
Middleware(
|
Middleware(
|
||||||
AuthenticationMiddleware,
|
AuthenticationMiddleware,
|
||||||
backend=AuthBackend()
|
backend=AuthBackend()
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
return middleware
|
return middleware
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
def create_app() -> FastAPI:
|
||||||
env = os.getenv("ENV")
|
env = os.getenv("ENV")
|
||||||
_app = FastAPI(
|
_app = FastAPI(
|
||||||
docs_url="/docs" if env != "production" else None,
|
docs_url="/docs" if env != "production" else None,
|
||||||
redoc_url="/redoc" if env != "production" else None,
|
redoc_url="/redoc" if env != "production" else None,
|
||||||
middleware=setup_middleware(),
|
middleware=setup_middleware(),
|
||||||
lifespan=lifespan
|
lifespan=lifespan
|
||||||
)
|
)
|
||||||
_app.include_router(router)
|
_app.include_router(router)
|
||||||
setup_listeners(_app)
|
setup_listeners(_app)
|
||||||
return _app
|
return _app
|
||||||
|
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
15
ielts_be/api/__init__.py
Normal file
15
ielts_be/api/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from .training import training_router
|
||||||
|
from .user import user_router
|
||||||
|
from .exam import exam_router
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api", tags=["Home"])
|
||||||
|
|
||||||
|
@router.get('/healthcheck')
|
||||||
|
async def healthcheck():
|
||||||
|
return {"healthy": True}
|
||||||
|
|
||||||
|
router.include_router(training_router, prefix="/training", tags=["Training"])
|
||||||
|
router.include_router(user_router, prefix="/user", tags=["Users"])
|
||||||
|
router.include_router(exam_router)
|
||||||
16
ielts_be/api/exam/__init__.py
Normal file
16
ielts_be/api/exam/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from .listening import listening_router
|
||||||
|
from .reading import reading_router
|
||||||
|
from .speaking import speaking_router
|
||||||
|
from .writing import writing_router
|
||||||
|
from .level import level_router
|
||||||
|
from .grade import grade_router
|
||||||
|
|
||||||
|
exam_router = APIRouter()
|
||||||
|
exam_router.include_router(listening_router, prefix="/listening", tags=["Listening"])
|
||||||
|
exam_router.include_router(reading_router, prefix="/reading", tags=["Reading"])
|
||||||
|
exam_router.include_router(speaking_router, prefix="/speaking", tags=["Speaking"])
|
||||||
|
exam_router.include_router(writing_router, prefix="/writing", tags=["Writing"])
|
||||||
|
exam_router.include_router(level_router, prefix="/level", tags=["Level"])
|
||||||
|
exam_router.include_router(grade_router, prefix="/grade", tags=["Grade"])
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from dependency_injector.wiring import inject, Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
from fastapi import APIRouter, Depends, Path, Request, BackgroundTasks
|
from fastapi import APIRouter, Depends, Path, Request, BackgroundTasks
|
||||||
|
|
||||||
from app.controllers.abc import IGradeController
|
from ielts_be.controllers import IGradeController
|
||||||
from app.dtos.writing import WritingGradeTaskDTO
|
from ielts_be.dtos.writing import WritingGradeTaskDTO
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
|
|
||||||
controller = "grade_controller"
|
controller = "grade_controller"
|
||||||
grade_router = APIRouter()
|
grade_router = APIRouter()
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from fastapi import APIRouter, Depends, UploadFile, Request
|
from fastapi import APIRouter, Depends, UploadFile, Request
|
||||||
|
|
||||||
from app.dtos.level import LevelExercisesDTO
|
from ielts_be.dtos.level import LevelExercisesDTO
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.controllers.abc import ILevelController
|
from ielts_be.controllers import ILevelController
|
||||||
|
|
||||||
controller = "level_controller"
|
controller = "level_controller"
|
||||||
level_router = APIRouter()
|
level_router = APIRouter()
|
||||||
@@ -3,10 +3,10 @@ import random
|
|||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from fastapi import APIRouter, Depends, Path, Query, UploadFile
|
from fastapi import APIRouter, Depends, Path, Query, UploadFile
|
||||||
|
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.controllers.abc import IListeningController
|
from ielts_be.controllers import IListeningController
|
||||||
from app.configs.constants import EducationalContent, ListeningExerciseType
|
from ielts_be.configs.constants import EducationalContent
|
||||||
from app.dtos.listening import SaveListeningDTO, GenerateListeningExercises, Dialog
|
from ielts_be.dtos.listening import GenerateListeningExercises, Dialog
|
||||||
|
|
||||||
controller = "listening_controller"
|
controller = "listening_controller"
|
||||||
listening_router = APIRouter()
|
listening_router = APIRouter()
|
||||||
@@ -4,10 +4,10 @@ from typing import Optional
|
|||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from fastapi import APIRouter, Depends, Path, Query, UploadFile
|
from fastapi import APIRouter, Depends, Path, Query, UploadFile
|
||||||
|
|
||||||
from app.configs.constants import EducationalContent
|
from ielts_be.configs.constants import EducationalContent
|
||||||
from app.dtos.reading import ReadingDTO
|
from ielts_be.dtos.reading import ReadingDTO
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.controllers.abc import IReadingController
|
from ielts_be.controllers import IReadingController
|
||||||
|
|
||||||
controller = "reading_controller"
|
controller = "reading_controller"
|
||||||
reading_router = APIRouter()
|
reading_router = APIRouter()
|
||||||
@@ -4,10 +4,10 @@ from typing import Optional
|
|||||||
from dependency_injector.wiring import inject, Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
from fastapi import APIRouter, Path, Query, Depends
|
from fastapi import APIRouter, Path, Query, Depends
|
||||||
|
|
||||||
from app.dtos.speaking import Video
|
from ielts_be.dtos.speaking import Video
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.configs.constants import EducationalContent
|
from ielts_be.configs.constants import EducationalContent
|
||||||
from app.controllers.abc import ISpeakingController
|
from ielts_be.controllers import ISpeakingController
|
||||||
|
|
||||||
controller = "speaking_controller"
|
controller = "speaking_controller"
|
||||||
speaking_router = APIRouter()
|
speaking_router = APIRouter()
|
||||||
42
ielts_be/api/exam/writing.py
Normal file
42
ielts_be/api/exam/writing.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from dependency_injector.wiring import inject, Provide
|
||||||
|
from fastapi import APIRouter, Path, Query, Depends, UploadFile, File
|
||||||
|
|
||||||
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
|
from ielts_be.configs.constants import EducationalContent
|
||||||
|
from ielts_be.controllers import IWritingController
|
||||||
|
|
||||||
|
controller = "writing_controller"
|
||||||
|
writing_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@writing_router.post(
|
||||||
|
'/{task}/attachment',
|
||||||
|
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
||||||
|
)
|
||||||
|
@inject
|
||||||
|
async def generate_writing_academic(
|
||||||
|
task: int = Path(..., ge=1, le=2),
|
||||||
|
file: UploadFile = File(...),
|
||||||
|
difficulty: str = Query(default=None),
|
||||||
|
writing_controller: IWritingController = Depends(Provide[controller])
|
||||||
|
):
|
||||||
|
difficulty = random.choice(EducationalContent.DIFFICULTIES) if not difficulty else difficulty
|
||||||
|
return await writing_controller.get_writing_task_academic_question(task, file, difficulty)
|
||||||
|
|
||||||
|
|
||||||
|
@writing_router.get(
|
||||||
|
'/{task}',
|
||||||
|
dependencies=[Depends(Authorized([IsAuthenticatedViaBearerToken]))]
|
||||||
|
)
|
||||||
|
@inject
|
||||||
|
async def generate_writing(
|
||||||
|
task: int = Path(..., ge=1, le=2),
|
||||||
|
difficulty: str = Query(default=None),
|
||||||
|
topic: str = Query(default=None),
|
||||||
|
writing_controller: IWritingController = Depends(Provide[controller])
|
||||||
|
):
|
||||||
|
difficulty = random.choice(EducationalContent.DIFFICULTIES) if not difficulty else difficulty
|
||||||
|
topic = random.choice(EducationalContent.MTI_TOPICS) if not topic else topic
|
||||||
|
return await writing_controller.get_writing_task_general_question(task, topic, difficulty)
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from fastapi import APIRouter, Depends, Request
|
from fastapi import APIRouter, Depends, Request
|
||||||
|
|
||||||
from app.dtos.training import FetchTipsDTO
|
from ielts_be.dtos.training import FetchTipsDTO
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.controllers.abc import ITrainingController
|
from ielts_be.controllers import ITrainingController
|
||||||
|
|
||||||
controller = "training_controller"
|
controller = "training_controller"
|
||||||
training_router = APIRouter()
|
training_router = APIRouter()
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
from dependency_injector.wiring import Provide, inject
|
from dependency_injector.wiring import Provide, inject
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from app.dtos.user_batch import BatchUsersDTO
|
from ielts_be.dtos.user_batch import BatchUsersDTO
|
||||||
from app.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
from ielts_be.middlewares import Authorized, IsAuthenticatedViaBearerToken
|
||||||
from app.controllers.abc import IUserController
|
from ielts_be.controllers import IUserController
|
||||||
|
|
||||||
controller = "user_controller"
|
controller = "user_controller"
|
||||||
user_router = APIRouter()
|
user_router = APIRouter()
|
||||||
@@ -9,11 +9,9 @@ from httpx import AsyncClient as HTTPClient
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from sentence_transformers import SentenceTransformer
|
from sentence_transformers import SentenceTransformer
|
||||||
|
|
||||||
from app.repositories.impl import *
|
from ielts_be.repositories.impl import *
|
||||||
from app.repositories.impl.document_stores.mongo import MongoDB
|
from ielts_be.services.impl import *
|
||||||
from app.services.impl import *
|
from ielts_be.controllers.impl import *
|
||||||
from app.controllers.impl import *
|
|
||||||
from app.services.impl.exam.evaluation import EvaluationService
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -33,7 +31,7 @@ class DependencyInjector:
|
|||||||
self._setup_services()
|
self._setup_services()
|
||||||
self._setup_controllers()
|
self._setup_controllers()
|
||||||
self._container.wire(
|
self._container.wire(
|
||||||
packages=["app"]
|
packages=["ielts_be"]
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -48,14 +46,14 @@ class DependencyInjector:
|
|||||||
self._container.tts = providers.Factory(AWSPolly, client=self._container.polly_client)
|
self._container.tts = providers.Factory(AWSPolly, client=self._container.polly_client)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with open('app/services/impl/third_parties/elai/conf.json', 'r') as file:
|
with open('ielts_be/services/impl/third_parties/elai/conf.json', 'r') as file:
|
||||||
elai_conf = json.load(file)
|
elai_conf = json.load(file)
|
||||||
|
|
||||||
with open('app/services/impl/third_parties/elai/avatars.json', 'r') as file:
|
with open('ielts_be/services/impl/third_parties/elai/avatars.json', 'r') as file:
|
||||||
elai_avatars = json.load(file)
|
elai_avatars = json.load(file)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open('app/services/impl/third_parties/heygen/avatars.json', 'r') as file:
|
with open('ielts_be/services/impl/third_parties/heygen/avatars.json', 'r') as file:
|
||||||
heygen_avatars = json.load(file)
|
heygen_avatars = json.load(file)
|
||||||
|
|
||||||
self._container.vid_gen = providers.Factory(
|
self._container.vid_gen = providers.Factory(
|
||||||
@@ -99,7 +97,7 @@ class DependencyInjector:
|
|||||||
WritingService, llm=self._container.llm, ai_detector=self._container.ai_detector
|
WritingService, llm=self._container.llm, ai_detector=self._container.ai_detector
|
||||||
)
|
)
|
||||||
|
|
||||||
with open('app/services/impl/exam/level/mc_variants.json', 'r') as file:
|
with open('ielts_be/services/impl/exam/level/mc_variants.json', 'r') as file:
|
||||||
mc_variants = json.load(file)
|
mc_variants = json.load(file)
|
||||||
|
|
||||||
self._container.level_service = providers.Factory(
|
self._container.level_service = providers.Factory(
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"error_and_above": {
|
"error_and_above": {
|
||||||
"()": "app.configs.logging.ErrorAndAboveFilter"
|
"()": "ielts_be.configs.logging.ErrorAndAboveFilter"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"stream": "ext://sys.stderr"
|
"stream": "ext://sys.stderr"
|
||||||
},
|
},
|
||||||
"queue_handler": {
|
"queue_handler": {
|
||||||
"class": "app.configs.logging.QueueListenerHandler",
|
"class": "ielts_be.configs.logging.QueueListenerHandler",
|
||||||
"handlers": [
|
"handlers": [
|
||||||
"cfg://handlers.console",
|
"cfg://handlers.console",
|
||||||
"cfg://handlers.error"
|
"cfg://handlers.error"
|
||||||
@@ -4,7 +4,7 @@ from queue import Queue
|
|||||||
import atexit
|
import atexit
|
||||||
|
|
||||||
|
|
||||||
class QueueHnadlerHelper:
|
class QueueHandlerHelper:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_handlers(l):
|
def resolve_handlers(l):
|
||||||
@@ -40,9 +40,9 @@ class QueueHnadlerHelper:
|
|||||||
class QueueListenerHandler(QueueHandler):
|
class QueueListenerHandler(QueueHandler):
|
||||||
|
|
||||||
def __init__(self, handlers, respect_handler_level=False, auto_run=True, queue=Queue(-1)):
|
def __init__(self, handlers, respect_handler_level=False, auto_run=True, queue=Queue(-1)):
|
||||||
queue = QueueHnadlerHelper.resolve_queue(queue)
|
queue = QueueHandlerHelper.resolve_queue(queue)
|
||||||
super().__init__(queue)
|
super().__init__(queue)
|
||||||
handlers = QueueHnadlerHelper.resolve_handlers(handlers)
|
handlers = QueueHandlerHelper.resolve_handlers(handlers)
|
||||||
self._listener = QueueListener(
|
self._listener = QueueListener(
|
||||||
self.queue,
|
self.queue,
|
||||||
*handlers,
|
*handlers,
|
||||||
3
ielts_be/controllers/__init__.py
Normal file
3
ielts_be/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .abc import *
|
||||||
|
|
||||||
|
__all__ = abc.__all__
|
||||||
11
ielts_be/controllers/abc/__init__.py
Normal file
11
ielts_be/controllers/abc/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from .grade import IGradeController
|
||||||
|
from .training import ITrainingController
|
||||||
|
from .user import IUserController
|
||||||
|
from .exam import *
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"IGradeController",
|
||||||
|
"ITrainingController",
|
||||||
|
"IUserController",
|
||||||
|
]
|
||||||
|
__all__.extend(exam.__all__)
|
||||||
@@ -1,19 +1,13 @@
|
|||||||
from .level import ILevelController
|
from .level import ILevelController
|
||||||
from .listening import IListeningController
|
from .listening import IListeningController
|
||||||
from .reading import IReadingController
|
from .reading import IReadingController
|
||||||
from .writing import IWritingController
|
from .writing import IWritingController
|
||||||
from .speaking import ISpeakingController
|
from .speaking import ISpeakingController
|
||||||
from .grade import IGradeController
|
|
||||||
from .training import ITrainingController
|
__all__ = [
|
||||||
from .user import IUserController
|
"IListeningController",
|
||||||
|
"IReadingController",
|
||||||
__all__ = [
|
"IWritingController",
|
||||||
"IListeningController",
|
"ISpeakingController",
|
||||||
"IReadingController",
|
"ILevelController",
|
||||||
"IWritingController",
|
]
|
||||||
"ISpeakingController",
|
|
||||||
"ILevelController",
|
|
||||||
"IGradeController",
|
|
||||||
"ITrainingController",
|
|
||||||
"IUserController",
|
|
||||||
]
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from fastapi import BackgroundTasks
|
|
||||||
|
|
||||||
|
|
||||||
class ISpeakingController(ABC):
|
class ISpeakingController(ABC):
|
||||||
14
ielts_be/controllers/abc/exam/writing.py
Normal file
14
ielts_be/controllers/abc/exam/writing.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from fastapi.datastructures import UploadFile
|
||||||
|
|
||||||
|
|
||||||
|
class IWritingController(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def get_writing_task_general_question(self, task: int, topic: str, difficulty: str):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def get_writing_task_academic_question(self, task: int, attachment: UploadFile, difficulty: str):
|
||||||
|
pass
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Dict, List, Union
|
from typing import Dict
|
||||||
from fastapi import BackgroundTasks
|
from fastapi import BackgroundTasks
|
||||||
from fastapi.datastructures import FormData
|
from fastapi.datastructures import FormData
|
||||||
|
|
||||||
8
ielts_be/controllers/abc/user.py
Normal file
8
ielts_be/controllers/abc/user.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class IUserController(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def batch_import(self, batch):
|
||||||
|
pass
|
||||||
12
ielts_be/controllers/impl/__init__.py
Normal file
12
ielts_be/controllers/impl/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from .training import TrainingController
|
||||||
|
from .grade import GradeController
|
||||||
|
from .user import UserController
|
||||||
|
from .exam import *
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"TrainingController",
|
||||||
|
"GradeController",
|
||||||
|
"UserController"
|
||||||
|
]
|
||||||
|
|
||||||
|
__all__.extend(exam.__all__)
|
||||||
@@ -1,19 +1,13 @@
|
|||||||
from .level import LevelController
|
from .level import LevelController
|
||||||
from .listening import ListeningController
|
from .listening import ListeningController
|
||||||
from .reading import ReadingController
|
from .reading import ReadingController
|
||||||
from .speaking import SpeakingController
|
from .speaking import SpeakingController
|
||||||
from .writing import WritingController
|
from .writing import WritingController
|
||||||
from .training import TrainingController
|
|
||||||
from .grade import GradeController
|
__all__ = [
|
||||||
from .user import UserController
|
"LevelController",
|
||||||
|
"ListeningController",
|
||||||
__all__ = [
|
"ReadingController",
|
||||||
"LevelController",
|
"SpeakingController",
|
||||||
"ListeningController",
|
"WritingController",
|
||||||
"ReadingController",
|
]
|
||||||
"SpeakingController",
|
|
||||||
"WritingController",
|
|
||||||
"TrainingController",
|
|
||||||
"GradeController",
|
|
||||||
"UserController"
|
|
||||||
]
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from watchfiles import awatch
|
from ielts_be.controllers import ILevelController
|
||||||
|
from ielts_be.services import ILevelService
|
||||||
from app.controllers.abc import ILevelController
|
|
||||||
from app.services.abc import ILevelService
|
|
||||||
|
|
||||||
|
|
||||||
class LevelController(ILevelController):
|
class LevelController(ILevelController):
|
||||||
@@ -3,9 +3,9 @@ import io
|
|||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
from starlette.responses import StreamingResponse, Response
|
from starlette.responses import StreamingResponse, Response
|
||||||
|
|
||||||
from app.controllers.abc import IListeningController
|
from ielts_be.controllers import IListeningController
|
||||||
from app.dtos.listening import GenerateListeningExercises, Dialog
|
from ielts_be.services import IListeningService
|
||||||
from app.services.abc import IListeningService
|
from ielts_be.dtos.listening import GenerateListeningExercises, Dialog
|
||||||
|
|
||||||
|
|
||||||
class ListeningController(IListeningController):
|
class ListeningController(IListeningController):
|
||||||
@@ -3,9 +3,9 @@ from typing import Optional
|
|||||||
|
|
||||||
from fastapi import UploadFile, Response
|
from fastapi import UploadFile, Response
|
||||||
|
|
||||||
from app.controllers.abc import IReadingController
|
from ielts_be.controllers import IReadingController
|
||||||
from app.dtos.reading import ReadingDTO
|
from ielts_be.services import IReadingService
|
||||||
from app.services.abc import IReadingService
|
from ielts_be.dtos.reading import ReadingDTO
|
||||||
|
|
||||||
|
|
||||||
class ReadingController(IReadingController):
|
class ReadingController(IReadingController):
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import random
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from app.controllers.abc import ISpeakingController
|
from ielts_be.controllers import ISpeakingController
|
||||||
from app.services.abc import ISpeakingService, IVideoGeneratorService
|
from ielts_be.services import ISpeakingService, IVideoGeneratorService
|
||||||
|
|
||||||
|
|
||||||
class SpeakingController(ISpeakingController):
|
class SpeakingController(ISpeakingController):
|
||||||
19
ielts_be/controllers/impl/exam/writing.py
Normal file
19
ielts_be/controllers/impl/exam/writing.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from fastapi import UploadFile, HTTPException
|
||||||
|
|
||||||
|
from ielts_be.controllers import IWritingController
|
||||||
|
from ielts_be.services import IWritingService
|
||||||
|
|
||||||
|
|
||||||
|
class WritingController(IWritingController):
|
||||||
|
|
||||||
|
def __init__(self, writing_service: IWritingService):
|
||||||
|
self._service = writing_service
|
||||||
|
|
||||||
|
async def get_writing_task_general_question(self, task: int, topic: str, difficulty: str):
|
||||||
|
return await self._service.get_writing_task_general_question(task, topic, difficulty)
|
||||||
|
|
||||||
|
async def get_writing_task_academic_question(self, task: int, attachment: UploadFile, difficulty: str):
|
||||||
|
if attachment.content_type not in ['image/jpeg', 'image/png']:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid file type. Only JPEG and PNG allowed.")
|
||||||
|
|
||||||
|
return await self._service.get_writing_task_academic_question(task, attachment, difficulty)
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict, List, Union
|
from typing import Dict
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from fastapi import BackgroundTasks, Response, HTTPException
|
from fastapi import BackgroundTasks, Response, HTTPException
|
||||||
from fastapi.datastructures import FormData
|
from fastapi.datastructures import FormData
|
||||||
|
|
||||||
from app.controllers.abc import IGradeController
|
from ielts_be.controllers import IGradeController
|
||||||
from app.dtos.evaluation import EvaluationType
|
from ielts_be.services import IGradeService, IEvaluationService
|
||||||
from app.dtos.speaking import GradeSpeakingItem
|
from ielts_be.dtos.evaluation import EvaluationType
|
||||||
from app.dtos.writing import WritingGradeTaskDTO
|
from ielts_be.dtos.speaking import GradeSpeakingItem
|
||||||
from app.services.abc import IGradeService, IEvaluationService
|
from ielts_be.dtos.writing import WritingGradeTaskDTO
|
||||||
|
|
||||||
class GradeController(IGradeController):
|
class GradeController(IGradeController):
|
||||||
|
|
||||||
@@ -26,6 +25,9 @@ class GradeController(IGradeController):
|
|||||||
self,
|
self,
|
||||||
task: int, dto: WritingGradeTaskDTO, background_tasks: BackgroundTasks
|
task: int, dto: WritingGradeTaskDTO, background_tasks: BackgroundTasks
|
||||||
):
|
):
|
||||||
|
if task == 1 and dto.type == "academic" and dto.attachment is None:
|
||||||
|
raise HTTPException(status_code=400, detail="Academic writing task requires a picture!")
|
||||||
|
|
||||||
await self._evaluation_service.create_evaluation(
|
await self._evaluation_service.create_evaluation(
|
||||||
dto.userId, dto.sessionId, dto.exerciseId, EvaluationType.WRITING, task
|
dto.userId, dto.sessionId, dto.exerciseId, EvaluationType.WRITING, task
|
||||||
)
|
)
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from app.controllers.abc import ITrainingController
|
from ielts_be.controllers import ITrainingController
|
||||||
from app.dtos.training import FetchTipsDTO
|
from ielts_be.services import ITrainingService
|
||||||
from app.services.abc import ITrainingService
|
from ielts_be.dtos.training import FetchTipsDTO
|
||||||
|
|
||||||
|
|
||||||
class TrainingController(ITrainingController):
|
class TrainingController(ITrainingController):
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from app.controllers.abc import IUserController
|
from ielts_be.controllers import IUserController
|
||||||
from app.dtos.user_batch import BatchUsersDTO
|
from ielts_be.services import IUserService
|
||||||
from app.services.abc import IUserService
|
from ielts_be.dtos.user_batch import BatchUsersDTO
|
||||||
|
|
||||||
|
|
||||||
class UserController(IUserController):
|
class UserController(IUserController):
|
||||||
@@ -2,7 +2,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.configs.constants import LevelExerciseType
|
from ielts_be.configs.constants import LevelExerciseType
|
||||||
|
|
||||||
|
|
||||||
class LevelExercises(BaseModel):
|
class LevelExercises(BaseModel):
|
||||||
@@ -4,7 +4,7 @@ from typing import List, Dict, Optional
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.configs.constants import MinTimers, EducationalContent, ListeningExerciseType
|
from ielts_be.configs.constants import MinTimers, EducationalContent, ListeningExerciseType
|
||||||
|
|
||||||
|
|
||||||
class SaveListeningDTO(BaseModel):
|
class SaveListeningDTO(BaseModel):
|
||||||
@@ -3,7 +3,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.configs.constants import ReadingExerciseType, EducationalContent
|
from ielts_be.configs.constants import ReadingExerciseType, EducationalContent
|
||||||
|
|
||||||
class ReadingExercise(BaseModel):
|
class ReadingExercise(BaseModel):
|
||||||
type: ReadingExerciseType
|
type: ReadingExerciseType
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
from typing import List, Dict
|
|
||||||
|
|
||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import random
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
@@ -7,3 +9,5 @@ class WritingGradeTaskDTO(BaseModel):
|
|||||||
exerciseId: str
|
exerciseId: str
|
||||||
question: str
|
question: str
|
||||||
answer: str
|
answer: str
|
||||||
|
type: str
|
||||||
|
attachment: Optional[str]
|
||||||
@@ -117,3 +117,9 @@ class FileHelper:
|
|||||||
await file.write(file_bytes)
|
await file.write(file_bytes)
|
||||||
|
|
||||||
return ext, path_id
|
return ext, path_id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def encode_image(image_path: str) -> str:
|
||||||
|
async with aiofiles.open(image_path, "rb") as image_file:
|
||||||
|
img = await image_file.read()
|
||||||
|
return base64.b64encode(img).decode('utf-8')
|
||||||
@@ -2,12 +2,12 @@ from typing import Dict, Any
|
|||||||
|
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from app.dtos.exams.level import (
|
from ielts_be.dtos.exams.level import (
|
||||||
MultipleChoiceExercise,
|
MultipleChoiceExercise,
|
||||||
FillBlanksExercise,
|
FillBlanksExercise,
|
||||||
Part, Exam, Text
|
Part, Exam, Text
|
||||||
)
|
)
|
||||||
from app.dtos.sheet import Sheet, Option, MultipleChoiceQuestion, FillBlanksWord
|
from ielts_be.dtos.sheet import Sheet, Option, MultipleChoiceQuestion, FillBlanksWord
|
||||||
|
|
||||||
|
|
||||||
class LevelMapper:
|
class LevelMapper:
|
||||||
@@ -2,7 +2,7 @@ from typing import Dict, Any, List, Union, Optional
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.dtos.exams.listening import (
|
from ielts_be.dtos.exams.listening import (
|
||||||
TrueFalseExercise,
|
TrueFalseExercise,
|
||||||
MultipleChoiceExercise,
|
MultipleChoiceExercise,
|
||||||
WriteBlanksExercise,
|
WriteBlanksExercise,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
from app.dtos.exams.reading import (
|
from ielts_be.dtos.exams.reading import (
|
||||||
Part, Exam, Context, FillBlanksExercise,
|
Part, Exam, Context, FillBlanksExercise,
|
||||||
TrueFalseExercise, MatchSentencesExercise,
|
TrueFalseExercise, MatchSentencesExercise,
|
||||||
WriteBlanksExercise, MultipleChoice
|
WriteBlanksExercise, MultipleChoice
|
||||||
@@ -5,7 +5,7 @@ from fastapi import Request
|
|||||||
from fastapi.openapi.models import APIKey, APIKeyIn
|
from fastapi.openapi.models import APIKey, APIKeyIn
|
||||||
from fastapi.security.base import SecurityBase
|
from fastapi.security.base import SecurityBase
|
||||||
|
|
||||||
from app.exceptions import CustomException, UnauthorizedException
|
from ielts_be.exceptions import CustomException, UnauthorizedException
|
||||||
|
|
||||||
|
|
||||||
class BaseAuthorization(ABC):
|
class BaseAuthorization(ABC):
|
||||||
3
ielts_be/repositories/__init__.py
Normal file
3
ielts_be/repositories/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .abc import *
|
||||||
|
|
||||||
|
__all__ = abc.__all__
|
||||||
6
ielts_be/repositories/impl/__init__.py
Normal file
6
ielts_be/repositories/impl/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from .document_stores import *
|
||||||
|
from .file_storage import *
|
||||||
|
|
||||||
|
__all__ = []
|
||||||
|
__all__.extend(document_stores.__all__)
|
||||||
|
__all__.extend(file_storage.__all__)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
from .firestore import Firestore
|
from .firestore import Firestore
|
||||||
#from .mongo import MongoDB
|
from .mongo import MongoDB
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Firestore",
|
"Firestore",
|
||||||
#"MongoDB"
|
"MongoDB"
|
||||||
]
|
]
|
||||||
@@ -4,7 +4,7 @@ from typing import Optional, List, Dict
|
|||||||
from google.cloud.firestore_v1.async_client import AsyncClient
|
from google.cloud.firestore_v1.async_client import AsyncClient
|
||||||
from google.cloud.firestore_v1.async_collection import AsyncCollectionReference
|
from google.cloud.firestore_v1.async_collection import AsyncCollectionReference
|
||||||
from google.cloud.firestore_v1.async_document import AsyncDocumentReference
|
from google.cloud.firestore_v1.async_document import AsyncDocumentReference
|
||||||
from app.repositories.abc import IDocumentStore
|
from ielts_be.repositories import IDocumentStore
|
||||||
|
|
||||||
|
|
||||||
class Firestore(IDocumentStore):
|
class Firestore(IDocumentStore):
|
||||||
@@ -4,7 +4,7 @@ from typing import Optional, List, Dict
|
|||||||
|
|
||||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||||
|
|
||||||
from app.repositories.abc import IDocumentStore
|
from ielts_be.repositories import IDocumentStore
|
||||||
|
|
||||||
|
|
||||||
class MongoDB(IDocumentStore):
|
class MongoDB(IDocumentStore):
|
||||||
@@ -4,7 +4,7 @@ from typing import Optional
|
|||||||
import aiofiles
|
import aiofiles
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
||||||
from app.repositories.abc import IFileStorage
|
from ielts_be.repositories import IFileStorage
|
||||||
|
|
||||||
|
|
||||||
class FirebaseStorage(IFileStorage):
|
class FirebaseStorage(IFileStorage):
|
||||||
3
ielts_be/services/__init__.py
Normal file
3
ielts_be/services/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from .abc import *
|
||||||
|
|
||||||
|
__all__ = abc.__all__
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
from abc import abstractmethod, ABC
|
from abc import abstractmethod, ABC
|
||||||
from typing import Union, List, Dict
|
|
||||||
|
|
||||||
from fastapi import BackgroundTasks
|
from fastapi import BackgroundTasks
|
||||||
|
|
||||||
from app.dtos.evaluation import EvaluationType
|
from ielts_be.dtos.evaluation import EvaluationType
|
||||||
|
|
||||||
class IEvaluationService(ABC):
|
class IEvaluationService(ABC):
|
||||||
|
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from fastapi import UploadFile
|
from fastapi import UploadFile
|
||||||
|
|
||||||
from app.configs.constants import EducationalContent
|
|
||||||
|
|
||||||
|
|
||||||
class ILevelService(ABC):
|
class ILevelService(ABC):
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user