Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
67897a3
wip
GDay Sep 19, 2025
9d99b24
add department create/list
GDay Sep 20, 2025
01fab29
adding selectors for templates/admin task
GDay Sep 22, 2025
57cb47b
fix tests
GDay Sep 23, 2025
4c74c9e
format
GDay Sep 23, 2025
07cfd45
redo migrations
GDay Sep 23, 2025
fed6fe6
fix copilot comments
GDay Sep 23, 2025
04325c5
update department on sequence
GDay Sep 23, 2025
972223f
format
GDay Sep 23, 2025
ebef5fb
revert admin_task refactor
GDay Sep 23, 2025
3be8f3c
format
GDay Sep 23, 2025
72b8c1c
limit items in sequence block view
GDay Sep 23, 2025
577038d
limit integrations
GDay Sep 24, 2025
f0b336c
Merge branch 'master' into add-permissions-based-on-department
GDay Sep 24, 2025
9693aa9
add form date widget instead of full form element
GDay Sep 25, 2025
d9878d0
Merge branch 'master' into add-permissions-based-on-department
GDay Sep 25, 2025
c6b520c
fix selector on user list intro
GDay Sep 25, 2025
3ce201d
update get_user_model to User
GDay Sep 25, 2025
43b05e2
change queryset name
GDay Sep 25, 2025
2b49ac9
remove else
GDay Sep 25, 2025
7458468
add ordering on department model
GDay Sep 25, 2025
2ba3199
add back defer content
GDay Sep 25, 2025
18a7e2a
change test to use template
GDay Sep 25, 2025
6774d3a
rename mixin
GDay Sep 25, 2025
3e8acdf
remove badge queryset
GDay Sep 25, 2025
0b34775
add distinct
GDay Sep 25, 2025
ea00823
replace get_object with object
GDay Sep 25, 2025
4489504
use self.object
GDay Sep 25, 2025
2f1f649
replace get_user_model with User
GDay Sep 25, 2025
e3eee87
explicitly set elif manager
GDay Sep 25, 2025
8a52ab2
remove else
GDay Sep 25, 2025
ae46fec
remove else
GDay Sep 25, 2025
0d15515
fix keyerror
GDay Sep 25, 2025
1e806e5
add test for formating departments
GDay Sep 25, 2025
f69875f
fix admin task test failure
GDay Sep 26, 2025
0f01135
Merge branch 'master' into add-permissions-based-on-department
GDay Oct 9, 2025
3640f3a
add block on removing existing departments + add tests
GDay Oct 13, 2025
9d77ef3
Merge branch 'master' into add-permissions-based-on-department
GDay Oct 13, 2025
735ce89
format
GDay Oct 13, 2025
48857f7
remove queryset on hardware create
GDay Oct 13, 2025
f5b8970
fix warning
GDay Oct 13, 2025
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
59 changes: 34 additions & 25 deletions back/admin/admin_tasks/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from django import forms
from django.utils.translation import gettext_lazy as _

from users.models import User
from users.selectors import (
get_all_managers_and_admins_for_departments_of_user,
get_all_managers_and_admins_for_departments_of_user_with_slack,
get_all_new_hires_for_departments_of_user,
)

from .models import AdminTask, AdminTaskComment

Expand All @@ -14,40 +18,32 @@ class Meta:
]


class AdminTaskUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = getattr(self, "instance", None)
if instance is not None and instance.completed:
for field in self.fields:
self.fields[field].widget.attrs["disabled"] = True

class Meta:
model = AdminTask
fields = ["name", "assigned_to", "date", "priority"]


class AdminTaskCreateForm(forms.ModelForm):
comment = forms.CharField(label=_("Comment"), max_length=12500)
new_hire = forms.ModelChoiceField(
label=_("New hire"), queryset=User.new_hires.all()
)
assigned_to = forms.ModelChoiceField(
label=_("Assigned to"), queryset=User.admins.all()
)
slack_user = forms.ModelChoiceField(
label=_("Slack user"), queryset=User.managers_and_admins.with_slack()
)
date = forms.DateField(
label=_("Date"),
required=False,
widget=forms.DateInput(attrs={"type": "date"}, format=("%Y-%m-%d")),
)

