Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Rajgupta36
Copy link
Collaborator

Proposed change

Resolves #1760

description

Created the TaskLevel model according to the issue

Checklist

  • Updated Module Model
  • Updated Task Model
  • Added TaskLevel Model

@Rajgupta36 Rajgupta36 requested a review from arkid15r as a code owner July 23, 2025 09:42
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 23, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new "TaskLevel" model to represent task difficulty levels and their prerequisites.
    • Added support for associating a difficulty level with each task.
    • Tasks now support labels and additional metadata fields.
  • Improvements

    • Enhanced the admin interface to manage and search task levels more efficiently.
  • Bug Fixes

    • None.
  • Documentation

    • None.

Walkthrough

This change introduces a new TaskLevel model to represent task difficulty and prerequisites, links it to the existing Task model via a nullable foreign key, and provides Django admin support for managing task levels. Associated migrations are included to update the database schema accordingly.

Changes

Cohort / File(s) Change Summary
Migrations for TaskLevel and Task updates
backend/apps/mentorship/migrations/0002_module_labels_task.py, backend/apps/mentorship/migrations/0003_tasklevel_task_level_and_more.py
Adds labels field to Module, introduces Task model, creates TaskLevel model, adds level foreign key to Task, and sets up related constraints and relationships.
TaskLevel Model Implementation
backend/apps/mentorship/models/task_level.py
Implements the TaskLevel model with fields for name, description, labels, module association, and self-referential prerequisites.
Task Model Update
backend/apps/mentorship/models/task.py
Adds a nullable foreign key level to associate each Task with a TaskLevel.
Model Exports
backend/apps/mentorship/models/__init__.py
Exports Task and TaskLevel from the models package.
Admin Registration
backend/apps/mentorship/admin/__init__.py, backend/apps/mentorship/admin/task_level.py
Registers TaskLevel in the Django admin with a custom admin class for improved management and searchability.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15–20 minutes

Assessment against linked issues

