aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Lemon <matt@matthewlemon.com>2020-09-02 12:04:19 +0100
committerMatthew Lemon <matt@matthewlemon.com>2020-09-02 12:04:19 +0100
commitedd91cb77191b386af95dfaca0b2706c7ee143b9 (patch)
treeb94edf8e069911d0aea4e1acfc23a72a180cb2cf
parent15c89a78c097dc00ad7b8ca3314581ed3b058187 (diff)
parentc406d3da83d20d65c2fc4da7d5d4d5db0f6ad115 (diff)
Merge branch 'db_rewrite' into master
Diffstat (limited to '')
-rw-r--r--config/settings/base.py1
-rw-r--r--ctrack/assessments/migrations/0001_initial.py55
-rw-r--r--ctrack/assessments/migrations/0002_cafassessment_caf.py22
-rw-r--r--ctrack/assessments/migrations/0003_cafassessment_completer.py22
-rw-r--r--ctrack/assessments/migrations/0004_achievementlevel_igp.py32
-rw-r--r--ctrack/assessments/migrations/0005_auto_20200512_1438.py27
-rw-r--r--ctrack/assessments/models.py45
-rw-r--r--ctrack/caf/admin.py47
-rw-r--r--ctrack/caf/forms.py38
-rw-r--r--ctrack/caf/managers.py12
-rw-r--r--ctrack/caf/migrations/0001_initial.py66
-rw-r--r--ctrack/caf/migrations/0002_auto_20200403_1407.py58
-rw-r--r--ctrack/caf/migrations/0002_caf_systems.py18
-rw-r--r--ctrack/caf/migrations/0003_auto_20200424_1924.py18
-rw-r--r--ctrack/caf/migrations/0004_auto_20200813_0953.py31
-rw-r--r--ctrack/caf/migrations/0005_applicablesystem_oes_categorisation.py18
-rw-r--r--ctrack/caf/migrations/0006_auto_20200813_1125.py22
-rw-r--r--ctrack/caf/migrations/0007_auto_20200814_1230.py23
-rw-r--r--ctrack/caf/migrations/0008_auto_20200814_1318.py23
-rw-r--r--ctrack/caf/models.py66
-rw-r--r--ctrack/caf/templates/caf/applicablesystem_detail.html39
-rw-r--r--ctrack/caf/templates/caf/caf_detail.html2
-rw-r--r--ctrack/caf/tests/factories.py3
-rw-r--r--ctrack/caf/views.py16
-rw-r--r--ctrack/core/utils.py24
-rw-r--r--ctrack/core/views.py2
-rw-r--r--ctrack/organisations/admin.py6
-rw-r--r--ctrack/organisations/migrations/0001_initial.py114
-rw-r--r--ctrack/organisations/migrations/0002_auto_20200403_1407.py44
-rw-r--r--ctrack/organisations/migrations/0003_auto_20200424_1607.py18
-rw-r--r--ctrack/organisations/migrations/0004_auto_20200513_1441.py18
-rw-r--r--ctrack/organisations/migrations/0005_auto_20200525_1502.py29
-rw-r--r--ctrack/organisations/migrations/0006_incidentreport.py35
-rw-r--r--ctrack/organisations/migrations/0007_auto_20200529_1520.py23
-rw-r--r--ctrack/organisations/migrations/0008_auto_20200529_1545.py38
-rw-r--r--ctrack/organisations/migrations/0009_incidentreport_person_involved.py18
-rw-r--r--ctrack/organisations/migrations/0010_auto_20200529_1602.py18
-rw-r--r--ctrack/organisations/migrations/0011_auto_20200531_1441.py19
-rw-r--r--ctrack/organisations/models.py14
-rw-r--r--ctrack/organisations/templates/organisations/organisation_detail.html43
-rw-r--r--ctrack/organisations/tests/test_models.py31
-rw-r--r--ctrack/organisations/views.py5
-rw-r--r--ctrack/register/migrations/0001_initial.py4
-rw-r--r--ctrack/register/migrations/0002_engagementevent_user.py4
-rw-r--r--ctrack/users/migrations/0001_initial.py5
-rw-r--r--ctrack/users/migrations/0002_user_is_person.py18
-rw-r--r--ctrack/users/migrations/0003_auto_20200522_1527.py18
-rw-r--r--ctrack/users/migrations/0004_auto_20200524_1945.py23
-rw-r--r--ctrack/users/migrations/0005_delete_userprofile.py16
-rw-r--r--ctrack/users/migrations/0006_user_stakeholder.py20
-rw-r--r--pytest.ini3
-rwxr-xr-xutility/drop_and_recreate.sh7
-rw-r--r--utility/drop_and_recreate.sql2
-rwxr-xr-xutility/remove_migrations.sh7
54 files changed, 477 insertions, 853 deletions
diff --git a/config/settings/base.py b/config/settings/base.py
index c1702d7..c699798 100644
--- a/config/settings/base.py
+++ b/config/settings/base.py
@@ -68,6 +68,7 @@ DJANGO_APPS = [
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.messages",
+ "django_pdb", ## this needs to be before django.contrib.staticfiles according to its docs
"django.contrib.staticfiles",
# "django.contrib.humanize", # Handy template tags
"django.contrib.admin",
diff --git a/ctrack/assessments/migrations/0001_initial.py b/ctrack/assessments/migrations/0001_initial.py
index 94afdf9..3789ad7 100644
--- a/ctrack/assessments/migrations/0001_initial.py
+++ b/ctrack/assessments/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 09:40
from django.db import migrations, models
import django.db.models.deletion
@@ -9,20 +9,46 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
+ ('organisations', '0001_initial'),
+ ('caf', '0001_initial'),
]
operations = [
migrations.CreateModel(
+ name='AchievementLevel',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('descriptor', models.CharField(max_length=50)),
+ ('colour_description', models.CharField(max_length=100)),
+ ('colour_hex', models.CharField(max_length=8)),
+ ],
+ ),
+ migrations.CreateModel(
name='CAFAssessment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('comments', models.TextField(max_length=500)),
+ ('caf', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='caf.CAF')),
+ ('completer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Person')),
],
options={
'verbose_name': 'CAF Assessment',
},
),
migrations.CreateModel(
+ name='CAFContributingOutcome',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('designation', models.CharField(help_text='e.g. A1.a, B3.c, etc', max_length=5)),
+ ('name', models.CharField(help_text='e.g. Board Direction', max_length=100)),
+ ('description', models.TextField(max_length=1000)),
+ ('order_id', models.IntegerField()),
+ ],
+ options={
+ 'verbose_name': 'CAF Contributing Outcome',
+ },
+ ),
+ migrations.CreateModel(
name='CAFObjective',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
@@ -35,33 +61,36 @@ class Migration(migrations.Migration):
},
),
migrations.CreateModel(
- name='CAFPrinciple',
+ name='IGP',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('designation', models.CharField(help_text='e.g. A1, B3, etc', max_length=5)),
- ('title', models.CharField(max_length=50)),
- ('description', models.TextField(max_length=1000)),
- ('order_id', models.IntegerField()),
- ('caf_objective', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFObjective')),
+ ('descriptive_text', models.CharField(max_length=2000)),
+ ('achievement_level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.AchievementLevel')),
+ ('contributing_outcome', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFContributingOutcome')),
],
options={
- 'verbose_name': 'CAF Principle',
+ 'verbose_name': 'IGP',
},
),
migrations.CreateModel(
- name='CAFContributingOutcome',
+ name='CAFPrinciple',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('designation', models.CharField(help_text='e.g. A1.a, B3.c, etc', max_length=5)),
- ('name', models.CharField(help_text='e.g. Board Direction', max_length=100)),
+ ('designation', models.CharField(help_text='e.g. A1, B3, etc', max_length=5)),
+ ('title', models.CharField(max_length=50)),
('description', models.TextField(max_length=1000)),
('order_id', models.IntegerField()),
- ('principle', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFPrinciple')),
+ ('caf_objective', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFObjective')),
],
options={
- 'verbose_name': 'CAF Contributing Outcome',
+ 'verbose_name': 'CAF Principle',
},
),
+ migrations.AddField(
+ model_name='cafcontributingoutcome',
+ name='principle',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFPrinciple'),
+ ),
migrations.CreateModel(
name='CAFAssessmentOutcomeScore',
fields=[
diff --git a/ctrack/assessments/migrations/0002_cafassessment_caf.py b/ctrack/assessments/migrations/0002_cafassessment_caf.py
deleted file mode 100644
index db08459..0000000
--- a/ctrack/assessments/migrations/0002_cafassessment_caf.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('assessments', '0001_initial'),
- ('caf', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='cafassessment',
- name='caf',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='caf.CAF'),
- ),
- ]
diff --git a/ctrack/assessments/migrations/0003_cafassessment_completer.py b/ctrack/assessments/migrations/0003_cafassessment_completer.py
deleted file mode 100644
index 63b3b18..0000000
--- a/ctrack/assessments/migrations/0003_cafassessment_completer.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('organisations', '0001_initial'),
- ('assessments', '0002_cafassessment_caf'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='cafassessment',
- name='completer',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Person'),
- ),
- ]
diff --git a/ctrack/assessments/migrations/0004_achievementlevel_igp.py b/ctrack/assessments/migrations/0004_achievementlevel_igp.py
deleted file mode 100644
index 581a115..0000000
--- a/ctrack/assessments/migrations/0004_achievementlevel_igp.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-12 14:00
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('assessments', '0003_cafassessment_completer'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='AchievementLevel',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('descriptor', models.CharField(max_length=50)),
- ('color_description', models.CharField(max_length=100)),
- ('color_hex', models.CharField(max_length=8)),
- ],
- ),
- migrations.CreateModel(
- name='IGP',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('descriptive_text', models.CharField(max_length=2000)),
- ('achievement_level', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.AchievementLevel')),
- ('contributing_outcome', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assessments.CAFContributingOutcome')),
- ],
- ),
- ]
diff --git a/ctrack/assessments/migrations/0005_auto_20200512_1438.py b/ctrack/assessments/migrations/0005_auto_20200512_1438.py
deleted file mode 100644
index 31a741a..0000000
--- a/ctrack/assessments/migrations/0005_auto_20200512_1438.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-12 14:38
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('assessments', '0004_achievementlevel_igp'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='igp',
- options={'verbose_name': 'IGP'},
- ),
- migrations.RenameField(
- model_name='achievementlevel',
- old_name='color_description',
- new_name='colour_description',
- ),
- migrations.RenameField(
- model_name='achievementlevel',
- old_name='color_hex',
- new_name='colour_hex',
- ),
- ]
diff --git a/ctrack/assessments/models.py b/ctrack/assessments/models.py
index d3cd4dd..89d83b8 100644
--- a/ctrack/assessments/models.py
+++ b/ctrack/assessments/models.py
@@ -13,16 +13,17 @@ class CAFAssessment(models.Model):
verbose_name = "CAF Assessment"
def get_title(self):
- return f"CAF Assessment for {self.caf.applicable_systems.first().organisation.name} - version {self.caf.version}"
+ return f"CAF Assessment for {self.caf.organisation.name} - version {self.caf.version}"
def __str__(self):
- return f"CAF Assessment for {self.caf.applicable_systems.first().organisation.name} - version {self.caf.version}"
+ return f"CAF Assessment for {self.caf.organisation.name} - version {self.caf.version}"
class CAFObjective(models.Model):
"""
One of 4 as set out in the framework...
"""
+
name = models.CharField(max_length=100, help_text="e.g. Managing Risk")
description = models.TextField(max_length=500)
order_id = models.IntegerField()
@@ -38,6 +39,7 @@ class CAFPrinciple(models.Model):
"""
One of 14 as set out in the framework.
"""
+
caf_objective = models.ForeignKey(CAFObjective, on_delete=models.CASCADE)
designation = models.CharField(max_length=5, help_text="e.g. A1, B3, etc")
title = models.CharField(max_length=50)
@@ -55,6 +57,7 @@ class CAFContributingOutcome(models.Model):
"""
One of the 39 as set out in the framework.
"""
+
designation = models.CharField(max_length=5, help_text="e.g. A1.a, B3.c, etc")
name = models.CharField(max_length=100, help_text="e.g. Board Direction")
description = models.TextField(max_length=1000)
@@ -73,20 +76,32 @@ class CAFAssessmentOutcomeScore(models.Model):
Details the assessment for an Outcome, and the baseline assessment.
Completed by an OES initially, but can be completed by anyone.
"""
+
ASSESSMENT_SCORE = (
("Achieved", "Achieved"),
("Partially Achieved", "Partially Achieved"),
("Not Achieved", "Not Achieved"),
)
- caf_assessment = models.ForeignKey(CAFAssessment, on_delete=models.CASCADE,
- verbose_name="CAF Assessment")
- caf_contributing_outcome = models.ForeignKey(CAFContributingOutcome, on_delete=models.CASCADE,
- verbose_name="CAF Contributing Outcome")
- assessment_score = models.CharField(max_length=20, choices=ASSESSMENT_SCORE,
- help_text="Choose an assessment score",
- verbose_name="Assessment Score")
- baseline_assessment_score = models.CharField(max_length=20, choices=ASSESSMENT_SCORE,
- help_text="Choose an assessment score", verbose_name="Baseline Score")
+ caf_assessment = models.ForeignKey(
+ CAFAssessment, on_delete=models.CASCADE, verbose_name="CAF Assessment"
+ )
+ caf_contributing_outcome = models.ForeignKey(
+ CAFContributingOutcome,
+ on_delete=models.CASCADE,
+ verbose_name="CAF Contributing Outcome",
+ )
+ assessment_score = models.CharField(
+ max_length=20,
+ choices=ASSESSMENT_SCORE,
+ help_text="Choose an assessment score",
+ verbose_name="Assessment Score",
+ )
+ baseline_assessment_score = models.CharField(
+ max_length=20,
+ choices=ASSESSMENT_SCORE,
+ help_text="Choose an assessment score",
+ verbose_name="Baseline Score",
+ )
class Meta:
verbose_name = "CAF Assessment Outcome Score"
@@ -98,7 +113,9 @@ class CAFAssessmentOutcomeScore(models.Model):
class AchievementLevel(models.Model):
descriptor = models.CharField(max_length=50)
colour_description = models.CharField(max_length=100)
- colour_hex = models.CharField(max_length=8) # CSS hex code or simple word descriptor
+ colour_hex = models.CharField(
+ max_length=8
+ ) # CSS hex code or simple word descriptor
def __str__(self):
return f"{self.descriptor}"
@@ -106,7 +123,9 @@ class AchievementLevel(models.Model):
class IGP(models.Model):
achievement_level = models.ForeignKey(AchievementLevel, on_delete=models.CASCADE)
- contributing_outcome = models.ForeignKey(CAFContributingOutcome, on_delete=models.CASCADE)
+ contributing_outcome = models.ForeignKey(
+ CAFContributingOutcome, on_delete=models.CASCADE
+ )
descriptive_text = models.CharField(max_length=2000)
class Meta:
diff --git a/ctrack/caf/admin.py b/ctrack/caf/admin.py
index f19e8f4..5513781 100644
--- a/ctrack/caf/admin.py
+++ b/ctrack/caf/admin.py
@@ -1,31 +1,56 @@
from django.contrib import admin
-from .models import CAF, FileStore, DocumentFile, Grading, ApplicableSystem
+from .models import (
+ CAF,
+ FileStore,
+ DocumentFile,
+ Grading,
+ ApplicableSystem,
+ EssentialService,
+)
+
+
+def get_system_org(obj):
+ es = obj.essentialservice_set.first() # just get the first if there are many
+ return es.organisation.name
+
+
+get_system_org.short_description = "Organisation"
+
+
+class EssentialServiceAdmin(admin.ModelAdmin):
+ model = EssentialService
class ApplicableSystemListAdmin(admin.ModelAdmin):
model = ApplicableSystem
- list_display = ["name", "organisation", "caf"]
+ list_display = ["name", get_system_org, "function"]
-class ApplicableSystemAdmin(admin.StackedInline):
- model = ApplicableSystem
- max_num = 3
- extra = 1
+# FIXME
+# class ApplicableSystemAdmin(admin.StackedInline):
+# model = ApplicableSystem
+# max_num = 3
+# extra = 1
-def get_caf_name(obj):
- ass = ApplicableSystem.objects.filter(caf=obj).first()
- return f"{ass.organisation.name}_v{obj.version}"
+# FIXME - NOT NEEDED
+# def get_caf_name(obj):
+# ass = ApplicableSystem.objects.filter(caf=obj).first()
+# return f"{ass.organisation.name}_v{obj.version}"
+# FIXME
class CAFAdmin(admin.ModelAdmin):
model = CAF
- inlines = [ApplicableSystemAdmin]
- list_display = [get_caf_name, "quality_grading", "confidence_grading", "file"]
+ # inlines = [ApplicableSystemAdmin]
+
+
+# list_display = ["quality_grading", "confidence_grading", "file"]
admin.site.register(CAF, CAFAdmin)
+admin.site.register(EssentialService, EssentialServiceAdmin)
admin.site.register(FileStore)
admin.site.register(DocumentFile)
admin.site.register(Grading)
diff --git a/ctrack/caf/forms.py b/ctrack/caf/forms.py
index 7367ccd..8e3d8ee 100644
--- a/ctrack/caf/forms.py
+++ b/ctrack/caf/forms.py
@@ -12,12 +12,13 @@ from django import forms
from django.forms import inlineformset_factory
from django.urls import reverse
-from ctrack.caf.models import CAF, ApplicableSystem
+from ctrack.caf.models import CAF, ApplicableSystem, EssentialService
from ctrack.organisations.models import Organisation
-CAFCreateInlineFormset = inlineformset_factory(
- CAF, ApplicableSystem, fields=("name", "organisation"), extra=2
-)
+# TODO - Replace this to get inlineformet working
+# CAFCreateInlineFormset = inlineformset_factory(
+# CAF, ApplicableSystem, fields=("name", "organisation"), extra=2
+# )
class ApplicableSystemCreateFromCafForm(forms.Form):
@@ -77,13 +78,14 @@ class ApplicableSystemCreateFromCafForm(forms.Form):
class ApplicableSystemCreateFromOrgForm(forms.Form):
name = forms.CharField(max_length=255)
function = forms.CharField(widget=forms.Textarea)
- organisation = forms.ModelChoiceField(queryset=Organisation.objects.all())
- caf = forms.ModelChoiceField(queryset=CAF.objects.all())
- essential_service = forms.CharField(
- widget=forms.Textarea,
- max_length=255,
- help_text="Description of the essential service which the system suppports.",
- )
+ # organisation = forms.ModelChoiceField(queryset=Organisation.objects.all())
+ # caf = forms.ModelChoiceField(queryset=CAF.objects.all())
+ # essential_service = forms.CharField(
+ # widget=forms.Textarea,
+ # max_length=255,
+ # help_text="Description of the essential service which the system suppports.",
+ # )
+ essential_service = forms.ModelChoiceField(queryset=EssentialService.objects.all())
dft_categorisation = forms.ChoiceField(
choices=ApplicableSystem.SYSTEM_CATEGORISATION,
help_text="Refer to documentation for description of these criteria",
@@ -93,16 +95,19 @@ class ApplicableSystemCreateFromOrgForm(forms.Form):
help_text="Categorisation based on OES' own internal prioritisation process.",
)
- def __init__(self, org_id, slug, org_name, org_cafs, *args, **kwargs):
+ def __init__(self, org_id, slug, org_name, *args, **kwargs):
super().__init__(*args, **kwargs)
cancel_redirect = reverse("organisations:detail", args=[slug])
# we need to create the choices we can use for the CAF dropdown in the form
- self.fields["caf"].queryset = CAF.objects.filter(
- pk__in=[caf.pk for caf in org_cafs]
- )
- self.fields["caf"].label = "CAF"
+ # self.fields["caf"].queryset = CAF.objects.filter(
+ # pk__in=[caf.pk for caf in org_cafs]
+ # )
+ # self.fields["caf"].label = "CAF"
self.fields["dft_categorisation"].label = "DfT Categorisation"
self.fields["oes_categorisation"].label = "OES Categorisation"
+ self.fields["essential_service"].queryset = EssentialService.objects.filter(
+ pk=org_id
+ )
self.helper = FormHelper(self)
self.helper.layout = Layout(
Fieldset(
@@ -113,7 +118,6 @@ class ApplicableSystemCreateFromOrgForm(forms.Form):
"dft_categorisation",
"oes_categorisation",
Hidden("organisation", org_id),
- "caf",
),
ButtonHolder(
Submit("submit", "Submit", css_class="btn-primary"),
diff --git a/ctrack/caf/managers.py b/ctrack/caf/managers.py
index cba8c83..4d8c134 100644
--- a/ctrack/caf/managers.py
+++ b/ctrack/caf/managers.py
@@ -1,8 +1,8 @@
from django.db import connection
from django.db import models
-import ctrack.caf.models # to deal with circular import
-from ctrack.organisations.models import Organisation, Person
+import ctrack.caf.models as caf_models # to deal with circular import
+import ctrack.organisations.models as org_models
class ApplicableSystemManager(models.Manager):
@@ -16,14 +16,16 @@ class ApplicableSystemManager(models.Manager):
Using Custom Managers Django docs for an example.
"""
with connection.cursor() as cursor:
- cursor.execute("""
+ cursor.execute(
+ """
SELECT a.id, a.name, o.id, c.id, sm.id, p.id, o.name
FROM caf_applicablesystem a, organisations_organisation o, organisations_person p, caf_caf c, organisations_submode sm
WHERE a.organisation_id = o.id AND a.caf_id = c.id AND p.organisation_id = o.id AND o.submode_id = sm.id AND p.primary_nis_contact = True;
- """)
+ """
+ )
result_list = []
for row in cursor.fetchall():
- org = Organisation.objects.get(pk=row[2])
+ org = org_models.Organisation.objects.get(pk=row[3])
caf = ctrack.caf.models.CAF.objects.get(pk=row[3])
ass = self.model(id=row[0], name=row[1], organisation=org, caf=caf)
ass.nis_contact = Person.objects.get(pk=row[5])
diff --git a/ctrack/caf/migrations/0001_initial.py b/ctrack/caf/migrations/0001_initial.py
index 5e184df..ca65af6 100644
--- a/ctrack/caf/migrations/0001_initial.py
+++ b/ctrack/caf/migrations/0001_initial.py
@@ -1,6 +1,8 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 09:40
+import ctrack.caf.models
from django.db import migrations, models
+import django.db.models.deletion
class Migration(migrations.Migration):
@@ -8,6 +10,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
+ ('organisations', '0001_initial'),
]
operations = [
@@ -15,49 +18,68 @@ class Migration(migrations.Migration):
name='ApplicableSystem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=256)),
- ('description', models.TextField(max_length=1000)),
+ ('name', models.CharField(help_text='System name assigned by OES', max_length=256)),
+ ('function', models.TextField(blank=True, help_text='How the system is relevant to delivering or supporting the essential service', max_length=1000, null=True)),
+ ('dft_categorisation', models.CharField(choices=[('CR', 'Critical'), ('IM', 'Important (Legacy use only)')], default='CR', help_text='Refer to documentation for description of these criteria', max_length=2, verbose_name='DfT Categorisation')),
+ ('oes_categorisation', models.CharField(default='NA', help_text="Categorisation based on OES' own internal prioritisation process.", max_length=255, verbose_name='OES Categorisation')),
],
options={
- 'verbose_name': 'Applicable System',
+ 'verbose_name': 'NIS System',
},
),
migrations.CreateModel(
- name='CAF',
+ name='Grading',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('version', models.CharField(blank=True, max_length=10, null=True)),
- ('triage_review_date', models.DateField(blank=True, null=True)),
- ('comments', models.TextField(max_length=1000)),
+ ('descriptor', models.CharField(help_text='Q1, C1, etc', max_length=2)),
+ ('description', models.TextField(max_length=250)),
+ ('type', models.CharField(choices=[('CONFIDENCE', 'Confidence'), ('QUALITY', 'Quality'), ('MISC', 'Misc')], help_text='Type of grading', max_length=20)),
],
- options={
- 'verbose_name': 'CAF',
- },
),
migrations.CreateModel(
- name='DocumentFile',
+ name='FileStore',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=255)),
- ('type', models.IntegerField(choices=[(1, 'Excel'), (2, 'Word'), (3, 'PDF'), (4, 'Hard Copy')], default=1)),
+ ('descriptor', models.CharField(max_length=100)),
+ ('virtual_location', models.CharField(help_text='USB, Rosa, email, etc', max_length=100)),
+ ('physical_location', models.CharField(blank=True, help_text='Cupboard, room, building, etc', max_length=100)),
+ ('physical_location_organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation')),
],
),
migrations.CreateModel(
- name='FileStore',
+ name='EssentialService',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('descriptor', models.CharField(max_length=100)),
- ('virtual_location', models.CharField(help_text='USB, Rosa, email, etc', max_length=100)),
- ('physical_location', models.CharField(blank=True, help_text='Cupboard, room, building, etc', max_length=100)),
+ ('name', models.CharField(max_length=256)),
+ ('description', models.CharField(max_length=512)),
+ ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation')),
+ ('systems', models.ManyToManyField(to='caf.ApplicableSystem')),
],
),
migrations.CreateModel(
- name='Grading',
+ name='DocumentFile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('descriptor', models.CharField(help_text='Q1, C1, etc', max_length=2)),
- ('description', models.TextField(max_length=250)),
- ('type', models.CharField(choices=[('CONFIDENCE', 'Confidence'), ('QUALITY', 'Quality'), ('MISC', 'Misc')], help_text='Type of grading', max_length=20)),
+ ('name', models.CharField(max_length=255)),
+ ('type', models.IntegerField(choices=[(1, 'Excel'), (2, 'Word'), (3, 'PDF'), (4, 'Hard Copy')], default=1)),
+ ('file_store_location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='caf.FileStore')),
],
),
+ migrations.CreateModel(
+ name='CAF',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('version', models.CharField(blank=True, max_length=10, null=True)),
+ ('triage_review_date', models.DateField(blank=True, null=True)),
+ ('comments', models.TextField(max_length=1000)),
+ ('confidence_grading', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='confidence_grading', to='caf.Grading')),
+ ('file', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='caf.DocumentFile')),
+ ('organisation', models.ForeignKey(on_delete=models.SET(ctrack.caf.models.CAF.get_sentinel_org), to='organisations.Organisation')),
+ ('quality_grading', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='quality_grading', to='caf.Grading')),
+ ('triage_review_inspector', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Person')),
+ ],
+ options={
+ 'verbose_name': 'CAF',
+ },
+ ),
]
diff --git a/ctrack/caf/migrations/0002_auto_20200403_1407.py b/ctrack/caf/migrations/0002_auto_20200403_1407.py
deleted file mode 100644
index e5963d5..0000000
--- a/ctrack/caf/migrations/0002_auto_20200403_1407.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
-
-import ctrack.caf.models
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('caf', '0001_initial'),
- ('organisations', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='filestore',
- name='physical_location_organisation',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation'),
- ),
- migrations.AddField(
- model_name='documentfile',
- name='file_store_location',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='caf.FileStore'),
- ),
- migrations.AddField(
- model_name='caf',
- name='confidence_grading',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='confidence_grading', to='caf.Grading'),
- ),
- migrations.AddField(
- model_name='caf',
- name='file',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='caf.DocumentFile'),
- ),
- migrations.AddField(
- model_name='caf',
- name='quality_grading',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='quality_grading', to='caf.Grading'),
- ),
- migrations.AddField(
- model_name='caf',
- name='triage_review_inspector',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Person'),
- ),
- migrations.AddField(
- model_name='applicablesystem',
- name='caf',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='applicable_systems', to='caf.CAF'),
- ),
- migrations.AddField(
- model_name='applicablesystem',
- name='organisation',
- field=models.ForeignKey(on_delete=models.SET(ctrack.caf.models.ApplicableSystem.get_sentinel_org), to='organisations.Organisation'),
- ),
- ]
diff --git a/ctrack/caf/migrations/0002_caf_systems.py b/ctrack/caf/migrations/0002_caf_systems.py
new file mode 100644
index 0000000..9e0f76a
--- /dev/null
+++ b/ctrack/caf/migrations/0002_caf_systems.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.12 on 2020-08-27 12:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('caf', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='caf',
+ name='systems',
+ field=models.ManyToManyField(to='caf.ApplicableSystem'),
+ ),
+ ]
diff --git a/ctrack/caf/migrations/0003_auto_20200424_1924.py b/ctrack/caf/migrations/0003_auto_20200424_1924.py
deleted file mode 100644
index 34613c6..0000000
--- a/ctrack/caf/migrations/0003_auto_20200424_1924.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-24 19:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0002_auto_20200403_1407'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='applicablesystem',
- name='description',
- field=models.TextField(blank=True, max_length=1000, null=True),
- ),
- ]
diff --git a/ctrack/caf/migrations/0004_auto_20200813_0953.py b/ctrack/caf/migrations/0004_auto_20200813_0953.py
deleted file mode 100644
index ad1ca96..0000000
--- a/ctrack/caf/migrations/0004_auto_20200813_0953.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 2.2.12 on 2020-08-13 09:53
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0003_auto_20200424_1924'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='applicablesystem',
- options={'verbose_name': 'NIS System'},
- ),
- migrations.RemoveField(
- model_name='applicablesystem',
- name='description',
- ),
- migrations.AddField(
- model_name='applicablesystem',
- name='function',
- field=models.TextField(blank=True, help_text='How the system is relevant to delivering or supporting the essential service', max_length=1000, null=True),
- ),
- migrations.AlterField(
- model_name='applicablesystem',
- name='name',
- field=models.CharField(help_text='System name assigned by OES', max_length=256),
- ),
- ]
diff --git a/ctrack/caf/migrations/0005_applicablesystem_oes_categorisation.py b/ctrack/caf/migrations/0005_applicablesystem_oes_categorisation.py
deleted file mode 100644
index dc8c928..0000000
--- a/ctrack/caf/migrations/0005_applicablesystem_oes_categorisation.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.12 on 2020-08-13 11:17
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0004_auto_20200813_0953'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='applicablesystem',
- name='oes_categorisation',
- field=models.CharField(choices=[('CR', 'Critical'), ('IM', 'Important')], default='CR', max_length=2),
- ),
- ]
diff --git a/ctrack/caf/migrations/0006_auto_20200813_1125.py b/ctrack/caf/migrations/0006_auto_20200813_1125.py
deleted file mode 100644
index 1e97dff..0000000
--- a/ctrack/caf/migrations/0006_auto_20200813_1125.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 2.2.12 on 2020-08-13 11:25
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0005_applicablesystem_oes_categorisation'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='applicablesystem',
- name='oes_categorisation',
- ),
- migrations.AddField(
- model_name='applicablesystem',
- name='dft_categorisation',
- field=models.CharField(choices=[('CR', 'Critical'), ('IM', 'Important')], default='CR', help_text='Refer to documentation for description of these criteria', max_length=2, verbose_name='DfT Categorisation'),
- ),
- ]
diff --git a/ctrack/caf/migrations/0007_auto_20200814_1230.py b/ctrack/caf/migrations/0007_auto_20200814_1230.py
deleted file mode 100644
index 68a12bd..0000000
--- a/ctrack/caf/migrations/0007_auto_20200814_1230.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2.12 on 2020-08-14 12:30
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0006_auto_20200813_1125'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='applicablesystem',
- name='oes_categorisation',
- field=models.CharField(default='', help_text="Categorisation based on OES' own internal prioritisation process.", max_length=255, verbose_name='OES Categorisation'),
- ),
- migrations.AlterField(
- model_name='applicablesystem',
- name='dft_categorisation',
- field=models.CharField(choices=[('CR', 'Critical'), ('IM', 'Important (Legacy use only)')], default='CR', help_text='Refer to documentation for description of these criteria', max_length=2, verbose_name='DfT Categorisation'),
- ),
- ]
diff --git a/ctrack/caf/migrations/0008_auto_20200814_1318.py b/ctrack/caf/migrations/0008_auto_20200814_1318.py
deleted file mode 100644
index 90f188f..0000000
--- a/ctrack/caf/migrations/0008_auto_20200814_1318.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2.12 on 2020-08-14 13:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('caf', '0007_auto_20200814_1230'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='applicablesystem',
- name='essential_service',
- field=models.CharField(default='NA', help_text='Name of the essential service which the system suppports.', max_length=255, verbose_name='Essential Service'),
- ),
- migrations.AlterField(
- model_name='applicablesystem',
- name='oes_categorisation',
- field=models.CharField(default='NA', help_text="Categorisation based on OES' own internal prioritisation process.", max_length=255, verbose_name='OES Categorisation'),
- ),
- ]
diff --git a/ctrack/caf/models.py b/ctrack/caf/models.py
index 27220bc..5fd586f 100644
--- a/ctrack/caf/models.py
+++ b/ctrack/caf/models.py
@@ -45,13 +45,6 @@ class DocumentFile(models.Model):
class ApplicableSystem(models.Model):
- CRITICAL = "CR"
- IMPORTANT = "IM"
- SYSTEM_CATEGORISATION = (
- (CRITICAL, "Critical"),
- (IMPORTANT, "Important (Legacy use only)"),
- )
-
def get_sentinel_org():
"""
We need this so that we can ensure models.SET() is applied with a callable
@@ -60,6 +53,13 @@ class ApplicableSystem(models.Model):
"""
return Organisation.objects.get_or_create(name="DELETED ORGANISATION")[0]
+ CRITICAL = "CR"
+ IMPORTANT = "IM"
+ SYSTEM_CATEGORISATION = (
+ (CRITICAL, "Critical"),
+ (IMPORTANT, "Important (Legacy use only)"),
+ )
+
name = models.CharField(max_length=256, help_text="System name assigned by OES")
function = models.TextField(
max_length=1000,
@@ -68,22 +68,6 @@ class ApplicableSystem(models.Model):
help_text="How the system is relevant to delivering or supporting the "
"essential service",
)
- organisation = models.ForeignKey(
- Organisation, on_delete=models.SET(get_sentinel_org)
- )
- caf = models.ForeignKey(
- "CAF",
- on_delete=models.CASCADE,
- blank=True,
- null=True,
- related_name="applicable_systems",
- )
- essential_service = models.CharField(
- max_length=255,
- default="NA",
- verbose_name="Essential Service",
- help_text="Description of the essential service which the system suppports.",
- )
dft_categorisation = models.CharField(
max_length=2,
choices=SYSTEM_CATEGORISATION,
@@ -105,12 +89,20 @@ class ApplicableSystem(models.Model):
return self.organisation.person_set.filter(primary_nis_contact=True)
def __str__(self):
- return f"{self.organisation.name} | {self.name}"
+ return self.name
objects = ApplicableSystemManager()
class CAF(models.Model):
+ def get_sentinel_org():
+ """
+ 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 Organisation
+ objects related to them being deleted also.
+ """
+ return Organisation.objects.get_or_create(name="DELETED ORGANISATION")[0]
+
quality_grading = models.ForeignKey(
Grading,
on_delete=models.CASCADE,
@@ -129,10 +121,14 @@ class CAF(models.Model):
DocumentFile, on_delete=models.CASCADE, blank=True, null=True
)
version = models.CharField(max_length=10, blank=True, null=True)
+ organisation = models.ForeignKey(
+ Organisation, on_delete=models.SET(get_sentinel_org)
+ )
triage_review_date = models.DateField(blank=True, null=True)
triage_review_inspector = models.ForeignKey(
Person, on_delete=models.CASCADE, blank=True, null=True
)
+ systems = models.ManyToManyField(ApplicableSystem)
comments = models.TextField(max_length=1000)
class Meta:
@@ -147,14 +143,24 @@ class CAF(models.Model):
"""
return ApplicableSystem.objects.filter(caf=self)
- def organisation(self):
- first_ass = ApplicableSystem.objects.filter(caf=self).first()
- return first_ass.organisation
+ # FIXME remove once we know we don't need it
+ # def organisation(self):
+ # first_ass = ApplicableSystem.objects.filter(caf=self).first()
+ # return first_ass.organisation
def sub_mode(self):
- return self.organisation().submode
+ return self.organisation.submode
def __str__(self):
# Get the organisation and applicable system
- ass = ApplicableSystem.objects.filter(caf=self).first()
- return f"CAF | {ass.organisation.name}_v{self.version}"
+ return f"CAF | {self.organisation.name}_v{self.version}"
+
+
+class EssentialService(models.Model):
+ name = models.CharField(max_length=256)
+ description = models.CharField(max_length=512)
+ organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE)
+ systems = models.ManyToManyField(ApplicableSystem)
+
+ def __str__(self):
+ return self.name
diff --git a/ctrack/caf/templates/caf/applicablesystem_detail.html b/ctrack/caf/templates/caf/applicablesystem_detail.html
index 9e12043..b8467b0 100644
--- a/ctrack/caf/templates/caf/applicablesystem_detail.html
+++ b/ctrack/caf/templates/caf/applicablesystem_detail.html
@@ -5,24 +5,31 @@
{% block title %}{{ object.name }}{% endblock %}
{% block content %}
- <div class="container mt-3">
- <div class="row">
- <div class="col-sm-12 pl-0 my-2">
+ <div class="container mt-3">
<div class="row">
- <h4>{{ object.name }}</h4>
+ <div class="col-sm-12 pl-0 my-2">
+ <div class="row">
+ <h4>{{ object.name }}</h4>
+ </div>
+ <div class="row">
+ <h5>{{ object.caf.organisation }}</h5>
+ </div>
+ <div class="row">
+ <h5>Function</h5>
+ </div>
+ <div class="row">
+ <p>{{ object.function }}</p>
+ <p>Supports Essential Services:</p>
+ </div>
+ <div class="row">
+ <ul>
+ {% for s in object.essentialservice_set.all %}
+ <li>{{ s.name }}</li>
+ {% endfor %}
+ </ul>
+ </div>
+ </div>
</div>
- <div class="row">
- <h5>{{ object.caf.organisation }}</h5>
- </div>
- <div class="row">
- <h6>Function</h6>
- </div>
- <div class="row">
- <p>{{ object.function }}</p>
- <p>Contained within CAF: <a href="{% url "caf:detail" object.caf.pk %}">{{ object.caf }}</a></p>
- </div>
- </div>
</div>
- </div>
{% endblock %}
diff --git a/ctrack/caf/templates/caf/caf_detail.html b/ctrack/caf/templates/caf/caf_detail.html
index fc2fcfd..fdd939e 100644
--- a/ctrack/caf/templates/caf/caf_detail.html
+++ b/ctrack/caf/templates/caf/caf_detail.html
@@ -72,7 +72,7 @@
<tr>
<td><a href="{% url "caf:ass_detail" system.id %}">{{ system.name }}</a></td>
<td>{{ system.function }}<br>
- <a href="{% url "caf:detail" system.caf.pk %}" class="small">
+ <a href="{% url "caf:detail" system.pk %}" class="small">
{{ system.caf }}
</a> <span class="text-muted"> | <a href="#" class="small">Edit System</a></span>
</td>
diff --git a/ctrack/caf/tests/factories.py b/ctrack/caf/tests/factories.py
index 3c62307..7aaf716 100644
--- a/ctrack/caf/tests/factories.py
+++ b/ctrack/caf/tests/factories.py
@@ -10,6 +10,7 @@ from ctrack.organisations.tests.factories import OrganisationFactory, PersonFact
class CAFFactory(factory.DjangoModelFactory):
quality_grading = factory.SubFactory("ctrack.caf.tests.factories.GradingFactory")
confidence_grading = factory.SubFactory("ctrack.caf.tests.factories.GradingFactory")
+ organisation = factory.SubFactory("ctrack.organisations.tests.OrganisationFactory")
file = None
version = Faker("bothify", text="??##", letters="ABCD")
triage_review_date = Faker("date_object")
@@ -29,8 +30,6 @@ class ApplicableSystemFactory(factory.DjangoModelFactory):
function = Faker(
"paragraph", nb_sentences=4, variable_nb_sentences=True, ext_word_list=None
)
- organisation = factory.SubFactory(OrganisationFactory)
- caf = factory.SubFactory("ctrack.caf.tests.factories.CAFFactory")
dft_categorisation = "CR"
class Meta:
diff --git a/ctrack/caf/views.py b/ctrack/caf/views.py
index 258774e..480e305 100644
--- a/ctrack/caf/views.py
+++ b/ctrack/caf/views.py
@@ -36,8 +36,8 @@ def caf_detail_view(request, pk):
context = {
"object": caf,
"assessments_and_scores": _scrs,
- "organisation": ApplicableSystem.objects.filter(caf=caf).first().organisation,
- "systems": caf.applicable_systems.all(),
+ "organisation": caf.organisation,
+ "systems": caf.systems.all(),
}
return render(request, "caf/caf_detail.html", context)
@@ -107,20 +107,22 @@ class ApplicableSystemCreateFromOrg(
ass = ApplicableSystem.objects.create(
name=form.cleaned_data["name"],
function=form.cleaned_data["function"],
- organisation=form.cleaned_data["organisation"],
- caf=form.cleaned_data["caf"],
+ # organisation=form.cleaned_data["organisation"],
+ # caf=form.cleaned_data["caf"],
)
+ es = form.cleaned_data["essential_service"]
+ es.systems.add(ass)
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
org = Organisation.objects.get(slug=self.kwargs["slug"])
- asses = org.applicablesystem_set.all()
- org_cafs = {ass.caf for ass in asses}
+ asses = org.applicable_systems()
+ # org_cafs = org.caf_set.all()
kwargs["org_id"] = org.id
kwargs["slug"] = org.slug
kwargs["org_name"] = org.name
- kwargs["org_cafs"] = list(org_cafs)
+ # kwargs["org_cafs"] = list(org_cafs)
return kwargs
def get_success_url(self):
diff --git a/ctrack/core/utils.py b/ctrack/core/utils.py
index 0612c52..af01549 100644
--- a/ctrack/core/utils.py
+++ b/ctrack/core/utils.py
@@ -15,7 +15,7 @@ from ctrack.assessments.models import (
CAFObjective,
CAFPrinciple,
)
-from ctrack.caf.models import CAF
+from ctrack.caf.models import CAF, EssentialService
from ctrack.caf.tests.factories import (
ApplicableSystemFactory,
CAFFactory,
@@ -52,17 +52,25 @@ fnames = [
def _create_caf_app_service(c_descriptors, org, q_descriptors):
+ # Get the essential services and systems belonging to the org
+
+ es = EssentialService.objects.create(
+ name="".join(["Essential Service for ", org.name]),
+ description="Random description",
+ organisation=org,
+ )
+ as1 = ApplicableSystemFactory.create(name=random.choice(fnames))
+ as2 = ApplicableSystemFactory.create(name=random.choice(fnames))
+ es.systems.add(as1, as2)
+
caf = CAFFactory.create(
quality_grading__descriptor=random.choice(q_descriptors),
confidence_grading__descriptor=random.choice(c_descriptors),
+ organisation=org,
triage_review_date=None,
triage_review_inspector=None,
)
- # Each CAF can have up to three systems associated with it
- for _ in range(random.randint(1, 3)):
- ApplicableSystemFactory.create(
- name=random.choice(fnames), organisation=org, caf=caf,
- )
+ caf.systems.add(as1, as2)
def populate_db(**kwargs):
@@ -219,9 +227,9 @@ def populate_db(**kwargs):
# File store
FileStoreFactory.create(physical_location_organisation=orgs[1])
- # Every org gets on CAF for now
+ # Every org gets on CAF and Essential Service for now
for org in orgs:
- # create a CAF and ApplicableService for it
+ # create a CAF
_create_caf_app_service(c_descriptors, org, q_descriptors)
# CAF submissions - they create EngagementEvents
diff --git a/ctrack/core/views.py b/ctrack/core/views.py
index 5f5627c..d7efe34 100644
--- a/ctrack/core/views.py
+++ b/ctrack/core/views.py
@@ -12,7 +12,7 @@ def home_page(request):
name=request.user.stakeholder.person.get_organisation_name()
)
irs = IncidentReport.objects.filter(organisation__name=org)
- systems = org.applicablesystem_set.all()
+ systems = org.applicable_systems()
peoples = org.person_set.all()
engagement_events = EngagementEvent.objects.filter(participants__in=peoples)
return render(
diff --git a/ctrack/organisations/admin.py b/ctrack/organisations/admin.py
index 203baa9..d3e6284 100644
--- a/ctrack/organisations/admin.py
+++ b/ctrack/organisations/admin.py
@@ -1,5 +1,7 @@
from django.contrib import admin
+from ctrack.caf.models import CAF
+
from .models import (
Address,
AddressType,
@@ -18,6 +20,10 @@ def get_organisation_name(person):
return Organisation.objects.filter(person__id=person.id).first().name
+def get_first_caf(org):
+ return CAF.objects.filter(organisation__id=org.id).first().version
+
+
# We need this to ensure the column header in the admin does't read the func name
get_organisation_name.short_description = "Organisation"
diff --git a/ctrack/organisations/migrations/0001_initial.py b/ctrack/organisations/migrations/0001_initial.py
index a420fa1..c5ab495 100644
--- a/ctrack/organisations/migrations/0001_initial.py
+++ b/ctrack/organisations/migrations/0001_initial.py
@@ -1,5 +1,6 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 09:40
+import django.contrib.auth
from django.db import migrations, models
import django.db.models.deletion
import django_extensions.db.fields
@@ -14,23 +15,6 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
- name='Address',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('line1', models.CharField(max_length=255)),
- ('line2', models.CharField(blank=True, max_length=255)),
- ('line3', models.CharField(blank=True, max_length=255)),
- ('city', models.CharField(max_length=100)),
- ('county', models.CharField(blank=True, max_length=100)),
- ('postcode', models.CharField(max_length=10)),
- ('country', models.CharField(max_length=100)),
- ('other_details', models.CharField(blank=True, max_length=255)),
- ],
- options={
- 'verbose_name_plural': 'Addresses',
- },
- ),
- migrations.CreateModel(
name='AddressType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
@@ -50,31 +34,16 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from=['name'])),
- ('oes', models.BooleanField()),
+ ('oes', models.BooleanField(default=True)),
('designation_type', models.IntegerField(choices=[(1, 'Automatic'), (2, 'Reserve Power'), (3, 'NA')], default=1)),
('registered_company_name', models.CharField(blank=True, max_length=255)),
('registered_company_number', models.CharField(blank=True, max_length=100)),
('date_updated', models.DateField(auto_now=True)),
- ('comments', models.TextField(max_length=500)),
+ ('comments', models.TextField(blank=True, max_length=500, null=True)),
('active', models.BooleanField(default=True)),
],
),
migrations.CreateModel(
- name='Role',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=100)),
- ],
- ),
- migrations.CreateModel(
- name='Submode',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('descriptor', models.CharField(max_length=100)),
- ('mode', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Mode')),
- ],
- ),
- migrations.CreateModel(
name='Person',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
@@ -100,10 +69,83 @@ class Migration(migrations.Migration):
('comments', models.TextField(blank=True, max_length=1000)),
('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation')),
('predecessor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='previous_person', to='organisations.Person')),
- ('role', models.ManyToManyField(to='organisations.Role')),
],
options={
'verbose_name_plural': 'People',
},
),
+ migrations.CreateModel(
+ name='Role',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=100)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Submode',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('descriptor', models.CharField(max_length=100)),
+ ('mode', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Mode')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Stakeholder',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Person')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='person',
+ name='role',
+ field=models.ManyToManyField(to='organisations.Role'),
+ ),
+ migrations.AddField(
+ model_name='organisation',
+ name='submode',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Submode'),
+ ),
+ migrations.CreateModel(
+ name='IncidentReport',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('person_involved', models.CharField(blank=True, max_length=100, verbose_name='Name of person reporting/detecting incident')),
+ ('role', models.CharField(blank=True, help_text='Role of person reporting/detecting incident', 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(verbose_name='Date/Time incident detected')),
+ ('date_time_incident_reported', models.DateTimeField(auto_now=True, verbose_name='Date/Time incident reported')),
+ ('incident_type', models.CharField(choices=[('Cyber', 'Cyber'), ('Non-Cyber', 'Non-Cyber'), ('Both', 'Both'), ('Power Outage', 'Power Outage')], help_text='This can be appoximate', max_length=20)),
+ ('incident_status', models.CharField(choices=[('Detected', 'Detected'), ('Suspected', 'Suspected'), ('Resolved', 'Resolved')], max_length=20)),
+ ('incident_stage', models.CharField(choices=[('Ongoing', 'Ongoing'), ('Ended', 'Ended'), ('Ongoing but managed', 'Ongoing but managed')], 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(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')),
+ ('dft_handle_status', models.CharField(choices=[('QUEUED', 'QUEUED'), ('REVIEWING', 'REVIEWING'), ('WAITING', 'WAITING'), ('COMPLETED', 'COMPLETED')], default='QUEUED', max_length=20)),
+ ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Organisation')),
+ ('reporting_person', models.ForeignKey(on_delete=models.SET(django.contrib.auth.get_user_model), to='organisations.Person', verbose_name='Person reporting the incident')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Address',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('line1', models.CharField(max_length=255)),
+ ('line2', models.CharField(blank=True, max_length=255)),
+ ('line3', models.CharField(blank=True, max_length=255)),
+ ('city', models.CharField(max_length=100)),
+ ('county', models.CharField(blank=True, max_length=100)),
+ ('postcode', models.CharField(max_length=10)),
+ ('country', models.CharField(max_length=100)),
+ ('other_details', models.CharField(blank=True, max_length=255)),
+ ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='organisations.Organisation')),
+ ('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.AddressType', verbose_name='Address Type')),
+ ],
+ options={
+ 'verbose_name_plural': 'Addresses',
+ },
+ ),
]
diff --git a/ctrack/organisations/migrations/0002_auto_20200403_1407.py b/ctrack/organisations/migrations/0002_auto_20200403_1407.py
deleted file mode 100644
index d9f3aed..0000000
--- a/ctrack/organisations/migrations/0002_auto_20200403_1407.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
-
-import ctrack.organisations.models
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('organisations', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='person',
- name='updated_by',
- field=models.ForeignKey(on_delete=models.SET(ctrack.organisations.models.Person.get_sentinel_user), to=settings.AUTH_USER_MODEL),
- ),
- migrations.AddField(
- model_name='organisation',
- name='submode',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Submode'),
- ),
- migrations.AddField(
- model_name='organisation',
- name='updated_by',
- field=models.ForeignKey(on_delete=models.SET(ctrack.organisations.models.Organisation.get_sentinel_user), to=settings.AUTH_USER_MODEL),
- ),
- migrations.AddField(
- model_name='address',
- name='organisation',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='organisations.Organisation'),
- ),
- migrations.AddField(
- model_name='address',
- name='type',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.AddressType', verbose_name='Address Type'),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0003_auto_20200424_1607.py b/ctrack/organisations/migrations/0003_auto_20200424_1607.py
deleted file mode 100644
index 75268a0..0000000
--- a/ctrack/organisations/migrations/0003_auto_20200424_1607.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.9 on 2020-04-24 16:07
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0002_auto_20200403_1407'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='organisation',
- name='comments',
- field=models.TextField(blank=True, max_length=500, null=True),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0004_auto_20200513_1441.py b/ctrack/organisations/migrations/0004_auto_20200513_1441.py
deleted file mode 100644
index 180211a..0000000
--- a/ctrack/organisations/migrations/0004_auto_20200513_1441.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-13 14:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0003_auto_20200424_1607'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='organisation',
- name='oes',
- field=models.BooleanField(default=True),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0005_auto_20200525_1502.py b/ctrack/organisations/migrations/0005_auto_20200525_1502.py
deleted file mode 100644
index 921fdbb..0000000
--- a/ctrack/organisations/migrations/0005_auto_20200525_1502.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-25 15:02
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0004_auto_20200513_1441'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='organisation',
- name='updated_by',
- ),
- migrations.RemoveField(
- model_name='person',
- name='updated_by',
- ),
- migrations.CreateModel(
- name='Stakeholder',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organisations.Person')),
- ],
- ),
- ]
diff --git a/ctrack/organisations/migrations/0006_incidentreport.py b/ctrack/organisations/migrations/0006_incidentreport.py
deleted file mode 100644
index 0528aaa..0000000
--- a/ctrack/organisations/migrations/0006_incidentreport.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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/migrations/0007_auto_20200529_1520.py b/ctrack/organisations/migrations/0007_auto_20200529_1520.py
deleted file mode 100644
index 59a5089..0000000
--- a/ctrack/organisations/migrations/0007_auto_20200529_1520.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 2.2.12 on 2020-05-29 15:20
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0006_incidentreport'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='incidentreport',
- name='dft_handle_status',
- field=models.CharField(choices=[('queued', 'QUEUED'), ('reviewing', 'REVIEWING'), ('waiting', 'WAITING'), ('completed', 'COMPLETE')], default='queued', max_length=20),
- ),
- migrations.AlterField(
- model_name='incidentreport',
- name='date_time_incident_reported',
- field=models.DateTimeField(auto_now=True, verbose_name='Date/Time incident reported'),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0008_auto_20200529_1545.py b/ctrack/organisations/migrations/0008_auto_20200529_1545.py
deleted file mode 100644
index 1c4cec6..0000000
--- a/ctrack/organisations/migrations/0008_auto_20200529_1545.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Generated by Django 2.2.12 on 2020-05-29 15:45
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0007_auto_20200529_1520'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='incidentreport',
- name='date_time_incident_detected',
- field=models.DateTimeField(verbose_name='Date/Time incident detected'),
- ),
- migrations.AlterField(
- model_name='incidentreport',
- name='dft_handle_status',
- field=models.CharField(choices=[('QUEUED', 'QUEUED'), ('REVIEWING', 'REVIEWING'), ('WAITING', 'WAITING'), ('COMPLETED', 'COMPLETED')], default='QUEUED', max_length=20),
- ),
- migrations.AlterField(
- model_name='incidentreport',
- name='incident_stage',
- field=models.CharField(choices=[('Ongoing', 'Ongoing'), ('Ended', 'Ended'), ('Ongoing but managed', 'Ongoing but managed')], max_length=20),
- ),
- migrations.AlterField(
- model_name='incidentreport',
- name='incident_status',
- field=models.CharField(choices=[('Detected', 'Detected'), ('Suspected', 'Suspected'), ('Resolved', 'Resolved')], max_length=20),
- ),
- migrations.AlterField(
- model_name='incidentreport',
- name='incident_type',
- field=models.CharField(choices=[('Cyber', 'Cyber'), ('Non-Cyber', 'Non-Cyber'), ('Both', 'Both'), ('Power Outage', 'Power Outage')], help_text='This can be appoximate', max_length=20),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0009_incidentreport_person_involved.py b/ctrack/organisations/migrations/0009_incidentreport_person_involved.py
deleted file mode 100644
index ea4799c..0000000
--- a/ctrack/organisations/migrations/0009_incidentreport_person_involved.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.12 on 2020-05-29 16:01
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0008_auto_20200529_1545'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='incidentreport',
- name='person_involved',
- field=models.CharField(blank=True, max_length=100, verbose_name='Name of person reporting/detecting incident'),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0010_auto_20200529_1602.py b/ctrack/organisations/migrations/0010_auto_20200529_1602.py
deleted file mode 100644
index 2986d36..0000000
--- a/ctrack/organisations/migrations/0010_auto_20200529_1602.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 2.2.12 on 2020-05-29 16:02
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0009_incidentreport_person_involved'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='incidentreport',
- name='role',
- field=models.CharField(blank=True, help_text='Role of person reporting/detecting incident', max_length=100),
- ),
- ]
diff --git a/ctrack/organisations/migrations/0011_auto_20200531_1441.py b/ctrack/organisations/migrations/0011_auto_20200531_1441.py
deleted file mode 100644
index 3dced7d..0000000
--- a/ctrack/organisations/migrations/0011_auto_20200531_1441.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.2.12 on 2020-05-31 14:41
-
-import django.contrib.auth
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0010_auto_20200529_1602'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='incidentreport',
- name='reporting_person',
- field=models.ForeignKey(on_delete=models.SET(django.contrib.auth.get_user_model), to='organisations.Person', verbose_name='Person reporting the incident'),
- ),
- ]
diff --git a/ctrack/organisations/models.py b/ctrack/organisations/models.py
index 9eb0a23..f91dabf 100644
--- a/ctrack/organisations/models.py
+++ b/ctrack/organisations/models.py
@@ -153,7 +153,19 @@ class Organisation(models.Model):
return self.person_set.filter(primary_nis_contact=True)
def applicable_systems(self):
- return self.applicablesystem_set.all()
+ # 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):
diff --git a/ctrack/organisations/templates/organisations/organisation_detail.html b/ctrack/organisations/templates/organisations/organisation_detail.html
index 8c9ac3b..a86adf1 100644
--- a/ctrack/organisations/templates/organisations/organisation_detail.html
+++ b/ctrack/organisations/templates/organisations/organisation_detail.html
@@ -65,6 +65,33 @@
</div>
</div>
</div>
+
+ <div class="row">
+ <div class="col-md-12 my-2">
+ <div class="card bg-light">
+ <div class="card-body">
+ <div class="card-title">Essential Services</div>
+ <div class="table table-responsive">
+ <table class="table">
+ {% if applicable_systems|length > 0 %}
+ {% for es in essential_services %}
+ <tr>
+ <td style="width: 18%">{{ es.name }}</td>
+ <td>{{ es.description }}<br>
+ <span class="text-muted"> | <a href="#" class="small">Edit System</a></span>
+ </td>
+ </tr>
+ {% endfor %}
+ {% else %}
+ <p>button to create new one here</p>
+ {% endif %}
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
<div class="row">
<div class="col-md-12 my-2">
<div class="card bg-light">
@@ -79,7 +106,7 @@
<tr>
<td><a href="{% url "caf:ass_detail" ass.id %}">{{ ass.name }}</a></td>
<td>{{ ass.function }}<br>
- <a href="{% url "caf:detail" ass.caf.pk %}" class="small">
+ <a href="{% url "caf:detail" ass.pk %}" class="small">
{{ ass.caf }}
</a> <span class="text-muted"> | <a href="#" class="small">Edit System</a></span>
</td>
@@ -95,21 +122,13 @@
</div>
</div>
</div>
+
+
<div class="row">
<div class="col-md-12 my-2">
<div class="card bg-light">
<div class="card-body">
- <div class="card-title">OES Engagements<a href="{% url "register:create" %}" class="btn btn-primary btn-sm float-right">Add
- new...</a></div>
- <div class="table table-responsive">
- <table class="table">
- {% if engagement_events|length > 0 %}
- {% for event in engagement_events %}
- <tr>
- <td><a href="#">{{ event.type}}</a></td>
- <td>{{ event.short_description }}</td>
- </tr>
- {% endfor %}
+ <div class="card-title">OES Engagements<a href="{% url "register:create" %}" class="btn btn-primary btn-sm float-right">Add new...</a></div> <div class="table table-responsive"> <table class="table"> {% if engagement_events|length > 0 %} {% for event in engagement_events %} <tr> <td><a href="#">{{ event.type}}</a></td> <td>{{ event.short_description }}</td> </tr> {% endfor %}
{% else %}
<a class="btn btn-primary" href="{% url "register:create" %}" role="button">Add
new...</a>
diff --git a/ctrack/organisations/tests/test_models.py b/ctrack/organisations/tests/test_models.py
index c6314af..108d0ce 100644
--- a/ctrack/organisations/tests/test_models.py
+++ b/ctrack/organisations/tests/test_models.py
@@ -1,7 +1,14 @@
+import random
+
import pytest
+
from slugify import slugify
from ctrack.organisations.models import IncidentReport, Organisation
+from ctrack.caf.models import CAF, Grading
+from ctrack.caf.tests.factories import ApplicableSystemFactory
+from ctrack.caf.models import EssentialService
+from ctrack.core.utils import fnames
pytestmark = pytest.mark.django_db
@@ -21,3 +28,27 @@ def test_update_organisation(org_with_people):
def test_new_address(addr):
# The address "has" an organisation
assert addr.organisation.name
+
+
+def test_essential_service(org):
+ q1 = Grading.objects.create(descriptor="Q1", description="baws", type="QUALITY")
+ c1 = Grading.objects.create(
+ descriptor="C1", description="baws_c", type="CONFIDENCE"
+ )
+ caf = CAF.objects.create(
+ quality_grading=q1,
+ organisation=org,
+ confidence_grading=c1,
+ triage_review_date=None,
+ triage_review_inspector=None,
+ )
+ ass = ApplicableSystemFactory.create(name=random.choice(fnames), caf=caf,)
+ ass2 = ApplicableSystemFactory.create(name=random.choice(fnames), caf=caf,)
+ es = EssentialService.objects.create(
+ name="Test ES", description="Test ES Description", organisation=org
+ )
+ es.systems.add(ass, ass2)
+ assert es.organisation.name == org.name
+ assert es.name == "Test ES"
+ assert es.systems.count() == 2
+ assert ass.name in [s.name for s in org.systems()]
diff --git a/ctrack/organisations/views.py b/ctrack/organisations/views.py
index 394966a..7652783 100644
--- a/ctrack/organisations/views.py
+++ b/ctrack/organisations/views.py
@@ -10,6 +10,7 @@ from ctrack.register.models import EngagementEvent
from .forms import AddressInlineFormSet, IncidentReportForm, OrganisationCreateForm
from .models import IncidentReport, Organisation, Person
+from ctrack.caf.models import EssentialService
class PersonListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
@@ -66,6 +67,7 @@ class OrganisationDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detail
org = kwargs["object"]
peoples = org.person_set.all()
engagement_events = EngagementEvent.objects.filter(participants__in=peoples)
+ essential_services = EssentialService.objects.filter(organisation=org)
no_addr = org.addresses.count()
if no_addr > 1:
context["no_addr"] = no_addr
@@ -77,9 +79,10 @@ class OrganisationDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detail
context["addr"] = addr
people = org.person_set.all()
context["people"] = people
- applicable_systems = org.applicablesystem_set.all()
+ applicable_systems = org.applicable_systems()
context["applicable_systems"] = applicable_systems
context["engagement_events"] = engagement_events
+ context["essential_services"] = essential_services
return context
diff --git a/ctrack/register/migrations/0001_initial.py b/ctrack/register/migrations/0001_initial.py
index 7db1237..30a69ba 100644
--- a/ctrack/register/migrations/0001_initial.py
+++ b/ctrack/register/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 12:44
from django.db import migrations, models
import django.db.models.deletion
@@ -9,8 +9,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('caf', '0002_auto_20200403_1407'),
('organisations', '0001_initial'),
+ ('caf', '0002_caf_systems'),
]
operations = [
diff --git a/ctrack/register/migrations/0002_engagementevent_user.py b/ctrack/register/migrations/0002_engagementevent_user.py
index 59c93d6..56866dc 100644
--- a/ctrack/register/migrations/0002_engagementevent_user.py
+++ b/ctrack/register/migrations/0002_engagementevent_user.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 12:44
import ctrack.register.models
from django.conf import settings
@@ -10,8 +10,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('register', '0001_initial'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
diff --git a/ctrack/users/migrations/0001_initial.py b/ctrack/users/migrations/0001_initial.py
index 2f760c2..df3cd12 100644
--- a/ctrack/users/migrations/0001_initial.py
+++ b/ctrack/users/migrations/0001_initial.py
@@ -1,8 +1,9 @@
-# Generated by Django 2.2.9 on 2020-04-03 14:07
+# Generated by Django 2.2.12 on 2020-08-27 12:44
import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
+import django.db.models.deletion
import django.utils.timezone
@@ -11,6 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
+ ('organisations', '0001_initial'),
('auth', '0011_update_proxy_permissions'),
]
@@ -31,6 +33,7 @@ class Migration(migrations.Migration):
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
+ ('stakeholder', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Stakeholder')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],
options={
diff --git a/ctrack/users/migrations/0002_user_is_person.py b/ctrack/users/migrations/0002_user_is_person.py
deleted file mode 100644
index 2223429..0000000
--- a/ctrack/users/migrations/0002_user_is_person.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-22 15:19
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('users', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='user',
- name='is_person',
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/ctrack/users/migrations/0003_auto_20200522_1527.py b/ctrack/users/migrations/0003_auto_20200522_1527.py
deleted file mode 100644
index dc4f7c1..0000000
--- a/ctrack/users/migrations/0003_auto_20200522_1527.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-22 15:27
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('users', '0002_user_is_person'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='user',
- old_name='is_person',
- new_name='oes_user',
- ),
- ]
diff --git a/ctrack/users/migrations/0004_auto_20200524_1945.py b/ctrack/users/migrations/0004_auto_20200524_1945.py
deleted file mode 100644
index 8839fd2..0000000
--- a/ctrack/users/migrations/0004_auto_20200524_1945.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-24 19:45
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('users', '0003_auto_20200522_1527'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='UserProfile',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ],
- ),
- migrations.RemoveField(
- model_name='user',
- name='oes_user',
- ),
- ]
diff --git a/ctrack/users/migrations/0005_delete_userprofile.py b/ctrack/users/migrations/0005_delete_userprofile.py
deleted file mode 100644
index 42f62bb..0000000
--- a/ctrack/users/migrations/0005_delete_userprofile.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-25 14:41
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('users', '0004_auto_20200524_1945'),
- ]
-
- operations = [
- migrations.DeleteModel(
- name='UserProfile',
- ),
- ]
diff --git a/ctrack/users/migrations/0006_user_stakeholder.py b/ctrack/users/migrations/0006_user_stakeholder.py
deleted file mode 100644
index d8a3089..0000000
--- a/ctrack/users/migrations/0006_user_stakeholder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 3.0.5 on 2020-05-25 15:02
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('organisations', '0005_auto_20200525_1502'),
- ('users', '0005_delete_userprofile'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='user',
- name='stakeholder',
- field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='organisations.Stakeholder'),
- ),
- ]
diff --git a/pytest.ini b/pytest.ini
index c0401a1..2d70732 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,6 +1,7 @@
[pytest]
addopts =
- --ds=config.settings.test --disable-warnings --nomigrations --reuse-db
+# --ds=config.settings.test --disable-warnings --nomigrations --reuse-db
+ --ds=config.settings.test --disable-warnings --nomigrations
filterwarnings =
ignore::DeprecationWarning
python_files = tests.py test_*.py
diff --git a/utility/drop_and_recreate.sh b/utility/drop_and_recreate.sh
new file mode 100755
index 0000000..89734f4
--- /dev/null
+++ b/utility/drop_and_recreate.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+echo "Ensure this is run from project root."
+echo "Dropping database..."
+sudo -u postgres psql -f utility/drop_and_recreate.sql
+echo "Done."
+
diff --git a/utility/drop_and_recreate.sql b/utility/drop_and_recreate.sql
new file mode 100644
index 0000000..4e03e74
--- /dev/null
+++ b/utility/drop_and_recreate.sql
@@ -0,0 +1,2 @@
+DROP DATABASE ctrack;
+CREATE DATABASE ctrack;
diff --git a/utility/remove_migrations.sh b/utility/remove_migrations.sh
new file mode 100755
index 0000000..8e3d6d1
--- /dev/null
+++ b/utility/remove_migrations.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+echo "Removing migrations from all migrations directories, except in .venv and contrib..."
+
+find . -path "*/migrations/*.py" -not -name "__init__.py" -not -path "./contrib/*" -not -path "./.venv/*" -delete
+find . -path "*/migrations/*.pyc" -not -path "./contrib/*" -not -path "./.venv/*" -delete
+find . -path "*/migrations/__pycache__" -not -path "./contrib/*" -not -path "./.venv/*" -delete