From f7db25af61392e41c06409ebb8b0929bfd6b53a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 05:26:52 +1100 Subject: [PATCH 1/7] Several minor improvement Fix naming or type, add control level and control to control reference. --- conformity/admin.py | 26 +++++++++++-------- conformity/forms.py | 2 +- .../0038_action_reference_control_level.py | 23 ++++++++++++++++ conformity/migrations/0039_control_control.py | 18 +++++++++++++ conformity/models.py | 26 ++++++++++++++++--- .../templates/conformity/action_form.html | 1 + .../templates/conformity/action_list.html | 6 +++++ .../conformity/conformity_orgpol_list.html | 6 ++--- .../conformity/controlpoint_form.html | 16 ++++++++++++ .../conformity/controlpoint_list.html | 2 +- 10 files changed, 106 insertions(+), 20 deletions(-) create mode 100644 conformity/migrations/0038_action_reference_control_level.py create mode 100644 conformity/migrations/0039_control_control.py diff --git a/conformity/admin.py b/conformity/admin.py index cce1e62..8196b68 100644 --- a/conformity/admin.py +++ b/conformity/admin.py @@ -5,58 +5,62 @@ from django.contrib import admin from import_export import resources from import_export.admin import ImportExportModelAdmin -from .models import Organization, Policy, Measure, Conformity, Audit, Finding, Action +from .models import Organization, Policy, Measure, Conformity, Audit, Finding, Action, Control, ControlPoint + -# Organization class OrganizationResources(resources.ModelResource): class Meta: model = Organization + class OrganizationAdmin(ImportExportModelAdmin): ressource_class = Organization -admin.site.register(Organization, OrganizationAdmin) -# Policy class PolicyResources(resources.ModelResource): class Meta: model = Policy + class PolicyAdmin(ImportExportModelAdmin): ressource_class = Policy -admin.site.register(Policy, PolicyAdmin) -# Measure class MeasureResources(resources.ModelResource): class Meta: model = Measure + class MeasureAdmin(ImportExportModelAdmin): ressource_class = Measure -admin.site.register(Measure, MeasureAdmin) -# Conformity class ConformityResources(resources.ModelResource): class Meta: model = Conformity + class ConformityAdmin(ImportExportModelAdmin): ressource_class = Conformity -admin.site.register(Conformity, ConformityAdmin) # Action class ActionResources(resources.ModelResource): class Meta: model = Action + class ActionAdmin(ImportExportModelAdmin): ressource_class = Action + +# Registration +admin.site.register(Policy, PolicyAdmin) +admin.site.register(Measure, MeasureAdmin) +admin.site.register(Conformity, ConformityAdmin) admin.site.register(Action, ActionAdmin) -### admin.site.register(Audit) admin.site.register(Finding) - +admin.site.register(Organization, OrganizationAdmin) +admin.site.register(Control) +admin.site.register(ControlPoint) diff --git a/conformity/forms.py b/conformity/forms.py index 3b164ef..837e5df 100644 --- a/conformity/forms.py +++ b/conformity/forms.py @@ -50,7 +50,7 @@ def __init__(self, *args, **kwargs): self.fields['create_date'].disabled = True self.fields['update_date'].disabled = True - generic_fields = ['title', 'owner', 'status', 'status_comment'] + generic_fields = ['title', 'owner', 'status', 'status_comment', 'reference'] analyse_fields = ['organization', 'associated_conformity', 'associated_findings', 'associated_controlPoints', 'description'] plan_fields = ['plan_start_date', 'plan_end_date', 'plan_comment'] implement_fields = ['implement_start_date', 'implement_end_date', 'implement_status', 'implement_comment'] diff --git a/conformity/migrations/0038_action_reference_control_level.py b/conformity/migrations/0038_action_reference_control_level.py new file mode 100644 index 0000000..9aecbdb --- /dev/null +++ b/conformity/migrations/0038_action_reference_control_level.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.6 on 2023-03-11 22:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('conformity', '0037_alter_controlpoint_control_date'), + ] + + operations = [ + migrations.AddField( + model_name='action', + name='reference', + field=models.URLField(blank=True), + ), + migrations.AddField( + model_name='control', + name='level', + field=models.IntegerField(choices=[(1, '1st level control'), (2, '2nd level control')], default=1), + ), + ] diff --git a/conformity/migrations/0039_control_control.py b/conformity/migrations/0039_control_control.py new file mode 100644 index 0000000..d30340d --- /dev/null +++ b/conformity/migrations/0039_control_control.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.6 on 2023-03-14 01:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('conformity', '0038_action_reference_control_level'), + ] + + operations = [ + migrations.AddField( + model_name='control', + name='control', + field=models.ManyToManyField(blank=True, to='conformity.control'), + ), + ] diff --git a/conformity/models.py b/conformity/models.py index 64b9505..9b67461 100644 --- a/conformity/models.py +++ b/conformity/models.py @@ -243,6 +243,7 @@ def update(self): # Callback functions + @receiver(pre_save, sender=Measure) def post_init_callback(instance, **kwargs): """This function keep hierarchy of the Measure working on each Measure instantiation""" @@ -376,10 +377,9 @@ def get_severity(self): """return the readable version of the Findings Severity""" return self.Severity(self.severity).label - @staticmethod - def get_absolute_url(): - """return the absolute URL for Forms, could probably do better""" - return reverse('conformity:audit_index') + def get_absolute_url(self): + """"return somewhere else when a edit has work """ + return reverse('conformity:audit_detail', kwargs={'pk': self.audit_id}) def get_action(self): """Return the list of Action associated with this Findings""" @@ -399,14 +399,27 @@ class Frequency(models.IntegerChoices): BIMONTHLY = '6', _('Bimonthly') MONTHLY = '12', _('Monthly') + class Level(models.IntegerChoices): + """ List of control level possible for a control """ + FIRST = '1', _('1st level control') + SECOND = '2', _('2nd level control') + title = models.CharField(max_length=256) description = models.TextField(max_length=4096, blank=True) organization = models.ForeignKey(Organization, on_delete=models.CASCADE, blank=True, null=True) conformity = models.ManyToManyField(Conformity, blank=True) + control = models.ManyToManyField('self', blank=True) frequency = models.IntegerField( choices=Frequency.choices, default=Frequency.YEARLY, ) + level = models.IntegerField( + choices=Level.choices, + default=Level.FIRST, + ) + + def __str__(self): + return "[" + str(self.organization) + "] " + self.title @staticmethod def get_absolute_url(): @@ -475,6 +488,10 @@ def __str__(self): + self.period_end_date.strftime('%b-%Y') + ")" + def get_action(self): + """Return the list of Action associated with this Findings""" + return Action.objects.filter(associated_controlPoints=self.id) + class Action(models.Model): """ Action class represent the actions taken by the Organization to improve security. @@ -502,6 +519,7 @@ class Status(models.TextChoices): default=Status.ANALYSING, ) status_comment = models.TextField(max_length=4096, blank=True) + reference = models.URLField(blank=True) active = models.BooleanField(default=True) ' Analyse Phase' diff --git a/conformity/templates/conformity/action_form.html b/conformity/templates/conformity/action_form.html index 226d563..b239670 100644 --- a/conformity/templates/conformity/action_form.html +++ b/conformity/templates/conformity/action_form.html @@ -13,6 +13,7 @@