def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super().__init__(*args, **kwargs)
self.fields["option"].initial = 0
self.fields["slack_user"].required = False
if "option" in self.fields:
self.fields["option"].initial = 0
if "new_hire" in self.fields:
Comment thread
GDay marked this conversation as resolved.
Outdated
self.fields[
"new_hire"
].queryset = get_all_new_hires_for_departments_of_user(user=user)
self.fields[
"assigned_to"
].queryset = get_all_managers_and_admins_for_departments_of_user(user=user)
if "slack_user" in self.fields:
self.fields[
"slack_user"
].queryset = get_all_managers_and_admins_for_departments_of_user_with_slack(
user=user
)
Comment thread
GDay marked this conversation as resolved.
Outdated

class Meta:
model = AdminTask
Expand All @@ -62,3 +58,16 @@ class Meta:
"slack_user",
"email",
]


class AdminTaskUpdateForm(AdminTaskCreateForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = getattr(self, "instance", None)
if instance is not None and instance.completed:
for field in self.fields:
self.fields[field].widget.attrs["disabled"] = True

class Meta:
model = AdminTask
fields = ["name", "assigned_to", "date", "priority"]
22 changes: 20 additions & 2 deletions back/admin/admin_tasks/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.conf import settings
from django.db import models
from django.db.models import Q
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _

Expand All @@ -13,7 +14,24 @@
)


class AminTaskManager(models.Manager):
class FilteredForAdminQuerySet(models.QuerySet):
Comment thread
GDay marked this conversation as resolved.
Outdated
def for_user(self, user):
if user.is_manager:
return self.filter(
Q(new_hire__departments__isnull=True)
| Q(new_hire__departments__in=user.departments.all())
)
else:
return self


class AdminTaskManager(models.Manager):
def for_user(self, user):
return self.get_queryset().for_user(user)

def get_queryset(self):
return FilteredForAdminQuerySet(self.model, using=self._db)

def create_admin_task(
self,
new_hire,
Expand Down Expand Up @@ -125,7 +143,7 @@ class Notification(models.IntegerChoices):
help_text=_("Only set if generated based on hardware."),
)

objects = AminTaskManager()
objects = AdminTaskManager()

def get_icon_template(self):
return render_to_string("_admin_task_icon.html")
Expand Down
10 changes: 10 additions & 0 deletions back/admin/admin_tasks/selectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from admin.admin_tasks.models import AdminTask
from users.models import User


def get_admin_tasks_for_department(*, user: User):
return AdminTask.objects.for_user(user=user)


def get_admin_tasks_for_user(*, user: User):
return AdminTask.objects.filter(assigned_to=user)
53 changes: 38 additions & 15 deletions back/admin/admin_tasks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView

from users.mixins import ManagerPermMixin
from admin.admin_tasks.selectors import (
get_admin_tasks_for_department,
get_admin_tasks_for_user,
)
from misc.mixins import FormWithUserContextMixin
from users.mixins import AdminOrManagerPermMixin

from .forms import AdminTaskCommentForm, AdminTaskCreateForm, AdminTaskUpdateForm
from .models import AdminTask, AdminTaskComment


class MyAdminTasksListView(ManagerPermMixin, ListView):
class MyAdminTasksListView(AdminOrManagerPermMixin, ListView):
template_name = "admin_tasks_yours.html"
paginate_by = 10

