aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Lemon <lemon@matthewlemon.com>2020-05-29 14:23:07 +0100
committerMatthew Lemon <lemon@matthewlemon.com>2020-05-29 14:23:07 +0100
commit739b3dd6aa383f70e5442e74fb4d217d5619d110 (patch)
tree54d9ef6fad57c7dbc6283b0a2cc2cd73955dc80c
parent8edc6a41c160ff75e964db45371e4ae63fb68c17 (diff)
IncidentReport model in place and func test passing so far
-rw-r--r--ctrack/organisations/admin.py6
-rw-r--r--ctrack/organisations/forms.py58
-rw-r--r--ctrack/organisations/migrations/0006_incidentreport.py35
-rw-r--r--ctrack/organisations/models.py62
-rw-r--r--ctrack/organisations/templates/organisations/incidentreport_form.html32
-rw-r--r--ctrack/organisations/tests/test_models.py7
-rw-r--r--ctrack/organisations/tests/test_views.py2
-rw-r--r--ctrack/organisations/views.py3
-rw-r--r--ctrack/users/tests/test_functional.py13
9 files changed, 194 insertions, 24 deletions
diff --git a/ctrack/organisations/admin.py b/ctrack/organisations/admin.py
index f85ddad..ce2b21b 100644
--- a/ctrack/organisations/admin.py
+++ b/ctrack/organisations/admin.py
@@ -3,6 +3,7 @@ from django.contrib import admin
from .models import (
Address,
AddressType,
+ IncidentReport,
Mode,
Organisation,
Person,
@@ -21,6 +22,10 @@ def get_organisation_name(person):
get_organisation_name.short_description = "Organisation"
+class IncidentReportAdmin(admin.ModelAdmin):
+ model = IncidentReport
+
+
class AddressTypeAdmin(admin.ModelAdmin):
pass
@@ -72,3 +77,4 @@ admin.site.register(Person, PersonAdmin)
admin.site.register(Mode, ModeAdmin)
admin.site.register(Submode, SubmodeAdmin)
admin.site.register(Stakeholder, StakeholderAdmin)
+admin.site.register(IncidentReport, IncidentReportAdmin)
diff --git a/ctrack/organisations/forms.py b/ctrack/organisations/forms.py
index 473bc67..d98e9f0 100644
--- a/ctrack/organisations/forms.py
+++ b/ctrack/organisations/forms.py
@@ -3,7 +3,7 @@ from crispy_forms.layout import Layout, Submit
from django import forms
from django.forms import inlineformset_factory
-from ctrack.organisations.models import Organisation, Address
+from ctrack.organisations.models import Address, IncidentReport, Organisation
class OrganisationCreateForm(forms.ModelForm):
@@ -20,12 +20,17 @@ class OrganisationCreateForm(forms.ModelForm):
class Meta:
model = Organisation
- fields = ["name", "submode", "oes", "designation_type",
- "registered_company_name", "registered_company_number",
- "comments", "active"]
- labels = {
- "oes": "OES"
- }
+ fields = [
+ "name",
+ "submode",
+ "oes",
+ "designation_type",
+ "registered_company_name",
+ "registered_company_number",
+ "comments",
+ "active",
+ ]
+ labels = {"oes": "OES"}
help_texts = {
"name": "Name of the organisation",
"submode": "e.g. Rail Maintenance, TOC, etc...",
@@ -49,15 +54,40 @@ class AddressCreateForm(forms.ModelForm):
class Meta:
model = Address
- fields = ('type', 'line1', 'line2', 'line3', 'city', 'county', 'postcode',
- 'country', 'other_details')
+ fields = (
+ "type",
+ "line1",
+ "line2",
+ "line3",
+ "city",
+ "county",
+ "postcode",
+ "country",
+ "other_details",
+ )
# https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6
# good advice on setting up the inlineformset - with crispy forms too
-AddressInlineFormSet = inlineformset_factory(Organisation, Address,
- fields=("type", "line1", "line2", "line3", "city",
- "county", "postcode", "country", "other_details"),
- form=AddressCreateForm,
- extra=2)
+AddressInlineFormSet = inlineformset_factory(
+ Organisation,
+ Address,
+ fields=(
+ "type",
+ "line1",
+ "line2",
+ "line3",
+ "city",
+ "county",
+ "postcode",
+ "country",
+ "other_details",
+ ),
+ form=AddressCreateForm,
+ extra=2,
+)
+
+class IncidentReportForm(forms.Form):
+ class Meta:
+ model = IncidentReport
diff --git a/ctrack/organisations/migrations/0006_incidentreport.py b/ctrack/organisations/migrations/0006_incidentreport.py
new file mode 100644
index 0000000..0528aaa
--- /dev/null
+++ b/ctrack/organisations/migrations/0006_incidentreport.py
@@ -0,0 +1,35 @@
+# Generated by Django 2.2.12 on 2020-05-29 12:56
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('organisations', '0005_auto_20200525_1502'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='IncidentReport',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('role', models.CharField(help_text='Please identify your role', max_length=100)),
+ ('phone_number', models.CharField(max_length=30)),
+ ('email', models.EmailField(max_length=254)),
+ ('internal_incident_number', models.CharField(blank=True, max_length=30)),
+ ('date_time_incident_detected', models.DateTimeField(help_text='This can be approximate', verbose_name='Date/Time incident detected')),
+ ('date_time_incident_reported', models.DateTimeField(verbose_name='Date/Time incident reported')),
+ ('incident_type', models.CharField(choices=[('cyber', 'Cyber'), ('non-cyber', 'Non-Cyber'), ('both', 'Both'), ('power', 'Power Outage')], help_text='This can be appoximate', max_length=10)),
+ ('incident_status', models.CharField(choices=[('detected', 'Detected'), ('suspected', 'Suspected'), ('resolved', 'Resolved')], max_length=10)),
+ ('incident_stage', models.CharField(choices=[('ongoing', 'Ongoing'), ('ended', 'Ended'), ('managed', 'Ongoing but managed')], max_length=10)),
+ ('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(help_text='What investigations and/or mitigations have you or a third party performed or plan to perform?', verbose_name='Investigations or mitigations')),
+ ('others_informed', models.TextField(help_text='Who else has been informed about this incident?(CSIRT, NCSC, NCA, etc)', verbose_name='Others parties informed')),
+ ('next_steps', models.TextField(help_text='What are your planned next steps?', verbose_name='Planned next steps')),
+ ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation')),
+ ('reporting_person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Person', verbose_name='Person reporting the incident')),
+ ],
+ ),
+ ]
diff --git a/ctrack/organisations/models.py b/ctrack/organisations/models.py
index 00e4db6..a107829 100644
--- a/ctrack/organisations/models.py
+++ b/ctrack/organisations/models.py
@@ -184,4 +184,64 @@ class Stakeholder(models.Model):
class IncidentReport(models.Model):
- pass
+ INCIDENT_TYPES = (
+ ("cyber", "Cyber"),
+ ("non-cyber", "Non-Cyber"),
+ ("both", "Both"),
+ ("power", "Power Outage"),
+ )
+ INCIDENT_STATUS = (
+ ("detected", "Detected"),
+ ("suspected", "Suspected"),
+ ("resolved", "Resolved"),
+ )
+ INCIDENT_STAGE = (
+ ("ongoing", "Ongoing"),
+ ("ended", "Ended"),
+ ("managed", "Ongoing but managed"),
+ )
+ organisation = models.ForeignKey(
+ Organisation, blank=False, on_delete=models.CASCADE
+ )
+ reporting_person = models.ForeignKey(
+ Person,
+ blank=False,
+ on_delete=models.CASCADE,
+ verbose_name="Person " "reporting the incident",
+ )
+ role = models.CharField(
+ max_length=100, blank=False, help_text="Please identify your role"
+ )
+ 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,
+ help_text="This can be approximate",
+ )
+ date_time_incident_reported = models.DateTimeField(
+ verbose_name="Date/Time incident reported"
+ )
+ incident_type = models.CharField(
+ choices=INCIDENT_TYPES, help_text="This can be appoximate", max_length=10
+ )
+ incident_status = models.CharField(choices=INCIDENT_STATUS, max_length=10)
+ incident_stage = models.CharField(choices=INCIDENT_STAGE, max_length=10)
+ 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?"
+ )
diff --git a/ctrack/organisations/templates/organisations/incidentreport_form.html b/ctrack/organisations/templates/organisations/incidentreport_form.html
new file mode 100644
index 0000000..5387f56
--- /dev/null
+++ b/ctrack/organisations/templates/organisations/incidentreport_form.html
@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+
+{% block title %}
+ Submit a new NIS Incident Report to DfT
+{% endblock title %}
+
+{% load crispy_forms_tags %}
+
+{% block content %}
+ <div class="container mt-3">
+ <div class="row">
+ <div class="col-md-12 pl-0 my-2">
+ <h4>Submit an Incident Report</h4>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-7 pt-2 border bg-light">
+ {% crispy form %}
+ </div>
+ <div class="col-sm-5">
+ <div class="card" style="width: 18rem;">
+ <div class="card-body">
+ <h5 class="card-title">Help submitting a form</h5>
+ <p class="card-text">An organisation is the parent/operating company. There is probably advice and
+ proper definitions for this somewhere. Maybe if you click the link below, it will take you there?</p>
+ <a href="#" class="btn btn-primary">Go somewhere</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+{% endblock content %}
diff --git a/ctrack/organisations/tests/test_models.py b/ctrack/organisations/tests/test_models.py
index 6c41c49..6f79cb4 100644
--- a/ctrack/organisations/tests/test_models.py
+++ b/ctrack/organisations/tests/test_models.py
@@ -1,7 +1,7 @@
import pytest
from slugify import slugify
-from ctrack.organisations.models import Organisation
+from ctrack.organisations.models import IncidentReport, Organisation
pytestmark = pytest.mark.django_db
@@ -29,8 +29,3 @@ def test_update_organisation(org_with_people):
def test_new_address(addr):
# The address "has" an organisation
assert addr.organisation.name
-
-
-def test_incident_report(org_with_people):
- breakpoint()
- pass
diff --git a/ctrack/organisations/tests/test_views.py b/ctrack/organisations/tests/test_views.py
index 1e2476d..cebcda9 100644
--- a/ctrack/organisations/tests/test_views.py
+++ b/ctrack/organisations/tests/test_views.py
@@ -36,5 +36,5 @@ def test_incident_report_create_view():
factory = RequestFactory()
request = factory.get(f"{org.name}/create-incident-report")
request.user = user
- response = IncidentReportCreateView.as_view()(request)
+ response = IncidentReportCreateView.as_view()(request, org.slug)
assert response.status_code == 200
diff --git a/ctrack/organisations/views.py b/ctrack/organisations/views.py
index 3363a33..e73792a 100644
--- a/ctrack/organisations/views.py
+++ b/ctrack/organisations/views.py
@@ -9,7 +9,7 @@ from django.db import transaction
from django.urls import reverse_lazy
from django.views.generic import CreateView, DetailView, ListView
-from .forms import AddressInlineFormSet, OrganisationCreateForm
+from .forms import AddressInlineFormSet, IncidentReportForm, OrganisationCreateForm
from .models import IncidentReport, Organisation
@@ -76,3 +76,4 @@ class OrganisationDetailView(LoginRequiredMixin, DetailView):
class IncidentReportCreateView(LoginRequiredMixin, CreateView):
model = IncidentReport
fields = "__all__"
+ form = IncidentReportForm
diff --git a/ctrack/users/tests/test_functional.py b/ctrack/users/tests/test_functional.py
index 882c974..55f8de3 100644
--- a/ctrack/users/tests/test_functional.py
+++ b/ctrack/users/tests/test_functional.py
@@ -127,9 +127,20 @@ def test_stakeholder_logs_into_system_and_submits_incident_form(
time.sleep(1)
current_url = browser.current_url
assert current_url == live_server + "/"
+ time.sleep(1)
+
+ browser.get(current_url)
# Clicks the Report a NIS incident button
- browser.find_element_by_id("id_submit_incident_button").submit()
+ # browser.find_element_by_id("id_submit_incident_button").submit()
+ button = browser.find_element_by_xpath('//*[@id="id_submit_incident_button"]')
+
+ button.click()
+ time.sleep(1)
+
+ current_url = browser.current_url
+
+ browser.get(current_url)
# Gets to the correct page
assert "Submit a new NIS Incident Report to DfT" in browser.title