Skip to content

Commit

Permalink
Add hardware template (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
GDay authored Nov 10, 2023
1 parent 2eb6cfe commit 20c45a9
Show file tree
Hide file tree
Showing 34 changed files with 812 additions and 42 deletions.
24 changes: 24 additions & 0 deletions back/admin/admin_tasks/migrations/0013_admintask_hardware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.5 on 2023-11-07 01:53

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


class Migration(migrations.Migration):
dependencies = [
("hardware", "0001_initial"),
("admin_tasks", "0012_admintask_manual_integration"),
]

operations = [
migrations.AddField(
model_name="admintask",
name="hardware",
field=models.ForeignKey(
help_text="Only set if generated based on hardware.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="hardware.hardware",
),
),
]
30 changes: 21 additions & 9 deletions back/admin/admin_tasks/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@ def create_admin_task(
new_hire,
assigned_to,
name,
option,
slack_user,
email,
date,
priority,
pending_admin_task,
manual_integration,
comment,
send_notification,
option=0,
slack_user=None,
email="",
date=None,
priority=2,
pending_admin_task=None,
hardware=None,
manual_integration=None,
comment="-",
send_notification=True,
):
admin_task = AdminTask.objects.create(
new_hire=new_hire,
Expand All @@ -39,6 +40,7 @@ def create_admin_task(
date=date,
priority=priority,
based_on=pending_admin_task,
hardware=hardware,
manual_integration=manual_integration,
)
AdminTaskComment.objects.create(
Expand Down Expand Up @@ -116,6 +118,12 @@ class Notification(models.IntegerChoices):
on_delete=models.SET_NULL,
help_text=_("Only set if generated based on a manual integration."),
)
hardware = models.ForeignKey(
"hardware.Hardware",
null=True,
on_delete=models.SET_NULL,
help_text=_("Only set if generated based on hardware."),
)

objects = AminTaskManager()

Expand Down Expand Up @@ -205,6 +213,10 @@ def mark_completed(self):
if self.manual_integration is not None:
self.manual_integration.register_manual_integration_run(self.new_hire)

# Check if we need to register hardware
if self.hardware is not None:
self.hardware.remove_or_add_to_user(self.new_hire)

# Get conditions with this to do item as (part of the) condition
conditions = self.new_hire.conditions.filter(
condition_admin_tasks=self.based_on
Expand Down
Empty file added back/admin/hardware/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions back/admin/hardware/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class HardwareConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "admin.hardware"
22 changes: 22 additions & 0 deletions back/admin/hardware/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import factory
from factory.fuzzy import FuzzyText
from pytest_factoryboy import register

from admin.hardware.models import Hardware


@register
class HardwareFactory(factory.django.DjangoModelFactory):
name = FuzzyText()
content = {
"time": 0,
"blocks": [
{
"data": {"text": "Should be returned when they get terminated"},
"type": "paragraph",
}
],
}

class Meta:
model = Hardware
54 changes: 54 additions & 0 deletions back/admin/hardware/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Field, Layout
from django.utils.translation import gettext_lazy as _

from admin.hardware.models import Hardware
from admin.templates.forms import MultiSelectField, TagModelForm, WYSIWYGField


class HardwareForm(TagModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False

# Check if assigned_to field should be hidden
hide_assigned_to = "d-none"
if self.data.get("person_type", None) == str(Hardware.PersonType.CUSTOM) or (
self.instance is not None
and self.instance.person_type == Hardware.PersonType.CUSTOM
):
hide_assigned_to = ""

layout = Layout(
Div(
Div(
Field("name"),
Field("person_type"),
Div(
Field("assigned_to"),
css_class=hide_assigned_to,
),
MultiSelectField("tags"),
css_class="col-4",
),
Div(
WYSIWYGField("content"),
css_class="col-8",
),
css_class="row",
),
)
self.helper.layout = layout

class Meta:
model = Hardware
exclude = ("template",)

def clean(self):
cleaned_data = super().clean()
assigned_to = cleaned_data.get("assigned_to")
person_type = cleaned_data["person_type"]
if person_type == Hardware.PersonType.CUSTOM and assigned_to is None:
self.add_error("assigned_to", _("This field is required"))
return cleaned_data
79 changes: 79 additions & 0 deletions back/admin/hardware/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Generated by Django 4.2.6 on 2023-11-03 00:20

import django.contrib.postgres.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models

import misc.fields
import misc.mixins


class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Hardware",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=240, verbose_name="Name")),
(
"tags",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=10200),
blank=True,
size=None,
verbose_name="Tags",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
("template", models.BooleanField(default=True)),
(
"content",
misc.fields.ContentJSONField(default=dict, verbose_name="Content"),
),
(
"person_type",
models.IntegerField(
blank=True,
choices=[(1, "Manager"), (2, "Buddy"), (3, "Custom")],
help_text=(
"Leave empty to automatically remove/add hardware without "
"notifications."
),
null=True,
verbose_name="Assigned to",
),
),
(
"assigned_to",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="assigned_user_hardware",
to=settings.AUTH_USER_MODEL,
verbose_name="Pick user",
),
),
],
options={
"abstract": False,
},
bases=(misc.mixins.ContentMixin, models.Model),
),
]
Empty file.
102 changes: 102 additions & 0 deletions back/admin/hardware/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from django.conf import settings
from django.db import models
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from admin.admin_tasks.models import AdminTask
from misc.fields import ContentJSONField
from organization.models import BaseItem, Notification


