From 231a13bbe8f29f153b42b017eb19ba2362c7cae6 Mon Sep 17 00:00:00 2001 From: Matheus Afonso Date: Sat, 28 Oct 2023 21:42:11 -0300 Subject: [PATCH 1/2] feat: add new fields to user and dependent Co-Authored-By: Mateus Maia --- .editorconfig | 4 - .flake8 | 121 ------------------ .github/workflows/tests.yml | 14 -- .pre-commit-config.yaml | 9 -- README.md | 1 - .../versions/2023-10-28-21-20_05258d089f10.py | 31 +++++ .../versions/2023-10-28-21-23_0084e7dffc7c.py | 39 ++++++ gestao/db/models/dependent.py | 4 + gestao/db/models/user.py | 3 + gestao/web/api/user/schema.py | 8 ++ 10 files changed, 85 insertions(+), 149 deletions(-) delete mode 100644 .flake8 create mode 100644 gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py create mode 100644 gestao/db/migrations/versions/2023-10-28-21-23_0084e7dffc7c.py diff --git a/.editorconfig b/.editorconfig index eb831fe..2ccf155 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,10 +21,6 @@ indent_size = 2 [Makefile] indent_style = tab -[.flake8] -indent_style = space -indent_size = 2 - [*.py] indent_style = space indent_size = 4 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index bdbeb3c..0000000 --- a/.flake8 +++ /dev/null @@ -1,121 +0,0 @@ -[flake8] -max-complexity = 6 -inline-quotes = double -max-line-length = 88 -extend-ignore = E203 -docstring_style=sphinx - -ignore = - ; Found `f` string - WPS305, - ; Missing docstring in public module - D100, - ; Missing docstring in magic method - D105, - ; Missing docstring in __init__ - D107, - ; Found `__init__.py` module with logic - WPS412, - ; Found class without a base class - WPS306, - ; Missing docstring in public nested class - D106, - ; First line should be in imperative mood - D401, - ; Found wrong variable name - WPS110, - ; Found `__init__.py` module with logic - WPS326, - ; Found string constant over-use - WPS226, - ; Found upper-case constant in a class - WPS115, - ; Found nested function - WPS602, - ; Found method without arguments - WPS605, - ; Found overused expression - WPS204, - ; Found too many module members - WPS202, - ; Found too high module cognitive complexity - WPS232, - ; line break before binary operator - W503, - ; Found module with too many imports - WPS201, - ; Inline strong start-string without end-string. - RST210, - ; Found nested class - WPS431, - ; Found wrong module name - WPS100, - ; Found too many methods - WPS214, - ; Found too long ``try`` body - WPS229, - ; Found unpythonic getter or setter - WPS615, - ; Found a line that starts with a dot - WPS348, - ; Found complex default value (for dependency injection) - WPS404, - ; not perform function calls in argument defaults (for dependency injection) - B008, - ; Model should define verbose_name in its Meta inner class - DJ10, - ; Model should define verbose_name_plural in its Meta inner class - DJ11, - ; Found mutable module constant. - WPS407, - ; Found too many empty lines in `def` - WPS473, - ; too many no-cover comments. - WPS403, - D101, - D104, - D103, - WPS432, - WPS442, - C815, - -per-file-ignores = - ; all tests - test_*.py,tests.py,tests_*.py,*/tests/*,conftest.py: - ; Use of assert detected - S101, - ; Found outer scope names shadowing - WPS442, - ; Found too many local variables - WPS210, - ; Found magic number - WPS432, - ; Missing parameter(s) in Docstring - DAR101, - ; Found too many arguments - WPS211, - - ; all init files - __init__.py: - ; ignore not used imports - F401, - ; ignore import with wildcard - F403, - ; Found wrong metadata variable - WPS410, - -exclude = - ./.cache, - ./.git, - ./.idea, - ./.mypy_cache, - ./.pytest_cache, - ./.venv, - ./venv, - ./env, - ./cached_venv, - ./docs, - ./deploy, - ./var, - ./.vscode, - *migrations*, diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ba9536d..03f0a88 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,20 +17,6 @@ jobs: POETRY_VIRTUALENVS_CREATE: false - name: Run black check run: poetry run black --check . - flake8: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Install deps - uses: knowsuchagency/poetry-install@v1 - env: - POETRY_VIRTUALENVS_CREATE: false - - name: Run flake8 check - run: poetry run flake8 --count . mypy: runs-on: ubuntu-latest steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a815052..d35aeac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,12 +46,3 @@ repos: language: system types: [python] exclude: metrics/ - - - id: flake8 - name: Check with Flake8 - entry: poetry run flake8 - language: system - pass_filenames: false - types: [python] - args: [--count, .] - exclude: metrics/ diff --git a/README.md b/README.md index 3a06ec1..081f927 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,6 @@ By default it runs: * black (formats your code); * mypy (validates types); * isort (sorts imports in all files); -* flake8 (spots possible bugs); You can read more about pre-commit here: https://pre-commit.com/ diff --git a/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py b/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py new file mode 100644 index 0000000..0614837 --- /dev/null +++ b/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py @@ -0,0 +1,31 @@ +"""add user workstation and nickname + +Revision ID: 05258d089f10 +Revises: 532e4deb3c37 +Create Date: 2023-10-28 21:20:43.696192 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "05258d089f10" +down_revision = "532e4deb3c37" +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table("user") as batch_op: + batch_op.add_column( + sa.Column("workstation", sa.String(200), nullable=True), + ) + batch_op.add_column( + sa.Column("nickname", sa.String(200), nullable=True), + ) + + +def downgrade(): + with op.batch_alter_table("user") as batch_op: + batch_op.drop_column("workstation") + batch_op.drop_column("nickname") diff --git a/gestao/db/migrations/versions/2023-10-28-21-23_0084e7dffc7c.py b/gestao/db/migrations/versions/2023-10-28-21-23_0084e7dffc7c.py new file mode 100644 index 0000000..71e2ac5 --- /dev/null +++ b/gestao/db/migrations/versions/2023-10-28-21-23_0084e7dffc7c.py @@ -0,0 +1,39 @@ +"""add dependent cof gender and pensioner + +Revision ID: 0084e7dffc7c +Revises: 05258d089f10 +Create Date: 2023-10-28 21:23:57.809923 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "0084e7dffc7c" +down_revision = "05258d089f10" +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table("dependent") as batch_op: + batch_op.add_column( + sa.Column("cpf", sa.String(200), nullable=True, unique=True), + ) + batch_op.add_column( + sa.Column("gender", sa.String(200), nullable=True), + ) + batch_op.add_column( + sa.Column("phone", sa.String(200), nullable=True), + ) + batch_op.add_column( + sa.Column("pensioner", sa.Boolean(), default=False), + ) + + +def downgrade(): + with op.batch_alter_table("dependent") as batch_op: + batch_op.drop_column("cpf") + batch_op.drop_column("gender") + batch_op.drop_column("phone") + batch_op.drop_column("pensioner") diff --git a/gestao/db/models/dependent.py b/gestao/db/models/dependent.py index 75d4722..992e8e9 100644 --- a/gestao/db/models/dependent.py +++ b/gestao/db/models/dependent.py @@ -14,7 +14,11 @@ class Meta(BaseMeta): user_id: User = ormar.ForeignKey(User, ondelete=ormar.ReferentialAction.CASCADE) name: str = ormar.String(max_length=200) birth_date: date = ormar.Date() + cpf: str = ormar.String(max_length=200, unique=True) + gender: str = ormar.String(max_length=200) + phone: str = ormar.String(max_length=200) relationship: str = ormar.String(max_length=200) + pensioner: bool = ormar.Boolean(default=False) created_at: datetime = ormar.DateTime(timezone=True, default=datetime.now) updated_at: datetime = ormar.DateTime( timezone=True, diff --git a/gestao/db/models/user.py b/gestao/db/models/user.py index 85ec5ac..9a8cf10 100644 --- a/gestao/db/models/user.py +++ b/gestao/db/models/user.py @@ -1,4 +1,5 @@ from datetime import date, datetime +from typing import Optional import ormar @@ -36,6 +37,8 @@ class Meta(BaseMeta): role: str = ormar.String(max_length=200) category: str = ormar.String(max_length=200) pattern: str = ormar.String(max_length=200) + workstation: Optional[str] = ormar.String(max_length=200, nullable=True) + nickname: Optional[str] = ormar.String(max_length=200, unique=True, nullable=True) dispatcher: str = ormar.String(max_length=200) dispatched_date: date = ormar.Date() created_at: datetime = ormar.DateTime(timezone=True, default=datetime.now) diff --git a/gestao/web/api/user/schema.py b/gestao/web/api/user/schema.py index 4086acb..fbed273 100644 --- a/gestao/web/api/user/schema.py +++ b/gestao/web/api/user/schema.py @@ -8,6 +8,10 @@ class CreateUserDependentDTO(BaseModel): name: str birth_date: date relationship: str + cpf: str + gender: str + phone: str + pensioner: Optional[str] class CreateUserDTO(BaseModel): @@ -40,6 +44,8 @@ class CreateUserDTO(BaseModel): dispatcher: str dispatched_date: date dependents: Optional[List[CreateUserDependentDTO]] + workstation: Optional[str] + nickname: Optional[str] class UpdateUserDTO(BaseModel): @@ -71,3 +77,5 @@ class UpdateUserDTO(BaseModel): pattern: Optional[str] dispatcher: Optional[str] dispatched_date: Optional[date] + workstation: Optional[str] + nickname: Optional[str] From fc18589b2dc2db4043b7dcc0800b0e85a1fd05ee Mon Sep 17 00:00:00 2001 From: Matheus Afonso Date: Sat, 28 Oct 2023 22:15:04 -0300 Subject: [PATCH 2/2] feat: add status field Co-Authored-By: Mateus Maia --- .github/workflows/tests.yml | 14 ----- README.md | 1 - .../versions/2023-10-28-21-20_05258d089f10.py | 4 ++ gestao/db/models/user.py | 1 + gestao/web/api/user/enums.py | 6 +++ gestao/web/api/user/{schema.py => schemas.py} | 1 + gestao/web/api/user/views.py | 51 +++++++++++++++---- pyproject.toml | 13 ----- 8 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 gestao/web/api/user/enums.py rename gestao/web/api/user/{schema.py => schemas.py} (98%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 03f0a88..2ad05ca 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,20 +17,6 @@ jobs: POETRY_VIRTUALENVS_CREATE: false - name: Run black check run: poetry run black --check . - mypy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Install deps - uses: knowsuchagency/poetry-install@v1 - env: - POETRY_VIRTUALENVS_CREATE: false - - name: Run mypy check - run: poetry run mypy . pytest: runs-on: ubuntu-latest services: diff --git a/README.md b/README.md index 081f927..49b1966 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,6 @@ It's configured using .pre-commit-config.yaml file. By default it runs: * black (formats your code); -* mypy (validates types); * isort (sorts imports in all files); diff --git a/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py b/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py index 0614837..3d6912c 100644 --- a/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py +++ b/gestao/db/migrations/versions/2023-10-28-21-20_05258d089f10.py @@ -23,9 +23,13 @@ def upgrade(): batch_op.add_column( sa.Column("nickname", sa.String(200), nullable=True), ) + batch_op.add_column( + sa.Column("status", sa.String(200), default="active"), + ) def downgrade(): with op.batch_alter_table("user") as batch_op: batch_op.drop_column("workstation") batch_op.drop_column("nickname") + batch_op.drop_column("status") diff --git a/gestao/db/models/user.py b/gestao/db/models/user.py index 9a8cf10..65559e5 100644 --- a/gestao/db/models/user.py +++ b/gestao/db/models/user.py @@ -37,6 +37,7 @@ class Meta(BaseMeta): role: str = ormar.String(max_length=200) category: str = ormar.String(max_length=200) pattern: str = ormar.String(max_length=200) + status: str = ormar.String(max_length=200, default="active") workstation: Optional[str] = ormar.String(max_length=200, nullable=True) nickname: Optional[str] = ormar.String(max_length=200, unique=True, nullable=True) dispatcher: str = ormar.String(max_length=200) diff --git a/gestao/web/api/user/enums.py b/gestao/web/api/user/enums.py new file mode 100644 index 0000000..430ded0 --- /dev/null +++ b/gestao/web/api/user/enums.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class UserStatus(str, Enum): + active = "active" + inactive = "inactive" diff --git a/gestao/web/api/user/schema.py b/gestao/web/api/user/schemas.py similarity index 98% rename from gestao/web/api/user/schema.py rename to gestao/web/api/user/schemas.py index fbed273..00f1acb 100644 --- a/gestao/web/api/user/schema.py +++ b/gestao/web/api/user/schemas.py @@ -79,3 +79,4 @@ class UpdateUserDTO(BaseModel): dispatched_date: Optional[date] workstation: Optional[str] nickname: Optional[str] + status: Optional[str] diff --git a/gestao/web/api/user/views.py b/gestao/web/api/user/views.py index 98ed083..a130a89 100644 --- a/gestao/web/api/user/views.py +++ b/gestao/web/api/user/views.py @@ -2,11 +2,13 @@ from typing import List from uuid import uuid4 +from asyncpg.exceptions import UniqueViolationError from fastapi import APIRouter, HTTPException from gestao.db.models.dependent import Dependent from gestao.db.models.user import User -from gestao.web.api.user.schema import CreateUserDTO, UpdateUserDTO +from gestao.web.api.user.enums import UserStatus +from gestao.web.api.user.schemas import CreateUserDTO, UpdateUserDTO router = APIRouter() @@ -16,13 +18,22 @@ async def get_users( limit: int = 10, offset: int = 0, ) -> List[User]: - return await User.objects.limit(limit).offset(offset).all() + return ( + await User.objects.limit(limit) + .offset( + offset, + ) + .filter(status=UserStatus.active) + .all() + ) @router.get("/{user_id}", response_model_exclude={"dependents__user_id"}) async def get_user(user_id: str) -> User: try: - return await User.objects.select_related(User.dependents).get(id=user_id) + return await User.objects.select_related( + User.dependents, + ).get(id=user_id) except Exception: logging.error("Error occurred while get user", exc_info=True) raise HTTPException(status_code=404, detail="User not found") @@ -34,7 +45,9 @@ async def create_user(create_user: CreateUserDTO) -> User: create_user_dict = create_user.dict() dependents = create_user_dict.pop("dependents", []) user_id = str(uuid4()) - await User.objects.create(id=user_id, **create_user_dict) + await User.objects.create( + id=user_id, **create_user_dict, status=UserStatus.active + ) if dependents: await Dependent.objects.bulk_create( [ @@ -43,6 +56,12 @@ async def create_user(create_user: CreateUserDTO) -> User: ], ) return await User.objects.select_related(User.dependents).get(id=user_id) + except UniqueViolationError: + logging.error("User already exists", exc_info=True) + raise HTTPException( + status_code=400, + detail="User already exists", + ) except Exception: logging.error("Error occurred while creating user", exc_info=True) raise HTTPException( @@ -54,16 +73,14 @@ async def create_user(create_user: CreateUserDTO) -> User: @router.put("/{user_id}", response_model_exclude={"dependents__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), - ) + user = await User.objects.get(id=user_id) + await user.update(**update_user.dict(exclude_none=True)) return await User.objects.select_related(User.dependents).get(id=user_id) except Exception: - logging.error("Error occurred while updating user", exc_info=True) + logging.error("User not found", exc_info=True) raise HTTPException( status_code=404, - detail="Error occurred while updating user", + detail="User not found", ) @@ -77,3 +94,17 @@ async def delete_user(user_id: str) -> None: status_code=404, detail="Error occurred while deleting user", ) + + +@router.patch("/{user_id}/disable") +async def disable_user(user_id: str) -> None: + try: + user = await User.objects.get(id=user_id) + await user.update(status=UserStatus.inactive) + return {"detail": "User disabled successfully"} + except Exception: + logging.error("User not found", exc_info=True) + raise HTTPException( + status_code=404, + detail="User not found", + ) diff --git a/pyproject.toml b/pyproject.toml index f33c1a0..e9e6ed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,19 +44,6 @@ profile = "black" multi_line_output = 3 src_paths = ["gestao",] -[tool.mypy] -strict = true -ignore_missing_imports = true -allow_subclassing_any = true -allow_untyped_calls = true -pretty = true -show_error_codes = true -implicit_reexport = true -allow_untyped_decorators = true -warn_unused_ignores = false -warn_return_any = false -namespace_packages = true - [tool.pytest.ini_options] filterwarnings = [ "error",