Skip to content

Commit

Permalink
feat:add security incidents report model
Browse files Browse the repository at this point in the history
  • Loading branch information
saladgg committed Sep 13, 2021
1 parent 3507414 commit ecc7126
Show file tree
Hide file tree
Showing 16 changed files with 540 additions and 0 deletions.
2 changes: 2 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
FacilityNetworkStatusViewSet,
FacilitySystemTicketViewSet,
FacilitySystemViewSet,
SecurityIncidenceViewSet,
SiteMentorshipViewSet,
StockReceiptVerificationViewSet,
TimeSheetViewSet,
Expand Down Expand Up @@ -43,6 +44,7 @@
router.register("network_status", FacilityNetworkStatusViewSet)
router.register("facility_devices", FacilityDeviceViewSet)
router.register("facility_device_requests", FacilityDeviceRequestViewSet)
router.register("security_incidents", SecurityIncidenceViewSet)

app_name = "api"
urlpatterns = router.urls
19 changes: 19 additions & 0 deletions fahari/ops/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
FacilityNetworkStatus,
FacilitySystem,
FacilitySystemTicket,
SecurityIncidence,
StockReceiptVerification,
TimeSheet,
UoM,
Expand Down Expand Up @@ -110,3 +111,21 @@ class FacilityDeviceRequestAdmin(BaseAdmin):
"date_requested",
"request_type",
)


@admin.register(SecurityIncidence)
class SecurityIncidenceAdmin(BaseAdmin):

list_display = (
"facility",
"title",
"details",
"reported_on",
"reported_by",
)
list_filter = (
"facility",
"title",
"reported_on",
"reported_by",
)
11 changes: 11 additions & 0 deletions fahari/ops/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
FacilityNetworkStatus,
FacilitySystem,
FacilitySystemTicket,
SecurityIncidence,
SiteMentorship,
StockReceiptVerification,
TimeSheet,
Expand Down Expand Up @@ -162,3 +163,13 @@ class Meta:

model = FacilityDeviceRequest
fields = "__all__"


class SecurityIncidenceFilter(CommonFieldsFilterset):

search = filters.SearchFilter()

class Meta:

model = SecurityIncidence
fields = "__all__"
23 changes: 23 additions & 0 deletions fahari/ops/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
FacilityNetworkStatus,
FacilitySystem,
FacilitySystemTicket,
SecurityIncidence,
SiteMentorship,
StockReceiptVerification,
TimeSheet,
Expand Down Expand Up @@ -447,3 +448,25 @@ class Meta(BaseModelForm.Meta):
}
),
}


class SecurityIncidenceForm(BaseModelForm):
field_order = (
"facility",
"title",
"details",
"reported_on",
"reported_by",
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper.form_id = "security_incidence_form"
self.fields["facility"].queryset = get_fahari_facilities_queryset()

class Meta(BaseModelForm.Meta):
model = SecurityIncidence
widgets = {
"facility": SearchableComboBox(),
"reported_on": DateInput(attrs={"hidden": True}),
}
40 changes: 40 additions & 0 deletions fahari/ops/migrations/0025_securityincidence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 3.2.7 on 2021-09-13 09:28

import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import uuid


class Migration(migrations.Migration):

dependencies = [
('common', '0020_alter_system_pattern'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('ops', '0024_facilitydevice_facilitydevicerequest_facilitynetworkstatus'),
]

operations = [
migrations.CreateModel(
name='SecurityIncidence',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('active', models.BooleanField(default=True)),
('created', models.DateTimeField(default=django.utils.timezone.now)),
('created_by', models.UUIDField(blank=True, null=True)),
('updated', models.DateTimeField(default=django.utils.timezone.now)),
('updated_by', models.UUIDField(blank=True, null=True)),
('title', models.CharField(max_length=200, verbose_name='Incidence title')),
('details', models.TextField(default='-', verbose_name='Incidence details')),
('reported_on', models.DateField(default=datetime.datetime.today, editable=False)),
('facility', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='common.facility')),
('organisation', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='ops_securityincidence_related', to='common.organisation')),
('reported_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('facility__name', '-updated'),
},
),
]
20 changes: 20 additions & 0 deletions fahari/ops/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,23 @@ def get_absolute_url(self):