def get_queryset(self):
return AdminTask.objects.filter(assigned_to=self.request.user).select_related(
return get_admin_tasks_for_user(user=self.request.user).select_related(
"new_hire", "assigned_to"
)

Expand All @@ -30,12 +35,14 @@ def get_context_data(self, **kwargs):
return context


class AllAdminTasksListView(ManagerPermMixin, ListView):
class AllAdminTasksListView(AdminOrManagerPermMixin, ListView):
template_name = "admin_tasks_all.html"
paginate_by = 10

def get_queryset(self):
Comment thread
GDay marked this conversation as resolved.
return AdminTask.objects.all().select_related("new_hire", "assigned_to")
return get_admin_tasks_for_department(user=self.request.user).select_related(
"new_hire", "assigned_to"
)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Expand All @@ -45,16 +52,19 @@ def get_context_data(self, **kwargs):
return context


class AdminTaskCompleteView(ManagerPermMixin, BaseDetailView):
model = AdminTask
class AdminTaskCompleteView(AdminOrManagerPermMixin, BaseDetailView):
def get_queryset(self):
Comment thread
GDay marked this conversation as resolved.
return get_admin_tasks_for_department(user=self.request.user)

def post(self, request, *args, **kwargs):
admin_task = self.get_object()
admin_task.mark_completed()
return redirect("admin_tasks:detail", pk=admin_task.id)


class AdminTasksCreateView(ManagerPermMixin, SuccessMessageMixin, CreateView):
class AdminTasksCreateView(
AdminOrManagerPermMixin, FormWithUserContextMixin, SuccessMessageMixin, CreateView
Comment thread
GDay marked this conversation as resolved.
):
template_name = "admin_tasks_create.html"
form_class = AdminTaskCreateForm
model = AdminTask
Expand Down Expand Up @@ -83,18 +93,26 @@ def get_context_data(self, **kwargs):
return context


class AdminTasksUpdateView(ManagerPermMixin, SuccessMessageMixin, UpdateView):
class AdminTasksUpdateView(AdminOrManagerPermMixin, SuccessMessageMixin, UpdateView):
template_name = "admin_tasks_detail.html"
form_class = AdminTaskUpdateForm
model = AdminTask
success_message = _("Task has been updated")

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.get_object().new_hire
return kwargs

def get_queryset(self):
return get_admin_tasks_for_department(user=self.request.user)

def get_success_url(self):
return self.request.path

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Comment thread
GDay marked this conversation as resolved.
task = get_object_or_404(AdminTask, pk=self.kwargs.get("pk"))
task = self.get_object()
context["object"] = task
context["title"] = _("Task: %(name)s") % {"name": task.name}
context["subtitle"] = _("Tasks")
Expand All @@ -103,7 +121,8 @@ def get_context_data(self, **kwargs):

def form_valid(self, form):
# send email/bot message to newly assigned person
Comment thread
GDay marked this conversation as resolved.
initial_assigned_to = AdminTask.objects.get(id=form.instance.id).assigned_to
task = self.get_object()
initial_assigned_to = task.assigned_to
form.save()
if (
form.cleaned_data["assigned_to"] != initial_assigned_to
Expand All @@ -113,7 +132,9 @@ def form_valid(self, form):
return super().form_valid(form)


class AdminTasksCommentCreateView(ManagerPermMixin, SuccessMessageMixin, CreateView):
class AdminTasksCommentCreateView(
AdminOrManagerPermMixin, SuccessMessageMixin, CreateView
):
template_name = "admin_tasks_detail.html"
model = AdminTaskComment
fields = [
Expand All @@ -122,11 +143,13 @@ class AdminTasksCommentCreateView(ManagerPermMixin, SuccessMessageMixin, CreateV
success_message = _("Comment has been posted")

def get_success_url(self):
task = get_object_or_404(AdminTask, pk=self.kwargs.get("pk"))
return reverse("admin_tasks:detail", args=[task.id])
return reverse("admin_tasks:detail", args=[self.object.admin_task.id])

def form_valid(self, form):
task = get_object_or_404(AdminTask, pk=self.kwargs.get("pk"))
task = get_object_or_404(
get_admin_tasks_for_department(user=self.request.user),
pk=self.kwargs.get("pk"),
)
# Can't post comments when item is completed
if task.completed:
raise Http404
Expand Down
15 changes: 13 additions & 2 deletions back/admin/appointments/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
from django.utils.translation import gettext_lazy as _

from admin.templates.forms import MultiSelectField, TagModelForm, WYSIWYGField
from misc.mixins import FilterDepartmentFieldByUserMixin

from .models import Appointment


class AppointmentForm(TagModelForm):
class AppointmentForm(FilterDepartmentFieldByUserMixin, TagModelForm):
content = WYSIWYGField()
date = forms.DateField(
label=_("Date"),
Expand All @@ -27,6 +28,7 @@ def __init__(self, *args, **kwargs):
Div(
Div(
Field("name"),
Field("departments"),
MultiSelectField("tags"),
Field("fixed_date"),
Div(
Expand Down Expand Up @@ -62,7 +64,16 @@ def clean(self):

class Meta:
model = Appointment
exclude = ("template",)
fields = (
"fixed_date",
"on_day",
"time",
"date",
"name",
"tags",
"content",
"departments",
)
widgets = {
"time": forms.TimeInput(attrs={"type": "time", "step": 300}),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 5.2.6 on 2025-09-23 02:35

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("appointments", "0008_auto_20220221_1338"),
("users", "0043_remove_user_department_user_departments_and_more"),
]

operations = [
migrations.AlterModelOptions(
name="appointment",
options={"ordering": ("name",)},
),
migrations.AddField(
model_name="appointment",
name="departments",
field=models.ManyToManyField(
blank=True,
help_text="Leave empty to make it available for all managers/admins",
to="users.department",
),
),
]
6 changes: 6 additions & 0 deletions back/admin/appointments/selectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from admin.appointments.models import Appointment
from users.models import User


def get_appointment_templates_for_user(*, user: User):
return Appointment.templates.for_user(user=user)
Loading
Loading