Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions backend/apps/mentorship/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from apps.mentorship.models.mentor import Mentor
from apps.mentorship.models.module import Module
from apps.mentorship.models.program import Program
from apps.mentorship.models.task import Task


class MenteeAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -86,8 +87,30 @@ class ProgramAdmin(admin.ModelAdmin):
filter_horizontal = ("admins",)


class TaskAdmin(admin.ModelAdmin):
"""Admin view for Task model."""

list_display = (
"issue",
"assignee",
"module",
"status",
"assigned_at",
"deadline_at",
)

search_fields = (
"issue__title",
"assignee__github_user__login",
"module__name",
)

list_filter = ("status", "module", "assignee")


admin.site.register(MenteeProgram, MenteeProgramAdmin)
admin.site.register(Mentee, MenteeAdmin)
admin.site.register(Mentor, MentorAdmin)
admin.site.register(Module, ModuleAdmin)
admin.site.register(Program, ProgramAdmin)
admin.site.register(Task, TaskAdmin)
98 changes: 98 additions & 0 deletions backend/apps/mentorship/migrations/0002_module_labels_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Generated by Django 5.2.4 on 2025-07-23 08:44

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("github", "0032_user_github_user_created_at_desc_and_more"),
("mentorship", "0001_initial"),
]

operations = [
migrations.AddField(
model_name="module",
name="labels",
field=models.JSONField(blank=True, default=list, verbose_name="Labels"),
),
migrations.CreateModel(
name="Task",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
("nest_created_at", models.DateTimeField(auto_now_add=True)),
("nest_updated_at", models.DateTimeField(auto_now=True)),
(
"status",
models.CharField(
choices=[
("TODO", "To Do"),
("IN_PROGRESS", "In Progress"),
("IN_REVIEW", "In Review"),
("COMPLETED", "Completed"),
],
default="TODO",
max_length=20,
verbose_name="Task status",
),
),
(
"assigned_at",
models.DateTimeField(
auto_now_add=True,
help_text="Timestamp when the task was assigned to the mentee.",
),
),
(
"deadline_at",
models.DateTimeField(
blank=True, help_text="Optional deadline for the task.", null=True
),
),
(
"metadata",
models.JSONField(blank=True, default=dict, help_text="Optional data"),
),
(
"assignee",
models.ForeignKey(
blank=True,
help_text="The GitHub user assigned to this task.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="tasks",
to="github.user",
),
),
(
"issue",
models.ForeignKey(
help_text="The GitHub issue this task corresponds to.",
on_delete=django.db.models.deletion.PROTECT,
related_name="mentorship_tasks",
to="github.issue",
),
),
(
"module",
models.ForeignKey(
help_text="The module this task is part of.",
on_delete=django.db.models.deletion.CASCADE,
related_name="tasks",
to="mentorship.module",
),
),
],
options={
"verbose_name_plural": "Tasks",
"db_table": "mentorship_tasks",
"ordering": ["deadline_at"],
"unique_together": {("issue", "assignee")},
},
),
]
1 change: 1 addition & 0 deletions backend/apps/mentorship/models/common/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .experience_level import ExperienceLevel
from .matching_attributes import MatchingAttributes
from .start_end_range import StartEndRange
from .status import Status
25 changes: 25 additions & 0 deletions backend/apps/mentorship/models/common/status.py
Copy link
Collaborator

Choose a reason for hiding this comment

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

You don't have to separate it if it's used by only one model.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Mentorship app start/end range."""

from django.db import models


class Status(models.Model):
"""Defines the possible states of a task."""

class Meta:
abstract = True

class StatusChoices(models.TextChoices):
"""Status choices."""

TODO = "TODO", "To Do"
IN_PROGRESS = "IN_PROGRESS", "In Progress"
IN_REVIEW = "IN_REVIEW", "In Review"
COMPLETED = "COMPLETED", "Completed"

status = models.CharField(
max_length=20,
choices=StatusChoices.choices,
default=StatusChoices.TODO,
verbose_name="Task status",
)
12 changes: 11 additions & 1 deletion backend/apps/mentorship/models/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
from django.db import models

from apps.common.models import TimestampedModel
from apps.mentorship.models.common import ExperienceLevel, MatchingAttributes, StartEndRange
from apps.mentorship.models.common import (
ExperienceLevel,
MatchingAttributes,
StartEndRange,
)


class Module(ExperienceLevel, MatchingAttributes, StartEndRange, TimestampedModel):
Expand Down Expand Up @@ -42,6 +46,12 @@ class Meta:
verbose_name="Project",
)

labels = models.JSONField(
blank=True,
default=list,
verbose_name="Labels",
)

# M2Ms.
mentors = models.ManyToManyField(
"mentorship.Mentor",
Expand Down
65 changes: 65 additions & 0 deletions backend/apps/mentorship/models/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Task model for the Mentorship app."""

from __future__ import annotations

from django.db import models

from apps.common.models import TimestampedModel
from apps.mentorship.models.common import Status


class Task(Status, TimestampedModel):
"""Connects a Module, a GitHub Issue, and a Mentee to track work."""

class Meta:
db_table = "mentorship_tasks"
verbose_name_plural = "Tasks"
unique_together = ("issue", "assignee")
ordering = ["deadline_at"]

# FKs.
assignee = models.ForeignKey(
"github.User",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="tasks",
help_text="The mentee assigned to this task.",
)

issue = models.ForeignKey(
"github.Issue",
on_delete=models.PROTECT,
related_name="mentorship_tasks",
help_text="The GitHub issue this task corresponds to.",
)

module = models.ForeignKey(
"mentorship.Module",
on_delete=models.CASCADE,
related_name="tasks",
help_text="The module this task is part of.",
)

assigned_at = models.DateTimeField(
auto_now_add=True,
help_text="Timestamp when the task was assigned to the mentee.",
)

deadline_at = models.DateTimeField(
null=True, blank=True, help_text="Optional deadline for the task."
)

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

def __str__(self) -> str:
"""Return a human-readable representation of the task."""
return (
f"Task for '{self.issue.title}' assigned to {self.assignee.login}"
if self.assignee
else f"Task: {self.issue.title} (Unassigned)"
)