Edit of {% bootstrap_field form.title layout='floating' %} {% bootstrap_field form.owner layout='floating' %} {% bootstrap_field form.status layout='floating' %} + {% bootstrap_field form.reference layout='floating' %} {% bootstrap_field form.status_comment layout='floating' %}

Created on {{ form.create_date.value }}, last update {{ form.update_date.value }}.

diff --git a/conformity/templates/conformity/action_list.html b/conformity/templates/conformity/action_list.html index 65c19e6..a4cca71 100644 --- a/conformity/templates/conformity/action_list.html +++ b/conformity/templates/conformity/action_list.html @@ -14,6 +14,7 @@

Actions

Owner Status Last Update + Ref. Edit @@ -56,6 +57,11 @@

Actions

{{ action.update_date | date:'d-M-Y'}} + + {% if action.reference %} + + {% endif %} + diff --git a/conformity/templates/conformity/conformity_orgpol_list.html b/conformity/templates/conformity/conformity_orgpol_list.html index d40a6ee..d449054 100644 --- a/conformity/templates/conformity/conformity_orgpol_list.html +++ b/conformity/templates/conformity/conformity_orgpol_list.html @@ -51,7 +51,7 @@

{% if con.comment %} - + {% endif %} @@ -86,7 +86,7 @@

{% if con.comment %} - + {% endif %} @@ -123,7 +123,7 @@

