-
-
Notifications
You must be signed in to change notification settings - Fork 94
Feature: NestBot block message template system #1301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the Summary by CodeRabbit
Summary by CodeRabbit
WalkthroughThis change introduces a comprehensive refactor of the Slack command handling system within the backend. The previous function-based, procedural command handlers are replaced with a class-based architecture, where each command is encapsulated in a subclass of a new Changes
Assessment against linked issues
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (20)
backend/apps/slack/templates/policies.template (1)
1-5
: New Policies Template Review
The template efficiently iterates over a collection of policies and renders each as a clickable link. A couple of points for consideration:
- Ensure that the variables
policies
,NL
,SECTION_BREAK
, andDIVIDER
are clearly defined in the context where this template is rendered.- The use of the
|safe
filter on{{ title }}
is appropriate if the input is sanitized; please verify that no untrusted content is passed in.
Overall, the template is well-structured for its intended purpose.backend/apps/slack/templates/jobs.template (1)
1-5
: New Jobs Template Review
This template is well-crafted for communicating job-related Slack channel information. The inclusion of a disclaimer and embedding of thefeedback.template
are good practices for modularity and transparency. One suggestion is to ensure that the variablejobs_channel
is provided reliably; consider a fallback or error handling mechanism if it might ever be missing.backend/apps/slack/template_system/loader.py (1)
8-8
: Consider adding a check to ensure templates directory exists.The code assumes that the templates directory exists, but if it doesn't, this would cause runtime errors when templates are loaded.
BASE_DIR = Path(__file__).resolve().parent.parent TEMPLATES_DIR = BASE_DIR / "templates" + +if not TEMPLATES_DIR.exists(): + raise FileNotFoundError(f"Templates directory not found: {TEMPLATES_DIR}")backend/apps/slack/models/event.py (1)
45-45
: Consider caching the Owasp instance to avoid repeated instantiation.Creating a new
Owasp
instance on each comparison is inefficient. Consider caching the command string or the instance to improve performance.- if command and command == Owasp().get_command(): + # Cache the command string to avoid creating a new Owasp instance each time + OWASP_COMMAND = Owasp().get_command() # This could be defined at module level + if command and command == OWASP_COMMAND:backend/apps/slack/templates/leaders.template (1)
8-11
: Consider verifying partial match logic.If full or exact matches are intended, you might replace substring checks with an equality check or use word boundaries. Otherwise, this approach is fine.
backend/tests/apps/slack/commands/committees_test.py (1)
78-81
: Good coverage of committees with mocked results.This test correctly verifies that the returned blocks contain the committee details. Consider adding assertions for any additional relevant fields or edge cases, if necessary.
backend/apps/slack/commands/staff.py (1)
8-23
: Add docstring details for thecommand
parameter.Currently, there's a simple docstring. Adding parameter details, expected structure, and examples will improve clarity and maintainability.
backend/apps/slack/templates/events.template (1)
1-10
: Consider adding a fallback message for empty categories or no events.
I 8000 fcategorized_events
or a specific category is empty, users might see a blank list. Showing an explicit "No upcoming events found" message could improve user clarity and provide better UX.backend/apps/slack/commands/projects.py (1)
11-25
: Effectively implemented rendering methodThe
get_render_blocks
method maintains the same functionality as the previous implementation while properly integrating with the new class-based structure. The method extracts the search query and passes it to the common handler with appropriate presentation settings.One minor observation: While the implementation uses reasonable default values for the
EntityPresentation
parameters, these could potentially be moved to class constants or configuration settings for easier customization in the future.backend/apps/slack/commands/users.py (1)
11-25
: Implementation of required template rendering methodThe
get_render_blocks
method cleanly extracts the search query and passes it to the existingget_blocks
function with appropriate presentation parameters. This maintains existing functionality while enabling the template system described in the PR objectives.Consider adding error handling for cases where
command["text"]
might not exist or could be None:- search_query = command["text"].strip() + search_query = command.get("text", "").strip()backend/apps/slack/commands/chapters.py (2)
12-16
: Consider validating thecommand["text"]
field
When overridingget_render_blocks
, you accesscommand["text"]
directly. If the Slack payload unexpectedly omits"text"
, this would raise a KeyError. Consider validating or defaulting to an empty string to ensure robustness.
18-29
: Repeated block generation pattern
This block generation with the same pagination and feedback config appears repeated in other commands. If feasible, consider consolidating shared parameters or providing a helper method to reduce duplication and streamline maintenance.backend/apps/slack/commands/contribute.py (1)
18-36
: Similar block generation logic
As in other commands, you’re hard-coding the limit and reusing a similarEntityPresentation
setup. If multiple commands share the same approach, consider factoring out common parameters into a utility function or config.backend/apps/slack/commands/committees.py (1)
11-25
: Recurring pattern for generating Slack blocks
Your use ofget_blocks
withEntityPresentation
is consistent. This approach is similar to other commands. Consider whether you can factor this out into a reusable helper, as it’s repeated in multiple places.backend/tests/apps/slack/commands/policies_test.py (1)
35-64
: Consider reducing duplication of policy data
Replicating the entire policy list in tests could mean additional maintenance overhead when changes occur. Consider referencing a common fixture or using shared data to keep the test consistent with actual code.backend/tests/apps/slack/commands/staff_test.py (1)
77-77
: Flexible substring assertion.Using
assert expected_message in blocks[0]["text"]["text"]
allows for slight variations in formatting. If exact matches are crucial, consider more precise matching; otherwise, this is suitable.backend/tests/apps/slack/commands/sponsors_test.py (2)
67-69
: Consider verifyingack
call in the test.After creating the
Sponsors
instance and callinghandler
, add an assertion thatack
was invoked, ensuring proper Slack command acknowledgment is tested.Example snippet:
sponsors_instance.handler(ack=ack, command=mock_slack_command, client=mock_slack_client) +ack.assert_called_once()
81-82
: No issues with hardcoded mock channel.Using the mock channel ID
"C123456"
is acceptable for testing, though consider documenting its test purpose if re-used.backend/apps/slack/commands/contact.py (1)
10-12
: Template naming consistency
Returning"navigate.template"
underContact
may be confusing. Consider using a file name that matches the command's purpose for clarity.backend/tests/apps/slack/commands/owasp_test.py (1)
6-9
: Update the class name to match the pattern used in other test files.While the import has been updated to use the
Owasp
class, the test class name hasn't been updated to match the pattern used in other test files (e.g.,TestNewsCommand
,TestSponsorCommand
).-class TestOwaspHandler: +class TestOwaspCommand:
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/poetry.lock
is excluded by!**/*.lock
📒 Files selected for processing (58)
backend/apps/slack/commands/__init__.py
(2 hunks)backend/apps/slack/commands/board.py
(1 hunks)backend/apps/slack/commands/chapters.py
(1 hunks)backend/apps/slack/commands/command.py
(1 hunks)backend/apps/slack/commands/committees.py
(1 hunks)backend/apps/slack/commands/community.py
(1 hunks)backend/apps/slack/commands/contact.py
(1 hunks)backend/apps/slack/commands/contribute.py
(1 hunks)backend/apps/slack/commands/donate.py
(1 hunks)backend/apps/slack/commands/events.py
(1 hunks)backend/apps/slack/commands/gsoc.py
(1 hunks)backend/apps/slack/commands/jobs.py
(1 hunks)backend/apps/slack/commands/leaders.py
(1 hunks)backend/apps/slack/commands/news.py
(1 hunks)backend/apps/slack/commands/owasp.py
(1 hunks)backend/apps/slack/commands/policies.py
(1 hunks)backend/apps/slack/commands/projects.py
(1 hunks)backend/apps/slack/commands/sponsor.py
(1 hunks)backend/apps/slack/commands/sponsors.py
(1 hunks)backend/apps/slack/commands/staff.py
(1 hunks)backend/apps/slack/commands/users.py
(1 hunks)backend/apps/slack/models/event.py
(2 hunks)backend/apps/slack/template_system/loader.py
(1 hunks)backend/apps/slack/templates/chapters.template
(1 hunks)backend/apps/slack/templates/contribute.template
(1 hunks)backend/apps/slack/templates/donate.template
(1 hunks)backend/apps/slack/templates/events.template
(1 hunks)backend/apps/slack/templates/feedback.template
(1 hunks)backend/apps/slack/templates/gsoc.template
(1 hunks)backend/apps/slack/templates/jobs.template
(1 hunks)backend/apps/slack/templates/leaders.template
(1 hunks)backend/apps/slack/templates/navigate.template
(1 hunks)backend/apps/slack/templates/news.template
(1 hunks)backend/apps/slack/templates/owasp.template
(1 hunks)backend/apps/slack/templates/policies.template
(1 hunks)backend/apps/slack/templates/sponsors.template
(1 hunks)backend/apps/slack/templates/staff.template
(1 hunks)backend/pyproject.toml
(1 hunks)backend/tests/apps/slack/commands/board_test.py
(3 hunks)backend/tests/apps/slack/commands/chapters_test.py
(3 hunks)backend/tests/apps/slack/commands/committees_test.py
(3 hunks)backend/tests/apps/slack/commands/community_test.py
(3 hunks)backend/tests/apps/slack/commands/contact_test.py
(3 hunks)backend/tests/apps/slack/commands/contribute_test.py
(4 hunks)backend/tests/apps/slack/commands/donate_test.py
(3 hunks)backend/tests/apps/slack/commands/events_test.py
(2 hunks)backend/tests/apps/slack/commands/gsoc_test.py
(4 hunks)backend/tests/apps/slack/commands/jobs_test.py
(3 hunks)backend/tests/apps/slack/commands/leaders_test.py
(4 hunks)backend/tests/apps/slack/commands/news_test.py
(5 hunks)backend/tests/apps/slack/commands/owasp_test.py
(3 hunks)backend/tests/apps/slack/commands/policies_test.py
(2 hunks)backend/tests/apps/slack/commands/projects_test.py
(4 hunks)backend/tests/apps/slack/commands/sponsor_test.py
(2 hunks)backend/tests/apps/slack/commands/sponsors_test.py
(3 hunks)backend/tests/apps/slack/commands/staff_test.py
(3 hunks)backend/tests/apps/slack/commands/users_test.py
(3 hunks)backend/tests/apps/slack/models/event_test.py
(3 hunks)
🧰 Additional context used
🧬 Code Definitions (38)
backend/tests/apps/slack/commands/jobs_test.py (2)
backend/apps/slack/commands/jobs.py (1)
Jobs
(12-24)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/tests/apps/slack/commands/community_test.py (2)
backend/apps/slack/commands/community.py (1)
Community
(7-20)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/models/event.py (2)
backend/apps/slack/commands/owasp.py (1)
Owasp
(7-42)backend/apps/slack/commands/command.py (1)
get_command
(57-59)
backend/tests/apps/slack/commands/contact_test.py (2)
backend/apps/slack/commands/contact.py (1)
Contact
(7-18)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/__init__.py (2)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/commands/command.py (2)
CommandBase
(11-98)config_all_commands
(20-23)
backend/tests/apps/slack/commands/committees_test.py (2)
backend/apps/slack/commands/committees.py (1)
Committees
(8-25)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/tests/apps/slack/commands/events_test.py (1)
backend/apps/slack/commands/events.py (1)
Events
(8-38)
backend/tests/apps/slack/commands/donate_test.py (2)
backend/apps/slack/commands/donate.py (1)
Donate
(7-17)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/community.py (3)
backend/apps/slack/commands/command.py (4)
CommandBase
(11-98)get_template_file_name
(70-72)get_render_text
(25-36)get_template_file
(61-68)backend/apps/slack/commands/board.py (2)
get_template_file_name
(10-12)get_render_text
(14-18)backend/apps/slack/commands/contact.py (2)
get_template_file_name
(10-12)get_render_text
(14-18)
backend/apps/slack/commands/sponsor.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(11-98)get_render_text
(25-36)
backend/apps/slack/commands/projects.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(11-98)get_render_blocks
(38-55)backend/apps/slack/common/presentation.py (1)
EntityPresentation
(7-25)
backend/apps/slack/commands/contribute.py (2)
backend/apps/slack/commands/command.py (4)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)get_render_blocks
(38-55)backend/apps/slack/common/handlers/contribute.py (1)
get_blocks
(15-98)
backend/apps/slack/commands/jobs.py (1)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)
backend/tests/apps/slack/commands/chapters_test.py (2)
backend/apps/slack/commands/chapters.py (1)
Chapters
(9-29)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/tests/apps/slack/models/event_test.py (2)
backend/apps/slack/commands/owasp.py (1)
Owasp
(7-42)backend/apps/slack/commands/command.py (1)
get_command
(57-59)
backend/apps/slack/commands/sponsors.py (2)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)backend/apps/slack/utils.py (1)
get_sponsors_data
(136-152)
backend/tests/apps/slack/commands/policies_test.py (1)
backend/apps/slack/commands/policies.py (1)
Policies
(7-42)
backend/tests/apps/slack/commands/staff_test.py (8)
backend/apps/slack/commands/staff.py (1)
Staff
(8-23)backend/apps/slack/commands/command.py (1)
handler
(78-98)backend/tests/apps/slack/commands/events_test.py (2)
mock_slack_command
(41-44)mock_slack_client
(47-50)backend/tests/apps/slack/commands/gsoc_test.py (2)
mock_slack_command
(13-17)mock_slack_client
(20-23)backend/tests/apps/slack/commands/leaders_test.py (2)
mock_slack_command
(12-16)mock_slack_client
(19-22)backend/tests/apps/slack/commands/sponsors_test.py (2)
mock_slack_command
(35-38)mock_slack_client
(41-44)backend/tests/apps/slack/events/user_joined_channel/contribute_test.py (1)
mock_slack_client
(20-23)backend/tests/apps/slack/events/user_joined_channel/gsoc_test.py (1)
mock_slack_client
(16-19)
backend/apps/slack/commands/gsoc.py (3)
backend/apps/common/utils.py (1)
get_absolute_url
(13-23)backend/apps/slack/commands/command.py (4)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)get_command
(57-59)backend/apps/slack/utils.py (1)
get_gsoc_projects
(34-55)
backend/apps/slack/commands/events.py (2)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)backend/apps/slack/utils.py (1)
get_events_data
(120-133)
backend/apps/slack/commands/chapters.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(11-98)get_render_blocks
(38-55)backend/apps/slack/commands/contribute.py (1)
get_render_blocks
(18-35)
backend/tests/apps/slack/commands/contribute_test.py (3)
backend/apps/slack/commands/contribute.py (1)
Contribute
(10-35)backend/apps/slack/commands/command.py (1)
handler
(78-98)backend/apps/slack/commands/owasp.py (1)
handler
(20-28)
backend/apps/slack/commands/donate.py (15)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)backend/apps/slack/commands/board.py (1)
get_render_text
(14-18)backend/apps/slack/commands/contact.py (1)
get_render_text
(14-18)backend/apps/slack/commands/community.py (1)
get_render_text
(14-20)backend/apps/slack/commands/contribute.py (1)
get_render_text
(13-16)backend/apps/slack/commands/events.py (1)
get_render_text
(11-38)backend/apps/slack/commands/jobs.py (1)
get_render_text
(15-24)backend/apps/slack/commands/news.py (1)
get_render_text
(11-26)backend/apps/slack/commands/gsoc.py (1)
get_render_text
(31-70)backend/apps/slack/commands/leaders.py (1)
get_render_text
(12-64)backend/apps/slack/commands/owasp.py (1)
get_render_text
(30-42)backend/apps/slack/commands/policies.py (1)
get_render_text
(10-42)backend/apps/slack/commands/sponsors.py (1)
get_render_text
(12-26)backend/apps/slack/commands/sponsor.py (1)
get_render_text
(9-11)backend/apps/slack/commands/staff.py (1)
get_render_text
(11-23)
backend/tests/apps/slack/commands/projects_test.py (2)
backend/apps/slack/commands/projects.py (1)
Projects
(8-25)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/tests/apps/slack/commands/news_test.py (2)
backend/apps/slack/commands/news.py (1)
News
(8-26)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/tests/apps/slack/commands/sponsor_test.py (2)
backend/apps/slack/commands/sponsor.py (1)
Sponsor
(6-11)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/leaders.py (5)
backend/apps/slack/commands/command.py (4)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)get_command
(57-59)backend/apps/slack/utils.py (1)
escape
(20-30)backend/apps/owasp/api/search/chapter.py (1)
get_chapters
(8-44)backend/apps/owasp/api/search/project.py (1)
get_projects
(8-48)backend/apps/common/utils.py (1)
get_absolute_url
(13-23)
backend/tests/apps/slack/commands/users_test.py (2)
backend/apps/slack/commands/users.py (1)
Users
(8-25)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/news.py (2)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)backend/apps/slack/utils.py (1)
get_news_data
(59-91)
backend/apps/slack/commands/policies.py (1)
backend/apps/slack/commands/command.py (3)
CommandBase
(11-98)get_render_text
(25-36)get_template_file
(61-68)
backend/tests/apps/slack/commands/board_test.py (2)
backend/apps/slack/commands/board.py (1)
Board
(7-18)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/committees.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(11-98)get_render_blocks
(38-55)backend/apps/slack/common/presentation.py (1)
EntityPresentation
(7-25)
backend/tests/apps/slack/commands/owasp_test.py (1)
backend/apps/slack/commands/owasp.py (2)
Owasp
(7-42)handler
(20-28)
backend/tests/apps/slack/commands/gsoc_test.py (1)
backend/apps/slack/commands/gsoc.py (1)
Gsoc
(28-70)
backend/apps/slack/commands/contact.py (1)
backend/apps/slack/commands/command.py (4)
CommandBase
(11-98)get_template_file_name
(70-72)get_render_text
(25-36)get_template_file
(61-68)
backend/apps/slack/commands/owasp.py (1)
backend/apps/slack/commands/command.py (6)
CommandBase
(11-98)get_all_commands
(15-17)handler
(78-98)get_render_text
(25-36)get_command
(57-59)get_template_file
(61-68)
backend/tests/apps/slack/commands/sponsors_test.py (2)
backend/apps/slack/commands/sponsors.py (1)
Sponsors
(9-26)backend/apps/slack/commands/command.py (1)
handler
(78-98)
backend/apps/slack/commands/command.py (3)
backend/apps/slack/blocks.py (1)
markdown
(14-27)backend/apps/slack/commands/donate.py (1)
get_render_text
(10-17)backend/apps/slack/commands/sponsor.py (1)
get_render_text
(9-11)
🔇 Additional comments (162)
backend/apps/slack/templates/owasp.template (2)
1-18
: Clear and Consistent Help Command RenderingThe template uses a straightforward conditional (
{% if help %}
) to display a well-formatted list of available OWASP commands when help is requested. The use of inline code formatting for placeholders (e.g.{{ command }}
) is consistent across the list and enhances readability.
19-21
: Effective Fallback for Unsupported CommandsThe else block cleanly notifies the user that the given command is not supported by dynamically including
{{ command }}
and{{ handler }}
. This provides clear user feedback and maintains consistency with the rest of the template.backend/apps/slack/templates/gsoc.template (3)
1-7
: Robust 'General' Mode ImplementationThe 'general' mode section effectively outlines the starting steps for new GSoC participants. The use of dynamic placeholders (e.g.
{{ gsoc_channel }}
,{{ contribute_channel }}
) and the inclusion of a newline token ({{ NL }}
) ensures that the message adapts well to varying contexts.
8-11
: Clear Presentation of Year-Specific InformationThe 'year' mode correctly iterates over the provided projects with a concise for-loop, and the additional information segment leverages nested conditionals effectively. The formatting—with newlines and separators—improves the clarity of the output.
12-16
: Comprehensive Handling of Edge CasesBoth the "unsupported_year" and "invalid" branches offer clear, user-friendly messages when inputs fall outside expectations. Using dynamic placeholders in these branches maintains consistency and provides precise guidance to the users.
backend/apps/slack/templates/donate.template (2)
1-2
: Detailed Donation Information ProvidedThe template clearly explains the donation process, including organizational details, donation methods, and multi-currency support. The use of a newline placeholder (
{{ NL }}
) ensures proper formatting when rendered in Slack.
3-3
: Effective Call-to-Action with Visual BreaksThe incorporation of section breaks (
{{ SECTION_BREAK }}
) and dividers ({{ DIVIDER }}
) makes the call-to-action stand out, guiding users on where to donate. The dynamic URL placeholder further enhances maintainability.backend/apps/slack/templates/feedback.template (1)
1-2
: Concise and Engaging Feedback PromptThis template succinctly invites users to share their feedback, dynamically referencing the bot's name and the appropriate feedback channel. The clear call-to-action supports user engagement and aligns with the overall design.
backend/apps/slack/templates/navigate.template (1)
1-2
: Simple and Effective Navigation MessageThe template clearly instructs users to visit a dynamic URL, using placeholders to insert both the URL and display name. The inclusion of a newline (
{{ NL }}
) ensures the message maintains proper formatting in Slack outputs.backend/pyproject.toml (1)
49-49
: Dependency Addition: Jinja2
The new dependencyjinja2 = "^3.1.6"
has been added correctly under the[tool.poetry.dependencies]
section. This aligns with the PR objectives to introduce a Jinja2-based template system for Slack commands. Please ensure that using this version is compatible with the rest of the dependencies and that no conflicts occur during deployment.backend/apps/slack/templates/contribute.template (1)
1-4
: New Contribute Template
The template clearly lists the available commands for contributing (/contribute
and/contribute <search term>
). The formatting is clean and easy to read. This file effectively decouples command text from logic.backend/apps/slack/templates/chapters.template (1)
1-5
: New Chapters Template Review
The chapters template is succinct and clearly outlines the available commands for retrieving chapters. Its bullet list format improves readability and consistency with other command templates.backend/apps/slack/commands/__init__.py (2)
1-3
: Good addition of required imports for the new command system.These imports provide access to the Slack app configuration and the new command base class, which are essential for the template-based command system implementation.
26-27
: Good defensive programming with conditional configuration.The conditional check ensures that commands are only configured when a valid Slack app instance exists. This prevents potential errors in environments where Slack credentials aren't available (like testing or development).
backend/apps/slack/templates/sponsors.template (1)
1-17
: Well-structured template with proper conditional handling.This template effectively formats sponsor information for Slack messages with:
- Proper conditional checks for sponsor availability
- Special handling for sponsors with/without URLs
- Clear formatting with appropriate spacing and sections
- Inclusion of a feedback template for reusability
- Clean whitespace control using Jinja2's trimming syntax (
{%-
and-%}
)These characteristics align well with the PR objective of improving message consistency and maintainability.
backend/apps/slack/templates/staff.template (1)
1-13
: Well-implemented template with robust error handling.This template effectively formats staff information for Slack messages with:
- Proper conditional handling for staff data availability
- Clear organization of staff member details
- Good use of default filter for potentially missing descriptions
- Appropriate formatting with section breaks and dividers
- Clean whitespace control using Jinja2's trimming syntax
The template structure aligns well with the PR objective of improving message consistency and maintainability.
backend/apps/slack/templates/news.template (1)
1-9
: Well-designed template with enhanced visual elements.This template effectively formats news information for Slack messages with:
- Clear conditional handling for news availability
- Bullet-point formatting for easy readability
- Good use of emojis (
:newspaper:
and:warning:
) to enhance visual appeal- Proper hyperlink formatting for news titles
- Appropriate error message when news can't be fetched
These improvements align with the PR objective of enhancing message consistency and user experience.
backend/apps/slack/template_system/loader.py (1)
1-10
: Good implementation of Jinja2 template environment.The implementation is clean, concise, and follows best practices:
- Clear module docstring
- Proper path resolution using
Path(__file__).resolve()
- Security-focused by enabling
autoescape=True
- Simple and focused responsibility
backend/tests/apps/slack/commands/jobs_test.py (3)
7-7
: LGTM: Class-based import to support refactoring.The import has been properly updated to use the new
Jobs
class instead of the function-based handler, which aligns with the refactoring to a class-based command system.
33-35
: LGTM: Creating class instance instead of using function.The test has been correctly updated to instantiate the
Jobs
class and invoke its handler method, which aligns with the new object-oriented approach to command handling.
60-60
: LGTM: Updated assertion to handle whitespace differences.The assertion now strips whitespace from
FEEDBACK_CHANNEL_MESSAGE
before checking if it's in the response text, which makes the test more robust against formatting changes in the templates.backend/apps/slack/models/event.py (1)
6-6
: LGTM: Class-based import to support refactoring.The import has been properly updated to use the
Owasp
class, which aligns with the refactoring to a class-based command system.backend/tests/apps/slack/commands/users_test.py (3)
6-6
: LGTM: Class-based import to support refactoring.The import has been properly updated to use the new
Users
class instead of the function-based handler, which aligns with the refactoring to a class-based command system.
45-46
: LGTM: Creating class instance instead of using function.The test has been correctly updated to instantiate the
Users
class and invoke its handler method, which aligns with the new object-oriented approach to command handling.
79-80
: LGTM: Creating class instance instead of using function.The second test has also been properly updated to use the class-based approach consistently.
backend/tests/apps/slack/commands/committees_test.py (2)
6-6
: Import statement looks correct.
57-62
: Test logic ensures commands are properly handled.The approach of instantiating the
Committees
class and verifyingack
andchat_postMessage
calls is correct. This test effectively covers the scenario ofSLACK_COMMANDS_ENABLED
.backend/apps/slack/commands/staff.py (1)
4-5
: New imports align with the refactored architecture.backend/tests/apps/slack/commands/chapters_test.py (4)
6-6
: Good import change to reflect the new OOP structure.
No issues found; this aligns with the shift from procedural to class-based commands.
57-59
: Tests correctly instantiate and invoke theChapters
class.
The approach is consistent with the new object-oriented refactor.
61-61
: Ensuring the command is acknowledged.
Theack.assert_called_once()
check is appropriate to verify that Slack commands are properly acknowledged.
81-83
: Repeated pattern for chapter retrieval.
Again, this is consistent with the class-based command design and ensures the correct flow is tested.backend/apps/slack/commands/policies.py (4)
4-5
: Importing the base command class.
No issues here; referencingCommandBase
aligns with the new command architecture.
7-9
: Class definition aligns with the pattern for Slack commands.
Implementing policies as a subclass ofCommandBase
centralizes and standardizes command handling.
37-42
: Efficient templating approach.
Rendering withself.get_template_file()
centralizes layout and ensures consistency across commands.
10-35
:✅ Verification successful
Check that policy links are current and comprehensive.
Although the code is correct, policy links can break or become outdated over time. Periodic verification ensures users always see valid and up-to-date URLs.
🏁 Script executed:
#!/bin/bash # Description: Check the validity of linked policy URLs by returning their HTTP status codes. links=( "https://owasp.org/www-policy/operational/chapters" "https://owasp.org/www-policy/operational/code-of-conduct" "https://owasp.org/www-policy/operational/committees" "https://owasp.org/www-policy/operational/conflict-resolution" "https://owasp.org/www-policy/operational/conflict-of-interest" "https://owasp.org/www-policy/operational/donations" "https://owasp.org/www-policy/operational/election" "https://owasp.org/www-policy/operational/events" "https://owasp.org/www-policy/operational/expense-reimbursement" "https://owasp.org/www-policy/operational/grants" "https://owasp.org/www-policy/operational/membership" "https://owasp.org/www-policy/operational/projects" "https://owasp.org/www-policy/operational/whistleblower" ) for link in "${links[@]}"; do echo "Checking: $link" curl -s -o /dev/null -w "%{http_code}\n" "$link" doneLength of output: 4183
Verified: Policy Links are Updated and Valid
The policy URLs in
backend/apps/slack/commands/policies.py
(lines 10-35) have been verified and all returned HTTP status code 200, confirming that they are current and accessible. No changes are required at this time—please continue periodic checks to ensure ongoing link validity.backend/tests/apps/slack/models/event_test.py (2)
3-3
: Updated import to use the newOwasp
classThe import has been updated to use the new class-based implementation, which is consistent with the PR's objective of refactoring command handlers into class-based implementations.
12-12
: Correctly migrated to dynamic command retrievalThe previous hardcoded constants have been replaced with dynamic method calls to
Owasp().get_command()
, which ensures the command string is consistently derived from the class name. This is a good approach that reduces duplication and improves maintainability.Also applies to: 57-57
backend/tests/apps/slack/commands/donate_test.py (3)
7-7
: Updated import to use the new class-based implementationThe import has been correctly updated to use the new
Donate
class instead of the previous function-based implementation.
32-34
: Successfully migrated to class-based handlerThe test has been updated to instantiate the
Donate
class and call itshandler
method, which aligns with the new object-oriented design. This is consistent with the PR's objective of refactoring command handlers to use a template-based system.
62-64
: Consistent application of the class-based patternThe same pattern has been correctly applied here, using the class-based approach for the second test method as well. This maintains consistency throughout the test file.
backend/apps/slack/commands/projects.py (2)
3-3
: Appropriate base class importThe import of
CommandBase
is correct and aligns with the PR's objective of creating a unified base for all command classes.
8-10
: Well-structured class definitionThe
Projects
class is properly defined as inheriting fromCommandBase
, with an appropriate docstring that clearly indicates its purpose.backend/tests/apps/slack/commands/contact_test.py (3)
7-7
: Updated import to use the new class-based implementationThe import has been correctly updated to use the new
Contact
class instead of the previous function-based implementation.
32-34
: Successfully migrated to class-based handlerThe test has been updated to instantiate the
Contact
class and call itshandler
method, which aligns with the new object-oriented design. This change maintains the same test behavior while adapting to the refactored command structure.
49-51
: Consistent application of the class-based patternThe same pattern has been correctly applied here, using the class-based approach for the second test method as well. This maintains consistency throughout the test file and properly validates the behavior of the command handler.
backend/tests/apps/slack/commands/community_test.py (3)
7-7
: Change of import reflects new OOP structureThe import change from a function
community_handler
to a classCommunity
indicates a shift from a procedural implementation to an object-oriented approach, which aligns with the PR objectives to make the codebase more maintainable.
34-35
: Implementation follows OOP pattern consistentlyThe test now instantiates a
Community
class and calls itshandler
method instead of directly calling the function. This is consistent with the refactoring approach described in the PR objectives to implement a template-based system.
56-57
: Consistent testing approachThe second test method follows the same pattern of instantiating the
Community
class and calling itshandler
method, maintaining consistency throughout the test file.backend/tests/apps/slack/commands/events_test.py (2)
6-6
: Import aligned with class-based architectureThe change from importing
events_handler
to importing theEvents
class aligns with the architectural shift to class-based commands in the PR.
72-74
: OOP implementation with explicit ack mockThe test now creates an instance of the
Events
class and calls itshandler
method. The addition of an explicitMagicMock()
for theack
parameter ensures proper testing of the acknowledgment functionality.backend/tests/apps/slack/commands/board_test.py (3)
7-7
: Updated import consistent with refactoringThe import change from
board_handler
function to theBoard
class reflects the consistent application of OOP principles across the codebase.
32-34
: Refactored handler invocationThe test now creates a
Board
instance before the acknowledgment mock and calls the instance'shandler
method. This maintains the same test flow while adapting to the new class-based structure.
49-51
: Consistent refactoring in block structure testThe second test method follows the same pattern of creating a
Board
instance and calling itshandler
method, ensuring consistency across all test methods.backend/apps/slack/commands/users.py (2)
3-3
: Updated imports for new class-based approachThe import statement now brings in the
CommandBase
class that serves as the foundation for the template-based system mentioned in the PR objectives.
8-10
: New class structure consistent with PR objectivesThe introduction of the
Users
class that inherits fromCommandBase
aligns with the PR's goal to introdu 8000 ce a base class for Slack commands. This allows for standardized handling of commands and template integration.backend/tests/apps/slack/commands/contribute_test.py (5)
6-6
: Looks good importing Contribute class.
The import ofContribute
aligns with the new class-based command approach.
41-41
: Mock return value usage approved.
Settingmock.return_value = 10
is appropriate and consistent with the test scenario for issue counts.
59-61
: Successfully transitioned to class-based handler.
InstantiatingContribute
and calling itshandler
method is a clean replacement for the old function-based approach.
63-63
: Good use of ack assertion.
Confirming thatack
is called exactly once ensures the handler logic aligns with Slack’s requirements.
83-85
: Consistent pattern for class-based testing.
Reusing the instantiation andhandler
invocation confirms the class-based approach remains consistent in this test scenario.backend/apps/slack/commands/board.py (4)
4-4
: Appropriate import for the base command class.
CommandBase
import affirms the shared structure for Slack commands.
7-8
: Well-defined Board command class.
Creating a dedicatedBoard
class with docstring clarifies its usage and improves maintainability.
10-12
: Properly named template file.
Providing"navigate.template"
ensures a distinct Jinja2 template for the/board
command.
14-18
: Jinja2 rendering approach is clear.
Using a template with placeholders for URL and name fosters maintainable, flexible responses.backend/apps/slack/commands/jobs.py (4)
4-4
: Consistent base command usage.
ImportingCommandBase
reflects the unified pattern for Slack commands.
5-9
: Appropriate constants import.
Utilizing constants fromapps.slack.constants
promotes clarity and maintainability for Slack channel references.
12-13
: Clear class documentation.
TheJobs
class and docstring assist in understanding the purpose of this command.
15-24
: Template-based text rendering is well-structured.
Populating placeholders for Slack channels and bot name aligns with the new Jinja2 rendering flow.backend/apps/slack/commands/chapters.py (2)
3-3
: New import aligns with the class-based architecture
No issues found. The import fromCommandBase
cleanly enables the new command structure.
9-10
: Class-based refactor promotes clarity
DefiningChapters
as a subclass ofCommandBase
is consistent with the new design. The docstring is succinct and helpful.backend/apps/slack/commands/contribute.py (3)
4-4
: Import for class-based command
This import is needed for the new approach. No concerns here.
10-11
: Class creation aligns with new design
InstantiatingContribute
fromCommandBase
promotes a consistent architecture for Slack commands.
13-16
: Template rendering withNL
PassingNL=NL
is a neat approach to handle newline needs within the template. Make sure templates requiring newlines are updated accordingly.backend/apps/slack/commands/committees.py (2)
3-3
: Import for class-based command
UsingCommandBase
lays the foundation for the revised Slack command structure. No issues found.
8-9
: Committees class introduction
This direct subclass usage ensures each command is well-encapsulated. Class docstring is clear and descriptive.backend/tests/apps/slack/commands/leaders_test.py (4)
7-7
: Good update of import to align with class-based refactoring.This change correctly imports the new
Leaders
class instead of the previousleaders_handler
function to support the shift to an object-oriented approach.
67-70
: Proper implementation of the new class-based handler structure.This refactoring successfully transitions from directly calling the
leaders_handler
function to instantiating theLeaders
class and invoking itshandler
method. This aligns with the new architecture while preserving the original test behavior.
107-108
: Consistent implementation of the Leaders class pattern.This section correctly follows the same pattern as in the previous test method, ensuring consistency throughout the test file.
146-149
: Well-structured implementation of the Leaders class pattern.This consistent implementation completes the refactoring of all test methods to use the new class-based approach, maintaining functionality while improving code organization.
backend/tests/apps/slack/commands/projects_test.py (3)
7-7
: Good update of import to support class-based structure.The import statement has been correctly updated to import the
Projects
class instead of the previous function-based handler.
54-61
: Properly implemented class-based handler with improved assertions.The refactoring correctly instantiates the
Projects
class and calls itshandler
method. The updated conditional structure with explicit assertions for both the enabled and disabled cases improves test clarity and coverage.
82-83
: Consistent application of the Projects class pattern.This section maintains the same pattern used in other test methods, ensuring consistency throughout the test suite.
backend/apps/slack/commands/events.py (3)
3-6
: Streamlined imports for the new class-based implementation.The imports have been properly simplified to include only what's necessary for the new class-based approach. This makes the code more maintainable and focused.
8-11
: Well-structured class definition with clear docstrings.The new
Events
class properly inherits fromCommandBase
and includes appropriate documentation, adhering to good object-oriented design principles.
11-38
: Comprehensive implementation of template-based rendering.This
get_render_text
method effectively processes event data and prepares it for templated rendering. The approach of categorizing events and constructing a structured data object enhances maintainability.Note that the method includes proper handling of events with missing categories by assigning them to "Other", which prevents potential errors.
backend/apps/slack/commands/sponsor.py (2)
3-3
: Simplified import for the new class-based implementation.The import statement has been properly streamlined to import only the
CommandBase
class, which is all that's needed for this placeholder implementation.
6-11
: Good placeholder implementation with minimal structure.This is a clean implementation of a placeholder command. The class correctly inherits from
CommandBase
and theget_render_text
method returns a simple "Coming soon..." message.While minimal, this approach allows the command to be registered and function within the system while deferring the actual implementation details for later development.
backend/tests/apps/slack/commands/policies_test.py (6)
7-7
: No issues with updated import
The new import aligns with the class-based approach for handling policies.
27-28
: Instantiation of thePolicies
class
Creating an instance and callinghandler
shifts the functionality from a function-based to an object-oriented approach cleanly.
65-65
: String building for policies
The use of a join operation and standard Slack formatting is clear and maintainable. No issues found.
67-67
: Assertion withstrip()
Excellent approach to handle extraneous whitespace in block text comparisons.
70-71
: Updated footer text
The footer text now points to the central policy page. This helps users find more information easily.
73-73
: Footer assertion
The assertion logic is straightforward and ensures correctness of the closing section.backend/apps/slack/commands/sponsors.py (2)
4-5
: Updated imports for class-based command
Switching to theCommandBase
import matches the new design. No concerns.
9-26
:❓ Verification inconclusive
Refactored Sponsors command
This class-based approach centralizes the sponsor logic and usesJinja2
templates. Ensure both the "no sponsors" and "some sponsors" scenarios are adequately tested.
🏁 Script executed:
#!/bin/bash # Execute sponsor command tests pytest backend/tests/apps/slack/commands/sponsors_test.py --maxfail=1 -qLength of output: 171
Manual Verification Needed: Testing Environment Issue
The refactored Sponsors command using Jinja2 templates looks good. However, the testing command returned an error ("pytest: command not found"), which means we couldn't verify that both the "no sponsors" and "sponsors available" scenarios are adequately covered. Please ensure the test environment includes pytest (or run the tests manually/with an alternative command such as
python -m pytest
) and verify the functionality accordingly. Once the tests pass in a proper environment, you can merge confidently.backend/apps/slack/commands/donate.py (3)
4-4
: Base class import
The import fromcommand
is correct and aligns with the class-based design.
7-8
: Introducing theDonate
class
A clean transition from function-based to class-based command structure. The docstring is concise.
10-17
: Template-based rendering for the donate command
Theget_render_text
method uses the inherited template:
• Incorporation ofOWASP_WEBSITE_URL
and placeholders is clear.backend/tests/apps/slack/commands/staff_test.py (6)
1-1
: Imports look appropriate.No issues with adding
ANY, MagicMock, patch
to the imports. This aligns with the new mocking logic.
7-7
: Confirmed import consistency.Importing
Staff
is consistent with the usage in the tests below and adheres to the new class-based command structure.
65-67
: New class-based testing approach is coherent.Switching to instantiating
Staff()
and calling itshandler
method is consistent with the move to class-based commands. This test aligns well with the updated design.
82-82
: Staff description check is valid.Asserting
staff["description"] in staff_block
ensures that each staff member's description is displayed properly. No issues here.
85-87
: Verifies correct user conversation.Ensuring
conversations_open
is called with the Slack user ID confirms a proper Slack conversation is started. This is robust.
89-90
: Blocks replaced with ANY.Using
ANY
for the blocks parameter is appropriate when the test focuses on other response aspects (e.g., channel). This is acceptable for broader coverage.backend/apps/slack/commands/community.py (4)
4-4
: Import aligns with new command structure.Importing
CommandBase
here conforms to the refactored class-based architecture for Slack commands.
7-8
: New Community command class.Defining
Community
as a subclass ofCommandBase
encapsulates command logic neatly, enhancing maintainability. Good approach.
10-12
: Template file resolution.
get_template_file_name
returning"navigate.template"
passes a consistent template name downstream. Straightforward and clear.
14-20
: Renders the community template.
get_render_text
uses the Jinja2 template to embed the URL and name for the OWASP community. The code is succinct and leveragesNL
for formatting. Looks good.backend/apps/slack/commands/gsoc.py (5)
3-7
: Added timezone and utility imports.Including
django.utils.timezone
,get_absolute_url
, andCommandBase
supports the new class-based Slack command approach. No issues found.
9-15
: Relevant Slack constants.Importing
NEST_BOT_NAME
and channel IDs centralizes Slack references for this command. This is consistent with the revised design.
22-25
: Year-based logic setup.Defining
MARCH
,now
, andgsoc_year
ensures the GSoC year is calculated correctly based on the current month. This is a sound approach to handle annual changes.
28-29
: Gsoc class introduction.Transitioning from a standalone function to the
Gsoc
class improves structure and maintainability.
31-70
: Comprehensive text rendering for GSoC.The
get_render_text
method:
- Distinguishes between empty, numeric, and invalid command text cases.
- Dynamically handles valid & invalid GSoC years, referencing
SUPPORTED_YEARS
.- Returns different template modes (general, year, unsupported_year, invalid).
- Uses
get_gsoc_projects
for retrieving data, sorted for readability.This neatly encapsulates logic and ensures clarity for Slack responses. Great use of templating.
backend/tests/apps/slack/commands/sponsors_test.py (3)
6-6
: Good import alignment with new class structure.Importing
Sponsors
aligns well with the refactoring into a class-based approach, matching the overall design of the PR.
77-79
: LGTM.Verifying
conversations_open
with the user ID is appropriate.
93-93
: Assert partial match or exact match?Checking for
expected_header
substring makes the test more flexible. If you want to ensure strict equality, revert to an equality check. Otherwise, this approach is suitable for handling possible formatting variations.backend/apps/slack/commands/news.py (5)
4-5
: Clear separation of command base and utility imports.These imports are concise and reflect the new class-based design.
8-9
: Class-based design improves maintainability.Defining
News
as a subclass ofCommandBase
aligns with the PR's goal of modularizing Slack commands.
11-13
: Simplified approach to news data retrieval.Calling
get_news_data()
insideget_render_text
is straightforward. Ensure any exceptions (e.g., network failures) are handled appropriately in upstream or the utility function.Do we have a higher-level error handling strategy for
get_news_data()
calls? Please confirm coverage in related tests or usage logs.
14-22
: Conditional template rendering looks good.Using the template to handle both cases (
has_news=True
andhas_news=False
) is clean and maintains separation of logic from presentation.
23-26
: Fallback path for no news items.Returning a separate rendered template states “no news.” This elegantly handles empty data scenarios.
backend/apps/slack/commands/leaders.py (8)
5-6
: Appropriate imports for the new command class.Pulling in
CommandBase
andescape
is consistent with the class-based Slack command pattern.
9-10
: Descriptive class docstring.The docstring for
/leaders
is concise and aligns with the Slack command naming conventions.
12-19
: Correctly escaping user input to prevent injection.Capturing and escaping
command["text"]
is a good step to avoid potential injection or formatting issues.
20-29
: Selective attributes ensure minimal data retrieval.Limiting returned fields (
idx_key
,idx_leaders
,idx_name
) can help performance and maintain focus in the Slack response.
31-37
: Parallel retrieval logic for projects.Consistent approach for
get_projects
ensures uniform handling of the user’s query.
39-47
: Building chapter URLs enhances clarity.Populating
chapters_with_urls
is understandable and helps later template rendering.
48-56
: Consistent approach for project URLs.Following the same pattern for
projects_with_urls
is maintainable and coherent.
57-64
: Template rendering covers multiple use cases.Passing
has_results
and the query details to the template ensures that UI can adapt to presence or absence of data.backend/apps/slack/commands/owasp.py (5)
3-3
: Migration to OOP structure
This updated import aligns with the new class-based design.
7-9
: Class-based approach for /owasp command
Defining a dedicatedOwasp
class is consistent with the existing command architecture.
20-29
: Chained command dispatch
Reusingcommand["text"]
for sub-commands is reasonable. However, if the subsequent command expects multiple tokens, ensure they are preserved.
30-42
: Template context ensures user input is escaped
User input is passed into the Jinja2 template. Please confirm autoescaping is enabled or that your templates sanitize/escape user input to prevent XSS.
10-19
:❓ Verification inconclusive
Check for potential edge cases in command lookup
The case-insensitive check is helpful, but confirm no unexpected partial matches or synonyms are required.Run the following script to locate all
find_command
calls and confirm usage expectations:
🏁 Script executed:
#!/bin/bash rg -A 5 'find_command'Length of output: 1730
Edge Case Handling in Command Lookup
The current implementation in
backend/apps/slack/commands/owasp.py
does a case-insensitive equality check, which seems aligned with its usage (as observed in both production and test files). However, please verify whether the existing logic fully meets the requirements for handling edge cases (for example, unexpected partial matches, synonyms, extra whitespace, or alternate aliases). If these scenarios should be supported, we might need to adjust the lookup logic or supplement with additional tests.backend/apps/slack/commands/contact.py (3)
4-4
: Refactored import
Switching toCommandBase
is consistent with the project's new class-based pattern.
7-8
: Dedicated class for /contact command
This class-based structure neatly encapsulates the/contact
logic.
14-18
: Context injection for contact template
Providingurl
,name
, andNL
is appropriate. Ensure the template references them correctly.backend/tests/apps/slack/commands/gsoc_test.py (6)
7-7
: Class import for Gsoc
Replacing the old function import with theGsoc
class aligns with the new design.
11-11
: Test class rename
Renaming toTestGsocCommand
better reflects that this test targets theGsoc
command class.
33-33
: Improved feedback message
Simplifying the invalid command message to "*/gsoc invalid
is not supported" is clear and user-friendly.
48-50
: Instantiation and handler call
InstantiatingGsoc
and then invokinghandler
is consistent with the new OOP approach.
63-63
: Refined assertion
Checking for "Getting Started with OWASP GSoC" corresponds to the updated template content.
78-83
: Mock project retrieval workflow
Properly patchesget_gsoc_projects
and verifies the resulting Slack blocks. This test flow validates the new approach thoroughly.backend/apps/slack/commands/command.py (5)
1-9
: Well-structured imports and documentation.The file has a clear module docstring and well-organized imports, separating standard library, Django, and application-specific imports.
11-24
: Good use of OOP principles with static methods for command management.The
CommandBase
class is well-designed with static methods to handle subclass discovery and configuration. This approach makes it easy to register all commands in a centralized way.
57-60
: LGTM!Good approach for command naming based on class name. This provides a consistent convention.
61-73
: Good template file handling approach.The methods for getting template files and their names follow a consistent naming convention based on the class name. This is a clean approach that will make template management straightforward.
74-77
: LGTM!Effective command registration with the Slack app.
backend/tests/apps/slack/commands/news_test.py (5)
6-10
: Class rename aligns with the new command-based approach.The import change and class rename from
TestNewsHandler
toTestNewsCommand
appropriately reflect the shift to the command-based architecture.
21-27
: Improved parameterization formatting.The test parameterization has been reformatted for better readability while maintaining the same logic.
50-51
: Object-oriented approach correctly implemented.The test now properly instantiates a
News
object and calls itshandler
method instead of using the previous function-based approa 8000 ch.
60-61
: Updated assertion logic for block content verification.The assertion logic has been updated to use
in
for checking block content and modified the slicing to exclude the last two blocks. This accommodates changes in the response format from the template-based system.
82-84
: Object-oriented approach consistently applied.The test for the no-items case also properly instantiates a
News
object and calls itshandler
method.backend/tests/apps/slack/commands/sponsor_test.py (3)
6-9
: Class rename aligns with the new command-based approach.The import change and class rename from
TestSponsorHandler
toTestSponsorCommand
appropriately reflect the shift to the command-based architecture.
30-31
: Object-oriented approach correctly implemented.The test now properly instantiates a
Sponsor
object and calls itshandler
method instead of using the previous function-based approach.
39-40
: Updated expected text to match template output.The expected text has been simplified from
f"Coming soon...{NL}"
to just"Coming soon..."
, which matches the implementation in theSponsor
class'sget_render_text
method.backend/tests/apps/slack/commands/owasp_test.py (5)
37-40
: Object-oriented approach correctly implemented.The test now properly instantiates an
Owasp
object and calls itshandler
method instead of using the previous function-based approach.
48-49
: Good additional assertion for disabled commands.Added an important assertion to verify that when commands are disabled,
chat_postMessage
is not called.
51-53
: Simplified parameter format.The parameterization has been simplified to a single list of subcommands, which is cleaner than the previous approach.
57-68
: Improved patching approach.The patching logic has been updated to use
patch.object
on theOwasp.find_command
method, which is more direct and easier to understand than the previous module path patching approach.
70-75
: Updated assertion logic for command output.The test now checks the content of the blocks directly, which is more straightforward than the previous approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (10)
backend/apps/slack/commands/jobs.py (1)
11-24
: Consider naming consistency forfeedback_channel
.In
get_template_context
,feedback_channel
is set toOWASP_PROJECT_NEST_CHANNEL_ID
, which might lead to some confusion if it’s genuinely intended for feedback collection. Consider renaming the variable or adding a short comment clarifying its purpose. This also helps maintain clarity if used outside the scope of actual feedback.backend/apps/slack/commands/contribute.py (1)
31-35
: Theget_template_context
method is redundantThis method doesn't add any unique context beyond what's already provided by the parent class. Since it simply returns the parent's context without modification, you can safely remove this method.
- def get_template_context(self, command): - """Get the template context.""" - return { - **super().get_template_context(command), - }backend/apps/slack/commands/leaders.py (2)
12-14
: Consider moving imports to module level.While importing functions inside a method follows good practice for import locality, consider moving these imports to the module level if they're always needed by this command. This would avoid repeated import overhead on each command execution.
from apps.common.utils import get_absolute_url from apps.slack.commands.command import CommandBase +from apps.owasp.api.search.chapter import get_chapters +from apps.owasp.api.search.project import get_projects class Leaders(CommandBase): """Slack bot /leaders command.""" def get_template_context(self, command): """Get the template context.""" - from apps.owasp.api.search.chapter import get_chapters - from apps.owasp.api.search.project import get_projects
54-63
: Consider using a single dictionary creation for context.For consistency with other command implementations (like Sponsors), consider using a single dictionary creation pattern that extends the parent context, rather than creating and then updating it separately.
- context = super().get_template_context(command) - context.update( - { - "chapters": chapters_with_urls, - "projects": projects_with_urls, - "search_query": search_query, - "has_results": bool(chapters or projects), - } - ) - return context + return { + **super().get_template_context(command), + "chapters": chapters_with_urls, + "projects": projects_with_urls, + "search_query": search_query, + "has_results": bool(chapters or projects), + }backend/apps/slack/commands/gsoc.py (1)
33-75
: Consider breaking down the template context creation into helper methods.The current implementation uses a series of conditionals to update the template context. As this function grows, it may become harder to maintain. Consider breaking it down into smaller helper methods for each mode.
def get_template_context(self, command): """Get the template context.""" + now = timezone.now() + gsoc_year = now.year if now.month > MARCH else now.year - 1 command_text = command["text"].strip() context = super().get_template_context(command) template_context = {"mode": "", "command": self.get_command()} if not command_text or command_text in COMMAND_START: - template_context.update( - { - "mode": "general", - "gsoc_channel": OWASP_GSOC_CHANNEL_ID, - "contribute_channel": OWASP_CONTRIBUTE_CHANNEL_ID, - "previous_year": gsoc_year, - "projects_url": projects_url, - "feedback_channel": OWASP_PROJECT_NEST_CHANNEL_ID, - "nest_bot_name": NEST_BOT_NAME, - } - ) + template_context.update(self._get_general_context(gsoc_year)) elif command_text.isnumeric(): year = int(command_text) if year in SUPPORTED_YEARS: - template_context.update( - { - "mode": "year", - "year": year, - "projects": sorted(get_gsoc_projects(year), key=lambda p: p["idx_name"]), - "has_announcement": year in SUPPORTED_ANNOUNCEMENT_YEARS, - } - ) + template_context.update(self._get_year_context(year)) else: - template_context.update( - { - "mode": "unsupported_year", - "year": year, - "supported_start": SUPPORTED_YEAR_START, - "supported_end": SUPPORTED_YEAR_END, - } - ) + template_context.update(self._get_unsupported_year_context(year)) else: - template_context.update( - { - "mode": "invalid", - "command_text": command_text, - } - ) + template_context.update(self._get_invalid_context(command_text)) context.update(template_context) return context + + def _get_general_context(self, gsoc_year): + """Get the general context.""" + return { + "mode": "general", + "gsoc_channel": OWASP_GSOC_CHANNEL_ID, + "contribute_channel": OWASP_CONTRIBUTE_CHANNEL_ID, + "previous_year": gsoc_year, + "projects_url": projects_url, + "feedback_channel": OWASP_PROJECT_NEST_CHANNEL_ID, + "nest_bot_name": NEST_BOT_NAME, + } + + def _get_year_context(self, year): + """Get the year context.""" + return { + "mode": "year", + "year": year, + "projects": sorted(get_gsoc_projects(year), key=lambda p: p["idx_name"]), + "has_announcement": year in SUPPORTED_ANNOUNCEMENT_YEARS, + } + + def _get_unsupported_year_context(self, year): + """Get the unsupported year context.""" + return { + "mode": "unsupported_year", + "year": year, + "supported_start": SUPPORTED_YEAR_START, + "supported_end": SUPPORTED_YEAR_END, + } + + def _get_invalid_context(self, command_text): + """Get the invalid context.""" + return { + "mode": "invalid", + "command_text": command_text, + }backend/apps/slack/commands/owasp.py (2)
10-18
: Consider using a more efficient command lookup mechanism.The current implementation iterates through all command classes to find a matching one. As the number of commands grows, this could become inefficient. Consider using a dictionary to map command names to command classes.
class Owasp(CommandBase): """Slack bot /owasp command.""" + def __init__(self): + """Initialize the command.""" + super().__init__() + self._command_map = None + + def _build_command_map(self): + """Build the command map.""" + self._command_map = {} + for cmd_class in [cls for cls in CommandBase.get_all_commands() if cls is not Owasp]: + self._command_map[cmd_class.__name__.lower()] = cmd_class + def find_command(self, command_name): """Find the command class by name.""" if not command_name: return None - for cmd_class in [cls for cls in CommandBase.get_all_commands() if cls is not Owasp]: - if cmd_class.__name__.lower() == command_name.lower(): - return cmd_class() - return None + if self._command_map is None: + self._build_command_map() + + cmd_class = self._command_map.get(command_name.lower()) + return cmd_class() if cmd_class else None
22-26
: Consider making a copy of the command object when delegating.The current implementation modifies the command's text in-place before delegating, which could lead to confusion if the original command text is referenced elsewhere in the code. Consider creating a new command object or using a different approach to pass the command parameters.
def handler(self, ack, command, client): """Handle the command.""" command_tokens = command["text"].split() cmd = self.find_command(command_tokens[0].strip().lower() if command_tokens else "") if cmd: - command["text"] = " ".join(command_tokens[1:]).strip() - return cmd.handler(ack, command, client) + # Create a modified copy of the command object + delegated_command = command.copy() + delegated_command["text"] = " ".join(command_tokens[1:]).strip() + return cmd.handler(ack, delegated_command, client)backend/apps/slack/commands/command.py (3)
1-2
: Rename the file-level docstring to reflect the base class more accurately.Currently, the docstring says "Slack bot board command," but this file implements a generic
CommandBase
. Renaming it to something like "Slack bot base command" maintains clarity and consistency.-"""Slack bot board command.""" +"""Slack bot base command."""
35-47
: Optional caching or preloading of Jinja2 templates.If your deployment environment frequently loads and renders the same template, you could consider caching the template object. This would reduce the overhead of loading templates repeatedly, though this is typically minor for smaller projects.
101-135
: Consider providing ephemeral responses or user context.Currently, all messages are sent via direct message to the user’s Slack channel. As an enhancement, you could:
- Offer an option for ephemeral (in-channel) responses if needed.
- Log minimal user context (e.g., user ID) at debug level for troubleshooting, if privacy policies permit.
These steps can help refine the user experience and debugging workflow.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/poetry.lock
is excluded by!**/*.lock
📒 Files selected for processing (20)
backend/apps/slack/commands/board.py
(1 hunks)- < 8000 code class="notranslate">backend/apps/slack/commands/command.py (1 hunks)
backend/apps/slack/commands/community.py
(1 hunks)backend/apps/slack/commands/contact.py
(1 hunks)backend/apps/slack/commands/contribute.py
(2 hunks)backend/apps/slack/commands/donate.py
(1 hunks)backend/apps/slack/commands/events.py
(1 hunks)backend/apps/slack/commands/gsoc.py
(1 hunks)backend/apps/slack/commands/jobs.py
(1 hunks)backend/apps/slack/commands/leaders.py
(1 hunks)backend/apps/slack/commands/news.py
(1 hunks)backend/apps/slack/commands/owasp.py
(1 hunks)backend/apps/slack/commands/policies.py
(1 hunks)backend/apps/slack/commands/sponsors.py
(1 hunks)backend/apps/slack/commands/staff.py
(1 hunks)backend/apps/slack/templates/donate.template
(1 hunks)backend/apps/slack/templates/jobs.template
(1 hunks)backend/apps/slack/templates/leaders.template
(1 hunks)backend/apps/slack/templates/staff.template
(1 hunks)frontend/.dockerignore
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/.dockerignore
🚧 Files skipped from review as they are similar to previous changes (3)
- backend/apps/slack/templates/jobs.template
- backend/apps/slack/templates/donate.template
- backend/apps/slack/templates/staff.template
🧰 Additional context used
🧬 Code Definitions (11)
backend/apps/slack/commands/news.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_news_data
(59-91)
backend/apps/slack/commands/policies.py (14)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/commands/board.py (1)
get_template_context
(13-19)backend/apps/slack/commands/contact.py (1)
get_template_context
(13-19)backend/apps/slack/commands/community.py (1)
get_template_context
(13-19)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/contribute.py (1)
get_template_context
(31-35)backend/apps/slack/commands/events.py (1)
get_template_context
(11-36)backend/apps/slack/commands/jobs.py (1)
get_template_context
(14-24)backend/apps/slack/commands/gsoc.py (1)
get_template_context
(30-77)backend/apps/slack/commands/leaders.py (1)
get_template_context
(10-63)backend/apps/slack/commands/owasp.py (1)
get_template_context
(30-40)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)backend/apps/slack/commands/news.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/board.py (3)
backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_template_file_name
(97-99)get_template_context
(48-63)backend/apps/slack/commands/contact.py (2)
get_template_file_name
(9-11)get_template_context
(13-19)backend/apps/slack/commands/community.py (2)
get_template_file_name
(9-11)get_template_context
(13-19)
backend/apps/slack/commands/events.py (4)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_events_data
(120-133)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/donate.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/jobs.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)
backend/apps/slack/commands/contribute.py (1)
backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_render_blocks
(65-82)get_template_context
(48-63)
backend/apps/slack/commands/leaders.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)
backend/apps/slack/commands/owasp.py (1)
backend/apps/slack/commands/command.py (4)
CommandBase
(16-134)get_all_commands
(25-27)handler
(105-134)get_template_context
(48-63)
backend/apps/slack/commands/gsoc.py (3)
backend/apps/common/utils.py (1)
get_absolute_url
(13-23)backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_template_context
(48-63)get_command
(84-86)backend/apps/slack/utils.py (1)
get_gsoc_projects
(34-55)
backend/apps/slack/commands/command.py (2)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/blocks.py (1)
markdown
(14-27)
🔇 Additional comments (30)
backend/apps/slack/templates/leaders.template (1)
1-37
: The template structure looks well organized and follows best practices for template design.The template effectively handles different display scenarios:
- Shows chapters with their leaders when available
- Shows projects with their leaders when available
- Displays a "No results found" message when there are no matches
- Highlights matching leaders by formatting them as code when they match the search query
The use of Jinja2 conditionals and loops is clean and readable, making future maintenance easier. This template-based approach aligns well with the PR objectives of separating logic from presentation and enabling future localization.
backend/apps/slack/commands/policies.py (3)
3-4
: Import correctly uses the new CommandBase class.The import statement properly references the base class from which all command classes now inherit, following the new architecture.
6-8
: Good class definition with descriptive docstring.The class definition follows the established pattern of inheriting from CommandBase and includes a clear docstring explaining its purpose.
9-39
: Well-structured method for template context preparation.The
get_template_context
method properly:
- Defines a comprehensive list of policies with their titles and URLs
- Extends the parent context using the
super()
call- Returns a merged context dictionary with the policies list
This implementation aligns well with the pattern used across other command files and provides a clean separation between data and presentation.
backend/apps/slack/commands/staff.py (3)
3-6
: Imports are clear and appropriate.The imports correctly include:
- Constants needed for URLs
- The CommandBase class for inheritance
- The utility function to retrieve staff data
8-10
: Class definition follows the established pattern.The Staff class properly inherits from CommandBase and includes a descriptive docstring.
11-22
: Well-implemented template context method.The
get_template_context
method:
- Properly calls the parent's context method first
- Retrieves staff data using the appropriate utility function
- Updates the context with staff-specific information including:
- Flag indicating if staff data is available
- The staff items themselves
- Website URL for reference
The implementation is consistent with the approach used in other command files.
backend/apps/slack/commands/board.py (4)
3-3
: Import correctly uses the new CommandBase class.The import statement properly references the base class from which all command classes now inherit.
6-8
: Class definition follows the established pattern.The Board class properly inherits from CommandBase and includes a descriptive docstring.
9-11
: Template file name is correctly overridden.This method properly overrides the default implementation from CommandBase to specify a shared template ("navigate.template") rather than using a command-specific one. This promotes template reuse across commands with similar display patterns.
13-19
: Well-implemented template context method.The
get_template_context
method:
- Properly calls the parent's context method using
super()
- Extends the context with command-specific data (URL and name)
- Uses the Python dictionary unpacking operator (
**
) correctlyThis implementation is consistent with the pattern used in other similar commands that navigate to external resources.
backend/apps/slack/commands/jobs.py (1)
3-8
: Imports are clear and aligned with new command structure.These imports correctly bring in the base command class and relevant constants. Nothing problematic here.
backend/apps/slack/commands/news.py (3)
3-5
: Imports for news retrieval and base command are appropriate.All necessary references are succinctly included with no apparent issues.
8-9
: Class definition aligns with the new Slack command convention.The
News
class neatly inherits fromCommandBase
, enabling the codebase’s updated approach to Slack commands.
11-23
:❓ Verification inconclusive
Consider verifying handling of failed or empty news data.
While the code appears correct, ensure
get_news_data()
consistently returns a list—even on error. If there’s any possibility of returningNone
or raising an exception, handle or log it to avoid potential runtime errors.Run this script to confirm unit tests cover scenarios where
get_news_data()
is empty or fails:
🏁 Script executed:
#!/bin/bash # Searches for test cases covering get_news_data usage within the Slack news command tests. fd -e py -I -t f | xargs rg -A 5 "test_.*news"Length of output: 1710
Ensure Consistent Return Type for News Data
The tests confirm that when there are no news items (see
test_news_handler_no_items
),get_news_data()
correctly returns an empty list. However, please verify that the implementation ofget_news_data()
guarantees a list even under error conditions. If there’s any chance it could returnNone
or raise an exception, consider adding error handling or logging to avoid potential runtime issues.backend/apps/slack/commands/events.py (2)
3-5
: Imports are concise and purposeful.These references correctly link the base command, event retrieval, and constants. Nothing appears problematic.
8-9
: Class definition is consistent with the command redesign.Adhering to the inherited
CommandBase
pattern fosters maintainability and uniformity across Slack commands.backend/apps/slack/commands/contribute.py (1)
12-17
: Good implementation of conditional renderingThe approach to handling both help commands and search queries is well-implemented. For help commands, it delegates to the parent class's template-based rendering, while for search queries it uses the specialized
get_blocks
function.backend/apps/slack/commands/contact.py (2)
9-11
: Good reuse of shared templatesUsing a shared
navigate.template
for multiple navigation-type commands is an efficient approach that promotes code reuse.
13-19
: Clean implementation of template contextThe context provision is clear and appropriately extends the parent class's context. The URL and name parameters will be correctly passed to the template.
backend/apps/slack/commands/donate.py (2)
10-15
: Clean implementation of template contextThe template context is properly extended with the website URL. The approach is consistent with other commands.
7-8
:✅ Verification successful
Note on template file name
Unlike some other commands, this class doesn't override
get_template_file_name()
, so it will use the default implementation which returns"donate.template"
. Make sure this template file exists.
🏁 Script executed:
#!/bin/bash # Check if the donate.template file exists in the templates directory echo "Checking for donate.template file..." fd "donate.template" --type fLength of output: 157
Donor Template File Confirmed
The
donate.template
file exists atbackend/apps/slack/templates/donate.template
as expected. No changes are required regarding the reference to the template in theDonate
class.backend/apps/slack/commands/community.py (2)
9-11
: Good reuse of shared templatesUsing a shared
navigate.template
for multiple navigation-type commands is an efficient approach that promotes code reuse, consistent with the approach in the Contact command.
13-19
: Clean implementation of template contextThe context provision is clear and appropriately extends the parent class's context. The URL and name parameters will be correctly passed to the template.
backend/apps/slack/commands/sponsors.py (1)
9-22
: Great implementation of the new Sponsors command class!This class cleanly implements the template context for the
/sponsors
command, inheriting fromCommandBase
. The implementation correctly sets up the template context with sponsor data and all necessary parameters for rendering.backend/apps/slack/commands/owasp.py (1)
20-28
:❓ Verification inconclusive
Ensure the handler properly acknowledges commands.
When delegating to another command, it's important to ensure that the command is properly acknowledged. The current implementation passes the
ack
callback directly to the delegated command, which works if each command handler acknowledges the request, but could lead to missed acknowledgments if that behavior changes.
🌐 Web query:
What is the purpose of the "ack" function in the Slack Bolt framework?
💡 Result:
The
ack()
function in the Slack Bolt framework serves as a mandatory acknowledgment mechanism to confirm receipt of incoming events like actions, commands, and modal submissions. This ensures Slack’s UI remains responsive and avoids user-facing errors.Key purposes of
ack()
Immediate confirmation to Slack
- Required within 3 seconds for interactive components (buttons, menus, modals) and slash commands[7][9].
- Prevents Slack from displaying a "failed to respond" message to users[7][9].
UI state management
- Removes interactive elements (like buttons) after acknowledgment to prevent duplicate interactions[7][9].
- For modals, can update UI with validation errors:
app.view('modal_callback', async ({ ack, view }) => { if (invalidInput) { await ack({ response_action: 'errors', errors: { field_id: 'Invalid value' } }); } else { await ack(); } });Payload validation gateway
- Lets apps validate inputs before proceeding with business logic[1][7].
- Example workflow:
- Receive interaction → 2. Validate using
ack()
→ 3. Process data if valid[3][7].Compatibility with async workflows
- Returns a promise that resolves when Slack processes the acknowledgment[1][7].
- Can be used with
async/await
for controlled flow:app.command('/weather', async ({ ack, respond }) => { await ack(); // Immediate confirmation const data = await fetchWeather(); await respond(`Forecast: ${data}`); });Special cases
- Options requests: Requires returning dynamic dropdown options via
ack()
[5][7].- Socket Mode: Works seamlessly with WebSocket-based connections[6][8].
- Workflow Builder: Enables synchronous handling for dynamic options with
autoAcknowledge: false
[5].Failure to call
ack()
results in Slack retrying the request and users seeing timeout errors[7][9]. The function is distinct fromsay()
/respond()
, which handle message responses[1][7].Citations:
- 1: https://www.npmjs.com/package/@slack/bolt/v/2.0.0
- 2: https://slack.com/blog/developers/build-slack-apps-in-a-flash
- 3: https://knock.app/blog/creating-interactive-slack-apps-with-bolt-and-nodejs
- 4: https://tools.slack.dev/java-slack-sdk/guides/bolt-basics
- 5: https://api.slack.com/workflows/custom-steps-dynamic-options
- 6: https://api.slack.com/tutorials/tracks/responding-to-app-mentions
- 7: https://tools.slack.dev/bolt-js/concepts/acknowledge/
- 8: https://github.com/slackapi/bolt-js
- 9: https://api.slack.com/interactivity/handling
- 10: https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KhioF0AR
ACK Acknowledgment Consistency
Please verify that all delegated command handlers invoked via
cmd.handler(ack, command, client)
(and ultimately the fallback viasuper().handler(...)
) call the mandatoryack()
within the required timefr 6D40 ame. In the Slack Bolt framework, failing to acknowledge an incoming command promptly results in Slack timing out the request. Consider adding either explicit fallback acknowledgment or logging to detect any delegated handlers that might miss callingack()
, especially as changes occur.backend/apps/slack/commands/command.py (4)
16-23
: Well-documented usage of special template delimiters.Thank you for clarifying the usage of
{{ SECTION_BREAK }}
and{{ DIVIDER }}
in the docstring. This helps future contributors understand how to structure templates properly.
48-64
: Context generation appears flexible and maintainable.The inclusion of placeholders (e.g.,
SECTION_BREAK
,DIVIDER
) plusNL
in the context is well organized, enabling consistent references to special tokens. No issues found here.
65-100
: Good implementation of block splitting logic.Splitting on
{{ SECTION_BREAK }}
and checking for a standalone{{ DIVIDER }}
aligns well with your documented approach. This design is straightforward and easy to extend for future Slack blocks if needed.
24-34
:❓ Verification inconclusive
Validate the discovery of all command subclasses.
The approach using
__subclasses__()
is sound, but ensure that any command subclasses are imported so they're recognized by this method at runtime. Unimported subclasses won't appear inCommandBase.__subclasses__()
.Here is an optional script to verify the presence of your command subclasses in the codebase:
🏁 Script executed:
#!/bin/bash # Description: Search for classes inheriting from CommandBase and compare results with usage in config_all_commands. echo "Classes inheriting from CommandBase:" ast-grep --pattern $'class $_ extends CommandBase {\n $$$\n}' -A 5 echo "Occurrences of config_command calls:" rg -A 3 "def config_command"Length of output: 553
Ensure all command subclasses are imported and verify the AST search targets Python syntax.
The current implementation using
CommandBase.__subclasses__()
is appropriate—but it will only capture those subclasses that have already been imported at runtime. Please double-check that every module defining a command subclass (e.g., those declared asclass MyCommand(CommandBase):
) is imported beforeget_all_commands()
is called.Notably, the optional verification script used an AST pattern designed for a language with an
extends
keyword rather than Python’s inheritance syntax. For Python, you might want to update the pattern—for example, searching for a regular expression likeclass .*\\(CommandBase\\):
—to effectively detect all subclasses.• Verify that all files defining command subclasses are imported so that
__subclasses__()
returns the complete set.
• Update the verification script to use a Python-compatible pattern to avoid false negatives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
backend/apps/slack/commands/events.py (1)
17-30
: Categorization logic is clean and well-structured.The event categorization into a dictionary with nested event details is well-implemented. This structure will be easy to iterate through in templates.
Consider a slight optimization for larger datasets using a defaultdict:
- categorized_events = {} - for event in sorted_events: - category = event.category or "Other" - if category not in categorized_events: - categorized_events[category] = {"events": []} + from collections import defaultdict + categorized_events = defaultdict(lambda: {"events": []}) + for event in sorted_events: + category = event.category or "Other"This would eliminate the need for the conditional check on each iteration, but the current implementation is perfectly valid for typical event datasets.
backend/apps/slack/commands/gsoc.py (3)
21-22
: Consider making SUPPORTED_YEAR_END dynamically calculated.The hardcoded
SUPPORTED_YEAR_END = 2025
will require periodic maintenance as time passes. Consider calculating this value dynamically based on the current year plus some reasonable buffer (e.g., current year + 2).-SUPPORTED_YEAR_END = 2025 +SUPPORTED_YEAR_END = timezone.now().year + 2 SUPPORTED_YEARS = set(range(SUPPORTED_YEAR_START, SUPPORTED_YEAR_END + 1))
28-77
: Consider breaking down the method into smaller, more focused methods.The
get_template_context
method handles multiple different modes and scenarios. To improve readability and maintainability, consider breaking it down into smaller, more focused methods for each mode.def get_template_context(self, command): """Get the template context.""" now = timezone.now() gsoc_year = now.year if now.month > MARCH else now.year - 1 command_text = command["text"].strip() context = super().get_template_context(command) template_context = {"mode": "", "command": self.get_command()} if not command_text or command_text in COMMAND_START: - template_context.update( - { - "mode": "general", - "gsoc_channel": OWASP_GSOC_CHANNEL_ID, - "contribute_channel": OWASP_CONTRIBUTE_CHANNEL_ID, - "previous_year": gsoc_year, - "projects_url": projects_url, - "feedback_channel": OWASP_PROJECT_NEST_CHANNEL_ID, - "nest_bot_name": NEST_BOT_NAME, - } - ) + self._update_context_for_general(template_context, gsoc_year) elif command_text.isnumeric(): year = int(command_text) if year in SUPPORTED_YEARS: - template_context.update( - { - "mode": "year", - "year": year, - "projects": sorted(get_gsoc_projects(year), key=lambda p: p["idx_name"]), - "has_announcement": year in SUPPORTED_ANNOUNCEMENT_YEARS, - } - ) + self._update_context_for_supported_year(template_context, year) else: - template_context.update( - { - "mode": "unsupported_year", - "year": year, - "supported_start": SUPPORTED_YEAR_START, - "supported_end": SUPPORTED_YEAR_END, - } - ) + self._update_context_for_unsupported_year(template_context, year) else: - template_context.update( - { - "mode": "invalid", - "command_text": command_text, - } - ) + self._update_context_for_invalid(template_context, command_text) context.update(template_context) return context +def _update_context_for_general(self, template_context, gsoc_year): + """Update template context for general information.""" + template_context.update( + { + "mode": "general", + "gsoc_channel": OWASP_GSOC_CHANNEL_ID, + "contribute_channel": OWASP_CONTRIBUTE_CHANNEL_ID, + "previous_year": gsoc_year, + "projects_url": projects_url, + "feedback_channel": OWASP_PROJECT_NEST_CHANNEL_ID, + "nest_bot_name": NEST_BOT_NAME, + } + ) + +def _update_context_for_supported_year(self, template_context, year): + """Update template context for a supported year.""" + template_context.update( + { + "mode": "year", + "year": year, + "projects": sorted(get_gsoc_projects(year), key=lambda p: p["idx_name"]), + "has_announcement": year in SUPPORTED_ANNOUNCEMENT_YEARS, + } + ) + +def _update_context_for_unsupported_year(self, template_context, year): + """Update template context for an unsupported year.""" + template_context.update( + { + "mode": "unsupported_year", + "year": year, + "supported_start": SUPPORTED_YEAR_START, + "supported_end": SUPPORTED_YEAR_END, + } + ) + +def _update_context_for_invalid(self, template_context, command_text): + """Update template context for an invalid command.""" + template_context.update( + { + "mode": "invalid", + "command_text": command_text, + } + )
30-31
: Consider using a constant for greater flexibility in GSoC year determination.The logic for determining the GSoC year is currently based on the month of March. To make this more flexible in case the GSoC schedule changes in the future, consider using a named constant for the cutoff month.
-now = timezone.now() -gsoc_year = now.year if now.month > MARCH else now.year - 1 +now = timezone.now() +# Month after which the GSoC year is considered to be the current year +gsoc_year = now.year if now.month > MARCH else now.year - 1
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
backend/apps/slack/commands/events.py
(1 hunks)backend/apps/slack/commands/gsoc.py
(1 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
backend/apps/slack/commands/events.py (10)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_events_data
(120-133)backend/apps/slack/commands/board.py (1)
get_template_context
(13-19)backend/apps/slack/commands/contact.py (1)
get_template_context
(13-19)backend/apps/slack/commands/community.py (1)
get_template_context
(13-19)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/news.py (1)
get_template_context
(11-22)backend/apps/slack/commands/policies.py (1)
get_template_context
(9-39)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/gsoc.py (3)
backend/apps/common/utils.py (1)
get_absolute_url
(13-23)backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_template_context
(48-63)get_command
(84-86)backend/apps/slack/utils.py (1)
get_gsoc_projects
(34-55)
🔇 Additional comments (4)
backend/apps/slack/commands/events.py (3)
13-15
: Well-handled data preparation!You've properly addressed the potential
None
return fromget_events_data()
withor []
, and included filtering and sorting of events. This ensures the template will receive well-formatted data even in edge cases.
32-36
: Clean context preparation with proper inheritance.Good job using the superclass context as a base and extending it with the specific data needed for this command. This approach ensures consistency across all commands while allowing for command-specific customization.
8-9
: Well-implemented class following the new template system.The Events class follows the CommandBase pattern consistently with the other commands in the system. This implementation aligns perfectly with the PR objectives of refactoring hardcoded text messages into a template-based system.
backend/apps/slack/commands/gsoc.py (1)
28-77
: The implementation properly addresses the previous review suggestion.The dynamic variables
now
andgsoc_year
are now calculated inside the method, ensuring they're always up-to-date when the command is executed, even for long-running server instances.
Hi @arkid15r, would you please review this PR at your convenience? |
ce0b46e
to
9c39dd7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
backend/apps/slack/commands/events.py (1)
13-13
: Properly handled potential None return from get_events_data().The code correctly uses the
or []
pattern to provide a default empty list ifget_events_data()
returnsNone
, addressing the issue identified in a previous review.
🧹 Nitpick comments (14)
backend/tests/apps/slack/commands/gsoc_test.py (3)
48-50
: Handler invocation updated to use class-based approach.The code now properly instantiates the
Gsoc
class and calls itshandler
method instead of directly calling a function. This correctly implements the transition to the class-based command system.Consider creating a fixture for the
Gsoc
instance to improve test efficiency and reduce duplication.@pytest.fixture +def gsoc_command(self): + return Gsoc() + +@pytest.fixture def mock_slack_command(self): return { "text": "",Then in the test method:
- handler_instance = Gsoc() + # Use the fixture instead ack = MagicMock() - handler_instance.handler(ack=ack, command=command, client=mock_slack_client) + gsoc_command.handler(ack=ack, command=command, client=mock_slack_client)
81-83
: Handler invocation updated for consistency.This is similar to the change at lines 48-50, where the function call has been replaced with class instantiation and method call. The change is consistent with the refactoring approach.
Consider applying the same fixture recommendation suggested for lines 48-50 here as well to reduce code duplication.
1-88
: Consider adding tests for exception handling.The current tests validate the happy path scenarios and basic error responses, but there are no tests for exception handling in the
handler
method. From the relevant code snippet ofhandler
in thecommand.py
file, it appears there's exception handling logic that should be tested.def test_handler_exception(self, mock_slack_client): """Test that exceptions in the handler are properly caught and an error message is sent.""" command = {"text": "", "user_id": "U123456"} settings.SLACK_COMMANDS_ENABLED = True with patch.object(Gsoc, "get_render_blocks", side_effect=Exception("Test exception")): handler_instance = Gsoc() ack = MagicMock() handler_instance.handler(ack=ack, command=command, client=mock_slack_client) # Verify error message was sent mock_slack_client.chat_postMessage.assert_called_once() blocks = mock_slack_client.chat_postMessage.call_args[1]["blocks"] assert len(blocks) == 1 assert ":warning: An error occurred. Please try again later." in str(blocks[0])backend/apps/slack/commands/users.py (1)
11-25
: Consider consistency with other commandsUnlike other commands that implement
get_template_context
to use the template system, this command directly overridesget_render_blocks
. While this works correctly, it bypasses the template system introduced by this PR.Consider whether this command should follow the same pattern as other commands to maintain consistency, unless there's a specific reason it needs special handling.
If using templates isn't suitable for this particular command, consider adding a comment explaining why this command needs a different approach.
backend/apps/slack/commands/staff.py (1)
11-22
: Consider standardizing context creation patternThe implementation is correct, but there's a minor inconsistency in how the context is constructed compared to other commands like
Policies
. This command usescontext.update()
while others use dictionary unpacking with**super().get_template_context(command)
.Both approaches work, but consistency across the codebase is preferred. Consider standardizing on one approach across all commands:
- context = super().get_template_context(command) - context.update( - { - "has_staff": bool(items), - "items": items, - "website_url": OWASP_WEBSITE_URL, - } - ) - return context + return { + **super().get_template_context(command), + "has_staff": bool(items), + "items": items, + "website_url": OWASP_WEBSITE_URL, + }backend/apps/slack/commands/sponsor.py (1)
6-11
: Verify if template system is intentionally bypassedUnlike other commands that use the template system, this implementation overrides
get_render_text()
to return a static string, bypassing the template rendering mechanism defined inCommandBase
.While this works for a "Coming soon..." message, it doesn't follow the same pattern as other commands that use templates. Consider whether this is intentional or if it would be better to use a simple template file for consistency.
If this is a temporary implementation until the sponsor feature is ready, consider adding a TODO comment to clarify this.
backend/apps/slack/commands/contribute.py (1)
31-35
: Simplify or remove unnecessary method.The
get_template_context
method doesn't add any new context and simply calls the parent method. Consider removing it entirely since it doesn't extend the functionality provided by the parent class.- def get_template_context(self, command): - """Get the template context.""" - return { - **super().get_template_context(command), - }backend/apps/slack/commands/leaders.py (3)
10-63
: Refactor long method to improve readability and maintainability.The
get_template_context
method is quite long and handles multiple responsibilities. Consider breaking it down into smaller, more focused methods.def get_template_context(self, command): """Get the template context.""" + search_query = command["text"].strip() + chapters_with_urls = self._get_chapters_with_urls(search_query) + projects_with_urls = self._get_projects_with_urls(search_query) + + return { + **super().get_template_context(command), + "chapters": chapters_with_urls, + "projects": projects_with_urls, + "search_query": search_query, + "has_results": bool(chapters_with_urls or projects_with_urls), + } + + def _get_chapters_with_urls(self, search_query): + """Get chapters with URLs for the given search query.""" from apps.owasp.api.search.chapter import get_chapters - from apps.owasp.api.search.project import get_projects - - search_query = command["text"].strip() - attributes = ["idx_key", "idx_leaders", "idx_name"] searchable_attributes = ["idx_leaders", "idx_name"] limit = 5 chapters = get_chapters( query=search_query, attributes=attributes, limit=limit, page=1, searchable_attributes=searchable_attributes, )["hits"] - + + return [ + self._create_entity_with_url(chapter, "chapters") + for chapter in chapters + ] + + def _get_projects_with_urls(self, search_query): + """Get projects with URLs for the given search query.""" + from apps.owasp.api.search.project import get_projects + + attributes = ["idx_key", "idx_leaders", "idx_name"] + searchable_attributes = ["idx_leaders", "idx_name"] + limit = 5 projects = get_projects( query=search_query, attributes=attributes, limit=limit, page=1, searchable_attributes=searchable_attributes, )["hits"] - - chapters_with_urls = [ - { - "idx_key": chapter["idx_key"], - "idx_name": chapter["idx_name"], - "idx_leaders": chapter["idx_leaders"], - "url": get_absolute_url(f"chapters/{chapter['idx_key']}"), - } - for chapter in chapters - ] - projects_with_urls = [ - { - "idx_key": project["idx_key"], - "idx_name": project["idx_name"], - "idx_leaders": project["idx_leaders"], - "url": get_absolute_url(f"projects/{project['idx_key']}"), - } - for project in projects - ] - context = super().get_template_context(command) - context.update( - { - "chapters": chapters_with_urls, - "projects": projects_with_urls, - "search_query": search_query, - "has_results": bool(chapters or projects), - } - ) - return context + 10000 + return [ + self._create_entity_with_url(project, "projects") + for project in projects + ] + + def _create_entity_with_url(self, entity, entity_type): + """Create an entity dictionary with URL.""" + return { + "idx_key": entity["idx_key"], + "idx_name": entity["idx_name"], + "idx_leaders": entity["idx_leaders"], + "url": get_absolute_url(f"{entity_type}/{entity['idx_key']}"), + }
12-14
: Move imports to module level.Consider moving these imports to the module level rather than inside the method for better readability and performance, unless there's a specific reason to avoid circular imports.
from apps.common.utils import get_absolute_url from apps.slack.commands.command import CommandBase +from apps.owasp.api.search.chapter import get_chapters +from apps.owasp.api.search.project import get_projects class Leaders(CommandBase): """Slack bot /leaders command.""" def get_template_context(self, command): """Get the template context.""" - from apps.owasp.api.search.chapter import get_chapters - from apps.owasp.api.search.project import get_projects
54-62
: Simplify context creation with the unpacking operator.Use the
**
unpacking operator to simplify context creation, which is consistent with the pattern used in other command files.- context = super().get_template_context(command) - context.update( - { - "chapters": chapters_with_urls, - "projects": projects_with_urls, - "search_query": search_query, - "has_results": bool(chapters or projects), - } - ) - return context + return { + **super().get_template_context(command), + "chapters": chapters_with_urls, + "projects": projects_with_urls, + "search_query": search_query, + "has_results": bool(chapters or projects), + }backend/apps/slack/commands/owasp.py (1)
20-28
: Add input validation for increased robustness.The handler assumes
command_tokens
will be a valid list when checking for its first element, but this might not always be the case.def handler(self, ack, command, client): """Handle the command.""" command_tokens = command["text"].split() - cmd = self.find_command(command_tokens[0].strip().lower() if command_tokens else "") + first_token = command_tokens[0].strip().lower() if command_tokens else "" + cmd = self.find_command(first_token) if cmd: command["text"] = " ".join(command_tokens[1:]).strip() return cmd.handler(ack, command, client) return super().handler(ack, command, client)backend/apps/slack/commands/command.py (3)
127-129
: Improve exception loggingThe current exception logging doesn't include the actual exception details in the log message, which would be helpful for debugging.
- except Exception: - logger.exception("Failed to handle command '%s'", self.get_command()) + except Exception as e: + logger.exception("Failed to handle command '%s': %s", self.get_command(), str(e))
84-86
: Consider making command name derivation more flexibleThe current implementation assumes the command name is always the lowercase class name. This might be too rigid if a command name needs to differ from its class name.
def get_command(self): """Get the command name.""" - return f"/{self.__class__.__name__.lower()}" + # Allow subclasses to override command_name + command_name = getattr(self, 'command_name', self.__class__.__name__.lower()) + return f"/{command_name}"
65-82
: Consider using Jinja2 extensions for special tokensThe current approach of string replacement after rendering works but requires passing literal tokens to templates. Using Jinja2 extensions or custom tags might be more elegant.
Instead of post-processing rendered text with string replacements, you could create custom Jinja2 extensions that directly output the appropriate Slack block structures. This would make templates more readable and avoid the need to pass special tokens like
{{ SECTION_BREAK }}
through the context.Example approach:
# In a Jinja2 extension file from jinja2 import nodes from jinja2.ext import Extension class SlackBlocksExtension(Extension): tags = {'section_break', 'divider'} def parse(self, parser): token = parser.stream.current if token.value == 'section_break': return nodes.CallBlock(self.call_method('_section_break'), [], [], []) elif token.value == 'divider': return nodes.CallBlock(self.call_method('_divider'), [], [], []) def _section_break(self, caller): return "{{ SECTION_BREAK }}" def _divider(self, caller): return "{{ DIVIDER }}" # Then in your template loader env = Environment( loader=FileSystemLoader(TEMPLATE_DIR), extensions=[SlackBlocksExtension] ) # In templates {% section_break %} {% divider %}
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/poetry.lock
is excluded by!**/*.lock
📒 Files selected for processing (58)
backend/apps/slack/commands/__init__.py
(2 hunks)backend/apps/slack/commands/board.py
(1 hunks)backend/apps/slack/commands/chapters.py
(1 hunks)backend/apps/slack/commands/command.py
(1 hunks)backend/apps/slack/commands/committees.py
(1 hunks)backend/apps/slack/commands/community.py
(1 hunks)backend/apps/slack/commands/contact.py
(1 hunks)backend/apps/slack/commands/contribute.py
(2 hunks)backend/apps/slack/commands/donate.py
(1 hunks)backend/apps/slack/commands/events.py
(1 hunks)backend/apps/slack/commands/gsoc.py
(1 hunks)backend/apps/slack/commands/jobs.py
(1 hunks)backend/apps/slack/commands/leaders.py
(1 hunks)backend/apps/slack/commands/news.py
(1 hunks)backend/apps/slack/commands/owasp.py
(1 hunks)backend/apps/slack/commands/policies.py
(1 hunks)backend/apps/slack/commands/projects.py
(1 hunks)backend/apps/slack/commands/sponsor.py
(1 hunks)backend/apps/slack/commands/sponsors.py
(1 hunks)backend/apps/slack/commands/staff.py
(1 hunks)backend/apps/slack/commands/users.py
(1 hunks)backend/apps/slack/models/event.py
(2 hunks)backend/apps/slack/template_system/loader.py
(1 hunks)backend/apps/slack/templates/chapters.template
(1 hunks)backend/apps/slack/templates/contribute.template
(1 hunks)backend/apps/slack/templates/donate.template
(1 hunks)backend/apps/slack/templates/events.template
(1 hunks)backend/apps/slack/templates/feedback.template
(1 hunks)backend/apps/slack/templates/gsoc.template
(1 hunks)backend/apps/slack/templates/jobs.template
(1 hunks)backend/apps/slack/templates/leaders.template
(1 hunks)backend/apps/slack/templates/navigate.template
(1 hunks)backend/apps/slack/templates/news.template
(1 hunks)backend/apps/slack/templates/owasp.template
(1 hunks)backend/apps/slack/templates/policies.template
(1 hunks)backend/apps/slack/templates/sponsors.template
(1 hunks)backend/apps/slack/templates/staff.template
(1 hunks)backend/pyproject.toml
(1 hunks)backend/tests/apps/slack/commands/board_test.py
(3 hunks)backend/tests/apps/slack/commands/chapters_test.py
(3 hunks)backend/tests/apps/slack/commands/committees_test.py
(3 hunks)backend/tests/apps/slack/commands/community_test.py
(3 hunks)backend/tests/apps/slack/commands/contact_test.py
(3 hunks)backend/tests/apps/slack/commands/contribute_test.py
(4 hunks)backend/tests/apps/slack/commands/donate_test.py
(3 hunks)backend/tests/apps/slack/commands/events_test.py
(2 hunks)backend/tests/apps/slack/commands/gsoc_test.py
(4 hunks)backend/tests/apps/slack/commands/jobs_test.py
(3 hunks)backend/tests/apps/slack/commands/leaders_test.py
(4 hunks)backend/tests/apps/slack/commands/news_test.py
(5 hunks)backend/tests/apps/slack/commands/owasp_test.py
(3 hunks)backend/tests/apps/slack/commands/policies_test.py
(2 hunks)backend/tests/apps/slack/commands/projects_test.py
(4 hunks)backend/tests/apps/slack/commands/sponsor_test.py
(2 hunks)backend/tests/apps/slack/commands/sponsors_test.py
(3 hunks)backend/tests/apps/slack/commands/staff_test.py
(3 hunks)backend/tests/apps/slack/commands/users_test.py
(3 hunks)backend/tests/apps/slack/models/event_test.py
(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (36)
- backend/apps/slack/templates/navigate.template
- backend/apps/slack/templates/leaders.template
- backend/tests/apps/slack/commands/donate_test.py
- backend/apps/slack/templates/staff.template
- backend/pyproject.toml
- backend/apps/slack/templates/feedback.template
- backend/apps/slack/templates/contribute.template
- backend/apps/slack/templates/owasp.template
- backend/apps/slack/templates/news.template
- backend/apps/slack/templates/policies.template
- backend/apps/slack/templates/events.template
- backend/tests/apps/slack/commands/community_test.py
- backend/apps/slack/models/event.py
- backend/tests/apps/slack/commands/policies_test.py
- backend/tests/apps/slack/models/event_test.py
- backend/tests/apps/slack/commands/chapters_test.py
- backend/tests/apps/slack/commands/events_test.py
- backend/apps/slack/templates/chapters.template
- backend/tests/apps/slack/commands/contribute_test.py
- backend/apps/slack/templates/donate.template
- backend/tests/apps/slack/commands/board_test.py
- backend/tests/apps/slack/commands/users_test.py
- backend/apps/slack/commands/init.py
- backend/tests/apps/slack/commands/contact_test.py
- backend/apps/slack/commands/projects.py
- backend/tests/apps/slack/commands/sponsor_test.py
- backend/apps/slack/templates/jobs.template
- backend/apps/slack/template_system/loader.py
- backend/apps/slack/templates/gsoc.template
- backend/apps/slack/templates/sponsors.template
- backend/tests/apps/slack/commands/jobs_test.py
- backend/tests/apps/slack/commands/sponsors_test.py
- backend/tests/apps/slack/commands/projects_test.py
- backend/tests/apps/slack/commands/committees_test.py
- backend/tests/apps/slack/commands/staff_test.py
- backend/tests/apps/slack/commands/leaders_test.py
🧰 Additional context used
🧬 Code Graph Analysis (15)
backend/apps/slack/commands/board.py (1)
backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_template_file_name
(97-99)get_template_context
(48-63)
backend/apps/slack/commands/policies.py (14)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/commands/community.py (1)
get_template_context
(13-19)backend/apps/slack/commands/board.py (1)
get_template_context
(13-19)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/gsoc.py (1)
get_template_context
(28-77)backend/apps/slack/commands/contact.py (1)
get_template_context
(13-19)backend/apps/slack/commands/contribute.py (1)
get_template_context
(31-35)backend/apps/slack/commands/events.py (1)
get_template_context
(11-36)backend/apps/slack/commands/jobs.py (1)
get_template_context
(14-24)backend/apps/slack/commands/news.py (1)
get_template_context
(11-22)backend/apps/slack/commands/leaders.py (1)
get_template_context
(10-63)backend/apps/slack/commands/owasp.py (1)
get_template_context
(30-40)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/events.py (3)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_events_data
(120-133)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)
backend/apps/slack/commands/staff.py (6)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_staff_data
(95-117)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/contribute.py (1)
get_template_context
(31-35)backend/apps/slack/commands/events.py (1)
get_template_context
(11-36)backend/apps/slack/commands/news.py (1)
get_template_context
(11-22)
backend/apps/slack/commands/jobs.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)
backend/apps/slack/commands/committees.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_render_blocks
(65-82)backend/apps/slack/common/handlers/committees.py (1)
get_blocks
(17-113)
backend/apps/slack/commands/leaders.py (4)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/owasp/api/search/chapter.py (1)
get_chapters
(8-44)backend/apps/owasp/api/search/project.py (1)
get_projects
(8-48)backend/apps/common/utils.py (1)
get_absolute_url
(13-23)
backend/apps/slack/commands/gsoc.py (3)
backend/apps/common/utils.py (1)
get_absolute_url
(13-23)backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_template_context
(48-63)get_command
(84-86)backend/apps/slack/utils.py (1)
get_gsoc_projects
(34-55)
backend/apps/slack/commands/sponsor.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_render_text
(35-46)
backend/apps/slack/commands/contribute.py (1)
backend/apps/slack/commands/command.py (3)
CommandBase
(16-134)get_render_blocks
(65-82)get_template_context
(48-63)
backend/tests/apps/slack/commands/news_test.py (2)
backend/apps/slack/commands/news.py (1)
News
(8-22)backend/apps/slack/commands/command.py (1)
handler
(105-134)
backend/tests/apps/slack/commands/gsoc_test.py (2)
backend/apps/slack/commands/gsoc.py (1)
Gsoc
(25-77)backend/apps/slack/commands/command.py (1)
handler
(105-134)
backend/apps/slack/commands/sponsors.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-134)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_sponsors_data
(136-152)
backend/apps/slack/commands/command.py (2)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/blocks.py (1)
markdown
(14-27)
backend/tests/apps/slack/commands/owasp_test.py (1)
backend/apps/slack/commands/owasp.py (2)
Owasp
(7-40)handler
(20-28)
🔇 Additional comments (55)
backend/tests/apps/slack/commands/gsoc_test.py (5)
7-7
: Import updated to reflect new class-based implementation.The import has been properly updated to use the class-based implementation (
Gsoc
class) instead of the previous function-based approach, aligning with the PR's objective to transition to a more maintainable object-oriented design.
11-11
: Class name updated to reflect command-based testing.The test class name change from
TestGsocHandler
toTestGsocCommand
properly reflects the shift from testing a handler function to testing a command class, maintaining consistency with the new architecture.
33-33
: Simplified error message format.The error message format has been simplified by removing extra formatting (likely a newline character), which should make the tests more readable and maintainable. This aligns with the PR's template-based approach.
63-63
: Assertion updated to match new template-based response.The assertion has been updated to check for the presence of "Getting Started with OWASP GSoC" instead of the static "GSOC_GENERAL_INFORMATION_BLOCKS", which aligns with the new template-based output system.
This change suggests the response text is now being generated from a template rather than hardcoded blocks, which is consistent with the PR's goal of implementing a template system.
78-78
: Updated import path for mocked function.The import path for the
get_gsoc_projects
function has been correctly updated to reflect its new location in the refactored code structure, ensuring the test mocks the function from the right module.backend/apps/slack/commands/policies.py (3)
3-4
: Implementation looks good!The command has been successfully refactored to use the new
CommandBase
class which supports the Jinja2 template system.
6-8
: LGTM!The
Policies
class is properly defined inheriting fromCommandBase
with appropriate docstring.
9-39
: Well-structured template context implementation!The
get_template_context
method properly extends the base context with the policies list. The policies list is comprehensive and well-organized with clear names and valid URLs.backend/apps/slack/commands/users.py (2)
3-6
: LGTM!Appropriate imports for the refactored command implementation.
8-10
: Class definition looks good!The
Users
class properly inherits fromCommandBase
with appropriate docstring.backend/apps/slack/commands/staff.py (2)
3-6
: LGTM!Appropriate imports for the refactored command implementation.
8-10
: Class definition looks good!The
Staff
class properly inherits fromCommandBase
with appropriate docstring.backend/apps/slack/commands/board.py (4)
3-4
: LGTM!Appropriate import for the refactored command implementation.
6-8
: Class definition looks good!The
Board
class properly inherits fromCommandBase
with appropriate docstring.
9-12
: Good template reuse!Overriding
get_template_file_name
to return "navigate.template" suggests that multiple navigation-type commands share the same template, which promotes code reuse.
13-19
: LGTM!The template context correctly extends the base context with the specific URL and name for the board command.
backend/apps/slack/commands/jobs.py (3)
1-9
: Clean imports and class structure for the new template-based system.The refactoring from a procedural approach to a class-based model is well-implemented. The imports are properly organized, including only what's necessary from the command base class and constants.
11-13
: Well-named class following command structure.The
Jobs
class appropriately inherits fromCommandBase
and includes a descriptive docstring that clarifies its purpose.
14-24
: Context handling implementation follows best practices.Good implementation of the
get_template_context
method, which properly extends the base class context with command-specific information. The use ofsuper()
ensures that all base context values are preserved while adding command-specific channel IDs and bot name.backend/apps/slack/commands/committees.py (3)
3-6
: Appropriate imports for the class-based structure.The imports are correctly updated to include the
CommandBase
while maintaining the necessary functionality from the common handlers and presentation modules.
8-10
: Well-defined class following the command pattern.The
Committees
class properly inherits fromCommandBase
and includes a clear docstring.
11-25
: Effective implementation ofget_render_blocks
.The method correctly overrides the base implementation to provide committees-specific block rendering. It properly handles the search query from the command text and uses the existing
get_blocks
function with appropriate presentation parameters.backend/apps/slack/commands/events.py (6)
3-6
: Streamlined imports focused on the new template system.The imports have been appropriately updated to support the class-based implementation, removing unnecessary imports while adding the required ones for the template context.
8-10
: Well-structured class definition with clear docstring.The
Events
class properly inherits fromCommandBase
and includes a descriptive docstring.
11-13
: Good method signature and documentation.The
get_template_context
method is properly defined with appropriate documentation.
14-16
: Effective event filtering and sorting.The code properly filters events to include only those with a valid start date and sorts them chronologically, ensuring a logical presentation order.
17-31
: Well-structured event categorization logic.The event categorization implementation is clean and efficient. It organizes events by category and extracts the necessary details into a structured format suitable for template rendering.
32-36
: Properly constructed template context.The method correctly combines the base context with the event-specific data using the spread operator, following best practices for extending dictionaries.
backend/apps/slack/commands/chapters.py (4)
3-7
: Appropriate imports for the class-based structure.The imports have been correctly updated to include the
CommandBase
while retaining the necessary constants and handlers.
9-11
: Well-defined class with clear purpose.The
Chapters
class properly inherits fromCommandBase
and includes a descriptive docstring.
12-17
: Intelligent command text handling with fallback to parent implementation.The method correctly checks if the command text is a help command and delegates to the parent implementation when appropriate. This maintains the functionality of help commands while allowing for custom rendering of search results.
18-29
: Effective search query processing and block rendering.The implementation properly constructs the search query based on the command text and uses the existing
get_blocks
function with appropriate presentation parameters.backend/apps/slack/commands/news.py (1)
7-22
: Implementation looks good!The
News
class follows a clean architecture with proper inheritance fromCommandBase
. Theget_template_context
method correctly builds the template context by:
- Getting news data via the utility function
- Extending the base context from the parent class
- Adding news-specific parameters with appropriate boolean flag for empty results
This implementation aligns well with the PR objectives to refactor hardcoded text into reusable templates.
backend/apps/slack/commands/donate.py (1)
7-15
: Clean implementation with proper context extensionThe
Donate
class correctly extends the base context using the spread operator with the parent's context, adding the website URL parameter. This follows the pattern established for template-based command responses.backend/apps/slack/commands/contact.py (2)
9-11
: Good reuse of common templateThe class correctly overrides
get_template_file_name()
to use a common "navigate.template" file rather than creating a command-specific template. This promotes template reuse for similar commands.
13-19
: Implementation follows the template pattern wellThe context building properly extends the base context and adds the URL and name specific to the contact command. This demonstrates the flexibility of the new template system to handle different command needs while maintaining a consistent structure.
backend/apps/slack/commands/community.py (1)
6-19
: Implementation follows the established pattern well.The Community class properly implements the template system by overriding the necessary methods and providing specific template context.
backend/apps/slack/commands/sponsors.py (1)
9-22
: Good implementation with comprehensive context.The Sponsors class correctly implements the template system with a detailed context that includes all necessary data for rendering.
backend/apps/slack/commands/gsoc.py (6)
21-22
: Good practice: Using dedicated constants and utility functions.The code defines
MARCH
as a constant and usesget_absolute_url("projects")
for the projects URL, making the code more maintainable and easier to understand.
28-32
: LGTM: Calculating time values inside the method ensures freshness.Moving the calculation of
now
andgsoc_year
into the method ensures these values are always up-to-date when the method is called, even if the server runs for a long time.
36-47
: Well-structured template context for general information.The template context for the general mode is comprehensive, providing all necessary information for rendering a useful response to the user.
48-67
: Good implementation of year-based logic.The code properly handles valid and invalid year inputs, with clear separation between supported and unsupported years. The sorting of projects by
idx_name
improves user experience with a consistent ordering.
68-74
: Good fallback for invalid commands.The code properly handles invalid commands by setting an appropriate mode in the template context, which will likely result in a user-friendly error message.
76-77
: Clean context update approach.The code updates the base context with the template-specific context, ensuring that both general command information and template-specific data are available for rendering.
backend/apps/slack/commands/owasp.py (2)
10-18
: Well-implemented command lookup functionality.The
find_command
method efficiently searches for commands by name while properly excluding theOwasp
class itself to prevent recursion issues.
30-40
: Clear context preparation for help and command execution.The
get_template_context
method returns a well-structured context based on the command, distinguishing between help requests and specific command invocations.backend/tests/apps/slack/commands/owasp_test.py (4)
37-39
: Code correctly updated to use the new class-based approach.The test has been properly updated to instantiate the
Owasp
class and call its handler method, reflecting the refactored implementation.
48-49
: Good addition: Testing disabled commands behavior.The test now verifies that when commands are disabled, no messages are posted to Slack, which is an important validation.
57-72
: Well-structured mocking for subcommands.The patch to
find_command
is a clean approach to testing the command delegation functionality, creating a mock handler that verifies the proper command flow.
74-75
: Precise assertion for subcommand response.The assertion properly verifies that the message sent to Slack contains the expected text from the delegated command.
backend/tests/apps/slack/commands/news_test.py (4)
10-10
: Class name updated to reflect the new testing approach.The class name has been appropriately changed from
TestNewsHandler
toTestNewsCommand
to reflect the shift from testing a function to testing a class.
50-51
: Test properly updated to use the class-based approach.The test now instantiates the
News
class and calls its handler method, correctly reflecting the refactored implementation.
60-61
: Flexible assertion usingin
operator.The assertion has been updated to use
in
instead of exact equality, which is more resilient to minor text changes while still verifying the essential content.
82-84
: Test case properly updated for no items scenario.The test for when no news items are available has been correctly updated to use the class-based approach, maintaining test coverage for this edge case.
backend/apps/slack/commands/command.py (1)
1-135
: Well-designed CommandBase class with good template handling!The implementation of CommandBase provides a solid foundation for the Slack command system with template-based responses. The class follows a clean object-oriented design and addresses previous review suggestions including error handling and template context support.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
backend/apps/slack/commands/command.py (2)
1-2
: Use a more appropriate module-level docstring.The current docstring states "Slack bot board command," but this file defines a shared base class for Slack commands rather than a board-specific command. Updating the docstring to clarify its broader purpose would help future maintainers quickly understand the file's scope.
-"""Slack bot board command.""" +"""Base class and common functionality for Slack commands."""
65-83
: Consider supporting multiple placeholders in the same section.Right now, each text section is processed as a single block unless it exactly matches
"{{ DIVIDER }}"
. If developers want multiple dividers in the same section (e.g., "Intro {{ DIVIDER }} More text"), this code won't handle them as separate dividers. Consider splitting the section further or switching to a more flexible parsing approach.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
backend/apps/slack/commands/command.py
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
backend/apps/slack/commands/command.py (14)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/blocks.py (1)
markdown
(14-27)backend/apps/slack/commands/sponsor.py (1)
get_render_text
(9-11)backend/apps/slack/commands/community.py (1)
get_template_context
(13-19)backend/apps/slack/commands/events.py (1)
get_template_context
(11-36)backend/apps/slack/commands/board.py (1)
get_template_context
(13-19)backend/apps/slack/commands/contact.py (1)
get_template_context
(13-19)backend/apps/slack/commands/gsoc.py (1)
get_template_context
(28-77)backend/apps/slack/commands/jobs.py (1)
get_template_context
(14-24)backend/apps/slack/commands/news.py (1)
get_template_context
(11-22)backend/apps/slack/commands/leaders.py (1)
get_template_context
(10-63)backend/apps/slack/commands/policies.py (1)
get_template_context
(9-39)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-22)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)
🔇 Additional comments (1)
backend/apps/slack/commands/command.py (1)
3-14
: No issues found.Also applies to: 16-23, 24-34, 35-47, 48-64, 84-107, 108-116, 117-147
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Li-ChangC
I'll look into it soon, just busy w/ other PRs now. Don't worry about the size, I can handle it.
Thanks 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
backend/apps/slack/commands/leaders.py (1)
12-13
: Consider moving imports to the module level.The imports for the search modules are located inside the method rather than at the module level. While this works, it's generally better practice to have imports at the top of the file unless there's a specific reason for delayed importing (like circular dependencies or performance).
- def get_template_context(self, command): - """Get the template context.""" - from apps.owasp.api.search.chapter import get_chapters - from apps.owasp.api.search.project import get_projects +from apps.owasp.api.search.chapter import get_chapters +from apps.owasp.api.search.project import get_projects + + def get_template_context(self, command): + """Get the template context."""backend/apps/slack/commands/command.py (3)
24-33
: Consider using class methods instead of static methods for command retrieval and configuration.The methods
get_all_commands()
andconfig_all_commands()
are defined as static methods. Converting these to class methods would allow subclasses to extend or override this behavior if needed in the future.- @staticmethod - def get_all_commands(): + @classmethod + def get_all_commands(cls): """Get all commands.""" - return list(CommandBase.__subclasses__()) + return list(cls.__subclasses__()) - @staticmethod - def config_all_commands(): + @classmethod + def config_all_commands(cls): """Configure all commands.""" - for cmd_class in CommandBase.get_all_commands(): + for cmd_class in cls.get_all_commands(): cmd_class().config_command()
84-106
: Make command and template name methods overridable by adding a default implementation.The current implementation assumes command names and template file names follow a specific convention (lowercase class name). This might be limiting for commands that need custom naming schemes.
def get_command(self): - """Get the command name.""" + """Get the command name. + + Returns: + str: The command name, defaulting to the lowercase class name with a '/' prefix. + + Note: + Subclasses can override this method to provide custom command names. + """ return f"/{self.__class__.__name__.lower()}" def get_template_file_name(self): - """Get the template file name.""" + """Get the template file name. + + Returns: + str: The template file name, defaulting to the lowercase class name with '.template' suffix. + + Note: + Subclasses can override this method to provide custom template file names. + """ return f"{self.__class__.__name__.lower()}.template"
131-146
: Consider providing more specific error messages for different exception types.While the current error handling is good, you could provide more specific error messages to users based on the type of exception that occurred. This would help users understand what went wrong without exposing sensitive details.
try: blocks = self.get_render_blocks(command) conversation = client.conversations_open(users=command["user_id"]) client.chat_postMessage( blocks=blocks, channel=conversation["channel"]["id"], text=get_text(blocks), ) - except Exception: - logger.exception("Failed to handle command '%s'", self.get_command()) + except FileNotFoundError as e: + logger.exception("Template not found for command '%s'", self.get_command()) + error_message = ":warning: Configuration error. Please contact support." + except ValueError as e: + logger.exception("Value error in command '%s'", self.get_command()) + error_message = ":warning: Invalid command parameters. Please check your input." + except Exception as e: + logger.exception("Failed to handle command '%s'", self.get_command()) + error_message = ":warning: An error occurred. Please try again later." + + # Handle error response + if 'error_message' in locals(): conversation = client.conversations_open(users=command["user_id"]) client.chat_postMessage( - blocks=[markdown(":warning: An error occurred. Please try again later.")], + blocks=[markdown(error_message)], channel=conversation["channel"]["id"], text="An error occurred while processing your command.", )
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
backend/apps/slack/commands/command.py
(1 hunks)backend/apps/slack/commands/jobs.py
(1 hunks)backend/apps/slack/commands/leaders.py
(1 hunks)backend/apps/slack/commands/news.py
(1 hunks)backend/apps/slack/commands/staff.py
(1 hunks)backend/apps/slack/templates/leaders.template
(1 hunks)backend/apps/slack/templates/news.template
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/apps/slack/templates/leaders.template
- backend/apps/slack/templates/news.template
🧰 Additional context used
🧬 Code Graph Analysis (5)
backend/apps/slack/commands/staff.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-146)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_staff_data
(95-117)
backend/apps/slack/commands/leaders.py (4)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-146)get_template_context
(48-63)backend/apps/owasp/api/search/chapter.py (1)
get_chapters
(8-44)backend/apps/owasp/api/search/project.py (1)
get_projects
(8-48)backend/apps/common/utils.py (1)
get_absolute_url
(13-23)
backend/apps/slack/commands/news.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-146)get_template_context
(48-63)backend/apps/slack/utils.py (1)
get_news_data
(59-91)
backend/apps/slack/commands/jobs.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-146)get_template_context
(48-63)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)
backend/apps/slack/commands/command.py (15)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/blocks.py (1)
markdown
(14-27)backend/apps/slack/commands/sponsor.py (1)
get_render_text
(9-11)backend/apps/slack/commands/jobs.py (1)
get_template_context
(14-21)backend/apps/slack/commands/leaders.py (1)
get_template_context
(10-60)backend/apps/slack/commands/community.py (1)
get_template_context
(13-19)backend/apps/slack/commands/news.py (1)
get_template_context
(11-19)backend/apps/slack/commands/staff.py (1)
get_template_context
(11-19)backend/apps/slack/commands/board.py (1)
get_template_context
(13-19)backend/apps/slack/commands/events.py (1)
get_template_context
(11-36)backend/apps/slack/commands/contact.py (1)
get_template_context
(13-19)backend/apps/slack/commands/gsoc.py (1)
get_template_context
(28-77)backend/apps/slack/commands/donate.py (1)
get_template_context
(10-15)backend/apps/slack/commands/policies.py (1)
get_template_context
(9-39)backend/apps/slack/commands/sponsors.py (1)
get_template_context
(12-22)
🔇 Additional comments (8)
backend/apps/slack/commands/staff.py (1)
8-19
: Clean implementation of CommandBase pattern.The Staff class follows a clean implementation of the CommandBase pattern. It correctly gets staff data and builds a template context with necessary information. The structure separates data preparation from rendering, which aligns with the PR objective of implementing a template system.
backend/apps/slack/commands/jobs.py (1)
11-21
: LGTM - Appropriate context for template rendering.The Jobs class implementation correctly provides channel IDs and bot name for the template context. This implementation aligns with the template-based messaging approach and maintains consistency with other command implementations.
backend/apps/slack/commands/news.py (1)
8-19
: Good implementation with proper error handling.The News command implementation properly handles the case where no news items are found by including the
has_news
boolean flag in the context. This ensures the template can display appropriate messaging when news data is unavailable.backend/apps/slack/commands/leaders.py (1)
7-60
: Well-structured implementation with good data processing.The Leaders command implementation is robust and properly processes search results for both chapters and projects. The code correctly:
- Extracts search query from the command
- Performs targeted searches with appropriate attributes
- Builds enhanced data structures with URLs for template rendering
- Handles the case of no results through the
has_results
flagThis implementation shows good separation between data retrieval, processing, and presentation preparation.
backend/apps/slack/commands/command.py (4)
47-63
: The base template context is well-structured and extendable.The implementation provides a solid foundation for template contexts and includes the necessary special delimiters. The design makes it easy for subclasses to extend the context with additional variables.
65-83
: Good implementation of block rendering with special delimiters.The
get_render_blocks
method effectively processes the template output and handles the special delimiters to create properly formatted Slack blocks.
108-116
: Effective null check for SlackConfig.app.The null check for
SlackConfig.app
prevents potential runtime errors when the Slack configuration isn't properly set up.
95-102
: Excellent error handling for template loading.The error handling in
get_template_file
is well-implemented with detailed logging that includes both the template name and command name. Usinglogger.exception
ensures that the full traceback is included in the logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Li-ChangC thanks for a great PR!
I've pushed some updates directly instead of commenting/suggesting changes.
Please review/verify if that works as expected and looks good to you.
Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (6)
backend/apps/slack/commands/contribute.py (1)
31-35
: Consider removing unnecessary method overrideThe
get_template_context
method doesn't add any additional context data beyond what the parent class provides. If there's no specialized context needed for this command, consider removing this method override to simplify the code.- def get_template_context(self, command): - """Get the template context.""" - return { - **super().get_template_context(command), - }backend/apps/slack/commands/gsoc.py (1)
35-35
:COMMAND_START
check is case‑sensitive
command_text in COMMAND_START
will fail for users typing/gsoc Start
or/gsoc START
. A simple.lower()
comparison avoids surprise errors.- if not command_text or command_text in COMMAND_START: + if not command_text or command_text.lower() in (c.lower() for c in COMMAND_START):backend/apps/slack/commands/owasp.py (1)
15-18
: Sub‑command lookup can be O(N²) and misses cached instances
You instantiate a new sub‑command object for every call. Consider caching instances or building a lookup dict once in__init__
to avoid repeated reflection.backend/apps/slack/commands/command.py (2)
24-34
:get_commands()
only returns direct subclasses
CommandBase.__subclasses__()
is shallow, so nested subclasses are missed (e.g., if a command subclasses another command). A simple recursive generator future‑proofs this.- yield from CommandBase.__subclasses__() + for cls in CommandBase.__subclasses__(): + yield cls + yield from cls.__subclasses__()
111-116
: Log level nitpick
Usingwarning
when the Slack app is unavailable is fine, but it might spam logs during local dev. Consider downgrading toinfo
or guarding behindDEBUG
.backend/apps/slack/commands/staff.py (1)
1-19
: Consider adding test information in docstring.The implementation is good, but since this is a significant refactoring, it might be helpful to add a note in the class docstring referencing where the tests for this class can be found, to help future developers.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
backend/poetry.lock
is excluded by!**/*.lock
📒 Files selected for processing (44)
backend/apps/slack/commands/__init__.py
(2 hunks)backend/apps/slack/commands/board.py
(1 hunks)backend/apps/slack/commands/command.py
(1 hunks)backend/apps/slack/commands/committees.py
(1 hunks)backend/apps/slack/commands/community.py
(1 hunks)backend/apps/slack/commands/contact.py
(1 hunks)backend/apps/slack/commands/contribute.py
(2 hunks)backend/apps/slack/commands/gsoc.py
(1 hunks)backend/apps/slack/commands/jobs.py
(1 hunks)backend/apps/slack/commands/leaders.py
(1 hunks)backend/apps/slack/commands/news.py
(1 hunks)backend/apps/slack/commands/owasp.py
(1 hunks)backend/apps/slack/commands/policies.py
(1 hunks)backend/apps/slack/commands/projects.py
(1 hunks)backend/apps/slack/commands/sponsors.py
(1 hunks)backend/apps/slack/commands/staff.py
(1 hunks)backend/apps/slack/commands/users.py
(1 hunks)backend/apps/slack/models/event.py
(1 hunks)backend/apps/slack/template_loader.py
(1 hunks)backend/apps/slack/templates/leaders.template
(1 hunks)backend/apps/slack/templates/news.template
(1 hunks)backend/apps/slack/templates/sponsors.template
(1 hunks)backend/apps/slack/templates/staff.template
(1 hunks)backend/pyproject.toml
(1 hunks)backend/tests/apps/slack/commands/board_test.py
(3 hunks)backend/tests/apps/slack/commands/chapters_test.py
(3 hunks)backend/tests/apps/slack/commands/committees_test.py
(3 hunks)backend/tests/apps/slack/commands/community_test.py
(4 hunks)backend/tests/apps/slack/commands/contact_test.py
(3 hunks)backend/tests/apps/slack/commands/contribute_test.py
(4 hunks)backend/tests/apps/slack/commands/donate_test.py
(3 hunks)backend/tests/apps/slack/commands/events_test.py
(2 hunks)backend/tests/apps/slack/commands/gsoc_test.py
(4 hunks)backend/tests/apps/slack/commands/jobs_test.py
(3 hunks)backend/tests/apps/slack/commands/leaders_test.py
(4 hunks)backend/tests/apps/slack/commands/news_test.py
(4 hunks)backend/tests/apps/slack/commands/owasp_test.py
(3 hunks)backend/tests/apps/slack/commands/policies_test.py
(2 hunks)backend/tests/apps/slack/commands/projects_test.py
(4 hunks)backend/tests/apps/slack/commands/sponsor_test.py
(2 hunks)backend/tests/apps/slack/commands/sponsors_test.py
(3 hunks)backend/tests/apps/slack/commands/staff_test.py
(3 hunks)backend/tests/apps/slack/commands/users_test.py
(3 hunks)backend/tests/apps/slack/models/event_test.py
(3 hunks)
✅ Files skipped from review due to trivial changes (1)
- backend/apps/slack/template_loader.py
🚧 Files skipped from review as they are similar to previous changes (32)
- backend/apps/slack/models/event.py
- backend/pyproject.toml
- backend/apps/slack/commands/init.py
- backend/tests/apps/slack/commands/community_test.py
- backend/tests/apps/slack/commands/contact_test.py
- backend/tests/apps/slack/commands/donate_test.py
- backend/apps/slack/commands/committees.py
- backend/apps/slack/templates/leaders.template
- backend/tests/apps/slack/commands/contribute_test.py
- backend/tests/apps/slack/commands/leaders_test.py
- backend/tests/apps/slack/commands/board_test.py
- backend/tests/apps/slack/commands/users_test.py
- backend/tests/apps/slack/commands/sponsor_test.py
- backend/tests/apps/slack/models/event_test.py
- backend/tests/apps/slack/commands/committees_test.py
- backend/tests/apps/slack/commands/staff_test.py
- backend/apps/slack/commands/users.py
- backend/apps/slack/templates/staff.template
- backend/tests/apps/slack/commands/owasp_test.py
- backend/apps/slack/commands/projects.py
- backend/apps/slack/templates/sponsors.template
- backend/tests/apps/slack/commands/events_test.py
- backend/tests/apps/slack/commands/news_test.py
- backend/tests/apps/slack/commands/chapters_test.py
- backend/tests/apps/slack/commands/gsoc_test.py
- backend/tests/apps/slack/commands/policies_test.py
- backend/tests/apps/slack/commands/sponsors_test.py
- backend/tests/apps/slack/commands/projects_test.py
- backend/apps/slack/commands/board.py
- backend/tests/apps/slack/commands/jobs_test.py
- backend/apps/slack/templates/news.template
- backend/apps/slack/commands/community.py
🧰 Additional context used
🧬 Code Graph Analysis (4)
backend/apps/slack/commands/sponsors.py (2)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-147)get_template_context
(47-62)backend/apps/slack/utils.py (1)
get_sponsors_data
(136-152)
backend/apps/slack/commands/jobs.py (1)
backend/apps/slack/commands/command.py (2)
CommandBase
(16-147)get_template_context
(47-62)
backend/apps/slack/commands/gsoc.py (3)
backend/apps/common/utils.py (1)
get_absolute_url
(13-23)backend/apps/slack/commands/command.py (3)
CommandBase
(16-147)get_template_context
(47-62)get_command_name
(83-85)backend/apps/slack/utils.py (1)
get_gsoc_projects
(34-55)
backend/apps/slack/commands/command.py (2)
backend/apps/slack/apps.py (1)
SlackConfig
(10-21)backend/apps/slack/blocks.py (1)
markdown
(14-27)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Run frontend unit tests
🔇 Additional comments (17)
backend/apps/slack/commands/sponsors.py (1)
9-21
: Well implemented class-based command structureThe
Sponsors
class correctly inherits fromCommandBase
and properly overrides theget_template_context
method. It extends the base context with sponsor-specific data and uses constants for configuration values, which is good practice.I appreciate how you've maintained a clean separation of concerns: data retrieval in this class and presentation in the template file.
backend/apps/slack/commands/leaders.py (1)
7-59
: Good implementation with clear data transformationThe
Leaders
class implements a more complex command with search functionality and data transformation. The code is well-structured with:
- Clear separation between data retrieval and preparation
- Appropriate use of list comprehensions for data transformation
- Good variable naming that communicates intent
- Proper use of inheritance with
super().get_template_context(command)
The approach to enriching search results with URLs is clean and maintainable.
backend/apps/slack/commands/news.py (1)
8-18
: Clean, concise implementationThis implementation is straightforward and follows the established command pattern. It properly inherits from
CommandBase
, extends the context dictionary with news-specific data, and maintains separation of concerns.backend/apps/slack/commands/jobs.py (1)
11-21
: Simple and effective implementationThe
Jobs
class implementation is minimalist but effective. It provides just the necessary context variables to the template without any additional logic, which is appropriate for this command's requirements.backend/apps/slack/commands/contribute.py (1)
12-29
: The implementation appears to bypass the template system for non-help commandsThe
get_render_blocks
method returns help blocks via the parent class method (which presumably uses templates) when help is requested, but directly returns blocks fromget_blocks()
for search queries. This appears to diverge from the template-based approach described in the PR and implemented in other commands.Consider whether this command should consistently use templates like the other commands or if there's a specific reason for this hybrid approach.
backend/apps/slack/commands/gsoc.py (1)
29-31
: Potential off‑by‑one for GSoC “current year” calculation
gsoc_year = now.year if now.month > MARCH else now.year - 1
excludes March itself (now.month == 3
). If the intention is “from March onwards we are in the new GSoC season”, the comparison should be>= MARCH
. Please double‑check the business rule.Would you like a quick patch to adjust the comparison?
backend/apps/slack/commands/command.py (1)
30-34
: Duplicate registration on Django code‑reload
configure_commands()
instantiates and registers every call. When Django’s autoreload runs, commands will accumulate multiple identical handlers. Track a “configured” flag or register once at import time.backend/apps/slack/commands/policies.py (3)
3-4
: Clean import structure.The import has been simplified to just include the
CommandBase
which is good for maintainability and reduces dependencies.
6-8
: Good use of class structure and documentation.The class implementation with proper docstring follows good Python practices and clearly communicates the purpose of this class.
9-39
: Well-structured data and context preparation.The
get_template_context
method cleanly organizes the policies data as tuples and properly leverages inheritance by calling the parent method viasuper()
. This approach separates the data preparation from presentation logic, which aligns with the PR's goal of improving maintainability.backend/apps/slack/commands/contact.py (4)
3-3
: Clean import structure.The import has been simplified to just include the
CommandBase
which follows the principle of only importing what's needed.
6-8
: Proper class definition with documentation.The class is well-defined with appropriate documentation, making the code's purpose clear.
9-12
: Template customization.Good implementation of
get_template_file_name
to specify a custom template. This demonstrates how the new architecture allows for template reuse across different commands.
13-19
: Clean context preparation.The
get_template_context
method properly extends the base context and adds specific data needed for this command. This separation of concerns makes the code more maintainable.backend/apps/slack/commands/staff.py (3)
3-6
: Appropriate imports.The imports are focused and import only what's needed for this command class. Including utilities like
get_staff_data
shows good separation of concerns.
8-10
: Proper class definition with documentation.The class follows the established pattern with clear documentation.
11-18
: Effective data retrieval and context preparation.The method retrieves staff data through a utility function and properly constructs the context by extending the base context. This approach keeps the command class focused on its specific responsibilities.
Thanks! I’ve reviewed the changes and tested them in my Slack workspace, and everything works as expected. Appreciate your help! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
|
Resolves #342
Changes:
.template
files to improve maintainability and consistency.BaseCommand
class to streamline the implementation of new Slack commands.Reason: