From c0a4e3b46b2e7a6bd82db4efe148af592377ad14 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Date: Sun, 22 Oct 2023 01:04:05 -0300 Subject: [PATCH 1/3] feat: add user migration and model and remove dummy Co-authored-by: Mateus Maia --- gestao/db/dao/__init__.py | 1 - gestao/db/dao/dummy_dao.py | 40 ------------- .../versions/2021-08-16-16-53_819cbf6e030b.py | 22 ------- .../versions/2021-08-16-16-55_2b7380507a71.py | 32 ----------- .../versions/2023-10-21-23-53_071cefd86342.py | 57 +++++++++++++++++++ gestao/db/models/dummy_model.py | 13 ----- gestao/db/models/user.py | 43 ++++++++++++++ gestao/tests/test_dummy.py | 46 --------------- gestao/web/api/dummy/__init__.py | 4 -- gestao/web/api/dummy/schema.py | 21 ------- gestao/web/api/dummy/views.py | 41 ------------- 11 files changed, 100 insertions(+), 220 deletions(-) delete mode 100644 gestao/db/dao/__init__.py delete mode 100644 gestao/db/dao/dummy_dao.py delete mode 100644 gestao/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py delete mode 100644 gestao/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py create mode 100644 gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py delete mode 100644 gestao/db/models/dummy_model.py create mode 100644 gestao/db/models/user.py delete mode 100644 gestao/tests/test_dummy.py delete mode 100644 gestao/web/api/dummy/__init__.py delete mode 100644 gestao/web/api/dummy/schema.py delete mode 100644 gestao/web/api/dummy/views.py diff --git a/gestao/db/dao/__init__.py b/gestao/db/dao/__init__.py deleted file mode 100644 index db62a0a..0000000 --- a/gestao/db/dao/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""DAO classes.""" diff --git a/gestao/db/dao/dummy_dao.py b/gestao/db/dao/dummy_dao.py deleted file mode 100644 index 68e7a9e..0000000 --- a/gestao/db/dao/dummy_dao.py +++ /dev/null @@ -1,40 +0,0 @@ -from typing import List, Optional - -from gestao.db.models.dummy_model import DummyModel - - -class DummyDAO: - """Class for accessing dummy table.""" - - async def create_dummy_model(self, name: str) -> None: - """ - Add single dummy to session. - - :param name: name of a dummy. - """ - await DummyModel.objects.create(name=name) - - async def get_all_dummies(self, limit: int, offset: int) -> List[DummyModel]: - """ - Get all dummy models with limit/offset pagination. - - :param limit: limit of dummies. - :param offset: offset of dummies. - :return: stream of dummies. - """ - return await DummyModel.objects.limit(limit).offset(offset).all() - - async def filter( - self, - name: Optional[str] = None, - ) -> List[DummyModel]: - """ - Get specific dummy model. - - :param name: name of dummy instance. - :return: dummy models. - """ - query = DummyModel.objects - if name: - query = query.filter(DummyModel.name == name) - return await query.all() diff --git a/gestao/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py b/gestao/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py deleted file mode 100644 index 3b22742..0000000 --- a/gestao/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py +++ /dev/null @@ -1,22 +0,0 @@ -"""Initial migration. - -Revision ID: 819cbf6e030b -Revises: -Create Date: 2021-08-16 16:53:05.484024 - -""" - - -# revision identifiers, used by Alembic. -revision = "819cbf6e030b" -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade() -> None: - pass - - -def downgrade() -> None: - pass diff --git a/gestao/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py b/gestao/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py deleted file mode 100644 index f06778f..0000000 --- a/gestao/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Created Dummy Model. - -Revision ID: 2b7380507a71 -Revises: 819cbf6e030b -Create Date: 2021-08-16 16:55:25.157309 - -""" -import sqlalchemy as sa -from alembic import op - -# revision identifiers, used by Alembic. -revision = "2b7380507a71" -down_revision = "819cbf6e030b" -branch_labels = None -depends_on = None - - -def upgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "dummy_model", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=200), nullable=False), - sa.PrimaryKeyConstraint("id"), - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table("dummy_model") - # ### end Alembic commands ### diff --git a/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py b/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py new file mode 100644 index 0000000..72ce1d6 --- /dev/null +++ b/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py @@ -0,0 +1,57 @@ +"""user table + +Revision ID: 071cefd86342 +Revises: +Create Date: 2023-10-21 23:53:52.242776 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "071cefd86342" +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table( + "user", + sa.Column("id", sa.String(length=200), primary_key=True, nullable=False), + sa.Column("name", sa.String(length=200), nullable=False), + sa.Column("address", sa.String(length=200), nullable=False), + sa.Column("neighborhood", sa.String(length=200), nullable=False), + sa.Column("city", sa.String(length=100), nullable=False), + sa.Column("state", sa.String(length=100), nullable=False), + sa.Column("zipcode", sa.String(length=100), nullable=False), + sa.Column("cpf", sa.String(length=200), unique=True, nullable=False), + sa.Column("rg", sa.String(length=200), unique=True, nullable=False), + sa.Column("birth_date", sa.Date(), nullable=False), + sa.Column("place_of_birth", sa.String(length=200), nullable=False), + sa.Column("blood_type", sa.String(length=200), nullable=False), + sa.Column("gender", sa.String(length=200), nullable=False), + sa.Column("father_name", sa.String(length=200), nullable=False), + sa.Column("mother_date", sa.String(length=200), nullable=False), + sa.Column("position", sa.String(length=200), nullable=False), + sa.Column("occupancy", sa.String(length=200), nullable=False), + sa.Column("admission_date", sa.Date(), nullable=False), + sa.Column("situation", sa.String(length=200), nullable=False), + sa.Column("phone", sa.String(length=200), nullable=False), + sa.Column("email", sa.String(length=200), nullable=False), + sa.Column("marital_status", sa.String(length=200), nullable=False), + sa.Column("education", sa.String(length=200), nullable=False), + sa.Column("registration", sa.String(length=200), unique=True, nullable=False), + sa.Column("role", sa.String(length=200), nullable=False), + sa.Column("category", sa.String(length=200), nullable=False), + sa.Column("pattern", sa.String(length=200), nullable=False), + sa.Column("dispatcher", sa.String(length=200), nullable=False), + sa.Column("dispatched_date", sa.Date(), nullable=False), + sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + schema="public", + ) + + +def downgrade() -> None: + op.drop_table("user") diff --git a/gestao/db/models/dummy_model.py b/gestao/db/models/dummy_model.py deleted file mode 100644 index 4de7dee..0000000 --- a/gestao/db/models/dummy_model.py +++ /dev/null @@ -1,13 +0,0 @@ -import ormar - -from gestao.db.base import BaseMeta - - -class DummyModel(ormar.Model): - """Model for demo purpose.""" - - class Meta(BaseMeta): - tablename = "dummy_model" - - id: int = ormar.Integer(primary_key=True) - name: str = ormar.String(max_length=200) # noqa: WPS432 diff --git a/gestao/db/models/user.py b/gestao/db/models/user.py new file mode 100644 index 0000000..e1ef2a2 --- /dev/null +++ b/gestao/db/models/user.py @@ -0,0 +1,43 @@ +import ormar +from datetime import date, datetime + +from gestao.db.base import BaseMeta + + +class User(ormar.Model): + """User model.""" + + class Meta(BaseMeta): + tablename = "user" + + id: str = ormar.String(max_length=200, primary_key=True) + name: str = ormar.String(max_length=200) + address: str = ormar.String(max_length=200) + neighborhood: str = ormar.String(max_length=200) + city: str = ormar.String(max_length=100) + state: str = ormar.String(max_length=100) + zipcode: str = ormar.String(max_length=100) + cpf: str = ormar.String(max_length=200, unique=True) + rg: str = ormar.String(max_length=200, unique=True) + birth_date: date = ormar.Date() + place_of_birth: str = ormar.String(max_length=200) + blood_type: str = ormar.String(max_length=200) + gender: str = ormar.String(max_length=200) + father_name: str = ormar.String(max_length=200) + mother_date: str = ormar.String(max_length=200) + position: str = ormar.String(max_length=200) + occupancy: str = ormar.String(max_length=200) + admission_date: date = ormar.Date() + situation: str = ormar.String(max_length=200) + phone: str = ormar.String(max_length=200) + email: str = ormar.String(max_length=200) + marital_status: str = ormar.String(max_length=200) + education: str = ormar.String(max_length=200) + registration: str = ormar.String(max_length=200, unique=True) + role: str = ormar.String(max_length=200) + category: str = ormar.String(max_length=200) + pattern: str = ormar.String(max_length=200) + dispatcher: str = ormar.String(max_length=200) + dispatched_date: date = ormar.Date() + created_at: datetime = ormar.DateTime(timezone=True, default=datetime.now) + updated_at: datetime = ormar.DateTime(timezone=True, default=datetime.now, onupdate=datetime.now) diff --git a/gestao/tests/test_dummy.py b/gestao/tests/test_dummy.py deleted file mode 100644 index 7f691c1..0000000 --- a/gestao/tests/test_dummy.py +++ /dev/null @@ -1,46 +0,0 @@ -import uuid - -import pytest -from fastapi import FastAPI -from httpx import AsyncClient -from starlette import status - -from gestao.db.dao.dummy_dao import DummyDAO - - -@pytest.mark.anyio -async def test_creation( - fastapi_app: FastAPI, - client: AsyncClient, -) -> None: - """Tests dummy instance creation.""" - url = fastapi_app.url_path_for("create_dummy_model") - test_name = uuid.uuid4().hex - response = await client.put( - url, - json={ - "name": test_name, - }, - ) - assert response.status_code == status.HTTP_200_OK - dao = DummyDAO() - instances = await dao.filter(name=test_name) - assert instances[0].name == test_name - - -@pytest.mark.anyio -async def test_getting( - fastapi_app: FastAPI, - client: AsyncClient, -) -> None: - """Tests dummy instance retrieval.""" - dao = DummyDAO() - test_name = uuid.uuid4().hex - await dao.create_dummy_model(name=test_name) - url = fastapi_app.url_path_for("get_dummy_models") - response = await client.get(url) - dummies = response.json() - - assert response.status_code == status.HTTP_200_OK - assert len(dummies) == 1 - assert dummies[0]["name"] == test_name diff --git a/gestao/web/api/dummy/__init__.py b/gestao/web/api/dummy/__init__.py deleted file mode 100644 index 5e9016c..0000000 --- a/gestao/web/api/dummy/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -"""Dummy model API.""" -from gestao.web.api.dummy.views import router - -__all__ = ["router"] diff --git a/gestao/web/api/dummy/schema.py b/gestao/web/api/dummy/schema.py deleted file mode 100644 index 129518d..0000000 --- a/gestao/web/api/dummy/schema.py +++ /dev/null @@ -1,21 +0,0 @@ -from pydantic import BaseModel - - -class DummyModelDTO(BaseModel): - """ - DTO for dummy models. - - It returned when accessing dummy models from the API. - """ - - id: int - name: str - - class Config: - orm_mode = True - - -class DummyModelInputDTO(BaseModel): - """DTO for creating new dummy model.""" - - name: str diff --git a/gestao/web/api/dummy/views.py b/gestao/web/api/dummy/views.py deleted file mode 100644 index 0d2f221..0000000 --- a/gestao/web/api/dummy/views.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import List - -from fastapi import APIRouter -from fastapi.param_functions import Depends - -from gestao.db.dao.dummy_dao import DummyDAO -from gestao.db.models.dummy_model import DummyModel -from gestao.web.api.dummy.schema import DummyModelDTO, DummyModelInputDTO - -router = APIRouter() - - -@router.get("/", response_model=List[DummyModelDTO]) -async def get_dummy_models( - limit: int = 10, - offset: int = 0, - dummy_dao: DummyDAO = Depends(), -) -> List[DummyModel]: - """ - Retrieve all dummy objects from the database. - - :param limit: limit of dummy objects, defaults to 10. - :param offset: offset of dummy objects, defaults to 0. - :param dummy_dao: DAO for dummy models. - :return: list of dummy objects from database. - """ - return await dummy_dao.get_all_dummies(limit=limit, offset=offset) - - -@router.put("/") -async def create_dummy_model( - new_dummy_object: DummyModelInputDTO, - dummy_dao: DummyDAO = Depends(), -) -> None: - """ - Creates dummy model in the database. - - :param new_dummy_object: new dummy model item. - :param dummy_dao: DAO for dummy models. - """ - await dummy_dao.create_dummy_model(name=new_dummy_object.name) From 25099d5e51a7b66f055e862328c594fea16c1798 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Date: Sun, 22 Oct 2023 01:04:43 -0300 Subject: [PATCH 2/3] feat: add users route Co-authored-by: Mateus Maia --- .flake8 | 6 ++ .../versions/2023-10-21-23-53_071cefd86342.py | 14 +++- gestao/db/models/user.py | 11 +-- .../{test_patrimonio.py => test_gestao.py} | 6 -- gestao/web/api/router.py | 4 +- gestao/web/api/user/__init__.py | 3 + gestao/web/api/user/schema.py | 66 ++++++++++++++++++ gestao/web/api/user/views.py | 67 +++++++++++++++++++ 8 files changed, 163 insertions(+), 14 deletions(-) rename gestao/tests/{test_patrimonio.py => test_gestao.py} (70%) create mode 100644 gestao/web/api/user/__init__.py create mode 100644 gestao/web/api/user/schema.py create mode 100644 gestao/web/api/user/views.py diff --git a/.flake8 b/.flake8 index 4e1064a..bdbeb3c 100644 --- a/.flake8 +++ b/.flake8 @@ -72,6 +72,12 @@ ignore = WPS473, ; too many no-cover comments. WPS403, + D101, + D104, + D103, + WPS432, + WPS442, + C815, per-file-ignores = ; all tests diff --git a/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py b/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py index 72ce1d6..f7fec0e 100644 --- a/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py +++ b/gestao/db/migrations/versions/2023-10-21-23-53_071cefd86342.py @@ -47,8 +47,18 @@ def upgrade() -> None: sa.Column("pattern", sa.String(length=200), nullable=False), sa.Column("dispatcher", sa.String(length=200), nullable=False), sa.Column("dispatched_date", sa.Date(), nullable=False), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), - sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.func.now(), + nullable=False, + ), schema="public", ) diff --git a/gestao/db/models/user.py b/gestao/db/models/user.py index e1ef2a2..85ec5ac 100644 --- a/gestao/db/models/user.py +++ b/gestao/db/models/user.py @@ -1,12 +1,11 @@ -import ormar from datetime import date, datetime +import ormar + from gestao.db.base import BaseMeta class User(ormar.Model): - """User model.""" - class Meta(BaseMeta): tablename = "user" @@ -40,4 +39,8 @@ class Meta(BaseMeta): dispatcher: str = ormar.String(max_length=200) dispatched_date: date = ormar.Date() created_at: datetime = ormar.DateTime(timezone=True, default=datetime.now) - updated_at: datetime = ormar.DateTime(timezone=True, default=datetime.now, onupdate=datetime.now) + updated_at: datetime = ormar.DateTime( + timezone=True, + default=datetime.now, + onupdate=datetime.now, + ) diff --git a/gestao/tests/test_patrimonio.py b/gestao/tests/test_gestao.py similarity index 70% rename from gestao/tests/test_patrimonio.py rename to gestao/tests/test_gestao.py index 1afa908..911c4b4 100644 --- a/gestao/tests/test_patrimonio.py +++ b/gestao/tests/test_gestao.py @@ -6,12 +6,6 @@ @pytest.mark.anyio async def test_health(client: AsyncClient, fastapi_app: FastAPI) -> None: - """ - Checks the health endpoint. - - :param client: client for the app. - :param fastapi_app: current FastAPI application. - """ url = fastapi_app.url_path_for("health_check") response = await client.get(url) assert response.status_code == status.HTTP_200_OK diff --git a/gestao/web/api/router.py b/gestao/web/api/router.py index 68b5cb9..9c98314 100644 --- a/gestao/web/api/router.py +++ b/gestao/web/api/router.py @@ -1,8 +1,8 @@ from fastapi.routing import APIRouter -from gestao.web.api import dummy, echo, monitoring +from gestao.web.api import echo, monitoring, user api_router = APIRouter() api_router.include_router(monitoring.router) api_router.include_router(echo.router, prefix="/echo", tags=["echo"]) -api_router.include_router(dummy.router, prefix="/dummy", tags=["dummy"]) +api_router.include_router(user.router, prefix="/user", tags=["user"]) diff --git a/gestao/web/api/user/__init__.py b/gestao/web/api/user/__init__.py new file mode 100644 index 0000000..8ee7aa8 --- /dev/null +++ b/gestao/web/api/user/__init__.py @@ -0,0 +1,3 @@ +from gestao.web.api.user.views import router + +__all__ = ["router"] diff --git a/gestao/web/api/user/schema.py b/gestao/web/api/user/schema.py new file mode 100644 index 0000000..d61e3c8 --- /dev/null +++ b/gestao/web/api/user/schema.py @@ -0,0 +1,66 @@ +from datetime import date +from typing import Optional + +from pydantic import BaseModel + + +class CreateUserDTO(BaseModel): + name: str + address: str + neighborhood: str + city: str + state: str + zipcode: str + cpf: str + rg: str + birth_date: date + place_of_birth: str + blood_type: str + gender: str + father_name: str + mother_date: str + position: str + occupancy: str + admission_date: date + situation: str + phone: str + email: str + marital_status: str + education: str + registration: str + role: str + category: str + pattern: str + dispatcher: str + dispatched_date: date + + +class UpdateUserDTO(BaseModel): + name: Optional[str] + address: Optional[str] + neighborhood: Optional[str] + city: Optional[str] + state: Optional[str] + zipcode: Optional[str] + cpf: Optional[str] + rg: Optional[str] + birth_date: Optional[date] + place_of_birth: Optional[str] + blood_type: Optional[str] + gender: Optional[str] + father_name: Optional[str] + mother_date: Optional[str] + position: Optional[str] + occupancy: Optional[str] + admission_date: Optional[date] + situation: Optional[str] + phone: Optional[str] + email: Optional[str] + marital_status: Optional[str] + education: Optional[str] + registration: Optional[str] + role: Optional[str] + category: Optional[str] + pattern: Optional[str] + dispatcher: Optional[str] + dispatched_date: Optional[date] diff --git a/gestao/web/api/user/views.py b/gestao/web/api/user/views.py new file mode 100644 index 0000000..1977112 --- /dev/null +++ b/gestao/web/api/user/views.py @@ -0,0 +1,67 @@ +from typing import List + +from uuid import uuid4 +from fastapi import APIRouter, HTTPException + +from gestao.db.models.user import User +from gestao.web.api.user.schema import CreateUserDTO, UpdateUserDTO + +router = APIRouter() + + +@router.get("/") +async def get_users( + limit: int = 10, + offset: int = 0, +) -> List[User]: + return await User.objects.limit(limit).offset(offset).all() + + +@router.get("/{user_id}") +async def get_user(user_id: str) -> User: + try: + return await User.objects.get(id=user_id) + except Exception: + raise HTTPException(status_code=404, detail="User not found") + + +@router.post("/") +async def create_user(create_user: CreateUserDTO) -> User: + try: + return await User.objects.create( + **{ + "id": str(uuid4()), + **create_user.dict(), + } + ) + except Exception: + raise HTTPException( + status_code=400, + detail="Error occurred while creating user", + ) + + +@router.put("/{user_id}") +async def update_user(user_id: str, update_user: UpdateUserDTO) -> User: + try: + await User.objects.filter(id=user_id).update( + each=True, + **update_user.dict(exclude_none=True), + ) + return await User.objects.get(id=user_id) + except Exception: + raise HTTPException( + status_code=404, + detail="Error occurred while updating user", + ) + + +@router.delete("/{user_id}") +async def delete_user(user_id: str) -> None: + try: + await User.objects.delete(id=user_id) + except Exception: + raise HTTPException( + status_code=404, + detail="Error occurred while deleting user", + ) From 80f3b98015e77a4d4916e0896d5a4fda7f7b83a8 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Date: Sun, 22 Oct 2023 01:34:21 -0300 Subject: [PATCH 3/3] style: lint some stuff --- .github/workflows/tests.yml | 2 +- gestao/web/api/user/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 06c3985..ba9536d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -60,7 +60,7 @@ jobs: --health-timeout=5s --health-retries=5 ports: - - 5433:5432 + - 5432:5432 steps: - uses: actions/checkout@v2 - name: Set up Python diff --git a/gestao/web/api/user/views.py b/gestao/web/api/user/views.py index 1977112..8ef9f39 100644 --- a/gestao/web/api/user/views.py +++ b/gestao/web/api/user/views.py @@ -1,6 +1,6 @@ from typing import List - from uuid import uuid4 + from fastapi import APIRouter, HTTPException from gestao.db.models.user import User