{% if con.comment %} - + {% endif %} diff --git a/conformity/templates/conformity/controlpoint_form.html b/conformity/templates/conformity/controlpoint_form.html index 2ba076d..84fb5a1 100644 --- a/conformity/templates/conformity/controlpoint_form.html +++ b/conformity/templates/conformity/controlpoint_form.html @@ -19,7 +19,23 @@

{{ controlpoint.control.title }}


{% bootstrap_form form %} +

Associated actions

+
+ {% for action in controlpoint.get_action %} + + {{action}} {{ action.get_status_display }} + + {% empty %} +

No action associated

+ {% endfor %} + + Register a corrective action + +
+
+
{% bootstrap_button button_type="submit" content="Save" %} +
diff --git a/conformity/templates/conformity/controlpoint_list.html b/conformity/templates/conformity/controlpoint_list.html index 557324f..7c28d04 100644 --- a/conformity/templates/conformity/controlpoint_list.html +++ b/conformity/templates/conformity/controlpoint_list.html @@ -77,7 +77,7 @@

Control


- + From 1e92c716e3d2f9a8c20b0efe348be72f16c2106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 07:55:45 +1100 Subject: [PATCH 2/7] Implement filter and add link between conformity, actions and controls. --- conformity/filterset.py | 15 ++++++++++ conformity/models.py | 4 +++ .../templates/conformity/action_list.html | 2 +- .../conformity/conformity_orgpol_list.html | 30 +++++++++++++++++-- .../conformity/controlpoint_list.html | 4 +-- conformity/views.py | 20 ++++++------- oxomium/settings.py | 1 + requirements.txt | 1 + 8 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 conformity/filterset.py diff --git a/conformity/filterset.py b/conformity/filterset.py new file mode 100644 index 0000000..c5da980 --- /dev/null +++ b/conformity/filterset.py @@ -0,0 +1,15 @@ +from django_filters import FilterSet, CharFilter +from .models import Action, ControlPoint + + +class ActionFilter(FilterSet): + class Meta: + model = Action + fields = ['status', 'organization', 'owner', 'associated_conformity__id', 'associated_findings__id', + 'associated_controlPoints__id'] + + +class ControlFilter(FilterSet): + class Meta: + model = ControlPoint + fields = ['control__organization', 'control__conformity__id', 'control__control__id', 'control__frequency', 'control__level'] diff --git a/conformity/models.py b/conformity/models.py index 9b67461..b9c76df 100644 --- a/conformity/models.py +++ b/conformity/models.py @@ -211,6 +211,10 @@ def get_action(self): """Return the list of Action associated with this Conformity""" return Action.objects.filter(associated_conformity=self.id).filter(active=True) + def get_control(self): + """Return the list of Control associated with this Conformity""" + return Control.objects.filter(conformity=self.id) + def set_status(self, i): """Update the status and call recursive update function""" self.status = i diff --git a/conformity/templates/conformity/action_list.html b/conformity/templates/conformity/action_list.html index a4cca71..f7c55e3 100644 --- a/conformity/templates/conformity/action_list.html +++ b/conformity/templates/conformity/action_list.html @@ -19,7 +19,7 @@

Actions

- {% for action in action_list %} + {% for action in object_list %} {{ action.title }} diff --git a/conformity/templates/conformity/conformity_orgpol_list.html b/conformity/templates/conformity/conformity_orgpol_list.html index d449054..9fbb378 100644 --- a/conformity/templates/conformity/conformity_orgpol_list.html +++ b/conformity/templates/conformity/conformity_orgpol_list.html @@ -15,6 +15,7 @@

Measure Status Owner + Controls Actions Comment Edit @@ -42,10 +43,17 @@

{{ con.responsible|capfirst }} {% endif %} + + {% if con.get_control %} + + + + {% endif %} + {% if con.get_action %} - {{ con.get_action | length }} + + {% endif %} @@ -79,9 +87,18 @@

{{ con.responsible|capfirst }} {% endif %} + + {% if con.get_control %} + + + + {% endif %} + {% if con.get_action %} + + {% endif %} @@ -116,9 +133,18 @@

{{ con.responsible|capfirst }} {% endif %} + + {% if con.get_control %} + + + + {% endif %} + {% if con.get_action %} + + {% endif %} diff --git a/conformity/templates/conformity/controlpoint_list.html b/conformity/templates/conformity/controlpoint_list.html index 7c28d04..f67fad2 100644 --- a/conformity/templates/conformity/controlpoint_list.html +++ b/conformity/templates/conformity/controlpoint_list.html @@ -72,9 +72,9 @@

Control

