feat(itim): Ports for service management

!43 #69
This commit is contained in:
2024-07-21 06:12:53 +09:30
parent 73d875c4ac
commit 5914782252
12 changed files with 766 additions and 4 deletions

View File

@ -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

View File

@ -0,0 +1,196 @@
{% extends 'base.html.j2' %}
{% load markdown %}
{% block content %}
<script>
function openCity(evt, cityName) {
var i, tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(cityName).style.display = "block";
evt.currentTarget.className += " active";
}
</script>
<style>
.detail-view-field {
display: unset;
height: 30px;
line-height: 30px;
padding: 0px 20px 40px 20px;
}
.detail-view-field label {
display: inline-block;
font-weight: bold;
width: 200px;
margin: 10px;
height: 30px;
line-height: 30px;
}
.detail-view-field span {
display: inline-block;
width: 340px;
margin: 10px;
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
}
pre {
word-wrap: break-word;
white-space: pre-wrap;
}
</style>
<div class="tab">
<button onclick="window.location='{% url 'Settings:_ports' %}';"
style="vertical-align: middle; padding: auto; margin: 0px">
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"
style="vertical-align: middle; margin: 0px; padding: 0px border: none; " fill="#6a6e73">
<path d="m313-480 155 156q11 11 11.5 27.5T468-268q-11 11-28 11t-28-11L228-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T468-692q11 11 11 28t-11 28L313-480Zm264 0 155 156q11 11 11.5 27.5T732-268q-11 11-28 11t-28-11L492-452q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l184-184q11-11 27.5-11.5T732-692q11 11 11 28t-11 28L577-480Z" />
</svg>Back to Ports</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button class="tablinks" onclick="openCity(event, 'Services')">Services</button>
{% if perms.assistance.change_service %}
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
{% endif %}
</div>
<form method="post">
<div id="Details" class="tabcontent">
<h3>Details</h3>
{% csrf_token %}
<div style="align-items:flex-start; align-content: center; display: flexbox; width: 100%">
<div style="display: inline; width: 40%; margin: 30px;">
<div class="detail-view-field">
<label>{{ form.number.label }}</label>
<span>{{ form.number.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.description.label }}</label>
<span>
{% if form.description.value %}
{{ form.description.value }}
{% else %}
&nbsp;
{% endif %}
</span>
</div>
<div class="detail-view-field">
<label>{{ form.protocol.label }}</label>
<span>{{ form.protocol.value }}</span>
</div>
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>{{ item.organization }}</span>
</div>
<div class="detail-view-field">
<label>Created</label>
<span>{{ item.created }}</span>
</div>
<div class="detail-view-field">
<label>Modified</label>
<span>{{ item.modified }}</span>
</div>
</div>
<div style="display: inline; width: 40%; margin: 30px; text-align: left;">
<div>
<label
style="font-weight: bold; width: 100%; border-bottom: 1px solid #ccc; display: block; text-align: inherit;">{{ form.model_notes.label }}</label>
<div style="display: inline-block; text-align: left;">
{% if form.model_notes.value %}
{{ form.model_notes.value | markdown | safe }}
{% else %}
&nbsp;
{% endif %}
</div>
</div>
</div>
</div>
<input type="button" value="Edit" onclick="window.location='{% url 'Settings:_port_change' item.pk %}';">
<br>
<script>
document.getElementById("defaultOpen").click();
</script>
</div>
<div id="Services" class="tabcontent">
<h3>
Services
</h3>
<table>
<tr>
<th>Name</th>
<th>Organization</th>
</tr>
{% for service in services %}
<tr>
<td><a href="{% url 'ITIM:_service_view' service.pk %}">{{ service.name }}</a></td>
<td>{{ service.organization }}</td>
</tr>
{% endfor%}
</table>
</div>
</div>
{% if perms.assistance.change_knowledgebase %}
<div id="Notes" class="tabcontent">
<h3>
Notes
</h3>
{{ notes_form }}
<input type="submit" name="{{ notes_form.prefix }}" value="Submit" />
<div class="comments">
{% if notes %}
{% for note in notes %}
{% include 'note.html.j2' %}
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
</form>
{% endblock %}

View File

@ -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,
)

View File

@ -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,
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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'

View File

@ -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'],))

View File

@ -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/<int:pk>/edit", ports.Change.as_view(), name="_port_change"),
path("port/<int:pk>/delete", ports.Delete.as_view(), name="_port_delete"),
path("port/<int:pk>", ports.View.as_view(), name="_port_view"),
]

View File

@ -12,3 +12,5 @@ IT Infrastructure Management (ITIM) is a crucial area of IT Service Management (
## ITIM Components
- [Services](./service.md)
- [Ports](./port.md)

View File

@ -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

View File

@ -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: