From 0b220424bbefb9c889a6751af92c253c266b7793 Mon Sep 17 00:00:00 2001 From: Jon Date: Sun, 21 Jul 2024 06:12:53 +0930 Subject: [PATCH] feat(itim): Ports for service management !43 #69 --- app/core/views/history.py | 6 + app/itim/templates/itim/port.html.j2 | 196 ++++++++++++++++++ app/itim/tests/unit/port/test_port.py | 42 ++++ .../tests/unit/port/test_port_core_history.py | 78 +++++++ .../unit/port/test_port_history_permission.py | 95 +++++++++ .../tests/unit/port/test_port_permission.py | 189 +++++++++++++++++ app/itim/tests/unit/port/test_port_views.py | 29 +++ app/itim/views/ports.py | 115 +++++++++- app/settings/urls.py | 4 + .../projects/centurion_erp/user/itim/index.md | 2 + docs/projects/centurion_erp/user/itim/port.md | 12 ++ mkdocs.yml | 2 + 12 files changed, 766 insertions(+), 4 deletions(-) create mode 100644 app/itim/templates/itim/port.html.j2 create mode 100644 app/itim/tests/unit/port/test_port.py create mode 100644 app/itim/tests/unit/port/test_port_core_history.py create mode 100644 app/itim/tests/unit/port/test_port_history_permission.py create mode 100644 app/itim/tests/unit/port/test_port_permission.py create mode 100644 app/itim/tests/unit/port/test_port_views.py create mode 100644 docs/projects/centurion_erp/user/itim/port.md diff --git a/app/core/views/history.py b/app/core/views/history.py index 6b3698f6..67a14dc4 100644 --- a/app/core/views/history.py +++ b/app/core/views/history.py @@ -99,6 +99,12 @@ class View(OrganizationPermission, generic.View): self.model = Organization + case 'port': + + from itim.models.services import Port + + self.model = Port + case 'team': self.model = Team diff --git a/app/itim/templates/itim/port.html.j2 b/app/itim/templates/itim/port.html.j2 new file mode 100644 index 00000000..02a0db7c --- /dev/null +++ b/app/itim/templates/itim/port.html.j2 @@ -0,0 +1,196 @@ +{% extends 'base.html.j2' %} + +{% load markdown %} + +{% block content %} + + + + +
+ + + + + {% if perms.assistance.change_service %} + + {% endif %} +
+ +
+
+

Details

+ + {% csrf_token %} + + +
+ +
+ +
+ + {{ form.number.value }} +
+ +
+ + + {% if form.description.value %} + {{ form.description.value }} + {% else %} +   + {% endif %} + +
+ +
+ + {{ form.protocol.value }} +
+ +
+ + {{ item.organization }} +
+ +
+ + {{ item.created }} +
+ +
+ + {{ item.modified }} +
+ + +
+ +
+
+ + +
+ {% if form.model_notes.value %} + {{ form.model_notes.value | markdown | safe }} + {% else %} +   + {% endif %} +
+
+
+ +
+ + + + +
+ + + +
+ +
+

+ Services +

+ + + + + + + {% for service in services %} + + + + + {% endfor%} +
NameOrganization
{{ service.name }}{{ service.organization }}
+
+ + + + {% if perms.assistance.change_knowledgebase %} +
+

+ Notes +