- No data to display + No data to display {% endfor %} - +
diff --git a/conformity/views.py b/conformity/views.py index 589a5f2..d7c7123 100644 --- a/conformity/views.py +++ b/conformity/views.py @@ -6,7 +6,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic import DetailView, ListView, TemplateView from django.views.generic.edit import UpdateView, CreateView, DeleteView +from django_filters.views import FilterView from auditlog.models import LogEntry + +from .filterset import ActionFilter, ControlFilter from .forms import ConformityForm, AuditForm, FindingForm, ActionForm, OrganizationForm, ControlForm, ControlPointForm from .models import Organization, Policy, Conformity, Audit, Action, Finding, Control, ControlPoint @@ -154,18 +157,11 @@ class ActionCreateView(LoginRequiredMixin, CreateView): form_class = ActionForm -class ActionIndexView(LoginRequiredMixin, ListView): +class ActionIndexView(LoginRequiredMixin, FilterView): model = Action ordering = ['status', '-update_date'] - - -class ActionIndexForConformityView(LoginRequiredMixin, ListView): - model = Action - ordering = ['status', '-update_date'] - template_name = 'conformity/action_list.html' - - def get_queryset(self, **kwargs): - return Action.objects.filter(associated_conformity=self.kwargs['con']).order_by('status').order_by('-update_date') + filterset_class = ActionFilter + template_name = "conformity/action_list.html" class ActionUpdateView(LoginRequiredMixin, UpdateView): @@ -183,8 +179,10 @@ class ControlCreateView(LoginRequiredMixin, CreateView): form_class = ControlForm -class ControlIndexView(LoginRequiredMixin, ListView): +class ControlIndexView(LoginRequiredMixin, FilterView): model = ControlPoint + filterset_class = ControlFilter + template_name = 'conformity/controlpoint_list.html' class ControlUpdateView(LoginRequiredMixin, UpdateView): diff --git a/oxomium/settings.py b/oxomium/settings.py index 5bdcf01..c800de8 100644 --- a/oxomium/settings.py +++ b/oxomium/settings.py @@ -42,6 +42,7 @@ # Application definition INSTALLED_APPS = [ + 'django_filters', 'auditlog', 'conformity.apps.ConformityConfig', 'import_export', diff --git a/requirements.txt b/requirements.txt index 87e749d..aa8a648 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ django-bootstrap5 django-import-export django-auditlog django-tinymce +django-filters \ No newline at end of file From cae457d1249cc2d838aa7bb43776343b93863ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 16:04:03 +1100 Subject: [PATCH 3/7] cleanup old "filter" --- conformity/urls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conformity/urls.py b/conformity/urls.py index bb671ca..6db9f9c 100644 --- a/conformity/urls.py +++ b/conformity/urls.py @@ -32,7 +32,6 @@ path('policy//', views.PolicyDetailView.as_view(), name='policy_detail'), path('action/', views.ActionIndexView.as_view(), name='action_index'), - path('action/index/conformity/', views.ActionIndexForConformityView.as_view(), name='action_con_index'), path('action/create', views.ActionCreateView.as_view(), name='action_create'), path('action/update/', views.ActionUpdateView.as_view(), name='action_form'), From 66aad50058a0259f287c873012c0eb9035b8aa54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 16:12:11 +1100 Subject: [PATCH 4/7] fix typo in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index aa8a648..bde76e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ django-bootstrap5 django-import-export django-auditlog django-tinymce -django-filters \ No newline at end of file +django-filter \ No newline at end of file From 705636b8d2761b952f673f95255eded5e1ac4009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 16:14:45 +1100 Subject: [PATCH 5/7] remove ActionIndexForConformityView from test --- conformity/tests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/conformity/tests.py b/conformity/tests.py index 9fda58f..bca136e 100644 --- a/conformity/tests.py +++ b/conformity/tests.py @@ -355,7 +355,6 @@ def setUp(self): ConformityUpdateView, ActionCreateView, ActionIndexView, - ActionIndexForConformityView, ActionUpdateView, AuditLogDetailView, # Form From 4ecd1e0919105e0ca7cd8bbe6562e80438af8a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Sat, 18 Mar 2023 16:28:55 +1100 Subject: [PATCH 6/7] Add level display --- .../migrations/0040_alter_control_level.py | 18 ++++++++++++++++++ conformity/models.py | 4 ++-- .../conformity/controlpoint_list.html | 11 +++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 conformity/migrations/0040_alter_control_level.py diff --git a/conformity/migrations/0040_alter_control_level.py b/conformity/migrations/0040_alter_control_level.py new file mode 100644 index 0000000..1ea5306 --- /dev/null +++ b/conformity/migrations/0040_alter_control_level.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.6 on 2023-03-18 05:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('conformity', '0039_control_control'), + ] + + operations = [ + migrations.AlterField( + model_name='control', + name='level', + field=models.IntegerField(choices=[(1, '1st level'), (2, '2nd level')], default=1), + ), + ] diff --git a/conformity/models.py b/conformity/models.py index b9c76df..a95a042 100644 --- a/conformity/models.py +++ b/conformity/models.py @@ -405,8 +405,8 @@ class Frequency(models.IntegerChoices): class Level(models.IntegerChoices): """ List of control level possible for a control """ - FIRST = '1', _('1st level control') - SECOND = '2', _('2nd level control') + FIRST = '1', _('1st level') + SECOND = '2', _('2nd level') title = models.CharField(max_length=256) description = models.TextField(max_length=4096, blank=True) diff --git a/conformity/templates/conformity/controlpoint_list.html b/conformity/templates/conformity/controlpoint_list.html index f67fad2..596ccf6 100644 --- a/conformity/templates/conformity/controlpoint_list.html +++ b/conformity/templates/conformity/controlpoint_list.html @@ -11,6 +11,7 @@

