Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
41 changes: 41 additions & 0 deletions backend/apps/mentorship/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
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
from apps.mentorship.models.tasl_level import TaskLevel


class MenteeAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -86,8 +88,47 @@ 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")


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

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

search_fields = (
"name",
"module__name",
)


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)
admin.site.register(TaskLevel, TaskLevelAdmin)
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")},
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Generated by Django 5.2.4 on 2025-07-23 09:39

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", "0002_module_labels_task"),
]

operations = [
migrations.AlterField(
model_name="task",
name="assignee",
field=models.ForeignKey(
blank=True,
help_text="The mentee assigned to this task.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="tasks",
to="github.user",
),
),
migrations.CreateModel(
name="TaskLevel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
(
"description",
models.TextField(blank=True, default="", verbose_name="Description"),
),
(
"github_labels",
models.JSONField(blank=True, default=list, verbose_name="GitHub Labels"),
),
("name", models.CharField(max_length=200, verbose_name="Name")),
(
"module",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="task_levels",
to="mentorship.module",
verbose_name="Module",
),
),
(
"needs",
models.ManyToManyField(
blank=True,
related_name="required_by",
to="mentorship.tasklevel",
verbose_name="Prerequisite Levels",
),
),
],
options={
"verbose_name_plural": "Task Levels",
"db_table": "mentorship_task_levels",
"ordering": ["name"],
},
),
migrations.AddField(
model_name="task",
name="level",
field=models.ForeignKey(
blank=True,
help_text="The difficulty level of this task.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="tasks",
to="mentorship.tasklevel",
),
),
]
2 changes: 2 additions & 0 deletions backend/apps/mentorship/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
from .mentor_module import MentorModule
from .module import Module
from .program import Program
from .task import Task
from .tasl_level import TaskLevel
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
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
74 changes: 74 additions & 0 deletions backend/apps/mentorship/models/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""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.",
)

level = models.ForeignKey(
"mentorship.TaskLevel",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="tasks",
help_text="The difficulty level of this task.",
)

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)"
)
Loading
Loading