diff --git a/app/core/views/history.py b/app/core/views/history.py
index b983a7036..5f24a009d 100644
--- a/app/core/views/history.py
+++ b/app/core/views/history.py
@@ -53,6 +53,12 @@ def get_object(self):
self.model = Cluster
+ case 'clustertype':
+
+ from itim.models.clusters import ClusterType
+
+ self.model = ClusterType
+
case 'configgroups':
self.model = ConfigGroups
diff --git a/app/itim/forms/cluster_type.py b/app/itim/forms/cluster_type.py
new file mode 100644
index 000000000..7bf7ba759
--- /dev/null
+++ b/app/itim/forms/cluster_type.py
@@ -0,0 +1,84 @@
+from django import forms
+from django.forms import ValidationError
+from django.urls import reverse
+
+from itim.models.clusters import ClusterType
+
+from app import settings
+
+from core.forms.common import CommonModelForm
+
+
+
+class ClusterTypeForm(CommonModelForm):
+
+
+ class Meta:
+
+ fields = '__all__'
+
+ model = ClusterType
+
+ prefix = 'cluster_type'
+
+ def __init__(self, *args, **kwargs):
+
+ super().__init__(*args, **kwargs)
+
+
+
+class DetailForm(ClusterTypeForm):
+
+
+ tabs: dict = {
+ "details": {
+ "name": "Details",
+ "slug": "details",
+ "sections": [
+ {
+ "layout": "double",
+ "left": [
+ 'name',
+ 'organization',
+ 'c_created',
+ 'c_modified'
+ ],
+ "right": [
+ 'model_notes',
+ ]
+ },
+ ]
+ },
+ "notes": {
+ "name": "Notes",
+ "slug": "notes",
+ "sections": []
+ }
+ }
+
+
+ def __init__(self, *args, **kwargs):
+
+ super().__init__(*args, **kwargs)
+
+
+ self.fields['c_created'] = forms.DateTimeField(
+ label = 'Created',
+ input_formats=settings.DATETIME_FORMAT,
+ disabled = True,
+ initial = self.instance.created,
+ )
+
+ self.fields['c_modified'] = forms.DateTimeField(
+ label = 'Modified',
+ input_formats=settings.DATETIME_FORMAT,
+ disabled = True,
+ initial = self.instance.modified,
+ )
+
+ self.tabs['details'].update({
+ "edit_url": reverse('Settings:_cluster_type_change', args=(self.instance.pk,))
+ })
+
+ self.url_index_view = reverse('Settings:_cluster_types')
+
diff --git a/app/itim/migrations/0001_initial.py b/app/itim/migrations/0001_initial.py
index d85e6bce4..71baeb9c1 100644
--- a/app/itim/migrations/0001_initial.py
+++ b/app/itim/migrations/0001_initial.py
@@ -29,8 +29,8 @@ class Migration(migrations.Migration):
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
],
options={
- 'verbose_name': 'ClusterType',
- 'verbose_name_plural': 'ClusterTypes',
+ 'verbose_name': 'Cluster Type',
+ 'verbose_name_plural': 'Cluster Types',
'ordering': ['name'],
},
),
diff --git a/app/itim/migrations/0002_clustertype_created_clustertype_modified.py b/app/itim/migrations/0002_clustertype_created_clustertype_modified.py
new file mode 100644
index 000000000..c55a2d113
--- /dev/null
+++ b/app/itim/migrations/0002_clustertype_created_clustertype_modified.py
@@ -0,0 +1,25 @@
+# Generated by Django 5.0.7 on 2024-08-18 03:57
+
+import access.fields
+import django.utils.timezone
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('itim', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='clustertype',
+ name='created',
+ field=access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False),
+ ),
+ migrations.AddField(
+ model_name='clustertype',
+ name='modified',
+ field=access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False),
+ ),
+ ]
diff --git a/app/itim/models/clusters.py b/app/itim/models/clusters.py
index 31010e7dc..d003157fb 100644
--- a/app/itim/models/clusters.py
+++ b/app/itim/models/clusters.py
@@ -18,9 +18,9 @@ class Meta:
'name',
]
- verbose_name = "ClusterType"
+ verbose_name = "Cluster Type"
- verbose_name_plural = "ClusterTypes"
+ verbose_name_plural = "Cluster Types"
id = models.AutoField(
@@ -39,6 +39,15 @@ class Meta:
slug = AutoSlugField()
+ created = AutoCreatedField()
+
+ modified = AutoLastModifiedField()
+
+
+ def __str__(self):
+
+ return self.name
+
class Cluster(TenancyObject):
diff --git a/app/itim/templates/itim/cluster_type.html.j2 b/app/itim/templates/itim/cluster_type.html.j2
new file mode 100644
index 000000000..ad99f6eb0
--- /dev/null
+++ b/app/itim/templates/itim/cluster_type.html.j2
@@ -0,0 +1,22 @@
+{% extends 'detail.html.j2' %}
+
+{% load json %}
+{% load markdown %}
+
+
+{% block tabs %}
+
+
+
+ {% include 'content/section.html.j2' with tab=form.tabs.details %}
+
+
+
+
+
+
+ {% include 'content/section.html.j2' with tab=form.tabs.notes %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/itim/templates/itim/cluster_type_index.html.j2 b/app/itim/templates/itim/cluster_type_index.html.j2
new file mode 100644
index 000000000..cb8e38b21
--- /dev/null
+++ b/app/itim/templates/itim/cluster_type_index.html.j2
@@ -0,0 +1,47 @@
+{% extends 'base.html.j2' %}
+
+{% block content %}
+
+
+
+
+ Title |
+ |
+ Organization |
+ |
+
+ {% if items %}
+ {% for item in items %}
+
+ {{ item.name }} |
+ |
+ {{ item.organization }} |
+ |
+
+ {% endfor %}
+ {% else %}
+
+ Nothing Found |
+
+ {% endif %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/itim/views/cluster_types.py b/app/itim/views/cluster_types.py
new file mode 100644
index 000000000..55380d1e8
--- /dev/null
+++ b/app/itim/views/cluster_types.py
@@ -0,0 +1,185 @@
+from django.contrib.auth import decorators as auth_decorator
+from django.urls import reverse
+from django.utils.decorators import method_decorator
+
+from core.forms.comment import AddNoteForm
+from core.models.notes import Notes
+from core.views.common import AddView, ChangeView, DeleteView, IndexView
+
+from itim.forms.cluster_type import ClusterTypeForm, DetailForm
+from itim.models.clusters import ClusterType
+
+from settings.models.user_settings import UserSettings
+
+
+
+class Add(AddView):
+
+ form_class = ClusterTypeForm
+
+ model = ClusterType
+
+ permission_required = [
+ 'itim.add_clustertype',
+ ]
+
+
+ def get_initial(self):
+
+ initial: dict = {
+ 'organization': UserSettings.objects.get(user = self.request.user).default_organization
+ }
+
+ if 'pk' in self.kwargs:
+
+ if self.kwargs['pk']:
+
+ initial.update({'parent': self.kwargs['pk']})
+
+ self.model.parent.field.hidden = True
+
+ return initial
+
+
+ def get_success_url(self, **kwargs):
+
+ return reverse('Settings:_cluster_type_types')
+
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ context['content_title'] = 'New Cluster Type'
+
+ return context
+
+
+
+class Change(ChangeView):
+
+ form_class = ClusterTypeForm
+
+ model = ClusterType
+
+ permission_required = [
+ 'itim.change_clustertype',
+ ]
+
+
+ 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:_cluster_type_view', args=(self.kwargs['pk'],))
+
+
+
+class Delete(DeleteView):
+
+ model = ClusterType
+
+ permission_required = [
+ 'itim.delete_clustertype',
+ ]
+
+
+ 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:_cluster_types')
+
+
+
+class Index(IndexView):
+
+ context_object_name = "items"
+
+ model = ClusterType
+
+ paginate_by = 10
+
+ permission_required = [
+ 'itim.view_clustertype'
+ ]
+
+ template_name = 'itim/cluster_type_index.html.j2'
+
+
+ def get_context_data(self, **kwargs):
+
+ context = super().get_context_data(**kwargs)
+
+ context['model_docs_path'] = self.model._meta.app_label + '/' + self.model._meta.model_name
+
+ context['content_title'] = 'Cluster Types'
+
+ return context
+
+
+
+class View(ChangeView):
+
+ context_object_name = "item"
+
+ form_class = DetailForm
+
+ model = ClusterType
+
+ permission_required = [
+ 'itim.view_clustertype',
+ ]
+
+ template_name = 'itim/cluster_type.html.j2'
+
+
+ def get_context_data(self, **kwargs):
+
+ context = super().get_context_data(**kwargs)
+
+ context['notes_form'] = AddNoteForm(prefix='note')
+ context['notes'] = Notes.objects.filter(service=self.kwargs['pk'])
+
+ context['model_pk'] = self.kwargs['pk']
+ context['model_name'] = self.model._meta.model_name
+
+ context['model_delete_url'] = reverse('Settings:_cluster_type_delete', args=(self.kwargs['pk'],))
+
+
+ context['content_title'] = self.object.name
+
+ return context
+
+
+ def post(self, request, *args, **kwargs):
+
+ item = ClusterType.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.service = item
+
+ notes.instance.organization = item.organization
+
+ notes.save()
+
+
+ def get_success_url(self, **kwargs):
+
+ return reverse('Settings:_cluster_type_view', args=(self.kwargs['pk'],))
diff --git a/app/settings/templates/settings/home.html.j2 b/app/settings/templates/settings/home.html.j2
index f73089628..fda2e5afd 100644
--- a/app/settings/templates/settings/home.html.j2
+++ b/app/settings/templates/settings/home.html.j2
@@ -62,6 +62,7 @@ div#content article h3 {
ITIM
diff --git a/app/settings/urls.py b/app/settings/urls.py
index 38fc2c3e0..7f2661314 100644
--- a/app/settings/urls.py
+++ b/app/settings/urls.py
@@ -8,24 +8,20 @@
from itam.views import device_type, device_model, software_category
-from itim.views import ports
+from itim.views import cluster_types, ports
app_name = "Settings"
urlpatterns = [
path("", home.View.as_view(), name="Settings"),
- path("external_links", external_link.Index.as_view(), name="External Links"),
- path("external_links/add", external_link.Add.as_view(), name="_external_link_add"),
- path("external_links/", external_link.View.as_view(), name="_external_link_view"),
- path("external_links//edit", external_link.Change.as_view(), name="_external_link_change"),
- path("external_links//delete", external_link.Delete.as_view(), name="_external_link_delete"),
-
-
path('application', app_settings.View.as_view(), name="_settings_application"),
-
- path("task_results", celery_log.Index.as_view(), name="_task_results"),
- path("task_result/", celery_log.View.as_view(), name="_task_result_view"),
+
+ path("cluster_types", cluster_types.Index.as_view(), name="_cluster_types"),
+ path("cluster_types/add", cluster_types.Add.as_view(), name="_cluster_type_add"),
+ path("cluster_types//edit", cluster_types.Change.as_view(), name="_cluster_type_change"),
+ path("cluster_types//delete", cluster_types.Delete.as_view(), name="_cluster_type_delete"),
+ path("cluster_types/", cluster_types.View.as_view(), name="_cluster_type_view"),
path("device_models", device_models.Index.as_view(), name="_device_models"),
path("device_model/", device_model.View.as_view(), name="_device_model_view"),
@@ -39,18 +35,18 @@
path("device_type//delete", device_type.Delete.as_view(), name="_device_type_delete"),
path("device_type//edit", device_type.Change.as_view(), name="_device_type_change"),
+ path("external_links", external_link.Index.as_view(), name="External Links"),
+ path("external_links/add", external_link.Add.as_view(), name="_external_link_add"),
+ path("external_links/", external_link.View.as_view(), name="_external_link_view"),
+ path("external_links//edit", external_link.Change.as_view(), name="_external_link_change"),
+ path("external_links//delete", external_link.Delete.as_view(), name="_external_link_delete"),
+
path("kb/category", knowledge_base_category.Index.as_view(), name="KB Categories"),
path("kb/category/add", knowledge_base_category.Add.as_view(), name="_knowledge_base_category_add"),
path("kb/category//edit", knowledge_base_category.Change.as_view(), name="_knowledge_base_category_change"),
path("kb/category//delete", knowledge_base_category.Delete.as_view(), name="_knowledge_base_category_delete"),
path("kb/category/", knowledge_base_category.View.as_view(), name="_knowledge_base_category_view"),
- path("software_category", software_categories.Index.as_view(), name="_software_categories"),
- path("software_category/", software_category.View.as_view(), name="_software_category_view"),
- path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
- path("software_category//edit", software_category.Change.as_view(), name="_software_category_change"),
- path("software_category//delete", software_category.Delete.as_view(), name="_software_category_delete"),
-
path("manufacturers", manufacturer.Index.as_view(), name="_manufacturers"),
path("manufacturer/", manufacturer.View.as_view(), name="_manufacturer_view"),
path("manufacturer/add/", manufacturer.Add.as_view(), name="_manufacturer_add"),
@@ -63,4 +59,13 @@
path("port//delete", ports.Delete.as_view(), name="_port_delete"),
path("port/", ports.View.as_view(), name="_port_view"),
+ path("software_category", software_categories.Index.as_view(), name="_software_categories"),
+ path("software_category/", software_category.View.as_view(), name="_software_category_view"),
+ path("software_category/add/", software_category.Add.as_view(), name="_software_category_add"),
+ path("software_category//edit", software_category.Change.as_view(), name="_software_category_change"),
+ path("software_category//delete", software_category.Delete.as_view(), name="_software_category_delete"),
+
+ path("task_results", celery_log.Index.as_view(), name="_task_results"),
+ path("task_result/", celery_log.View.as_view(), name="_task_result_view"),
+
]