diff options
| author | 2024-07-07 02:29:26 +0100 | |
|---|---|---|
| committer | 2024-07-08 15:00:10 +0100 | |
| commit | d0e09d2ba567f23d91ac76d1844966bafb9b063a (patch) | |
| tree | 9e825e3f09df02ab32e401c7e9555df26356dd4c /backend/models | |
| parent | Change linting config to Ruff (diff) | |
Apply fixable lint settings with Ruff
Diffstat (limited to 'backend/models')
| -rw-r--r-- | backend/models/__init__.py | 8 | ||||
| -rw-r--r-- | backend/models/discord_role.py | 14 | ||||
| -rw-r--r-- | backend/models/discord_user.py | 30 | ||||
| -rw-r--r-- | backend/models/form.py | 98 | ||||
| -rw-r--r-- | backend/models/form_response.py | 15 | ||||
| -rw-r--r-- | backend/models/question.py | 33 | 
6 files changed, 99 insertions, 99 deletions
| diff --git a/backend/models/__init__.py b/backend/models/__init__.py index a9f76e0..336e28b 100644 --- a/backend/models/__init__.py +++ b/backend/models/__init__.py @@ -7,13 +7,13 @@ from .question import CodeQuestion, Question  __all__ = [      "AntiSpam", +    "CodeQuestion", +    "DiscordMember",      "DiscordRole",      "DiscordUser", -    "DiscordMember",      "Form", +    "FormList",      "FormResponse", -    "CodeQuestion",      "Question", -    "FormList", -    "ResponseList" +    "ResponseList",  ] diff --git a/backend/models/discord_role.py b/backend/models/discord_role.py index ada35ef..195f557 100644 --- a/backend/models/discord_role.py +++ b/backend/models/discord_role.py @@ -1,13 +1,11 @@ -import typing -  from pydantic import BaseModel  class RoleTags(BaseModel):      """Meta information about a discord role.""" -    bot_id: typing.Optional[str] -    integration_id: typing.Optional[str] +    bot_id: str | None +    integration_id: str | None      premium_subscriber: bool      def __init__(self, **data) -> None: @@ -20,7 +18,7 @@ class RoleTags(BaseModel):          We manually parse the raw data to determine if the field exists, and give it a useful          bool value.          """ -        data["premium_subscriber"] = "premium_subscriber" in data.keys() +        data["premium_subscriber"] = "premium_subscriber" in data          super().__init__(**data) @@ -31,10 +29,10 @@ class DiscordRole(BaseModel):      name: str      color: int      hoist: bool -    icon: typing.Optional[str] -    unicode_emoji: typing.Optional[str] +    icon: str | None +    unicode_emoji: str | None      position: int      permissions: str      managed: bool      mentionable: bool -    tags: typing.Optional[RoleTags] +    tags: RoleTags | None diff --git a/backend/models/discord_user.py b/backend/models/discord_user.py index 0eca15b..be10672 100644 --- a/backend/models/discord_user.py +++ b/backend/models/discord_user.py @@ -11,15 +11,15 @@ class _User(BaseModel):      username: str      id: str      discriminator: str -    avatar: t.Optional[str] -    bot: t.Optional[bool] -    system: t.Optional[bool] -    locale: t.Optional[str] -    verified: t.Optional[bool] -    email: t.Optional[str] -    flags: t.Optional[int] -    premium_type: t.Optional[int] -    public_flags: t.Optional[int] +    avatar: str | None +    bot: bool | None +    system: bool | None +    locale: str | None +    verified: bool | None +    email: str | None +    flags: int | None +    premium_type: int | None +    public_flags: int | None  class DiscordUser(_User): @@ -33,16 +33,16 @@ class DiscordMember(BaseModel):      """A discord guild member."""      user: _User -    nick: t.Optional[str] -    avatar: t.Optional[str] +    nick: str | None +    avatar: str | None      roles: list[str]      joined_at: datetime.datetime -    premium_since: t.Optional[datetime.datetime] +    premium_since: datetime.datetime | None      deaf: bool      mute: bool -    pending: t.Optional[bool] -    permissions: t.Optional[str] -    communication_disabled_until: t.Optional[datetime.datetime] +    pending: bool | None +    permissions: str | None +    communication_disabled_until: datetime.datetime | None      def dict(self, *args, **kwargs) -> dict[str, t.Any]:          """Convert the model to a python dict, and encode timestamps in a serializable format.""" diff --git a/backend/models/form.py b/backend/models/form.py index 10c8bfd..3db267e 100644 --- a/backend/models/form.py +++ b/backend/models/form.py @@ -5,6 +5,7 @@ from pydantic import BaseModel, Field, constr, root_validator, validator  from pydantic.error_wrappers import ErrorWrapper, ValidationError  from backend.constants import DISCORD_GUILD, FormFeatures, WebHook +  from .question import Question  PUBLIC_FIELDS = [ @@ -14,20 +15,22 @@ PUBLIC_FIELDS = [      "name",      "description",      "submitted_text", -    "discord_role" +    "discord_role",  ]  class _WebHook(BaseModel):      """Schema model of discord webhooks.""" +      url: str -    message: t.Optional[str] +    message: str | None      @validator("url")      def validate_url(cls, url: str) -> str:          """Validates URL parameter."""          if "discord.com/api/webhooks/" not in url: -            raise ValueError("URL must be a discord webhook.") +            msg = "URL must be a discord webhook." +            raise ValueError(msg)          return url @@ -40,56 +43,55 @@ class Form(BaseModel):      questions: list[Question]      name: str      description: str -    submitted_text: t.Optional[str] = None +    submitted_text: str | None = None      webhook: _WebHook = None -    discord_role: t.Optional[str] -    response_readers: t.Optional[list[str]] -    editors: t.Optional[list[str]] +    discord_role: str | None +    response_readers: list[str] | None +    editors: list[str] | None      class Config:          allow_population_by_field_name = True      @validator("features") -    def validate_features(cls, value: list[str]) -> t.Optional[list[str]]: +    def validate_features(cls, value: list[str]) -> list[str]:          """Validates is all features in allowed list."""          # Uppercase everything to avoid mixed case in DB          value = [v.upper() for v in value]          allowed_values = [v.value for v in FormFeatures.__members__.values()]          if any(v not in allowed_values for v in value): -            raise ValueError("Form features list contains one or more invalid values.") +            msg = "Form features list contains one or more invalid values." +            raise ValueError(msg)          if FormFeatures.REQUIRES_LOGIN.value not in value:              if FormFeatures.COLLECT_EMAIL.value in value: -                raise ValueError( -                    "COLLECT_EMAIL feature require REQUIRES_LOGIN feature." -                ) +                msg = "COLLECT_EMAIL feature require REQUIRES_LOGIN feature." +                raise ValueError(msg)              if FormFeatures.ASSIGN_ROLE.value in value: -                raise ValueError("ASSIGN_ROLE feature require REQUIRES_LOGIN feature.") +                msg = "ASSIGN_ROLE feature require REQUIRES_LOGIN feature." +                raise ValueError(msg)          return value      @validator("response_readers", "editors") -    def validate_role_scoping(cls, value: t.Optional[list[str]]) -> t.Optional[list[str]]: +    def validate_role_scoping(cls, value: list[str] | None) -> list[str]:          """Ensure special role based permissions aren't granted to the @everyone role.""" -        if value and str(DISCORD_GUILD) in value: -            raise ValueError("You can not add the everyone role as an access scope.") +        if value and DISCORD_GUILD in value: +            msg = "You can not add the everyone role as an access scope." +            raise ValueError(msg)          return value      @root_validator -    def validate_role(cls, values: dict[str, t.Any]) -> t.Optional[dict[str, t.Any]]: +    def validate_role(cls, values: dict[str, t.Any]) -> dict[str, t.Any]:          """Validates does Discord role provided when flag provided.""" -        if ( -            FormFeatures.ASSIGN_ROLE.value in values.get("features", []) -            and not values.get("discord_role") -        ): -            raise ValueError( -                "discord_role field is required when ASSIGN_ROLE flag is provided." -            ) +        is_role_assigner = FormFeatures.ASSIGN_ROLE.value in values.get("features", []) +        if is_role_assigner and not values.get("discord_role"): +            msg = "discord_role field is required when ASSIGN_ROLE flag is provided." +            raise ValueError(msg)          return values -    def dict(self, admin: bool = True, **kwargs) -> dict[str, t.Any]: +    def dict(self, admin: bool = True, **kwargs) -> dict[str, t.Any]:  # noqa: FBT001, FBT002          """Wrapper for original function to exclude private data for public access."""          data = super().dict(**kwargs) @@ -97,10 +99,7 @@ class Form(BaseModel):          if not admin:              for field in PUBLIC_FIELDS: -                if field == "id" and kwargs.get("by_alias"): -                    fetch_field = "_id" -                else: -                    fetch_field = field +                fetch_field = "_id" if field == "id" and kwargs.get("by_alias") else field                  returned_data[field] = data[fetch_field]          else: @@ -110,17 +109,20 @@ class Form(BaseModel):  class FormList(BaseModel): -    __root__: t.List[Form] +    __root__: list[Form] -async def validate_hook_url(url: str) -> t.Optional[ValidationError]: +async def validate_hook_url(url: str) -> ValidationError | None:      """Validator for discord webhook urls.""" -    async def validate() -> t.Optional[str]: + +    async def validate() -> str | None:          if not isinstance(url, str): -            raise ValueError("Webhook URL must be a string.") +            msg = "Webhook URL must be a string." +            raise TypeError(msg)          if "discord.com/api/webhooks/" not in url: -            raise ValueError("URL must be a discord webhook.") +            msg = "URL must be a discord webhook." +            raise ValueError(msg)          try:              async with httpx.AsyncClient() as client: @@ -129,36 +131,32 @@ async def validate_hook_url(url: str) -> t.Optional[ValidationError]:          except httpx.RequestError as error:              # Catch exceptions in request format -            raise ValueError( -                f"Encountered error while trying to connect to url: `{error}`" -            ) +            msg = f"Encountered error while trying to connect to url: `{error}`" +            raise ValueError(msg)          except httpx.HTTPStatusError as error:              # Catch exceptions in response              status = error.response.status_code              if status == 401: -                raise ValueError( -                    "Could not authenticate with target. Please check the webhook url." -                ) -            elif status == 404: -                raise ValueError( -                    "Target could not find webhook url. Please check the webhook url." -                ) -            else: -                raise ValueError( -                    f"Unknown error ({status}) while connecting to target: {error}" -                ) +                msg = "Could not authenticate with target. Please check the webhook url." +                raise ValueError(msg) +            if status == 404: +                msg = "Target could not find webhook url. Please check the webhook url." +                raise ValueError(msg) + +            msg = f"Unknown error ({status}) while connecting to target: {error}" +            raise ValueError(msg)          return url      # Validate, and return errors, if any      try:          await validate() -    except Exception as e: +    except Exception as e:  # noqa: BLE001          loc = (              WebHook.__name__.lower(), -            WebHook.URL.value +            WebHook.URL.value,          )          return ValidationError([ErrorWrapper(e, loc=loc)], _WebHook) diff --git a/backend/models/form_response.py b/backend/models/form_response.py index 933f5e4..3c8297b 100644 --- a/backend/models/form_response.py +++ b/backend/models/form_response.py @@ -11,19 +11,20 @@ class FormResponse(BaseModel):      """Schema model for form response."""      id: str = Field(alias="_id") -    user: t.Optional[DiscordUser] -    antispam: t.Optional[AntiSpam] +    user: DiscordUser | None +    antispam: AntiSpam | None      response: dict[str, t.Any]      form_id: str      timestamp: str      @validator("timestamp", pre=True) -    def set_timestamp(cls, iso_string: t.Optional[str]) -> t.Optional[str]: +    def set_timestamp(cls, iso_string: str | None) -> str:          if iso_string is None: -            return datetime.datetime.now(tz=datetime.timezone.utc).isoformat() +            return datetime.datetime.now(tz=datetime.UTC).isoformat() -        elif not isinstance(iso_string, str): -            raise ValueError("Submission timestamp must be a string.") +        if not isinstance(iso_string, str): +            msg = "Submission timestamp must be a string." +            raise TypeError(msg)          # Convert to datetime and back to ensure string is valid          return datetime.datetime.fromisoformat(iso_string).isoformat() @@ -33,4 +34,4 @@ class FormResponse(BaseModel):  class ResponseList(BaseModel): -    __root__: t.List[FormResponse] +    __root__: list[FormResponse] diff --git a/backend/models/question.py b/backend/models/question.py index 201aa51..a13ce93 100644 --- a/backend/models/question.py +++ b/backend/models/question.py @@ -4,11 +4,12 @@ from pydantic import BaseModel, Field, root_validator, validator  from backend.constants import QUESTION_TYPES, REQUIRED_QUESTION_TYPE_DATA -_TESTS_TYPE = t.Union[t.Dict[str, str], int] +_TESTS_TYPE = dict[str, str] | int  class Unittests(BaseModel):      """Schema model for unittest suites in code questions.""" +      allow_failure: bool = False      tests: _TESTS_TYPE @@ -16,17 +17,19 @@ class Unittests(BaseModel):      def validate_tests(cls, value: _TESTS_TYPE) -> _TESTS_TYPE:          """Confirm that at least one test exists in a test suite."""          if isinstance(value, dict): -            keys = len(value.keys()) - (1 if "setUp" in value.keys() else 0) +            keys = len(value.keys()) - (1 if "setUp" in value else 0)              if keys == 0: -                raise ValueError("Must have at least one test in a test suite.") +                msg = "Must have at least one test in a test suite." +                raise ValueError(msg)          return value  class CodeQuestion(BaseModel):      """Schema model for questions of type `code`.""" +      language: str -    unittests: t.Optional[Unittests] +    unittests: Unittests | None  class Question(BaseModel): @@ -42,22 +45,20 @@ class Question(BaseModel):          allow_population_by_field_name = True      @validator("type", pre=True) -    def validate_question_type(cls, value: str) -> t.Optional[str]: +    def validate_question_type(cls, value: str) -> str:          """Checks if question type in currently allowed types list."""          value = value.lower()          if value not in QUESTION_TYPES: -            raise ValueError( -                f"{value} is not valid question type. " -                f"Allowed question types: {QUESTION_TYPES}." -            ) +            msg = f"{value} is not valid question type. Allowed question types: {QUESTION_TYPES}." +            raise ValueError(msg)          return value      @root_validator      def validate_question_data( -            cls, -            value: dict[str, t.Any] -    ) -> t.Optional[dict[str, t.Any]]: +        cls, +        value: dict[str, t.Any], +    ) -> dict[str, t.Any]:          """Check does required data exists for question type and remove other data."""          # When question type don't need data, don't add anything to keep DB clean.          if value.get("type") not in REQUIRED_QUESTION_TYPE_DATA: @@ -65,13 +66,15 @@ class Question(BaseModel):          for key, data_type in REQUIRED_QUESTION_TYPE_DATA[value["type"]].items():              if key not in value.get("data", {}): -                raise ValueError(f"Required question data key '{key}' not provided.") +                msg = f"Required question data key '{key}' not provided." +                raise ValueError(msg)              if not isinstance(value["data"][key], data_type): -                raise ValueError( +                msg = (                      f"Question data key '{key}' expects {data_type.__name__}, " -                    f"got {type(value['data'][key]).__name__} instead." +                    f"got {type(value["data"][key]).__name__} instead."                  ) +                raise TypeError(msg)              # Validate unittest options              if value.get("type").lower() == "code": | 