Control

Titre Organization + Level Frequency Start Date End Date @@ -28,6 +29,16 @@

Control

{{ cp.control.organization }} + + {% if cp.control.level == 1 %} + + {% endif %} + {% if cp.control.level == 2 %} + + {%endif %} + {{ cp.control.get_level_display }} + + {{ cp.control.get_frequency_display }} From ca2ba418de4aea458dea56dd034aeeb3e4ba74b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diacquenod?= Date: Mon, 1 Jan 2024 11:20:07 +1100 Subject: [PATCH 7/7] Add level display --- conformity/filterset.py | 2 +- .../templates/conformity/controlpoint_list.html | 15 +++++++++++---- conformity/templates/conformity/main.html | 1 + oxomium/settings.py | 1 + requirements.txt | 3 ++- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/conformity/filterset.py b/conformity/filterset.py index c5da980..e7d6673 100644 --- a/conformity/filterset.py +++ b/conformity/filterset.py @@ -12,4 +12,4 @@ class Meta: class ControlFilter(FilterSet): class Meta: model = ControlPoint - fields = ['control__organization', 'control__conformity__id', 'control__control__id', 'control__frequency', 'control__level'] + fields = ['control__level', 'control__organization', 'control__conformity__id', 'control__control__id', 'control__frequency', 'control__level'] diff --git a/conformity/templates/conformity/controlpoint_list.html b/conformity/templates/conformity/controlpoint_list.html index 596ccf6..f5d5bcf 100644 --- a/conformity/templates/conformity/controlpoint_list.html +++ b/conformity/templates/conformity/controlpoint_list.html @@ -1,10 +1,15 @@ {% extends "conformity/main.html" %} +{% load render_table from django_tables2 %} {% block header %}

Control

{% endblock %} {% block content %} +{% render_table object_list %} + +
+ @@ -30,14 +35,16 @@

Control

{{ cp.control.organization }}
List of all controls
+ {% if cp.control.level == 1 %} - + {% endif %} {% if cp.control.level == 2 %} - + {%endif %} - {{ cp.control.get_level_display }} - + {{ cp.control.get_level_display }} + + {{ cp.control.get_frequency_display }} diff --git a/conformity/templates/conformity/main.html b/conformity/templates/conformity/main.html index 16e6b75..9fb71ef 100644 --- a/conformity/templates/conformity/main.html +++ b/conformity/templates/conformity/main.html @@ -1,5 +1,6 @@ {% load django_bootstrap5 %} {% load static %} +{% load render_table from django_tables2 %} diff --git a/oxomium/settings.py b/oxomium/settings.py index c800de8..cb0962a 100644 --- a/oxomium/settings.py +++ b/oxomium/settings.py @@ -47,6 +47,7 @@ 'conformity.apps.ConformityConfig', 'import_export', 'django_bootstrap5', + 'django_tables2', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/requirements.txt b/requirements.txt index bde76e0..79e3a00 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ django-bootstrap5 django-import-export django-auditlog django-tinymce -django-filter \ No newline at end of file +django-filter +django-tables2 \ No newline at end of file