from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
from django_extensions.db.fields import AutoSlugField
from slugify import slugify
class AddressType(models.Model):
descriptor = models.CharField(max_length=50)
def __str__(self):
return self.descriptor
class Role(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Person(models.Model):
TITLES = [
(1, "Mr"),
(2, "Mrs"),
(3, "Miss"),
(4, "Ms"),
(5, "Dr."),
(6, "Professor"),
(7, "The Rt Hon."),
(8, "Lord"),
(9, "Lady"),
]
CLEARANCE_LEVEL = [
(1, "NA"),
(2, "BPSS"),
(3, "CTC"),
(4, "SC"),
(5, "DV"),
(6, "Other"),
]
def get_sentinel_user(): # type: ignore
"""
We need this so that we can ensure models.SET() is applied with a callable
to handle when Users are deleted from the system, preventing the Person objects
related to them being deleted also.
"""
return get_user_model().objects.get_or_create(username="DELETED USER")[0]
primary_nis_contact = models.BooleanField(
default=False, verbose_name="Primary NIS contact"
)
voluntary_point_of_contact = models.BooleanField(default=False)
has_egress = models.BooleanField(default=False, verbose_name="Has Egress")
title = models.IntegerField(choices=TITLES, default=1)
job_title = models.CharField(max_length=100)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
organisation = models.ForeignKey("Organisation", on_delete=models.CASCADE)
role = models.ManyToManyField(Role)
email = models.EmailField()
secondary_email = models.EmailField(blank=True)
mobile = models.CharField(max_length=20, blank=True)
landline = models.CharField(max_length=20, blank=True)
date_updated = models.DateField(auto_now=True)
# updated_by = models.ForeignKey(
# get_user_model(), on_delete=models.SET(get_sentinel_user)
# )
clearance = models.IntegerField(choices=CLEARANCE_LEVEL, default=1)
clearance_sponsor = models.CharField(max_length=100, blank=True)
clearance_start_date = models.DateField(blank=True, null=True)
clearance_last_checked = models.DateField(blank=True, null=True)
clearance_expiry = models.DateField(blank=True, null=True)
active = models.BooleanField(default=True)
date_ended = models.DateField(blank=True, null=True)
predecessor = models.ForeignKey(
"self",
blank=True,
on_delete=models.CASCADE,
related_name="previous_person",
null=True,
)
comments = models.TextField(max_length=1000, blank=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
def get_organisation_name(self):
return self.organisation.name
def get_full_name(self):
return " ".join([self.first_name, self.last_name])
def get_single_datetime_events(self):
from ctrack.register.models import SingleDateTimeEvent
return SingleDateTimeEvent.objects.filter(participants__id=self.pk)
class Meta:
verbose_name_plural = "People"
class Mode(models.Model):
descriptor = models.CharField(max_length=100)
def __str__(self):
return self.descriptor
class Submode(models.Model):
descriptor = models.CharField(max_length=100)
mode = models.ForeignKey(Mode, on_delete=models.CASCADE)
def __str__(self):
return self.descriptor
class Organisation(models.Model):
DESIGNATION_TYPE = [
(1, "Automatic"),
(2, "Reserve Power"),
(3, "NA"),
]
def get_sentinel_user(): # type: ignore
"""
We need this so that we can ensure models.SET() is applied with a callable
to handle when Users are deleted from the system, preventing the Organisations
related to them being deleted also.
"""
return get_user_model().objects.get_or_create(username="DELETED USER")[0]
name = models.CharField(max_length=255)
slug = AutoSlugField(populate_from=["name"])
submode = models.ForeignKey(
Submode, on_delete=models.CASCADE, blank=True, null=True
)
oes = models.BooleanField(default=True)
designation_type = models.IntegerField(choices=DESIGNATION_TYPE, default=1)
registered_company_name = models.CharField(max_length=255, blank=True)
registered_company_number = models.CharField(max_length=100, blank=True)
lead_inspector = models.ForeignKey(
"users.User",
blank=False,
null=True,
on_delete=models.SET(get_sentinel_user),
related_name="lead_inspector",
)
deputy_lead_inspector = models.ForeignKey(
"users.User",
blank=False,
null=True,
on_delete=models.SET(get_sentinel_user),
related_name="deputy_inspector",
)
date_updated = models.DateField(auto_now=True)
# updated_by = models.ForeignKey(
# get_user_model(), on_delete=models.SET(get_sentinel_user)
# )
comments = models.TextField(max_length=500, blank=True, null=True)
active = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse("organisations:detail", kwargs={"slug": self.slug})
def slugify_name(self):
return slugify(self.name)
def __str__(self):
return self.name
def get_people(self):
return self.person_set.all()
def primary_contacts(self):
return self.person_set.filter(primary_nis_contact=True)
def applicable_systems(self):
# return self.applicablesystem_set.all()
ess = self.essentialservice_set.all()
out = []
for es in ess:
out.extend(es.systems.all())
return out
def systems(self):
ess = self.essentialservice_set.all()
out = []
for es in ess:
out.extend(list(es.systems.all()))
return out
class Address(models.Model):
organisation = models.ForeignKey(
Organisation, related_name="addresses", on_delete=models.CASCADE, blank=False
)
type = models.ForeignKey(
AddressType, verbose_name="Address Type", on_delete=models.CASCADE, blank=False
)
line1 = models.CharField(max_length=255)
line2 = models.CharField(max_length=255, blank=True)
line3 = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=100)
county = models.CharField(max_length=100, blank=True)
postcode = models.CharField(max_length=10)
country = models.CharField(max_length=100)
other_details = models.CharField(max_length=255, blank=True)
def __str__(self):
return " ".join([self.organisation.name, self.line1])
class Meta:
verbose_name_plural = "Addresses"
class Stakeholder(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
def __str__(self):
return f"{self.person.first_name} {self.person.last_name}"
class IncidentReport(models.Model):
def get_sentinel_user(): # type: ignore
"""
We need this so that we can ensure models.SET() is applied with a callable
to handle when Users are deleted from the system, preventing the Person objects
related to them being deleted also.
"""
return get_user_model().objects.get_or_create(username="DELETED USER")[0]
DFT_HANDLE_STATUS = (
("QUEUED", "QUEUED"),
("REVIEWING", "REVIEWING"),
("WAITING", "WAITING"),
("COMPLETED", "COMPLETED"),
)
INCIDENT_TYPES = (
("Cyber", "Cyber"),
("Non-Cyber", "Non-Cyber"),
("Both", "Both"),
("Power Outage", "Power Outage"),
)
INCIDENT_STATUS = (
("Detected", "Detected"),
("Suspected", "Suspected"),
("Resolved", "Resolved"),
)
INCIDENT_STAGE = (
("Ongoing", "Ongoing"),
("Ended", "Ended"),
("Ongoing but managed", "Ongoing but managed"),
)
organisation = models.ForeignKey(
Organisation, blank=False, on_delete=models.CASCADE
)
reporting_person = models.ForeignKey(
Person,
blank=False,
on_delete=models.SET(get_user_model),
verbose_name="Person " "reporting the incident",
)
person_involved = models.CharField(
max_length=100,
blank=True,
verbose_name="Name of person reporting/detecting incident",
)
role = models.CharField(
max_length=100,
blank=True,
help_text="Role of person reporting/detecting incident",
)
phone_number = models.CharField(max_length=30, blank=False)
email = models.EmailField(blank=False)
internal_incident_number = models.CharField(max_length=30, blank=True)
date_time_incident_detected = models.DateTimeField(
verbose_name="Date/Time incident detected",
auto_now=False,
)
date_time_incident_reported = models.DateTimeField(
verbose_name="Date/Time incident reported", auto_now=True
)
incident_type = models.CharField(
choices=INCIDENT_TYPES, help_text="This can be appoximate", max_length=20
)
incident_status = models.CharField(choices=INCIDENT_STATUS, max_length=20)
incident_stage = models.CharField(choices=INCIDENT_STAGE, max_length=20)
summary = models.TextField(
help_text="Please provide a summary of your understanding of the incident, including"
" any impact to services and/or users."
)
mitigations = models.TextField(
verbose_name="Investigations or mitigations",
help_text="What investigations and/or mitigations have you or a third"
" party performed or plan to perform?",
)
others_informed = models.TextField(
verbose_name="Others parties informed",
help_text="Who else has been informed about this incident?"
"(CSIRT, NCSC, NCA, etc)",
)
next_steps = models.TextField(
verbose_name="Planned next steps", help_text="What are your planned next steps?"
)
dft_handle_status = models.CharField(
choices=DFT_HANDLE_STATUS, max_length=20, default="QUEUED"
)
def __str__(self):
return f"{self.reporting_person} - {self.date_time_incident_reported}"