class Meta:
ordering = ("facility__name", "-updated")


class SecurityIncidence(AbstractBase):
"""Facility security incidences."""

facility = models.ForeignKey(Facility, on_delete=models.PROTECT)
title = models.CharField(max_length=200, verbose_name="Incidence title")
details = models.TextField(default="-", verbose_name="Incidence details")
reported_on = models.DateField(default=timezone.datetime.today, editable=False)
reported_by = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)

def __str__(self) -> str:
return f"Facility: {self.facility.name}, Security incidence: {self.title}"

def get_absolute_url(self):
update_url = reverse("ops:security_incidence_update", kwargs={"pk": self.pk})
return update_url

class Meta:
ordering = ("facility__name", "-updated")
9 changes: 9 additions & 0 deletions fahari/ops/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
FacilityNetworkStatus,
FacilitySystem,
FacilitySystemTicket,
SecurityIncidence,
SiteMentorship,
StockReceiptVerification,
TimeSheet,
Expand Down Expand Up @@ -138,3 +139,11 @@ class FacilityDeviceRequestSerializer(BaseSerializer):
class Meta(BaseSerializer.Meta):
model = FacilityDeviceRequest
fields = "__all__"


class SecurityIncidenceSerializer(BaseSerializer):
facility_name = serializers.ReadOnlyField(source="facility.name")

class Meta(BaseSerializer.Meta):
model = SecurityIncidence
fields = "__all__"
134 changes: 134 additions & 0 deletions fahari/ops/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
FacilityNetworkStatus,
FacilitySystem,
FacilitySystemTicket,
SecurityIncidence,
SiteMentorship,
StockReceiptVerification,
TimeSheet,
Expand Down Expand Up @@ -1742,3 +1743,136 @@ def test_delete(self):
response.status_code,
302,
)


class SecurityIncidenceViewsetTest(LoggedInMixin, APITestCase):
def setUp(self):
self.url_list = reverse("api:securityincidence-list")
self.facility = baker.make(Facility, organisation=self.global_organisation)
super().setUp()

def test_create(self):
data = {
"facility": self.facility.pk,
"title": fake.text(max_nb_chars=50),
"details": fake.text(),
"reported_on": date.today().isoformat(),
"reported_by": self.user.pk,
}
response = self.client.post(self.url_list, data)
assert response.status_code == 201, response.json()
assert response.data["title"] == data["title"]
assert response.data["details"] == data["details"]
assert response.data["reported_on"] == data["reported_on"]
assert response.data["reported_by"] == data["reported_by"]

def test_retrieve(self):
instance = baker.make(
SecurityIncidence,
organisation=self.global_organisation,
title=fake.text(max_nb_chars=50),
)
response = self.client.get(self.url_list)
assert response.status_code == 200, response.json()
assert response.data["count"] >= 1, response.json()

incidences = [a["title"] for a in response.data["results"]]
assert instance.title in incidences

def test_patch(self):
instance = baker.make(
SecurityIncidence,
organisation=self.global_organisation,
title=fake.text(max_nb_chars=50),
)
edit = {"title": fake.text(max_nb_chars=50)}
url = reverse("api:securityincidence-detail", kwargs={"pk": instance.pk})
response = self.client.patch(url, edit)

assert response.status_code == 200, response.json()
assert response.data["title"] == edit["title"]

def test_put(self):
instance = baker.make(
SecurityIncidence,
organisation=self.global_organisation,
title=fake.text(max_nb_chars=50),
)
data = {
"facility": self.facility.pk,
"title": fake.text(max_nb_chars=50),
"details": fake.text(),
"reported_on": date.today().isoformat(),
"reported_by": self.user.pk,
}
url = reverse("api:securityincidence-detail", kwargs={"pk": instance.pk})
response = self.client.put(url, data)