class Hardware(BaseItem):
class PersonType(models.IntegerChoices):
MANAGER = 1, _("Manager")
BUDDY = 2, _("Buddy")
CUSTOM = 3, _("Custom")

content = ContentJSONField(default=dict, verbose_name=_("Content"))
person_type = models.IntegerField(
verbose_name=_("Assigned to"),
choices=PersonType.choices,
null=True,
blank=True,
help_text=_(
"Leave empty to automatically remove/add hardware without notifications."
),
)
assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_("Pick user"),
on_delete=models.CASCADE,
related_name="assigned_user_hardware",
null=True,
blank=True,
)

def remove_or_add_to_user(self, user):
add = user.termination_date is None
if add:
user.hardware.add(self)
else:
user.hardware.remove(self)

Notification.objects.create(
notification_type=self.notification_add_type
if add
else self.notification_remove_type,
extra_text=self.name,
created_for=user,
item_id=self.id,
)

def execute(self, user):
add = user.termination_date is None

if self.person_type is None:
# no person assigned, so add directly
self.remove_or_add_to_user(user)
return

if self.person_type == Hardware.PersonType.MANAGER:
assigned_to = user.manager
elif self.person_type == Hardware.PersonType.BUDDY:
assigned_to = user.buddy
else:
assigned_to = self.assigned_to

if add:
admin_task_name = _(
"Send hardware to new hire (%(new_hire)s): %(name)s"
) % {"new_hire": user.full_name, "name": self.name}
else:
admin_task_name = _(
"Reclaim hardware from employee (%(new_hire)s): %(name)s"
) % {"new_hire": user.full_name, "name": self.name}

AdminTask.objects.create_admin_task(
new_hire=user,
assigned_to=assigned_to,
name=admin_task_name,
hardware=self,
)

@property
def get_icon_template(self):
return render_to_string("_hardware_icon.html")

@property
def notification_add_type(self):
return Notification.Type.ADDED_HARDWARE

@property
def notification_remove_type(self):
return Notification.Type.REMOVED_HARDWARE

@property
def update_url(self):
return reverse("hardware:update", args=[self.id])

@property
def delete_url(self):
return reverse("hardware:delete", args=[self.id])
6 changes: 6 additions & 0 deletions back/admin/hardware/templates/_hardware_icon.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-devices" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M13 9a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1v10a1 1 0 0 1 -1 1h-6a1 1 0 0 1 -1 -1v-10z" />
<path d="M18 8v-3a1 1 0 0 0 -1 -1h-13a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h9" />
<path d="M16 9h2" />
</svg>
Loading

0 comments on commit 20c45a9

Please sign in to comment.