diff --git a/fastapi-app/.dockeringore b/fastapi-app/.dockeringore new file mode 100644 index 0000000..2b8052f --- /dev/null +++ b/fastapi-app/.dockeringore @@ -0,0 +1,3 @@ +venv +Dockerfile +tasks.db \ No newline at end of file diff --git a/fastapi-app/.gitignore b/fastapi-app/.gitignore new file mode 100644 index 0000000..979d491 --- /dev/null +++ b/fastapi-app/.gitignore @@ -0,0 +1,4 @@ +venv +tasks.db +.idea +__pycache__ \ No newline at end of file diff --git a/fastapi-app/Dockerfile b/fastapi-app/Dockerfile new file mode 100644 index 0000000..a929a1f --- /dev/null +++ b/fastapi-app/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.11-slim + +COPY ../../../../../AppData/Local/Temp . + +RUN pip install -r requirements.txt + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] + +# Для запуска введите две команды: +# docker build . --tag fastapi_app +# docker run -p 80:80 fastapi_app + +# Или одной командой +# docker build . --tag fastapi_app && docker run -p 80:80 fastapi_app diff --git a/fastapi-app/README.md b/fastapi-app/README.md new file mode 100644 index 0000000..b7630ff Binary files /dev/null and b/fastapi-app/README.md differ diff --git a/fastapi-app/database.py b/fastapi-app/database.py new file mode 100644 index 0000000..b42f927 --- /dev/null +++ b/fastapi-app/database.py @@ -0,0 +1,30 @@ +from typing import Optional + +from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column + +engine = create_async_engine("sqlite+aiosqlite:///tasks.db") +new_session = async_sessionmaker(engine, expire_on_commit=False) + + +class Model(DeclarativeBase): + pass + + +class TaskOrm(Model): + __tablename__ = "tasks" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + description: Mapped[Optional[str]] + + +async def create_tables(): + # https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html#synopsis-core + async with engine.begin() as conn: + await conn.run_sync(Model.metadata.create_all) + + +async def delete_tables(): + async with engine.begin() as conn: + await conn.run_sync(Model.metadata.drop_all) diff --git a/fastapi-app/main.py b/fastapi-app/main.py new file mode 100644 index 0000000..6fa0760 --- /dev/null +++ b/fastapi-app/main.py @@ -0,0 +1,20 @@ +from fastapi import FastAPI + +from contextlib import asynccontextmanager + +from database import create_tables, delete_tables +from router import router as tasks_router + + +@asynccontextmanager +async def lifespan(app: FastAPI): + await delete_tables() + print("База очищена") + await create_tables() + print("База готова к работе") + yield + print("Выключение") + + +app = FastAPI(lifespan=lifespan) +app.include_router(tasks_router) diff --git a/fastapi-app/repository.py b/fastapi-app/repository.py new file mode 100644 index 0000000..1805844 --- /dev/null +++ b/fastapi-app/repository.py @@ -0,0 +1,26 @@ +from sqlalchemy import select + +from database import new_session, TaskOrm +from schemas import STaskAdd, STask + + +class TaskRepository: + @classmethod + async def add_one(cls, data: STaskAdd) -> int: + async with new_session() as session: + task_dict = data.model_dump() + + task = TaskOrm(**task_dict) + session.add(task) + await session.flush() + await session.commit() + return task.id + + @classmethod + async def find_all(cls) -> list[STask]: + async with new_session() as session: + query = select(TaskOrm) + result = await session.execute(query) + task_models = result.scalars().all() + task_schemas = [STask.model_validate(task_model) for task_model in task_models] + return task_schemas diff --git a/fastapi-app/requirements.txt b/fastapi-app/requirements.txt new file mode 100644 index 0000000..712cf65 Binary files /dev/null and b/fastapi-app/requirements.txt differ diff --git a/fastapi-app/router.py b/fastapi-app/router.py new file mode 100644 index 0000000..f58400d --- /dev/null +++ b/fastapi-app/router.py @@ -0,0 +1,25 @@ +from typing import Annotated + +from fastapi import APIRouter, Depends + +from repository import TaskRepository +from schemas import STaskAdd, STask, STaskId + +router = APIRouter( + prefix="/tasks", + tags=["Таски"], +) + + +@router.post("") +async def add_task( + task: Annotated[STaskAdd, Depends()], +) -> STaskId: + task_id = await TaskRepository.add_one(task) + return {"ok": True, "task_id": task_id} + + +@router.get("") +async def get_tasks() -> list[STask]: + tasks = await TaskRepository.find_all() + return tasks diff --git a/fastapi-app/schemas.py b/fastapi-app/schemas.py new file mode 100644 index 0000000..3918fcb --- /dev/null +++ b/fastapi-app/schemas.py @@ -0,0 +1,19 @@ +from typing import Optional + +from pydantic import BaseModel, ConfigDict + + +class STaskAdd(BaseModel): + name: str + description: Optional[str] = None + + +class STask(STaskAdd): + id: int + + model_config = ConfigDict(from_attributes=True) + + +class STaskId(BaseModel): + ok: bool = True + task_id: int