+ {{ notes_form }} + +
+ {% if notes %} + {% for note in notes %} + {% include 'note.html.j2' %} + {% endfor %} + {% endif %} +
+ +
+ {% endif %} + +
+ +{% endblock %} \ No newline at end of file diff --git a/app/itim/tests/unit/port/test_port.py b/app/itim/tests/unit/port/test_port.py new file mode 100644 index 00000000..b787dda0 --- /dev/null +++ b/app/itim/tests/unit/port/test_port.py @@ -0,0 +1,42 @@ +import pytest +import unittest + +from django.test import TestCase + +from access.models import Organization + +from app.tests.abstract.models import TenancyModel + +from itim.models.services import Port + + + +@pytest.mark.django_db +class PortModel( + TestCase, + TenancyModel +): + + model = Port + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + 2. Create an item + + """ + + self.organization = Organization.objects.create(name='test_org') + + + self.item = self.model.objects.create( + organization = self.organization, + number = 1, + ) + + self.second_item = self.model.objects.create( + organization = self.organization, + number = 2, + ) diff --git a/app/itim/tests/unit/port/test_port_core_history.py b/app/itim/tests/unit/port/test_port_core_history.py new file mode 100644 index 00000000..29109f3c --- /dev/null +++ b/app/itim/tests/unit/port/test_port_core_history.py @@ -0,0 +1,78 @@ + +import pytest +import unittest +import requests + +from django.test import TestCase, Client + +from access.models import Organization + +from core.models.history import History +from core.tests.abstract.history_entry import HistoryEntry +from core.tests.abstract.history_entry_parent_model import HistoryEntryParentItem + +from itim.models.services import Port + + + +class PortHistory(TestCase, HistoryEntry, HistoryEntryParentItem): + + + model = Port + + + @classmethod + def setUpTestData(self): + """ Setup Test """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + self.item_parent = self.model.objects.create( + number = 1, + organization = self.organization + ) + + self.item_create = self.model.objects.create( + number = 2, + organization = self.organization, + ) + + + self.history_create = History.objects.get( + action = History.Actions.ADD[0], + item_pk = self.item_create.pk, + item_class = self.model._meta.model_name, + ) + + self.item_change = self.item_create + self.item_change.number = 3 + self.item_change.save() + + self.field_after_expected_value = '{"number": ' + str(self.item_change.number) + '}' + + self.history_change = History.objects.get( + action = History.Actions.UPDATE[0], + item_pk = self.item_change.pk, + item_class = self.model._meta.model_name, + ) + + self.item_delete = self.model.objects.create( + number = 4, + organization = self.organization, + ) + + self.deleted_pk = self.item_delete.pk + + self.item_delete.delete() + + self.history_delete = History.objects.filter( + item_pk = self.deleted_pk, + item_class = self.model._meta.model_name, + ) + + self.history_delete_children = History.objects.filter( + item_parent_pk = self.deleted_pk, + item_parent_class = self.item_parent._meta.model_name, + ) diff --git a/app/itim/tests/unit/port/test_port_history_permission.py b/app/itim/tests/unit/port/test_port_history_permission.py new file mode 100644 index 00000000..4b786dc2 --- /dev/null +++ b/app/itim/tests/unit/port/test_port_history_permission.py @@ -0,0 +1,95 @@ +# from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser, User +from django.contrib.contenttypes.models import ContentType +from django.shortcuts import reverse +from django.test import TestCase, Client + +import pytest +import unittest +import requests + +from access.models import Organization, Team, TeamUsers, Permission + +from itim.models.services import Port + +from core.tests.abstract.history_permissions import HistoryPermissions + + + +class PortHistoryPermissions(TestCase, HistoryPermissions): + + + item_model = Port + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + 2. create an organization that is different to item + 3. Create a device + 4. Add history device history entry as item + 5. create a user + 6. create user in different organization (with the required permission) + """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + different_organization = Organization.objects.create(name='test_different_organization') + + self.item = self.item_model.objects.create( + organization=organization, + number = 1 + ) + + self.history = self.model.objects.get( + item_pk = self.item.id, + item_class = self.item._meta.model_name, + action = self.model.Actions.ADD, + ) + + view_permissions = Permission.objects.get( + codename = 'view_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + view_team = Team.objects.create( + team_name = 'view_team', + organization = organization, + ) + + view_team.permissions.set([view_permissions]) + + + self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") + + + self.view_user = User.objects.create_user(username="test_user_view", password="password") + teamuser = TeamUsers.objects.create( + team = view_team, + user = self.view_user + ) + + self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password") + + + different_organization_team = Team.objects.create( + team_name = 'different_organization_team', + organization = different_organization, + ) + + different_organization_team.permissions.set([ + view_permissions, + ]) + + TeamUsers.objects.create( + team = different_organization_team, + user = self.different_organization_user + ) diff --git a/app/itim/tests/unit/port/test_port_permission.py b/app/itim/tests/unit/port/test_port_permission.py new file mode 100644 index 00000000..64b0bcd6 --- /dev/null +++ b/app/itim/tests/unit/port/test_port_permission.py @@ -0,0 +1,189 @@ +# from django.conf import settings +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser, User +from django.contrib.contenttypes.models import ContentType +from django.shortcuts import reverse +from django.test import TestCase, Client + +import pytest +import unittest +import requests + +from access.models import Organization, Team, TeamUsers, Permission + +from app.tests.abstract.model_permissions import ModelPermissions + +from itim.models.services import Port + + +class PortPermissions(TestCase, ModelPermissions): + + + model = Port + + app_namespace = 'Settings' + + url_name_view = '_port_view' + + url_name_add = '_port_add' + + url_name_change = '_port_change' + + url_name_delete = '_port_delete' + + url_delete_response = reverse('Settings:_ports') + + + @classmethod + def setUpTestData(self): + """Setup Test + + 1. Create an organization for user and item + . create an organization that is different to item + 2. Create a device + 3. create teams with each permission: view, add, change, delete + 4. create a user per team + """ + + organization = Organization.objects.create(name='test_org') + + self.organization = organization + + different_organization = Organization.objects.create(name='test_different_organization') + + + self.item = self.model.objects.create( + organization=organization, + number = 1 + ) + + + self.url_view_kwargs = {'pk': self.item.id} + + # self.url_add_kwargs = {'pk': self.item.id} + + self.add_data = {'device': 'device', 'organization': self.organization.id} + + self.url_change_kwargs = {'pk': self.item.id} + + self.change_data = {'device': 'device', 'organization': self.organization.id} + + self.url_delete_kwargs = {'pk': self.item.id} + + self.delete_data = {'device': 'device', 'organization': self.organization.id} + + + view_permissions = Permission.objects.get( + codename = 'view_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + view_team = Team.objects.create( + team_name = 'view_team', + organization = organization, + ) + + view_team.permissions.set([view_permissions]) + + + + add_permissions = Permission.objects.get( + codename = 'add_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + add_team = Team.objects.create( + team_name = 'add_team', + organization = organization, + ) + + add_team.permissions.set([add_permissions]) + + + + change_permissions = Permission.objects.get( + codename = 'change_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + change_team = Team.objects.create( + team_name = 'change_team', + organization = organization, + ) + + change_team.permissions.set([change_permissions]) + + + + delete_permissions = Permission.objects.get( + codename = 'delete_' + self.model._meta.model_name, + content_type = ContentType.objects.get( + app_label = self.model._meta.app_label, + model = self.model._meta.model_name, + ) + ) + + delete_team = Team.objects.create( + team_name = 'delete_team', + organization = organization, + ) + + delete_team.permissions.set([delete_permissions]) + + + self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password") + + + self.view_user = User.objects.create_user(username="test_user_view", password="password") + teamuser = TeamUsers.objects.create( + team = view_team, + user = self.view_user + ) + + self.add_user = User.objects.create_user(username="test_user_add", password="password") + teamuser = TeamUsers.objects.create( + team = add_team, + user = self.add_user + ) + + self.change_user = User.objects.create_user(username="test_user_change", password="password") + teamuser = TeamUsers.objects.create( + team = change_team, + user = self.change_user + ) + + self.delete_user = User.objects.create_user(username="test_user_delete", password="password") + teamuser = TeamUsers.objects.create( + team = delete_team, + user = self.delete_user + ) + + + self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password") + + + different_organization_team = Team.objects.create( + team_name = 'different_organization_team', + organization = different_organization, + ) + + different_organization_team.permissions.set([ + view_permissions, + add_permissions, + change_permissions, + delete_permissions, + ]) + + TeamUsers.objects.create( + team = different_organization_team, + user = self.different_organization_user + ) diff --git a/app/itim/tests/unit/port/test_port_views.py b/app/itim/tests/unit/port/test_port_views.py new file mode 100644 index 00000000..9776ecc0 --- /dev/null +++ b/app/itim/tests/unit/port/test_port_views.py @@ -0,0 +1,29 @@ +import pytest +import unittest +import requests + +from django.test import TestCase + +from app.tests.abstract.models import PrimaryModel + + + +class ServiceViews( + TestCase, + PrimaryModel +): + + add_module = 'itim.views.ports' + add_view = 'Add' + + change_module = add_module + change_view = 'Change' + + delete_module = add_module + delete_view = 'Delete' + + display_module = add_module + display_view = 'View' + + index_module = add_module + index_view = 'Index' diff --git a/app/itim/views/ports.py b/app/itim/views/ports.py index d3e3a84b..89a8cb86 100644 --- a/app/itim/views/ports.py +++ b/app/itim/views/ports.py @@ -7,7 +7,7 @@ from core.models.notes import Notes from core.views.common import AddView, ChangeView, DeleteView, IndexView from itim.forms.ports import PortForm -from itim.models.services import Port +from itim.models.services import Port, Service from settings.models.user_settings import UserSettings @@ -20,7 +20,7 @@ class Add(AddView): model = Port permission_required = [ - 'itam.add_service', + 'itim.add_port', ] @@ -49,12 +49,64 @@ class Add(AddView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['content_title'] = 'New Group' + context['content_title'] = 'New Port' return context +class Change(ChangeView): + + context_object_name = "item" + + form_class = PortForm + + model = Port + + permission_required = [ + 'itim.change_port', + ] + + + def get_context_data(self, **kwargs): + + context = super().get_context_data(**kwargs) + + context['content_title'] = str(self.object) + + return context + + + def get_success_url(self, **kwargs): + + return reverse('Settings:_port_view', args=(self.kwargs['pk'],)) + + + +class Delete(DeleteView): + + model = Port + + permission_required = [ + 'itim.delete_port', + ] + + + def get_context_data(self, **kwargs): + + context = super().get_context_data(**kwargs) + + context['content_title'] = 'Delete ' + str(self.object) + + return context + + + def get_success_url(self, **kwargs): + + return reverse('Settings:_ports') + + + class Index(IndexView): context_object_name = "items" @@ -64,7 +116,7 @@ class Index(IndexView): paginate_by = 10 permission_required = [ - 'assistance.view_service' + 'itim.view_port' ] template_name = 'itim/port_index.html.j2' @@ -79,3 +131,58 @@ class Index(IndexView): context['content_title'] = 'Ports' return context + + + +class View(ChangeView): + + context_object_name = "item" + + form_class = PortForm + + model = Port + + permission_required = [ + 'itim.view_port', + ] + + template_name = 'itim/port.html.j2' + + + def get_context_data(self, **kwargs): + + context = super().get_context_data(**kwargs) + + context['services'] = Service.objects.filter(port=self.kwargs['pk']).order_by('name', 'organization') + + context['notes_form'] = AddNoteForm(prefix='note') + context['notes'] = Notes.objects.filter(config_group=self.kwargs['pk']) + + context['model_pk'] = self.kwargs['pk'] + context['model_name'] = self.model._meta.model_name + + context['model_delete_url'] = reverse('Settings:_port_delete', args=(self.kwargs['pk'],)) + + + context['content_title'] = self.object + + return context + + + @method_decorator(auth_decorator.permission_required("itim.change_service", raise_exception=True)) + def post(self, request, *args, **kwargs): + + item = Port.objects.get(pk=self.kwargs['pk']) + + notes = AddNoteForm(request.POST, prefix='note') + + if notes.is_bound and notes.is_valid() and notes.instance.note != '': + + notes.instance.organization = item.organization + + notes.save() + + + def get_success_url(self, **kwargs): + + return reverse('Settings:_port_view', args=(self.kwargs['pk'],)) diff --git a/app/settings/urls.py b/app/settings/urls.py index 4e3c6db1..6d6f0525 100644 --- a/app/settings/urls.py +++ b/app/settings/urls.py @@ -55,4 +55,8 @@ urlpatterns = [ path("ports", ports.Index.as_view(), name="_ports"), path("port/add", ports.Add.as_view(), name="_port_add"), + path("port//edit", ports.Change.as_view(), name="_port_change"), + path("port//delete", ports.Delete.as_view(), name="_port_delete"), + path("port/", ports.View.as_view(), name="_port_view"), + ] diff --git a/docs/projects/centurion_erp/user/itim/index.md b/docs/projects/centurion_erp/user/itim/index.md index d67a58c4..387bce3b 100644 --- a/docs/projects/centurion_erp/user/itim/index.md +++ b/docs/projects/centurion_erp/user/itim/index.md @@ -12,3 +12,5 @@ IT Infrastructure Management (ITIM) is a crucial area of IT Service Management ( ## ITIM Components - [Services](./service.md) + +- [Ports](./port.md) diff --git a/docs/projects/centurion_erp/user/itim/port.md b/docs/projects/centurion_erp/user/itim/port.md new file mode 100644 index 00000000..fcf3ed93 --- /dev/null +++ b/docs/projects/centurion_erp/user/itim/port.md @@ -0,0 +1,12 @@ +--- +title: Ports +description: Ports as part of Service Management Documentation for Centurion ERP by No Fuss Computing +date: 2024-07-21 +template: project.html +about: https://gitlab.com/nofusscomputing/infrastructure/configuration-management/centurion_erp +--- + +This component within ITIM is an extension of Service Management, in particular the what in relation to the Layer 4 for the OSI layer for a [service](./service.md). + + +## Ports diff --git a/mkdocs.yml b/mkdocs.yml index d8799753..d8c44f1c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -200,6 +200,8 @@ nav: - projects/centurion_erp/user/itim/index.md + - projects/centurion_erp/user/itim/port.md + - projects/centurion_erp/user/itim/service.md - Settings: