From e7bbee1c14767560f83530a840a8995c227b488a Mon Sep 17 00:00:00 2001 From: Judson Stevens Date: Sun, 1 Oct 2023 12:57:33 -0500 Subject: [PATCH] More linting fixes and updating schemas for Pydantic 2.0 --- .mega-linter.yml | 12 +- .pre-commit-config.yaml | 2 +- README.md | 2 - docker/docker-compose.local.yml | 6 +- docker/docker-compose.weaviate.yml | 5 +- docker/docker-compose.yml | 3 +- main.py | 14 +- modules/handlers/mentorship_handler.py | 7 +- modules/models/greeting_models.py | 9 +- modules/models/shared_models.py | 2 +- modules/models/slack_models/action_models.py | 6 +- modules/models/slack_models/command_models.py | 9 +- modules/models/slack_models/event_models.py | 12 +- modules/models/slack_models/message_models.py | 2 +- modules/models/slack_models/shared_models.py | 217 ++++++++++++------ modules/models/slack_models/slack_models.py | 35 +-- pyproject.toml | 6 +- 17 files changed, 231 insertions(+), 118 deletions(-) diff --git a/.mega-linter.yml b/.mega-linter.yml index 714b46e..b243ccd 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -7,18 +7,21 @@ DISABLE: - TEKTON DISABLE_LINTERS: - JSON_PRETTIER - - REPOSITORY_GITLEAKS - - SPELL_CSPELL - - SPELL_PROSELINT - PYTHON_BANDIT - PYTHON_FLAKE8 # We use MyPy - PYTHON_PYLINT - PYTHON_PYRIGHT + - REPOSITORY_GITLEAKS + - SPELL_CSPELL + - SPELL_LYCHEE + - SPELL_PROSELINT DISABLE_ERRORS_LINTERS: - REPOSITORY_DEVSKIM - REPOSITORY_SEMGREP DOCKERFILE_HADOLINT_ARGUMENTS: "--ignore DL3008 --ignore DL3018 --ignore DL3013 --ignore DL3059 --ignore DL3005" +COPYPASTE_JSCPD_ARGUMENTS: + - "--ignore '**/handlers/**,**/vector*'" MARKDOWN_MARKDOWN_LINK_CHECK_CONFIG_FILE: ".markdown-link-check-config.json" MARKDOWN_MARKDOWN_LINK_CHECK_DISABLE_ERRORS: true PRINT_ALL_FILES: false @@ -35,9 +38,10 @@ PYTHON_MYPY_ARGUMENTS: "--disallow-any-generics", ] PYTHON_MYPY_CONFIG_FILE: "pyproject.toml" +PYTHON_RUFF_CONFIG_FILE: "pyproject.toml" REPOSITORY_DEVSKIM_ARGUMENTS: ["-g", ".mypy_cache/*"] SHOW_ELAPSED_TIME: true SPELL_MISSPELL_FILTER_REGEX_EXCLUDE: '(\.automation/generated|docs/descriptors)' -YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: '(templates/\.mega-linter\.yml/tests)' +YAML_YAMLLINT_FILTER_REGEX_EXCLUDE: '(templates/\.mega-linter\.yml/tests/**\/tests\/**)' YAML_PRETTIER_FILTER_REGEX_EXCLUDE: '(templates/\.mega-linter\.yml|mkdocs\.yml)' YAML_V8R_FILTER_REGEX_EXCLUDE: '(descriptors|templates/\.mega-linter\.yml|\.codecov\.yml)' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 746ce0d..55fb600 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,4 +38,4 @@ repos: - --install-types - --non-interactive - --strict-optional - - --disallow-any-generics \ No newline at end of file + - --disallow-any-generics diff --git a/README.md b/README.md index 822b406..b9080ae 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![Twitter Follow](https://img.shields.io/twitter/follow/operation_code.svg?style=social&label=Follow&style=social)](https://twitter.com/operation_code) [![Code-style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) - -[![CircleCI](https://circleci.com/gh/OperationCode/operationcode-pybot.svg?style=svg)](https://circleci.com/gh/OperationCode/operationcode-pybot) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://contributor-covenant.org/) # OperationCode-Pybot diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml index 41e6483..0588621 100644 --- a/docker/docker-compose.local.yml +++ b/docker/docker-compose.local.yml @@ -5,11 +5,11 @@ services: image: pybot:latest container_name: pybot01 ports: - - "8010:8010" + - "8010:8010" ngrok: image: wernight/ngrok:latest environment: - - NGROK_PORT=pybot:8010 + - NGROK_PORT=pybot:8010 ports: - - "4040:4040" + - "4040:4040" diff --git a/docker/docker-compose.weaviate.yml b/docker/docker-compose.weaviate.yml index 1029fcb..9939a73 100644 --- a/docker/docker-compose.weaviate.yml +++ b/docker/docker-compose.weaviate.yml @@ -1,9 +1,10 @@ +--- version: '3.4' services: weaviate: image: semitechnologies/weaviate:1.19.6 ports: - - "8055:8080" + - "8055:8080" environment: QUERY_DEFAULTS_LIMIT: 20 AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' @@ -15,4 +16,4 @@ services: t2v-transformers: image: semitechnologies/transformers-inference:sentence-transformers-all-mpnet-base-v2 environment: - ENABLE_CUDA: 1 \ No newline at end of file + ENABLE_CUDA: 1 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index bd3a081..c33fc91 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,3 +1,4 @@ +--- version: '3.9' services: @@ -9,4 +10,4 @@ services: dockerfile: docker/Dockerfile command: uvicorn main:api -host 0.0.0.0 --port 5001 --reload --log-level 'debug' ports: - - "5001:5001" \ No newline at end of file + - "5001:5001" diff --git a/main.py b/main.py index ea942a3..35de815 100644 --- a/main.py +++ b/main.py @@ -176,19 +176,29 @@ async def healthz() -> Response: # noqa: D103 @app.command("/mentor_request") -async def handle_mentor_request_command( # noqa: D103 +async def handle_mentor_request_command( context: AsyncBoltContext, body: dict[str, Any], ) -> None: + """Handle the /mentor_request command. + + :param context: The context of the request from the Bolt framework + :param body: The body of the request + """ logger.info("STAGE: Processing mentorship request...") await handle_mentor_request(SlackCommandRequestBody(**body), context) @app.view("mentorship_request_form_submit") -async def handle_mentorship_request_form_view_submit( # noqa: D103 +async def handle_mentorship_request_form_view_submit( body: dict[str, Any], context: AsyncBoltContext, ) -> None: + """Handle the submission of the mentorship request form. + + :param body: The body of the request + :param context: The context of the request from the Bolt framework + """ logger.info("STAGE: Processing mentorship form submission...") await handle_mentorship_request_form_submit(SlackViewRequestBody(**body), context) diff --git a/modules/handlers/mentorship_handler.py b/modules/handlers/mentorship_handler.py index a1f776c..9d35dd3 100644 --- a/modules/handlers/mentorship_handler.py +++ b/modules/handlers/mentorship_handler.py @@ -120,10 +120,15 @@ async def handle_mentorship_request_form_submit( # noqa: D103 ) -async def handle_mentorship_request_claim( # noqa: D103 +async def handle_mentorship_request_claim( parsed_body: SlackActionRequestBody, context: AsyncBoltContext, ) -> None: + """Handle a mentorship request claim submission from Slack. + + :param parsed_body: The parsed body of the Slack request. + :param context: The context object for the Slack request. + """ logger.info("STAGE: Handling mentorship request claim...") await context.ack() blocks = parsed_body.message.blocks diff --git a/modules/models/greeting_models.py b/modules/models/greeting_models.py index 2c6e024..1a6ea43 100644 --- a/modules/models/greeting_models.py +++ b/modules/models/greeting_models.py @@ -1,8 +1,11 @@ -from pydantic import BaseModel, Field # noqa: D100 +"""Models for the greeting module.""" +from pydantic import BaseModel, Field -class UserInfo(BaseModel): # noqa: D101 - id: str = Field( # noqa: A003 +class UserInfo(BaseModel): + """User info schema.""" + + id: str = Field( ..., example="U02RK2AL5LZ", description="The Slack ID of the new user", diff --git a/modules/models/shared_models.py b/modules/models/shared_models.py index fbbfaa3..255e87f 100644 --- a/modules/models/shared_models.py +++ b/modules/models/shared_models.py @@ -15,7 +15,7 @@ class ValidEnum(str, Enum): class AirtableUser(BaseModel): """Model for Airtable user.""" - id: str = Field( # noqa: A003 + id: str = Field( ..., example="usrAuExK7DEWFNiI6", description="Airtable provided unique ID of the user", diff --git a/modules/models/slack_models/action_models.py b/modules/models/slack_models/action_models.py index e70a171..77ea9d6 100644 --- a/modules/models/slack_models/action_models.py +++ b/modules/models/slack_models/action_models.py @@ -11,8 +11,10 @@ ) -class SlackActionRequestBody(BasicSlackRequest): # noqa: D101 - type: str = Field(..., example="block_actions", description="The type of action") # noqa: A003 +class SlackActionRequestBody(BasicSlackRequest): + """The body of a Slack action request.""" + + type: str = Field(..., example="block_actions", description="The type of action") user: SlackUserInfo = Field( ..., description="The user who triggered the action request", diff --git a/modules/models/slack_models/command_models.py b/modules/models/slack_models/command_models.py index a8ba419..63772d6 100644 --- a/modules/models/slack_models/command_models.py +++ b/modules/models/slack_models/command_models.py @@ -3,7 +3,12 @@ from modules.models.slack_models.shared_models import BasicSlackRequest -class SlackCommandRequestBody(BasicSlackRequest): # noqa: D101 +class SlackCommandRequestBody(BasicSlackRequest): + """The body of a Slack command request. + + These are typically received from the Slack application after a slash command is used. + """ + command: str = Field( ..., example="/mentor_request", @@ -29,7 +34,7 @@ class SlackCommandRequestBody(BasicSlackRequest): # noqa: D101 example="directmessage", description="The name of the channel where the command was triggered", ) - response_url: str = Field( + response_url: str | None = Field( None, example="https://hooks.slack.com/actions/T01SBLfdsaQ57A/2902419552385/BiWpNhRSURKF9CvqujZ3x1MQ", description="The URL to send the response to that will automatically put the response in the right place", diff --git a/modules/models/slack_models/event_models.py b/modules/models/slack_models/event_models.py index 449548e..91f3b6b 100644 --- a/modules/models/slack_models/event_models.py +++ b/modules/models/slack_models/event_models.py @@ -3,8 +3,10 @@ from modules.models.slack_models.shared_models import SlackMessageInfo -class MemberJoinedChannelEvent(BaseModel): # noqa: D101 - type: str = Field( # noqa: A003 +class MemberJoinedChannelEvent(BaseModel): + """The body of a Slack member_joined_channel event.""" + + type: str = Field( ..., example="member_joined_channel", description="The type of event, should always be member_joined_channel", @@ -32,7 +34,9 @@ class MemberJoinedChannelEvent(BaseModel): # noqa: D101 ) -class MessageReceivedChannelEvent(BaseModel): # noqa: D101 +class MessageReceivedChannelEvent(BaseModel): + """The body of a Slack message event.""" + team_id: str = Field( ..., example="T024BE7LD", @@ -47,7 +51,7 @@ class MessageReceivedChannelEvent(BaseModel): # noqa: D101 ..., description="The information about the message that was received", ) - type: str = Field( # noqa: A003 + type: str = Field( ..., example="event_callback", description="The type of event, should always be event_callback", diff --git a/modules/models/slack_models/message_models.py b/modules/models/slack_models/message_models.py index 9445155..3719a90 100644 --- a/modules/models/slack_models/message_models.py +++ b/modules/models/slack_models/message_models.py @@ -1 +1 @@ -# noqa: D100 +"""Models for messages. Unused for now.""" diff --git a/modules/models/slack_models/shared_models.py b/modules/models/slack_models/shared_models.py index 3d3c372..d49c6ac 100644 --- a/modules/models/slack_models/shared_models.py +++ b/modules/models/slack_models/shared_models.py @@ -1,14 +1,16 @@ import logging # noqa: D100 import os -from typing import Any, Union +from typing import Any from pydantic import BaseModel, Field logger = logging.getLogger(__name__) -class SlackUserInfo(BaseModel): # noqa: D101 - id: str = Field( # noqa: A003 +class SlackUserInfo(BaseModel): + """Information about a Slack user.""" + + id: str = Field( ..., example="U01RN31JSTD", description="Slack ID of the user", @@ -25,7 +27,9 @@ class SlackUserInfo(BaseModel): # noqa: D101 ) -class SlackEditedInfo(BaseModel): # noqa: D101 +class SlackEditedInfo(BaseModel): + """Information about when a Slack message was last edited.""" + user: str = Field( ..., example="B02QRQ4KU5V", @@ -38,8 +42,10 @@ class SlackEditedInfo(BaseModel): # noqa: D101 ) -class SlackTextObjectInfo(BaseModel): # noqa: D101 - type: str = Field(..., example="mrkdwn", description="The type of text object") # noqa: A003 +class SlackTextObjectInfo(BaseModel): + """A Slack text object is a piece of text that can be used in a Slack message or block.""" + + type: str = Field(..., example="mrkdwn", description="The type of text object") text: str = Field( ..., example="Testing text for a text object", @@ -47,35 +53,39 @@ class SlackTextObjectInfo(BaseModel): # noqa: D101 ) -class SlackBlockInfo(BaseModel): # noqa: D101 - type: str = Field(..., example="section", description="The type of block") # noqa: A003 +class SlackBlockInfo(BaseModel): + """A Slack block is a section of a message.""" + + type: str = Field(..., example="section", description="The type of block") block_id: str = Field( ..., example="report_title_block", description="ID of the block - must be unique within the immediate set of blocks. Will be added by Slack if it's missing in the definition", # noqa: E501 ) - text: SlackTextObjectInfo = Field( + text: SlackTextObjectInfo | None = Field( None, description="Optional text object for this block", ) - class Config: # noqa: D106 + class Config: # Allows extra attributes on this model extra = "allow" -class SlackViewInfo(BaseModel): # noqa: D101 - id: str = Field( # noqa: A003 +class SlackViewInfo(BaseModel): + """A Slack view is a modal that can be used to collect information from a user.""" + + id: str = Field( ..., example="V02S65HDH9Q", description="Slack ID of the view", ) - type: str = Field(..., example="modal", description="The type of view") # noqa: A003 + type: str = Field(..., example="modal", description="The type of view") blocks: list[SlackBlockInfo] = Field( ..., description="List of blocks in the view - there must be at least one", ) - private_metadata: str = Field( + private_metadata: str | None = Field( None, description="Private data that can be included on a view and sent with a submission - not visible to the user", ) @@ -84,35 +94,35 @@ class SlackViewInfo(BaseModel): # noqa: D101 example="report_form_submit", description="Callback for the submission action of the view, used to handle the submission", ) - state: dict[str, Any] = Field( + state: dict[str, Any] | None = Field( None, description="State of a view, if it exists - contains the value of the input elements in the view", ) - hash: str = Field( # noqa: A003 + hash: str = Field( ..., example="1640903702.u8C2NM3Y", description="Hash string sent with the submission of the view, this is used by the update and publish views API calls to ensure that only the most recent view is updated or published", # noqa: E501 ) - title: SlackTextObjectInfo = Field( + title: SlackTextObjectInfo | None = Field( None, description="The text object used for the title of the view", ) - previous_view_id: str = Field( - "", + previous_view_id: str | None = Field( + None, example="V02S65HDH9Q", description="The previous view's ID - typically used in workflows", ) - root_view_id: str = Field( + root_view_id: str | None = Field( None, example="V02S65HDH9Q", description="The root view's ID", ) - external_id: str = Field( + external_id: str | None = Field( None, example="report_form_modal", description="The optional external ID for the view, must be unique across all views - this is added by the bot", # noqa: E501 ) - bot_id: str = Field( + bot_id: str | None = Field( None, example="B02QRQ4KU5V", description="The ID of the bot that generated the view", @@ -120,24 +130,26 @@ class SlackViewInfo(BaseModel): # noqa: D101 class SlackMessageInfo(BaseModel): # noqa: D101 - client_msg_id: str = Field(None, example="de437daf-67fd-48a6-b9bd-03f9336509e9") - bot_id: str = Field( + client_msg_id: str | None = Field(None, example="de437daf-67fd-48a6-b9bd-03f9336509e9") + bot_id: str | None = Field( None, example="B02QRQ4KU5V", - description="Slack ID of the bot that sent the message - provided that the original message was sent from a bot", # noqa: E501 + description="Slack ID of the bot that sent the message - provided that the original message was sent " + "from a bot", ) - type: str = Field(..., example="message", description="The type of message") # noqa: A003 - text: str = Field( + type: str = Field(..., example="message", description="The type of message") + text: str | None = Field( None, example="Typical fallback text...", - description="If blocks are provided, this is the fallback text for the message. If no blocks are present, this is the message", # noqa: E501 + description="If blocks are provided, this is the fallback text for the message. If no blocks are present, " + "this is the message", ) user: str = Field( ..., example="U02RK2AL5LZ", description="Slack user ID of the user who triggered the action", ) - blocks: list[Union[Any, SlackBlockInfo]] = Field( # noqa: UP007 + blocks: list[Any | SlackBlockInfo] | None = Field( None, description="The list of blocks for a particular message", ) @@ -146,65 +158,68 @@ class SlackMessageInfo(BaseModel): # noqa: D101 example="1640727423.003500", description="Unix Epoch timestamp the message was received by Slack - typically used to locate the message", ) - edited: SlackEditedInfo = Field( + edited: SlackEditedInfo | None = Field( None, description="Information about who and when the message was last edited", ) - thread_ts: str = Field( + thread_ts: str | None = Field( None, example="1640727423.003500", description="The Unix Epoch timestamp the thread was created", ) - reply_count: int = Field(None, description="The number of replies the message has") - reply_users_count: int = Field( + reply_count: int | None = Field(None, description="The number of replies the message has") + reply_users_count: int | None = Field( None, description="The number of users who have replied to the message", ) - latest_reply: str = Field( + latest_reply: str | None = Field( None, example="1640727423.003500", description="The Unix Epoch timestamp of when the latest reply was created", ) - reply_users: list[str] = Field( + reply_users: list[str] | None = Field( None, example=["U02RK2AL5LZ"], description="A list of Slack user IDs of users who have replied to the message", ) - last_read: str = Field( + last_read: str | None = Field( None, example="1640727423.003500", description="The Unix Epoch timestamp of when the message was last read", ) - class Config: # noqa: D106 + class Config: arbitrary_types_allowed = True -class SlackActionInfo(BaseModel): # noqa: D101 +class SlackActionInfo(BaseModel): + """The action information for a Slack action.""" + action_id: str = Field( ..., example="reset_greet_new_user_claim", - description="The ID that identifies this particular action and allows the application to handle it when triggered", # noqa: E501 + description="The ID that identifies this particular action and allows the application to" + " handle it when triggered", ) - block_id: str = Field( + block_id: str | None = Field( None, example="reset_claim_action", description="The ID that identifies the block the action is part of", ) - text: SlackTextObjectInfo = Field( + text: SlackTextObjectInfo | None = Field( None, description="The text object that represents the text on the action (button, etc)", ) - value: Union[dict[str, Any], str] = Field( # noqa: UP007 + value: dict[str, Any] | str | None = Field( None, description="The value sent to the application when the action is triggered", ) - style: str = Field( + style: str | None = Field( None, example="danger", description="The style of the action, typically the style of the button", ) - type: str = Field(..., example="button", description="The type of action") # noqa: A003 + type: str = Field(..., example="button", description="The type of action") action_ts: str = Field( ..., example="1640727423.003500", @@ -212,8 +227,10 @@ class SlackActionInfo(BaseModel): # noqa: D101 ) -class SlackActionContainerInfo(BaseModel): # noqa: D101 - type: str = Field( # noqa: A003 +class SlackActionContainerInfo(BaseModel): + """The container information for a Slack action.""" + + type: str = Field( ..., example="message", description="The type of container the action came from", @@ -234,12 +251,16 @@ class SlackActionContainerInfo(BaseModel): # noqa: D101 ) -class SlackChannelInfo(BaseModel): # noqa: D101 - id: str = Field(..., example="C01S0K034TB", description="Slack ID of the channel") # noqa: A003 +class SlackChannelInfo(BaseModel): + """Information about a Slack channel.""" + + id: str = Field(..., example="C01S0K034TB", description="Slack ID of the channel") name: str = Field(..., example="general", description="Name of the channel") -class BasicSlackRequest(BaseModel): # noqa: D101 +class BasicSlackRequest(BaseModel): + """The basic information that is included in all Slack requests.""" + trigger_id: str = Field( ..., example="2875577934983.1895692821248.5b6bb2ed4127b90954e8d32a86e2cafc", @@ -253,9 +274,12 @@ class BasicSlackRequest(BaseModel): # noqa: D101 class SlackConversationInfo(BaseModel): - """Slack used to call these channels, but now they are called conversations, of which channels are a subset along with IMs and MPIMs (Multi Person IMs).""" # noqa: E501 + """Slack used to call these channels, but now they are called conversations. + + Of which channels are a subset along with IMs and MPIMs (Multi Person IMs). + """ - id: str = Field( # noqa: A003 + id: str = Field( ..., example="C012AB3CD", description="Slack ID of the conversation", @@ -276,13 +300,15 @@ class SlackConversationInfo(BaseModel): ) -class BaseSlackTeamInfo(BaseModel): # noqa: D101 - id: str = Field( # noqa: A003 +class BaseSlackTeamInfo(BaseModel): + """Basic information about a Slack team.""" + + id: str = Field( ..., example="T01SBLCQ57A", description="Slack ID of the team", ) - domain: str = Field( + domain: str | None = Field( None, example="bot-testing-field", description="The domain of the team", @@ -303,59 +329,100 @@ class SlackTeamInfo(BaseSlackTeamInfo): ) -class SlackTeam: # noqa: D101 - def __init__(self, team_info: SlackTeamInfo) -> None: # noqa: ANN101, D107 - logger.debug(f"Initializing the Slack Team with team_info: {team_info}") # noqa: G004 +class SlackTeam: + """A Slack team is a workspace that contains channels and users.""" + + def __init__(self: "SlackTeam", team_info: SlackTeamInfo) -> None: + """Initialize the Slack team with the team information.""" + logger.info("Initializing the Slack Team with team_info", extra={"team_info": team_info}) self._team_info = team_info - def find_channel_by_name(self, channel_name: str) -> SlackConversationInfo: # noqa: ANN101, D102 - logger.debug(f"Finding channel by name: {channel_name}") # noqa: G004 - logger.debug( - f"Full channel list: {[conversation_info.name for conversation_info in self.full_conversation_list]}", # noqa: E501, G004 - ) + def find_channel_by_name(self: "SlackTeam", channel_name: str) -> SlackConversationInfo: + """Find a Slack channel by name. + + :param channel_name: The name of the channel to find + :return: The Slack channel information + """ + logger.info("Finding channel by name", extra={"channel_name": channel_name}) + full_channel_list = [conversation_info.name for conversation_info in self.full_conversation_list] + logger.info("Full channel list:", extra={"full_channel_list": full_channel_list}) try: return next( conversation for conversation in self.full_conversation_list if conversation.name == channel_name ) except IndexError: - logger.exception(f"Could not find channel by name: {channel_name}") # noqa: G004 - raise Exception( # noqa: B904, TRY002, TRY003, TRY200 - f"Could not find channel by name: {channel_name}" # noqa: EM102, COM812 - ) # noqa: B904, EM102, RUF100, TRY002, TRY003, TRY200 + logger.exception("Could not find channel by name", extra={"channel_name": channel_name}) + raise @property - def slack_id(self) -> str: # noqa: ANN101, D102 + def slack_id(self: "SlackTeam") -> str: + """Return the Slack ID of the team. + + :return: The Slack ID of the team + """ return self._team_info.id @property - def name(self) -> str: # noqa: ANN101, D102 + def name(self: "SlackTeam") -> str: + """Return the name of the team. + + :return: The name of the team + """ return self._team_info.name @property - def full_conversation_list(self) -> list[SlackConversationInfo]: # noqa: ANN101, D102 + def full_conversation_list(self: "SlackTeam") -> list[SlackConversationInfo]: + """Return the full list of conversations in the team. + + :return: The full list of conversations in the team + """ return self._team_info.conversations @property - def greetings_channel(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def greetings_channel(self: "SlackTeam") -> SlackConversationInfo: + """Return the greeting channel. + + :return: The greeting channel + """ return self.find_channel_by_name(os.getenv("GREETINGS_CHANNEL_NAME", "")) @property - def mentors_internal_channel(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def mentors_internal_channel(self: "SlackTeam") -> SlackConversationInfo: + """Return the mentor internal channel. + + :return: The mentor internal channel + """ return self.find_channel_by_name(os.getenv("MENTORS_CHANNEL_NAME", "")) @property - def moderators_channel(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def moderators_channel(self: "SlackTeam") -> SlackConversationInfo: + """Return the moderator channel. + + :return: The moderator channel + """ return self.find_channel_by_name(os.getenv("MODERATORS_CHANNEL_NAME", "")) @property - def general_channel(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def general_channel(self: "SlackTeam") -> SlackConversationInfo: + """Return the general channel. + + :return: The general channel + """ return self.find_channel_by_name(os.getenv("GENERAL_CHANNEL_NAME", "")) @property - def pride_channel(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def pride_channel(self: "SlackTeam") -> SlackConversationInfo: + """Return the pride channel. + + :return: The pride channel + """ return self.find_channel_by_name(os.getenv("PRIDE_CHANNEL_NAME", "")) @property - def blacks_in_tech(self) -> SlackConversationInfo: # noqa: ANN101, D102 + def blacks_in_tech(self: "SlackTeam") -> SlackConversationInfo: + """Return the blacks_in_tech channel. + + :return: The blacks_in_tech channel + """ return self.find_channel_by_name(os.getenv("BLACKS_IN_TECH_CHANNEL_NAME", "")) diff --git a/modules/models/slack_models/slack_models.py b/modules/models/slack_models/slack_models.py index 46f8849..e31ed98 100644 --- a/modules/models/slack_models/slack_models.py +++ b/modules/models/slack_models/slack_models.py @@ -13,8 +13,10 @@ ) -class SlackResponseBody(BasicSlackRequest): # noqa: D101 - type: str = Field( # noqa: A003 +class SlackResponseBody(BasicSlackRequest): + """The body of a Slack response.""" + + type: str = Field( ..., example="view_submission", description="The type of request the response is responding to", @@ -23,33 +25,35 @@ class SlackResponseBody(BasicSlackRequest): # noqa: D101 ..., description="The info of the user who triggered the request", ) - view: SlackViewInfo = Field( + view: SlackViewInfo | None = Field( None, description="View object of the original message if it exists", ) - container: SlackActionContainerInfo = Field( + container: SlackActionContainerInfo | None = Field( None, description="The container that the action originated from if it exists", ) - channel: SlackChannelInfo = Field( + channel: SlackChannelInfo | None = Field( None, description="The channel information for where the original request was from", ) - message: SlackMessageInfo = Field( + message: SlackMessageInfo | None = Field( None, description="The original message from the request, if it exists", ) - response_urls: list[str] = Field( + response_urls: list[str] | None = Field( None, description="List of response URLs, typically included with a view response", ) - actions: list[SlackActionInfo] = Field( + actions: list[SlackActionInfo] | None = Field( None, description="The list of actions in this message", ) -class BotInfo(BaseModel): # noqa: D101 +class BotInfo(BaseModel): + """Information about the bot that sent the request.""" + slack_id: str = Field( ..., example="B02QRQ4KU5V", @@ -72,7 +76,9 @@ class BotInfo(BaseModel): # noqa: D101 ) -class BasicSlackBotResponse(BaseModel): # noqa: D101 +class BasicSlackBotResponse(BaseModel): + """Basic information about a Slack bot response.""" + date_time_received: str = Field( ..., example="Tue, 28 Dec 2021 05:36:22 GMT", @@ -85,7 +91,8 @@ class BasicSlackBotResponse(BaseModel): # noqa: D101 ) status_ok: bool = Field( ..., - description="Status of the request that triggered the response, true means the request was successful while false means it was in error", # noqa: E501 + description="Status of the request that triggered the response, true means the request was successful while " + "false means it was in error", ) received_timestamp: str = Field( ..., @@ -94,7 +101,9 @@ class BasicSlackBotResponse(BaseModel): # noqa: D101 ) -class SlackBotResponseContent(BasicSlackBotResponse): # noqa: D101 +class SlackBotResponseContent(BasicSlackBotResponse): + """The content of a Slack bot response.""" + channel: str = Field( ..., example="D02R6CR6DMG", @@ -104,7 +113,7 @@ class SlackBotResponseContent(BasicSlackBotResponse): # noqa: D101 ..., description="Information about the bot that sent the request", ) - request_blocks: list[dict[str, Any]] = Field( + request_blocks: list[dict[str, Any]] | None = Field( None, description="List of blocks in the original request", ) diff --git a/pyproject.toml b/pyproject.toml index d63506b..f4b55c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,7 +103,11 @@ max-complexity = 10 ] "**/schemas/*" = [ "A003", # We should allow shadowing - "D106", # We should allow missing docstring in the Config class + "D106", # We should allow missing docstring +] +"**/models/*" = [ + "A003", # We should allow shadowing + "D106", # We should allow missing docstring ] "**/routers/*" = [ "BLE001", # We use broad exceptions