aboutsummaryrefslogblamecommitdiffstats
path: root/ctrack/organisations/models.py
blob: dca590f086f0e467dc53df83ce6e01c4b44cfe34 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                              
                            
                               
                                                     


                           


                                                


                              
 







                                           



















                           
 
                                            




                                                                                       
                                                                                 
 


                                                         





                                                                              


                                                                              
                                                   

                                                          
                                                  


                                                                      

                                                                       


                                                                    
                                              
                                                        
                                    




                                       
     
                                                            

                      
                                                    



                                     


                                                          

                                                              
 

                                                                           


                                      
 














                                                            
                                 


                             
                  

     
                                            




                                                                                      
                                                                                 
 
                                           



                                                                
                                           
                                                                               

                                                                            













                                                
                                                  


                                                                      
                                                                      
                                              

                               
                                                                          



                                 


                        


                                    


                                                               
                                 
                                                


                                             
                                        

                  
                      


                                             
                                              

                  
 
                            


                                                                                     


                                                                                       
                                            

                                                        
                                           
                                                         

                                              



                                                                


                                         



                                                                


                                                                  


                                   







                                                                                       
                         



                                   
     
                      



                                         

                       


                                   

                      


                                                       






                                                           
                                             

                                                        




                                                                   
                            


                                                                




                                                                          

                                                   

                                                       
                                                                 

                                     
                                                                                 
     

                                                                              

                                                                                             
                                                         



                                                                              
                                                         



                                                                   
                                            



                                                                                        
                                         
                                                                  



                                                                              
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}"