Objective Addressed Explanation
Introduce a flexible TaskLevel model with fields for name, description, labels, module, and prerequisites (#1760)
Link each Task to a TaskLevel via a foreign key (#1760)
Use GitHub labels to reflect task levels externally (labels field in TaskLevel) (#1760)
Provide admin interface to manage TaskLevels for a module (#1760)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes were identified.

Possibly related PRs

  • Added Task model #1787: Lays foundational work for the Task model and updates to Module; this PR builds upon it by introducing TaskLevel and linking it to Task.

Suggested reviewers

  • arkid15r

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17d1a7b and 3f51a6c.

📒 Files selected for processing (1)
  • backend/apps/mentorship/admin/task_level.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/mentorship/admin/task_level.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run backend tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Rajgupta36 Rajgupta36 self-assigned this Jul 23, 2025
@Rajgupta36 Rajgupta36 marked this pull request as draft July 23, 2025 09:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
backend/apps/mentorship/admin.py (1)

112-126: Consider adding list filters to TaskLevelAdmin.

The admin class is functional but could benefit from list filters to improve the admin user experience.

 class TaskLevelAdmin(admin.ModelAdmin):
     """Admin view for TaskLevel model."""

     list_display = (
         "description",
         "github_labels",
         "module",
         "name",
     )

     search_fields = (
         "name",
         "module__name",
     )
+
+    list_filter = ("module",)
backend/apps/mentorship/models/common/status.py (1)

1-1: Fix incorrect docstring.

The docstring mentions "start/end range" which is incorrect for a Status model. This appears to be copied from another file.

-"""Mentorship app start/end range."""
+"""Mentorship app status model."""
backend/apps/mentorship/models/tasl_level.py (2)

22-26: Consider validating the github_labels structure.

The github_labels field uses a generic JSONField with default=list, but there's no validation to ensure the stored data maintains the expected list structure or contains valid label data.

Consider adding a custom validator:

+from django.core.exceptions import ValidationError

+def validate_github_labels(value):
+    """Validate that github_labels is a list of strings or objects."""
+    if not isinstance(value, list):
+        raise ValidationError("GitHub labels must be a list.")
+    for label in value:
+        if not isinstance(label, (str, dict)):
+            raise ValidationError("Each label must be a string or dictionary.")

 github_labels = models.JSONField(
     default=list,
     blank=True,
     verbose_name="GitHub Labels",
+    validators=[validate_github_labels],
 )

50-57: Handle potential attribute errors in str method.

The __str__ method assumes self.module and self.module.name are always available, but if the module FK is deleted or corrupted, this could raise an AttributeError.

Add defensive programming:

 def __str__(self) -> str:
     """Return a human-readable representation of the task level.
 
     Returns:
         str: Module name with task level name.
 
     """
-    return f"{self.module.name} - {self.name}"
+    module_name = getattr(self.module, 'name', 'Unknown Module') if self.module else 'No Module'
+    return f"{module_name} - {self.name}"
backend/apps/mentorship/models/task.py (2)

53-60: Inconsistent field behavior for assigned_at vs auto-timestamps.

The assigned_at field uses auto_now_add=True, which sets the timestamp only on creation. However, if a task is reassigned (assignee changes), this timestamp won't update, making it misleading. Consider the workflow implications.

If reassignment is expected, consider updating the field when assignee changes:

def save(self, *args, **kwargs):
    """Override save to update assigned_at when assignee changes."""
    if self.pk and self.assignee:
        old_task = Task.objects.get(pk=self.pk)
        if old_task.assignee != self.assignee:
            self.assigned_at = timezone.now()
    super().save(*args, **kwargs)

Or rename the field to created_at if it should only track creation time.


68-74: Handle None assignee gracefully in str method.

The __str__ method correctly handles the case where assignee is None, but it doesn't handle the case where assignee.login might be None or unavailable.

Add defensive handling:

 def __str__(self) -> str:
     """Return a human-readable representation of the task."""
+    assignee_display = getattr(self.assignee, 'login', 'Unknown User') if self.assignee else None
     return (
-        f"Task for '{self.issue.title}' assigned to {self.assignee.login}"
+        f"Task for '{self.issue.title}' assigned to {assignee_display}"
         if self.assignee
         else f"Task: {self.issue.title} (Unassigned)"
     )
backend/apps/mentorship/migrations/0002_module_labels_task.py (1)

62-71: Inconsistent help_text between migrations.

In this migration, the assignee field has help_text "The GitHub user assigned to this task." but in migration 0003 and the model definition, it's "The mentee assigned to this task." This inconsistency could cause confusion.

The help_text should be consistent across migrations. Consider updating the model definition to match the intended purpose (mentee vs GitHub user).

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17a1178 and 488a4af.

📒 Files selected for processing (9)
  • backend/apps/mentorship/admin.py (2 hunks)
  • backend/apps/mentorship/migrations/0002_module_labels_task.py (1 hunks)
  • backend/apps/mentorship/migrations/0003_alter_task_assignee_tasklevel_task_level.py (1 hunks)
  • backend/apps/mentorship/models/__init__.py (1 hunks)
  • backend/apps/mentorship/models/common/__init__.py (1 hunks)
  • backend/apps/mentorship/models/common/status.py (1 hunks)
  • backend/apps/mentorship/models/module.py (2 hunks)
  • backend/apps/mentorship/models/task.py (1 hunks)
  • backend/apps/mentorship/models/tasl_level.py (1 hunks)
🧠 Learnings (1)
backend/apps/mentorship/models/common/status.py (1)

Learnt from: Rajgupta36
PR: #1717
File: frontend/src/components/ModuleForm.tsx:112-134
Timestamp: 2025-07-14T16:18:07.287Z
Learning: In the OWASP Nest mentorship system, date validation for modules (ensuring start date precedes end date) is handled on the backend in the module GraphQL mutations via the _validate_module_dates helper function in backend/apps/mentorship/graphql/mutations/module.py, which prevents invalid date ranges from being stored in the database.

🪛 GitHub Actions: Run CI/CD
backend/apps/mentorship/models/__init__.py

[error] 9-9: CSpell: Unknown word 'tasl' detected.

backend/apps/mentorship/admin.py

[error] 11-11: CSpell: Unknown word 'tasl' detected.

🧰 Additional context used
🧠 Learnings (1)
backend/apps/mentorship/models/common/status.py (1)

Learnt from: Rajgupta36
PR: #1717
File: frontend/src/components/ModuleForm.tsx:112-134
Timestamp: 2025-07-14T16:18:07.287Z
Learning: In the OWASP Nest mentorship system, date validation for modules (ensuring start date precedes end date) is handled on the backend in the module GraphQL mutations via the _validate_module_dates helper function in backend/apps/mentorship/graphql/mutations/module.py, which prevents invalid date ranges from being stored in the database.

🪛 GitHub Actions: Run CI/CD
backend/apps/mentorship/models/__init__.py

[error] 9-9: CSpell: Unknown word 'tasl' detected.

backend/apps/mentorship/admin.py

[error] 11-11: CSpell: Unknown word 'tasl' detected.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (10)
backend/apps/mentorship/models/__init__.py (1)

8-8: LGTM: Task model import looks correct.

The Task model import follows proper Django conventions and aligns with the PR objectives.

backend/apps/mentorship/models/common/__init__.py (1)

4-4: LGTM: Status model import is properly added.

The Status import correctly exposes the new abstract Status model for use by other models in the mentorship app.

backend/apps/mentorship/models/module.py (2)

8-12: LGTM: Import formatting improvement.

The multi-line import style improves readability and follows Python best practices for long import statements.


49-53: LGTM: Labels field implementation is correct.

The JSONField configuration is appropriate with:

  • blank=True allowing empty values in forms
  • default=list providing a safe mutable default
  • Proper verbose name for admin interface
backend/apps/mentorship/admin.py (2)

91-110: LGTM: TaskAdmin configuration is comprehensive.

The admin class provides excellent functionality with:

  • Comprehensive list display showing key task information
  • Good search fields covering issue title, assignee, and module
  • Useful filters for status, module, and assignee

133-134: LGTM: Admin registrations are correct.

Both new admin classes are properly registered with the Django admin site.

backend/apps/mentorship/models/common/status.py (1)

6-25: LGTM: Status model implementation is well-designed.

The abstract Status model provides:

  • Clear StatusChoices with appropriate values and labels
  • Proper CharField configuration with sensible max_length
  • Good default value (TODO)
  • Abstract base class design for reusability
backend/apps/mentorship/models/tasl_level.py (1)

42-48: Verify the self-referential relationship configuration.

The needs field creates a self-referential many-to-many relationship for prerequisites. The configuration looks correct with symmetrical=False and appropriate related names, which properly supports dependency chains.

backend/apps/mentorship/migrations/0003_alter_task_assignee_tasklevel_task_level.py (1)

69-80: Verify the field addition order and dependencies.

The level field is added to the task model after creating the TaskLevel model, which is the correct order to avoid foreign key reference errors. The field definition matches the model specification.

backend/apps/mentorship/migrations/0002_module_labels_task.py (1)

31-43: Status choices verified
The hardcoded choices in 0002_module_labels_task.py align exactly with StatusChoices in backend/apps/mentorship/models/common/status.py. No further action required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 488a4af and 0905691.

📒 Files selected for processing (3)
  • backend/apps/mentorship/admin.py (2 hunks)
  • backend/apps/mentorship/models/__init__.py (1 hunks)
  • backend/apps/mentorship/models/task_level.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/apps/mentorship/models/init.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/apps/mentorship/admin.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run frontend e2e tests
🔇 Additional comments (5)
backend/apps/mentorship/models/task_level.py (5)

1-6: LGTM! Clean imports and documentation.

The file header follows Python best practices with proper docstring and minimal necessary imports.


8-15: Well-structured model declaration.

The class declaration and Meta configuration follow Django best practices with appropriate table naming, ordering, and documentation.


34-48: Well-designed relationship fields.

The foreign key and many-to-many relationships are properly configured with appropriate delete behavior and related names. The self-referential prerequisite relationship with symmetrical=False correctly models the directional nature of prerequisites.


50-57: Excellent string representation method.

The __str__ method is well-documented and provides a clear, informative representation combining module and task level names.


22-26: Fix mutable default argument in JSONField.

Using default=list creates a mutable default that can lead to unexpected behavior where all instances share the same list object.

Apply this fix:

-    github_labels = models.JSONField(
-        default=list,
-        blank=True,
-        verbose_name="GitHub Labels",
-    )
+    github_labels = models.JSONField(
+        default=list,
+        blank=True,
+        verbose_name="GitHub Labels",
+    )

Wait, let me correct that - the proper fix is:

-    github_labels = models.JSONField(
-        default=list,
-        blank=True,
-        verbose_name="GitHub Labels",
-    )
+    github_labels = models.JSONField(
+        default=lambda: [],
+        blank=True,
+        verbose_name="GitHub Labels",
+    )

Likely an incorrect or invalid review comment.

@abhayymishraa abhayymishraa marked this pull request as ready for review July 23, 2025 14:46
@abhayymishraa abhayymishraa marked this pull request as draft July 23, 2025 14:46
default="",
)

github_labels = models.JSONField(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep the naming consistent -- either github_labels or labels everywhere (see the Module model)

@Rajgupta36 Rajgupta36 requested a review from arkid15r July 23, 2025 19:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
backend/apps/mentorship/models/task.py (1)

16-16: The unique_together constraint remains problematic for task reassignment workflows.

This constraint was previously identified as too restrictive. It prevents reassigning tasks after completion or when assignees change, which is common in mentorship workflows.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0905691 and df2e34f.

📒 Files selected for processing (5)
  • backend/apps/mentorship/admin.py (2 hunks)
  • backend/apps/mentorship/migrations/0002_module_labels_task.py (1 hunks)
  • backend/apps/mentorship/migrations/0003_tasklevel_task_level.py (1 hunks)
  • backend/apps/mentorship/models/task.py (1 hunks)
  • backend/apps/mentorship/models/task_level.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/apps/mentorship/admin.py
  • backend/apps/mentorship/migrations/0002_module_labels_task.py
  • backend/apps/mentorship/models/task_level.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
  • GitHub Check: Run backend tests
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (3)
backend/apps/mentorship/models/task.py (2)

69-73: Avoid mutable default arguments in model fields.

Using default=dict creates a shared mutable object across all instances, which can lead to unexpected behavior where changes to one instance's metadata affect others.

Apply this fix to use a callable default:

 metadata = models.JSONField(
-    default=dict,
+    default=dict,
     blank=True,
     help_text="Optional data",
 )

Wait, that's not the right fix. Use a lambda or callable:

 metadata = models.JSONField(
-    default=dict,
+    default=lambda: {},
     blank=True,
     help_text="Optional data",
 )

Likely an incorrect or invalid review comment.


44-51: TaskLevel model verified

The TaskLevel model is defined in backend/apps/mentorship/models/task_level.py with appropriate fields, metadata (db_table, ordering), and relationships (module FK and self-referential M2M for prerequisites). It aligns correctly with the level = ForeignKey("mentorship.TaskLevel", ...) usage in the Task model. No further changes required.

backend/apps/mentorship/migrations/0003_tasklevel_task_level.py (1)

53-64: The level field addition looks correct.

The migration properly adds the nullable foreign key relationship between Task and TaskLevel with appropriate cascade behavior (SET_NULL).

@Rajgupta36 Rajgupta36 marked this pull request as ready for review July 24, 2025 12:04
)

name = models.CharField(
max_length=200,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default="" ?

@Rajgupta36 Rajgupta36 requested a review from arkid15r August 4, 2025 16:24
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63a5bac and 17d1a7b.

📒 Files selected for processing (2)
  • backend/apps/mentorship/admin/__init__.py (1 hunks)
  • backend/apps/mentorship/admin/task_level.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend/apps/mentorship/admin/init.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: Rajgupta36
PR: OWASP/Nest#1717
File: backend/apps/mentorship/graphql/mutations/module.py:28-29
Timestamp: 2025-07-16T13:49:58.648Z
Learning: In the OWASP Nest mentorship system, mentors can be created with only github_user initially (without nest_user) when assigned to modules. This allows mentors to be assigned before they've signed into the system. When these users eventually sign in, the nest_user is automatically linked to the existing github_user, creating a complete mentor profile. This design provides flexibility in mentor assignment workflows.
🪛 GitHub Actions: Run CI/CD
backend/apps/mentorship/admin/task_level.py

[error] 1-7: pre-commit hook 'ruff' failed due to formatting issues. The hook made changes to this file to fix errors. Run 'pre-commit run --all-files' locally to reproduce and fix.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CodeQL (javascript-typescript)
  • GitHub Check: CodeQL (python)
🔇 Additional comments (2)
backend/apps/mentorship/admin/task_level.py (2)

3-4: LGTM!

The imports are correct and appropriate for Django admin configuration.


7-20: Admin configuration looks good.

The TaskLevelAdmin class provides a sensible configuration with appropriate fields for display and search functionality. The list_display includes key fields like description, labels, module, and name, while search_fields enables searching by name and related module name.

@arkid15r arkid15r enabled auto-merge August 5, 2025 02:49
@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 5, 2025

@arkid15r arkid15r added this pull request to the merge queue Aug 5, 2025
Merged via the queue into OWASP:main with commit 2c0c2ef Aug 5, 2025
24 checks passed
trucodd pushed a commit to trucodd/Nest that referenced this pull request Aug 5, 2025
* Added Task module

* Update code

* Update code

* Added TaskLevel Model

* spell fix

* update code

* update code

* updated schema

* update code

* update code

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
@coderabbitai coderabbitai bot mentioned this pull request Oct 8, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add TaskLevel Model for Complexity & Prerequisites

2 participants