diff --git a/api/apistructs/items.py b/api/apistructs/items.py index 58f0ba6c..2fc953f7 100644 --- a/api/apistructs/items.py +++ b/api/apistructs/items.py @@ -3,31 +3,32 @@ from datetime import date from typing import Any -from pydantic import BaseModel, EmailStr, Field, HttpUrl, validator +from pydantic import field_validator, BaseModel, EmailStr, Field, HttpUrl class ProjectItem(BaseModel): ''' ProjectItem ''' id: str = Field(description='`pid`, project id') name: str = Field(description='project name') - desc: str | None = Field(description='desc') - action_date: date | None = Field(description='action date') - owners: list[str] | None = Field(description='list of owners') - calendar: str | None = Field(description='calendar url') - gitlab_project_id: str | None = Field(description='gitlab project id') + desc: str | None = Field(None, description='desc') + action_date: date | None = Field(None, description='action date') + owners: list[str] | None = Field(None, description='list of owners') + calendar: str | None = Field(None, description='calendar url') + gitlab_project_id: str | None = Field(None, description='gitlab project id') mailling_leader: EmailStr | None = Field( - description='mailing list of leader') + None, description='mailing list of leader') mailling_staff: EmailStr | None = Field( - description='mailing list of staff') + None, description='mailing list of staff') mattermost_ch_id: str | None = Field( - description='Mattermost main channel id') - shared_drive: HttpUrl | None = Field(description='Google shared drive') + None, description='Mattermost main channel id') + shared_drive: HttpUrl | None = Field(None, description='Google shared drive') traffic_fee_doc: HttpUrl | None = Field( - description='doc fields for traffic fee') + None, description='doc fields for traffic fee') volunteer_certificate_hours: int | None = Field( - description='hours for volunteer certificate') + None, description='hours for volunteer certificate') - @validator('*', pre=True) + @field_validator('*', mode="before") + @classmethod def skip_empty_str(cls, value: Any) -> Any: # pylint:disable=no-self-argument ''' skip empty string ''' if isinstance(value, str): @@ -44,13 +45,14 @@ class TeamItem(BaseModel): pid: str = Field(description='`pid`, project id') id: str = Field(description='`tid`, team id') name: str = Field(description='team name') - chiefs: list[str] | None = Field(description="list of chiefs' uids") - members: list[str] | None = Field(description="list of members' uids") - desc: str | None = Field(description='desc') - mailling: EmailStr | None = Field(description='mailing list for team') - headcount: int | None = Field(description='the headcount of team') + chiefs: list[str] | None = Field(None, description="list of chiefs' uids") + members: list[str] | None = Field(None, description="list of members' uids") + desc: str | None = Field(None, description='desc') + mailling: EmailStr | None = Field(None, description='mailing list for team') + headcount: int | None = Field(None, description='the headcount of team') - @validator('*', pre=True) + @field_validator('*', mode="before") + @classmethod def skip_empty_str(cls, value: Any) -> Any: # pylint:disable=no-self-argument ''' skip empty string ''' if isinstance(value, str): @@ -72,6 +74,6 @@ class UserItem(BaseModel): id: str = Field(description='user id') badge_name: str = Field(description='badge name') avatar: HttpUrl = Field(description='url for avatar') - intro: str | None = Field(description='introduction') - chat: MattermostAccount | None = Field(description='Mattermost account') - is_chief: bool | None = Field(description='is the chief in the team') + intro: str | None = Field(None, description='introduction') + chat: MattermostAccount | None = Field(None, description='Mattermost account') + is_chief: bool | None = Field(None, description='is the chief in the team') diff --git a/api/apistructs/projects.py b/api/apistructs/projects.py index 636e75cf..e8d24f9b 100644 --- a/api/apistructs/projects.py +++ b/api/apistructs/projects.py @@ -2,7 +2,7 @@ from datetime import date, datetime import arrow -from pydantic import BaseModel, EmailStr, Field, HttpUrl, validator +from pydantic import field_validator, BaseModel, EmailStr, Field, HttpUrl from api.apistructs.items import ProjectItem, TeamItem from structs.projects import ProjectTrafficLocationFeeItem @@ -29,7 +29,8 @@ class ProjectCreateOutput(ProjectCreate): ''' Project create output ''' pid: str = Field(description='project id', alias='_id') - @validator('action_date', pre=True) + @field_validator('action_date', mode="before") + @classmethod def convert_action_date(cls, value: datetime) -> str: # pylint: disable=no-self-argument ''' convert_action_date ''' return arrow.get(value).format('YYYY/MM/DD') @@ -37,28 +38,29 @@ def convert_action_date(cls, value: datetime) -> str: # pylint: disable=no-self class ProjectItemUpdateInput(BaseModel): ''' Update project item input ''' - name: str | None = Field(description='project name') - desc: str | None = Field(description='desc') - action_date: date | None = Field(description='action date') - calendar: str | None = Field(description='calendar url') - gitlab_project_id: str | None = Field(description='gitlab project id') + name: str | None = Field(None, description='project name') + desc: str | None = Field(None, description='desc') + action_date: date | None = Field(None, description='action date') + calendar: str | None = Field(None, description='calendar url') + gitlab_project_id: str | None = Field(None, description='gitlab project id') mailling_leader: EmailStr | None = Field( - description='mailing list of leader') + None, description='mailing list of leader') mailling_staff: EmailStr | None = Field( - description='mailing list of staff') + None, description='mailing list of staff') mattermost_ch_id: str | None = Field( - description='Mattermost main channel id') - shared_drive: HttpUrl | None = Field(description='Google shared drive') + None, description='Mattermost main channel id') + shared_drive: HttpUrl | None = Field(None, description='Google shared drive') traffic_fee_doc: HttpUrl | None = Field( - description='doc fields for traffic fee') + None, description='doc fields for traffic fee') volunteer_certificate_hours: int | None = Field( - description='hours for volunteer certificate') + None, description='hours for volunteer certificate') class ProjectItemUpdateOutput(ProjectItemUpdateInput): ''' Update project item output ''' - @validator('action_date', pre=True) + @field_validator('action_date', mode="before") + @classmethod def convert_action_date(cls, value: str | int | float) -> date: # pylint:disable=no-self-argument ''' convert action_date to date ''' return arrow.get(value).date() diff --git a/api/apistructs/sender.py b/api/apistructs/sender.py index 72fd2b4f..1820a575 100644 --- a/api/apistructs/sender.py +++ b/api/apistructs/sender.py @@ -3,7 +3,7 @@ from typing import Any import arrow -from pydantic import BaseModel, Field, validator +from pydantic import field_validator, BaseModel, Field class SenderCampaignInput(BaseModel): @@ -18,7 +18,8 @@ class Creater(BaseModel): uid: str = Field(description='user id') at: datetime = Field(description='created at in timestamp') - @validator('at', pre=True) + @field_validator('at', mode="before") + @classmethod def convert_to_datetime(cls, value: int | float | datetime) -> datetime: # pylint: disable=no-self-argument ''' convert to datetime ''' return arrow.get(value).naive @@ -41,7 +42,8 @@ class Receiver(BaseModel): all_users: bool = Field( default=False, description='Send to all users in platform') - @validator('team_w_tags', pre=True) + @field_validator('team_w_tags', mode="before") + @classmethod def convert_to_list(cls, # pylint: disable=no-self-argument value: Any | list[dict[str, str | list[str]]]) -> list[ dict[str, str | list[str]]]: diff --git a/api/apistructs/tasks.py b/api/apistructs/tasks.py index f4c21a73..712b4b90 100644 --- a/api/apistructs/tasks.py +++ b/api/apistructs/tasks.py @@ -1,7 +1,7 @@ ''' API Structs - Tasks ''' from datetime import datetime -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from api.apistructs.items import UserItem from structs.tasks import TaskItem @@ -23,11 +23,7 @@ class TaskCreateInput(BaseModel): endtime: datetime = Field( default_factory=datetime.now, description='task end') limit: int = Field(default=1, ge=1, description='expect required users') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True - validate_assignment = True + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True) class TaskCreateOutput(TaskItem): @@ -40,25 +36,19 @@ class TaskGetOutput(TaskItem): class TaskUpdateInput(BaseModel): ''' TaskUpdateInput ''' - title: str | None = Field(description='title') - cate: str | None = Field(description='cate') - desc: str | None = Field(description='desc') - starttime: datetime | None = Field(description='task start') - endtime: datetime | None = Field(description='task end') - limit: int | None = Field(description='expect required users') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True + title: str | None = Field(None, description='title') + cate: str | None = Field(None, description='cate') + desc: str | None = Field(None, description='desc') + starttime: datetime | None = Field(None, description='task start') + endtime: datetime | None = Field(None, description='task end') + limit: int | None = Field(None, description='expect required users') + model_config = ConfigDict(str_strip_whitespace=True) class TaskAttendeeInput(BaseModel): ''' TaskAttendeeInput ''' uids: list[str] = Field(description='uids') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True + model_config = ConfigDict(str_strip_whitespace=True) class TaskGetAttendeeOutput(BaseModel): diff --git a/api/apistructs/teams.py b/api/apistructs/teams.py index 4e2b2bcb..5999fbc2 100644 --- a/api/apistructs/teams.py +++ b/api/apistructs/teams.py @@ -1,19 +1,20 @@ ''' API Structs - Teams ''' from typing import Any -from pydantic import BaseModel, EmailStr, Field, validator +from pydantic import field_validator, ConfigDict, BaseModel, EmailStr, Field from api.apistructs.items import UserItem class TeamItemUpdateInput(BaseModel): ''' Update Team item input ''' - name: str | None = Field(description='team name') - desc: str | None = Field(description='desc') - mailling: EmailStr | None = Field(description='mailing list for team') - headcount: int | None = Field(description='the headcount of team') + name: str | None = Field(None, description='team name') + desc: str | None = Field(None, description='desc') + mailling: EmailStr | None = Field(None, description='mailing list for team') + headcount: int | None = Field(None, description='the headcount of team') - @validator('*', pre=True) + @field_validator('*', mode="before") + @classmethod def skip_empty_str(cls, value: Any) -> Any: # pylint:disable=no-self-argument ''' skip empty string ''' if isinstance(value, str): @@ -37,10 +38,7 @@ class TeamCreateInput(BaseModel): ''' Team create input ''' id: str = Field(description='team id') name: str = Field(description='team name') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True + model_config = ConfigDict(str_strip_whitespace=True) class TeamCreateOutput(TeamCreateInput): @@ -50,10 +48,7 @@ class TeamCreateOutput(TeamCreateInput): class TeamUpdateMembers(BaseModel): ''' TeamUpdateMembers ''' uids: list[str] = Field(description='uids') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True + model_config = ConfigDict(str_strip_whitespace=True) class TeamUpdateMembersOutput(BaseModel): diff --git a/api/apistructs/users.py b/api/apistructs/users.py index bc5ee07a..5f50b9e8 100644 --- a/api/apistructs/users.py +++ b/api/apistructs/users.py @@ -1,7 +1,7 @@ ''' API Structs - Users ''' from datetime import date -from pydantic import BaseModel, Field, HttpUrl +from pydantic import ConfigDict, BaseModel, Field, HttpUrl from api.apistructs.items import ProjectItem, TeamItem from module.skill import SkillEnum, StatusEnum, TeamsEnum, TobeVolunteerStruct @@ -75,7 +75,7 @@ class UserMeDietaryHabitInput(BaseModel): ''' UserMeDietaryHabitInput ''' checked: list[str] = Field( description='lists value of `DietaryHabitItemsValue`', - example=['0.001', '0.002'], + examples=[['0.001', '0.002']], ) @@ -116,11 +116,7 @@ class UserMeToBeVolunteerInput(BaseModel): hours: int = Field(description='Hours in an week') status: StatusEnum = Field(description='status') desc: str = Field(default='', description='more description') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace: bool = True - use_enum_values: bool = True + model_config = ConfigDict() class UserMeToBeVolunteerOutput(BaseModel): diff --git a/module/api_token.py b/module/api_token.py index 0c5a256a..7f9e3121 100644 --- a/module/api_token.py +++ b/module/api_token.py @@ -5,7 +5,7 @@ import arrow from passlib.context import CryptContext # type: ignore -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field from models.api_tokendb import APITokenDB @@ -22,10 +22,7 @@ class APITokenBase(BaseModel): create_at: datetime = Field(default_factory=datetime.now) alive: bool = Field(default=True) token_type: APITokenType - - class Config: # pylint: disable=too-few-public-methods - ''' config ''' - use_enum_values = True + model_config = ConfigDict(use_enum_values=True) class APITokenTemp(APITokenBase): diff --git a/module/budget.py b/module/budget.py index 4eca5d32..9075d1e6 100644 --- a/module/budget.py +++ b/module/budget.py @@ -4,7 +4,7 @@ from typing import Any, Optional, Union import arrow -from pydantic import BaseModel, error_wrappers, validator +from pydantic import field_validator, ConfigDict, BaseModel, error_wrappers from pymongo.cursor import Cursor from models.budgetdb import BudgetDB @@ -64,12 +64,10 @@ class BudgetImportItem(BaseModel): currency: Currency paydate: str estimate: str + model_config = ConfigDict(use_enum_values=True) - class Config: # pylint: disable=too-few-public-methods - ''' Model config ''' - use_enum_values = True - - @validator('total') + @field_validator('total') + @classmethod def verify_total(cls, value: str) -> Union[int, float]: ''' verify total. @@ -85,7 +83,8 @@ def verify_total(cls, value: str) -> Union[int, float]: return int(value) - @validator('paydate') + @field_validator('paydate') + @classmethod def verify_paydate(cls, value: str, **kwargs: Any) -> str: ''' verify paydate diff --git a/module/skill.py b/module/skill.py index dfcbfd2b..985551fe 100644 --- a/module/skill.py +++ b/module/skill.py @@ -2,7 +2,7 @@ # pylint: disable=too-few-public-methods from enum import Enum, IntEnum, unique -from pydantic import BaseModel, Field +from pydantic import ConfigDict, BaseModel, Field @unique @@ -121,13 +121,9 @@ class TobeVolunteerStruct(BaseModel): skill: list[SkillEnum] = Field( default_factory=list, description='list of skills') hours: int = Field(default=0, description='Hours in an week') - status: StatusEnum | None = Field(description='status') + status: StatusEnum | None = Field(None, description='status') desc: str = Field(default='', description='more description') - - class Config: - ''' Config ''' - anystr_strip_whitespace: bool = True - use_enum_values: bool = True + model_config = ConfigDict() class RecruitQuery(BaseModel): @@ -145,7 +141,4 @@ class RecruitQuery(BaseModel): default_factory=list, description='list of skills') status: list[StatusEnum] = Field( default_factory=list, description='list of status') - - class Config: - ''' Config ''' - use_enum_values = True + model_config = ConfigDict(use_enum_values=True) diff --git a/poetry.lock b/poetry.lock index 33f18482..70f4c348 100644 --- a/poetry.lock +++ b/poetry.lock @@ -265,6 +265,23 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > [package.extras] crt = ["awscrt (==0.23.4)"] +[[package]] +name = "bump-pydantic" +version = "0.8.0" +description = "Convert Pydantic from V1 to V2 ♻" +optional = false +python-versions = ">=3.8" +files = [ + {file = "bump_pydantic-0.8.0-py3-none-any.whl", hash = "sha256:6cbb4deb5869a69baa5a477f28f3e2d8fb09b687e114c018bd54470590ae7bf7"}, + {file = "bump_pydantic-0.8.0.tar.gz", hash = "sha256:6092e61930e85619e74eeb04131b4387feda16f02d8bb2e3cf9507fa492c69e9"}, +] + +[package.dependencies] +libcst = ">=0.4.2" +rich = "*" +typer = ">=0.7.0" +typing-extensions = "*" + [[package]] name = "cachetools" version = "5.5.0" @@ -1215,6 +1232,57 @@ sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] yaml = ["PyYAML (>=3.10)"] zookeeper = ["kazoo (>=2.8.0)"] +[[package]] +name = "libcst" +version = "1.6.0" +description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "libcst-1.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f02d0da6dfbad44e6ec4d1e5791e17afe95d9fe89bce4374bf109fd9c103a50"}, + {file = "libcst-1.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48406225378ee9208edb1e5a10451bea810262473af1a2f2473737fd16d34e3a"}, + {file = "libcst-1.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf59a21e9968dc4e7c301fac660bf54bc7d4dcadc0b1abf31b1cac34e800555"}, + {file = "libcst-1.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d65550ac686bff9395398afacbc88fe812363703a4161108e8a6db066d30b96e"}, + {file = "libcst-1.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5ac6d68364031f0b554d8920a69b33f25ec6ef351fa31b4e8f3676abb729ce36"}, + {file = "libcst-1.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0c0fb2f7b74605832cc38d79e9d104f92a8aaeec7bf8f2759b20c5ba3786a321"}, + {file = "libcst-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:1bd11863889b630fe41543b4eb5e2dd445447a7f89e6b58229e83c9e52a74942"}, + {file = "libcst-1.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a9e71a046b4a91950125967f5ee67389f25a2511103e5595508f0591a5f50bc0"}, + {file = "libcst-1.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df3f452e074893dfad7746a041caeb3cde75bd9fbca4ea7b223012e112d1da8c"}, + {file = "libcst-1.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31e45f88d4a9a8e5b690ed14a564fcbace14b10f5e7b6797d6d97f4226b395da"}, + {file = "libcst-1.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1bd00399d20bf93590b6f02647f8be08e2b730e050e6b7360f669254e69c98f5"}, + {file = "libcst-1.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d25132f24edc24895082589645dbb8972c0eff6c9716ff71932fa72643d7c74f"}, + {file = "libcst-1.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:38f3f25d4f5d8713cdb6a7bd41d75299de3c2416b9890a34d9b05417b8e64c1d"}, + {file = "libcst-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:91242ccbae6e7a070b33ebe03d3677c54bf678653538fbaa89597a59e4a13b2d"}, + {file = "libcst-1.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd2b28688dabf0f7a166b47ab1c7d5c0b6ef8c9a05ad932618471a33fe591a4a"}, + {file = "libcst-1.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a12a4766ce5874ccb31a1cc095cff47e2fb35755954965fe77458d9e5b361a8"}, + {file = "libcst-1.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfcd78a5e775f155054ed50d047a260cd23f0f6a89ef2a57e10bdb9c697680b8"}, + {file = "libcst-1.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5786240358b122ad901bb0b7e6b7467085b2317333233d7c7d7cac46388fbd77"}, + {file = "libcst-1.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c527472093b5b64ffa65d33c472da38952827abbca18c786d559d6d6122bc891"}, + {file = "libcst-1.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:63a8893dfc344b9b08bfaf4e433b16a7e2e9361f8362fa73eaecc4d379c328ba"}, + {file = "libcst-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:4cd011fcd79b76be216440ec296057780223674bc2566662c4bc50d3c5ecd58e"}, + {file = "libcst-1.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:96506807dc01c9efcea8ab57d9ea18fdc87b85514cc8ee2f8568fab6df861f02"}, + {file = "libcst-1.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dac722aade8796a1e78662c3ed424f0ab9f1dc0e8fdf3088610354cdd709e53f"}, + {file = "libcst-1.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8370d0f7092a17b7fcda0e1539d0162cf35a0c19af94842b09c9dddc382acd"}, + {file = "libcst-1.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e4fcd791cab0fe8287b6edd0d78512b6475b87d906562a5d2d0999cb6d23b8d"}, + {file = "libcst-1.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3fb953fc0155532f366ff40f6a23f191250134d6928e02074ae4eb3531fa6c30"}, + {file = "libcst-1.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2f3c85602e5a6d3aec0a8fc74230363f943004d7c2b2a6a1c09b320b61692241"}, + {file = "libcst-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:c4486921bebd33d67bbbd605aff8bfaefd2d13dc73c20c1fde2fb245880b7fd6"}, + {file = "libcst-1.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b3d274115d134a550fe8a0b38780a28a659d4a35ac6068c7c92fffe6661b519c"}, + {file = "libcst-1.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d45513f6cd3dbb2a80cf21a53bc6e6e560414edea17c474c784100e10aebe921"}, + {file = "libcst-1.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8c70a124d7a7d326abdc9a6261013c57d36f21c6c6370de5dd3e6a040c4ee5e"}, + {file = "libcst-1.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc95df61838d708adb37e18af1615491f6cac59557fd11077664dd956fe4528"}, + {file = "libcst-1.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:05c32de72553cb93ff606c7d2421ce1eab1f0740c8c4b715444e2ae42f42b1b6"}, + {file = "libcst-1.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69b705f5b1faa66f115ede52a970d7613d3a8fb988834f853f7fb46870a041d2"}, + {file = "libcst-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:984512829a80f963bfc1803342219a4264a8d4206df0a30eae9bce921357a938"}, + {file = "libcst-1.6.0.tar.gz", hash = "sha256:e80ecdbe3fa43b3793cae8fa0b07a985bd9a693edbe6e9d076f5422ecadbf0db"}, +] + +[package.dependencies] +pyyaml = ">=5.2" + +[package.extras] +dev = ["Sphinx (>=5.1.1)", "black (==24.8.0)", "build (>=0.10.0)", "coverage[toml] (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.1.1)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.5)", "jupyter (>=1.0.0)", "maturin (>=1.7.0,<1.8)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools_scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.8.0)", "usort (==1.0.8.post1)"] + [[package]] name = "lxml" version = "5.3.0" @@ -1384,6 +1452,30 @@ files = [ docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + [[package]] name = "markupsafe" version = "3.0.2" @@ -1465,6 +1557,17 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mergedeep" version = "1.3.4" @@ -2673,6 +2776,24 @@ requests = ">=2.0.0" [package.extras] rsa = ["oauthlib[signedtoken] (>=3.0.0)"] +[[package]] +name = "rich" +version = "13.9.4" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"}, + {file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "rsa" version = "4.7.2" @@ -2714,6 +2835,17 @@ files = [ {file = "sentinels-1.0.0.tar.gz", hash = "sha256:7be0704d7fe1925e397e92d18669ace2f619c92b5d4eb21a89f31e026f9ff4b1"}, ] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + [[package]] name = "six" version = "1.17.0" @@ -2810,6 +2942,23 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] +[[package]] +name = "typer" +version = "0.15.1" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.15.1-py3-none-any.whl", hash = "sha256:7994fb7b8155b64d3402518560648446072864beefd44aa2dc36972a5972e847"}, + {file = "typer-0.15.1.tar.gz", hash = "sha256:a0588c0a7fa68a1978a069818657778f86abe6ff5ea6abf472f940a08bfe4f0a"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + [[package]] name = "types-markdown" version = "3.7.0.20241204" @@ -3235,4 +3384,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "~3.11.7" -content-hash = "74071af36c0ed54cacfa3f6a02a9575b2a9268824540353f376fee3612decde9" +content-hash = "7e76795dde0818b69f916982e4d1aef7d6fe80ae94cea8626ed4c73507c095a3" diff --git a/pyproject.toml b/pyproject.toml index 9463aa6a..bd622310 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ pytest = "^8" pytest-cov = "^6.0.0" types-markdown = "^3.4.2.1" types-requests = "^2.29" +bump-pydantic = "^0.8.0" [build-system] build-backend = "poetry.core.masonry.api" diff --git a/structs/projects.py b/structs/projects.py index 2a37c07e..7e3e8191 100644 --- a/structs/projects.py +++ b/structs/projects.py @@ -3,7 +3,7 @@ from typing import Any import arrow -from pydantic import BaseModel, EmailStr, Field, HttpUrl, validator +from pydantic import ConfigDict, BaseModel, EmailStr, Field, HttpUrl, validator def skip_empty_str(value: Any) -> Any: @@ -39,18 +39,18 @@ class ProjectBase(BaseModel): name: str = Field(description='project name') owners: list[str] = Field(description='list of owners') action_date: datetime = Field(description='action date') - desc: str | None = Field(description='desc') - calendar: str | None = Field(description='calendar url') - gitlab_project_id: str | None = Field(description='gitlab project id') + desc: str | None = Field(None, description='desc') + calendar: str | None = Field(None, description='calendar url') + gitlab_project_id: str | None = Field(None, description='gitlab project id') mailling_leader: EmailStr | None = Field( - description='mailing list of leader') + None, description='mailing list of leader') mailling_staff: EmailStr | None = Field( - description='mailing list of staff') + None, description='mailing list of staff') mattermost_ch_id: str | None = Field( - description='Mattermost main channel id') - shared_drive: HttpUrl | None = Field(description='Google shared drive') + None, description='Mattermost main channel id') + shared_drive: HttpUrl | None = Field(None, description='Google shared drive') traffic_fee_doc: HttpUrl | None = Field( - description='doc fields for traffic fee') + None, description='doc fields for traffic fee') volunteer_certificate_hours: int = Field( default=16, ge=0, description='hours for volunteer certificate') @@ -67,20 +67,20 @@ class ProjectBase(BaseModel): class ProjectBaseUpdate(BaseModel): ''' ProjectBaseUpdate ''' - name: str | None = Field(description='project name') - action_date: datetime | None = Field(description='action date') - desc: str | None = Field(description='desc') - calendar: str | None = Field(description='calendar url') - gitlab_project_id: str | None = Field(description='gitlab project id') + name: str | None = Field(None, description='project name') + action_date: datetime | None = Field(None, description='action date') + desc: str | None = Field(None, description='desc') + calendar: str | None = Field(None, description='calendar url') + gitlab_project_id: str | None = Field(None, description='gitlab project id') mailling_leader: EmailStr | None = Field( - description='mailing list of leader') + None, description='mailing list of leader') mailling_staff: EmailStr | None = Field( - description='mailing list of staff') + None, description='mailing list of staff') mattermost_ch_id: str | None = Field( - description='Mattermost main channel id') - shared_drive: HttpUrl | None = Field(description='Google shared drive') + None, description='Mattermost main channel id') + shared_drive: HttpUrl | None = Field(None, description='Google shared drive') traffic_fee_doc: HttpUrl | None = Field( - description='doc fields for traffic fee') + None, description='doc fields for traffic fee') volunteer_certificate_hours: int = Field( default=16, ge=0, description='hours for volunteer certificate') @@ -99,11 +99,7 @@ class ProjectTrafficLocationFeeItem(BaseModel): ''' ProjectTrafficLocationFeeItem ''' location: str = Field(default='', description='location') fee: int = Field(default=0, description='fee in TWD', gt=0) - - class Config: - ''' Config ''' - # pylint: disable=too-few-public-methods - anystr_strip_whitespace = True + model_config = ConfigDict(str_strip_whitespace=True) class ProjectTrafficLocationFee(BaseModel): diff --git a/structs/tasks.py b/structs/tasks.py index e21b63d6..3236773a 100644 --- a/structs/tasks.py +++ b/structs/tasks.py @@ -4,7 +4,7 @@ from uuid import uuid4 import arrow -from pydantic import BaseModel, Field, validator +from pydantic import ConfigDict, BaseModel, Field, validator def convert_datetime(value: Any) -> datetime: @@ -39,8 +39,4 @@ class TaskItem(BaseModel): _validate_convert_datetime = validator( 'created_at', 'starttime', 'endtime', pre=True, allow_reuse=True, always=True)(convert_datetime) - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True - validate_assignment = True + model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True) diff --git a/structs/teams.py b/structs/teams.py index 0e4b6852..eed585c7 100644 --- a/structs/teams.py +++ b/structs/teams.py @@ -1,7 +1,7 @@ ''' Teams Structs ''' from datetime import datetime -from pydantic import BaseModel, EmailStr, Field, validator +from pydantic import field_validator, ConfigDict, BaseModel, EmailStr, Field class TagMembers(BaseModel): @@ -15,21 +15,18 @@ class TeamBase(BaseModel): id: str = Field(description='`tid`, team id', alias='tid') pid: str = Field(description='`pid`, project id') name: str = Field(description='team name') - owners: list[str] | None = Field(description="list of owners' uids") - chiefs: list[str] | None = Field(description="list of chiefs' uids") - members: list[str] | None = Field(description="list of members' uids") - desc: str | None = Field(description='desc') - mailling: EmailStr | None = Field(description='mailing list for team') - headcount: int | None = Field(description='the headcount of team') - public_desc: str | None = Field(description='public desc for not members') + owners: list[str] | None = Field(None, description="list of owners' uids") + chiefs: list[str] | None = Field(None, description="list of chiefs' uids") + members: list[str] | None = Field(None, description="list of members' uids") + desc: str | None = Field(None, description='desc') + mailling: EmailStr | None = Field(None, description='mailing list for team') + headcount: int | None = Field(None, description='the headcount of team') + public_desc: str | None = Field(None, description='public desc for not members') disabled: bool = Field(default=False, description='disabled the team') tag_members: list[TagMembers] | None = Field( - description='tags for members') - created_at: datetime | None = Field(description='create at') - - class Config: # pylint: disable=too-few-public-methods - ''' Config ''' - anystr_strip_whitespace = True + None, description='tags for members') + created_at: datetime | None = Field(None, description='create at') + model_config = ConfigDict(str_strip_whitespace=True) class TeamUsers(BaseModel): @@ -41,7 +38,8 @@ class TeamUsers(BaseModel): members: list[str] = Field( default_factory=list, description="list of members' uids") - @validator('*', pre=True) + @field_validator('*', mode="before") + @classmethod def process_none(cls, value: list[str] | None) -> list[str]: # pylint: disable=no-self-argument ''' process none ''' if value is None: diff --git a/structs/users.py b/structs/users.py index bcd5218c..bce9544e 100644 --- a/structs/users.py +++ b/structs/users.py @@ -4,7 +4,7 @@ from time import time from typing import Optional -from pydantic import BaseModel, EmailStr, Field +from pydantic import ConfigDict, BaseModel, EmailStr, Field from module.dietary_habit import DietaryHabitItemsValue @@ -17,17 +17,9 @@ class UserProfle(BaseModel): intro: introduction. ''' - badge_name: str = Field(default='', example='Badge Name') + badge_name: str = Field(default='', examples=['Badge Name']) intro: str = Field(default='', description='Markdown format') - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - - ''' - anystr_strip_whitespace: bool = True + model_config = ConfigDict() class UserBank(BaseModel): @@ -44,15 +36,7 @@ class UserBank(BaseModel): no: str = Field(default='', description='bank account numbers') branch: str = Field(default='', description='bank branch name') name: str = Field(default='', description='bank account name') - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - - ''' - anystr_strip_whitespace: bool = True + model_config = ConfigDict() class UserAddress(BaseModel): @@ -67,15 +51,7 @@ class UserAddress(BaseModel): code: str = Field(default='') receiver: str = Field(default='') address: str = Field(default='') - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - - ''' - anystr_strip_whitespace: bool = True + model_config = ConfigDict() class UserProfleRealBase(BaseModel): @@ -92,15 +68,7 @@ class UserProfleRealBase(BaseModel): phone: str = Field(default='') roc_id: str = Field(default='') company: str = Field(default='') - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - - ''' - anystr_strip_whitespace: bool = True + model_config = ConfigDict() class UserProfleReal(UserProfleRealBase): @@ -118,17 +86,7 @@ class UserProfleReal(UserProfleRealBase): address: Optional[UserAddress] = Field(default_factory=UserAddress) dietary_habit: Optional[list[DietaryHabitItemsValue]] = Field( default_factory=list) - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - use_enum_values: `True` - - ''' - anystr_strip_whitespace: bool = True - use_enum_values: bool = True + model_config = ConfigDict() class User(BaseModel): @@ -149,17 +107,9 @@ class User(BaseModel): id: str = Field(..., alias='_id') created_at: int = Field(default_factory=lambda: int(time())) mail: EmailStr = Field(..., description="User's mail") - profile: Optional[UserProfle] - profile_real: Optional[UserProfleReal] - - class Config: # pylint: disable=too-few-public-methods - ''' Config - - Attributes: - anystr_strip_whitespace: `True` - - ''' - anystr_strip_whitespace: bool = True + profile: Optional[UserProfle] = None + profile_real: Optional[UserProfleReal] = None + model_config = ConfigDict() class PolicyType(str, Enum): @@ -181,7 +131,4 @@ class PolicySigned(BaseModel): type: PolicyType = Field(description='Policy type') sign_at: datetime = Field( description='The policy signed at', default_factory=datetime.now) - - class Config: # pylint: disable=too-few-public-methods - ''' Config''' - use_enum_values = True + model_config = ConfigDict(use_enum_values=True)