assert response.status_code == 200, response.json()
assert response.data["title"] == data["title"]
assert response.data["details"] == data["details"]
assert response.data["reported_on"] == data["reported_on"]
assert response.data["reported_by"] == data["reported_by"]


class SecurityIncidenceFormTest(LoggedInMixin, TestCase):
def setUp(self):
self.user = baker.make(settings.AUTH_USER_MODEL, email=fake.email())
self.facility = baker.make(
Facility,
is_fahari_facility=True,
county=random.choice(WHITELIST_COUNTIES),
operation_status="Operational",
organisation=self.global_organisation,
)
super().setUp()

def test_create(self):
data = {
"facility": self.facility.pk,
"title": fake.text(max_nb_chars=50),
"details": fake.text(),
"reported_on": date.today().isoformat(),
"reported_by": self.user.pk,
}
response = self.client.post(reverse("ops:security_incidence_create"), data=data)
self.assertEqual(
response.status_code,
302,
)

def test_update(self):
instance = baker.make(
SecurityIncidence,
title=fake.text(max_nb_chars=50),
details=fake.text(),
)
data = {
"pk": instance.pk,
"facility": self.facility.pk,
"title": fake.text(max_nb_chars=50),
"details": fake.text(),
"reported_on": date.today().isoformat(),
"reported_by": self.user.pk,
}
response = self.client.post(
reverse("ops:security_incidence_update", kwargs={"pk": instance.pk}), data=data
)
self.assertEqual(
response.status_code,
302,
)

def test_delete(self):
instance = baker.make(
SecurityIncidence,
organisation=self.global_organisation,
title=fake.text(max_nb_chars=50),
)
response = self.client.post(
reverse("ops:security_incidence_delete", kwargs={"pk": instance.pk}),
)
self.assertEqual(
response.status_code,
302,
)
18 changes: 18 additions & 0 deletions fahari/ops/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
FacilitySystem,
FacilitySystemTicket,
OperationalArea,
SecurityIncidence,
SiteMentorship,
StockReceiptVerification,
TimeSheet,
Expand Down Expand Up @@ -360,3 +361,20 @@ def test_facility_device_request_str():
)
url = facility_device_req.get_absolute_url()
assert f"/ops/facility_device_request_update/{facility_device_req.pk}" in url


def test_security_incidence_str():
org = baker.make(Organisation)
facility = baker.make(Facility, organisation=org, name=fake.text(max_nb_chars=30))
sec_incidence = baker.make(
SecurityIncidence,
facility=facility,
title=fake.text(max_nb_chars=50),
details=fake.text(),
)
assert str(sec_incidence) == "Facility: %s, Security incidence: %s" % (
sec_incidence.facility.name,
sec_incidence.title,
)
url = sec_incidence.get_absolute_url()
assert f"/ops/security_incidence_update/{sec_incidence.pk}" in url
15 changes: 15 additions & 0 deletions fahari/ops/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
FacilityNetworkStatusListView,
FacilitySystemsView,
FacilitySystemTicketsView,
SecurityIncidentsListView,
TimeSheetApproveView,
UoMCategoryListView,
UoMListView,
Expand Down Expand Up @@ -187,6 +188,13 @@ def test_facility_device_request_context_data():
assert ctx["selected"] == "facility_device_requests"


def test_security_incidence_context_data():
v = SecurityIncidentsListView()
ctx = v.get_context_data()
assert ctx["active"] == "hardware-network-nav"
assert ctx["selected"] == "security_incidents"


def test_facility_network_status_view(user_with_all_permissions, client):
client.force_login(user_with_all_permissions)
url = reverse("ops:facility_network_status")
Expand All @@ -206,3 +214,10 @@ def test_facility_device_request_view(user_with_all_permissions, client):
url = reverse("ops:facility_device_requests")
response = client.get(url)
assert response.status_code == status.HTTP_200_OK


def test_security_incidence_view(user_with_all_permissions, client):
client.force_login(user_with_all_permissions)
url = reverse("ops:security_incidents")
response = client.get(url)
assert response.status_code == status.HTTP_200_OK

0 comments on commit ecc7126

Please sign in to comment.