@ -1,6 +1,8 @@
|
||||
from django import forms
|
||||
from django.db.models import Q
|
||||
|
||||
from app import settings
|
||||
|
||||
from itam.models.device import DeviceOperatingSystem
|
||||
|
||||
|
||||
@ -16,3 +18,13 @@ class Update(forms.ModelForm):
|
||||
'operating_system_version',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['_created'] = forms.DateTimeField(
|
||||
label="Install Date",
|
||||
input_formats=settings.DATETIME_FORMAT,
|
||||
initial=kwargs['instance'].installdate,
|
||||
disabled=True
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
# Generated by Django 5.0.6 on 2024-05-20 06:36
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0005_alter_operatingsystemversion_name_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='devicesoftware',
|
||||
options={'ordering': ['-action', 'software']},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='installdate',
|
||||
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Install Date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicesoftware',
|
||||
name='installed',
|
||||
field=models.DateTimeField(blank=True, null=True, verbose_name='Install Date'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='devicesoftware',
|
||||
name='installedversion',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='installedversion', to='itam.softwareversion'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='action',
|
||||
field=models.CharField(blank=True, choices=[('1', 'Install'), ('0', 'Remove')], default=None, max_length=1, null=True),
|
||||
),
|
||||
]
|
@ -89,25 +89,27 @@ class Device(DeviceCommonFieldsName):
|
||||
}
|
||||
|
||||
for software in softwares:
|
||||
|
||||
if software.action:
|
||||
|
||||
if int(software.action) == 1:
|
||||
if int(software.action) == 1:
|
||||
|
||||
state = 'present'
|
||||
state = 'present'
|
||||
|
||||
elif int(software.action) == 0:
|
||||
elif int(software.action) == 0:
|
||||
|
||||
state = 'absent'
|
||||
state = 'absent'
|
||||
|
||||
software_action = {
|
||||
"name": software.software.slug,
|
||||
"state": state
|
||||
}
|
||||
software_action = {
|
||||
"name": software.software.slug,
|
||||
"state": state
|
||||
}
|
||||
|
||||
|
||||
if software.version:
|
||||
software_action['version'] = software.version.name
|
||||
if software.version:
|
||||
software_action['version'] = software.version.name
|
||||
|
||||
config['software'] = config['software'] + [ software_action ]
|
||||
config['software'] = config['software'] + [ software_action ]
|
||||
|
||||
return config
|
||||
|
||||
@ -116,6 +118,12 @@ class Device(DeviceCommonFieldsName):
|
||||
class DeviceSoftware(DeviceCommonFields):
|
||||
""" A way for the device owner to configure software to install/remove """
|
||||
|
||||
class Meta:
|
||||
ordering = [
|
||||
'-action',
|
||||
'software'
|
||||
]
|
||||
|
||||
|
||||
class Actions(models.TextChoices):
|
||||
INSTALL = '1', 'Install'
|
||||
@ -128,7 +136,6 @@ class DeviceSoftware(DeviceCommonFields):
|
||||
default = None,
|
||||
null = False,
|
||||
blank= False
|
||||
|
||||
)
|
||||
|
||||
software = models.ForeignKey(
|
||||
@ -137,13 +144,14 @@ class DeviceSoftware(DeviceCommonFields):
|
||||
default = None,
|
||||
null = False,
|
||||
blank= False
|
||||
|
||||
)
|
||||
|
||||
action = models.CharField(
|
||||
max_length=1,
|
||||
choices=Actions,
|
||||
default=None,
|
||||
null=True,
|
||||
blank = True,
|
||||
)
|
||||
|
||||
version = models.ForeignKey(
|
||||
@ -152,7 +160,22 @@ class DeviceSoftware(DeviceCommonFields):
|
||||
default = None,
|
||||
null = True,
|
||||
blank= True
|
||||
|
||||
)
|
||||
|
||||
|
||||
installedversion = models.ForeignKey(
|
||||
SoftwareVersion,
|
||||
related_name = 'installedversion',
|
||||
on_delete=models.CASCADE,
|
||||
default = None,
|
||||
null = True,
|
||||
blank= True
|
||||
)
|
||||
|
||||
installed = models.DateTimeField(
|
||||
verbose_name = 'Install Date',
|
||||
null = True,
|
||||
blank = True
|
||||
)
|
||||
|
||||
|
||||
@ -184,3 +207,10 @@ class DeviceOperatingSystem(DeviceCommonFields):
|
||||
null = False,
|
||||
blank = False,
|
||||
)
|
||||
|
||||
installdate = models.DateTimeField(
|
||||
verbose_name = 'Install Date',
|
||||
null = True,
|
||||
blank = True,
|
||||
default = None,
|
||||
)
|
||||
|
@ -68,13 +68,16 @@
|
||||
|
||||
<div id="Software" class="tabcontent">
|
||||
<h3>Software</h3>
|
||||
|
||||
<hr>
|
||||
Installed Software: {{ installed_software }}
|
||||
<input type="button" value="Add Software Action" onclick="window.location='{% url 'ITAM:_device_software_add' device.id %}';">
|
||||
<table>
|
||||
<thead>
|
||||
<th>Name</th>
|
||||
<th>Category</th>
|
||||
<th>Action</th>
|
||||
<th>Version</th>
|
||||
<th>Desired Version</th>
|
||||
<th>Installed Version</th>
|
||||
<th>Installed</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
@ -82,22 +85,44 @@
|
||||
{% for software in softwares %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_software_view' pk=software.software_id %}">{{ software.software }}</a></td>
|
||||
<td><a href="{% url 'ITAM:_device_software_view' device_id=device.id pk=software.id %}">{{ software.get_action_display }}</a></td>
|
||||
<td>{{ software.software.category }}</td>
|
||||
<td>
|
||||
{% if software.version %}
|
||||
{{ software.version }}
|
||||
{% url 'ITAM:_device_software_view' device_id=device.id pk=software.id as icon_link %}
|
||||
{% if software.get_action_display == 'Install' %}
|
||||
{% include 'icons/success_text.html.j2' with icon_text=software.get_action_display icon_link=icon_link %}
|
||||
{% elif software.get_action_display == 'Remove'%}
|
||||
{% include 'icons/cross_text.html.j2' with icon_text=software.get_action_display icon_link=icon_link %}
|
||||
{% else %}
|
||||
Any
|
||||
|
||||
{% include 'icons/add_link.html.j2' with icon_text='Add' icon_link=icon_link %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% include 'icons/issue_link.html.j2' with issue=2 %}
|
||||
{% if software.version %}
|
||||
{{ software.version }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if software.installedversion %}
|
||||
{{ software.installedversion }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if software.installed %}
|
||||
{{ software.installed }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<td colspan="5">Nothing Found {% include 'icons/issue_link.html.j2' with issue=2 %}</td>
|
||||
<td colspan="5">Nothing Found</td>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
|
@ -121,7 +121,13 @@
|
||||
<td><a href="{% url 'ITAM:_device_view' pk=install.device_id %}">{{ install.device }}</a></td>
|
||||
<td>{{ install.organization }}</td>
|
||||
<td>{{ install.version }}</td>
|
||||
<td>{% include 'icons/issue_link.html.j2' with issue=2 %}</td>
|
||||
<td>
|
||||
{% if install.installdate %}
|
||||
{{ install.installdate }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -69,7 +69,7 @@
|
||||
{% for version in software_versions %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_software_version_view' software_id=software.id pk=version.id %}">{{ version.name }}</a></td>
|
||||
<td>{% include 'icons/issue_link.html.j2' with issue=2 %}</td>
|
||||
<td>{{ version.installs }}</td>
|
||||
<td>{% include 'icons/issue_link.html.j2' with issue=3 %}</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
@ -115,8 +115,8 @@
|
||||
<th>Device</th>
|
||||
<th>Organization</th>
|
||||
<th title="Not Set/Install/Remove">Action</th>
|
||||
<th>Version</th>
|
||||
<th title="Date Software Installed">Installed</th>
|
||||
<th>Installed Version</th>
|
||||
<th title="Date Software Installed">Install Date</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
{% if device_software %}
|
||||
@ -130,17 +130,23 @@
|
||||
{% elif device.get_action_display == 'Remove'%}
|
||||
{% include 'icons/cross_text.html.j2' with icon_text=device.get_action_display %}
|
||||
{% else %}
|
||||
{{ device.get_action_display }}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if device.version %}
|
||||
{{ device.version }}
|
||||
{% if device.installedversion %}
|
||||
{{ device.installedversion }}
|
||||
{% else %}
|
||||
Any
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% include 'icons/issue_link.html.j2' with issue=2 %}</td>
|
||||
<td>
|
||||
{% if device.installed %}
|
||||
{{ device.installed }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -10,6 +10,7 @@
|
||||
<table class="data">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Category</th>
|
||||
<th>Created</th>
|
||||
<th>Modified</th>
|
||||
<th>Organization</th>
|
||||
@ -18,6 +19,7 @@
|
||||
{% for software in softwares %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ITAM:_software_view' pk=software.id %}">{{ software.name }}</a></td>
|
||||
<td>{{ software.category }}</td>
|
||||
<td>{{ software.created }}</td>
|
||||
<td>{{ software.modified }}</td>
|
||||
<td>{% if software.is_global %}Global{% else %}{{ software.organization }}{% endif %}</td>
|
||||
|
@ -2,12 +2,14 @@ import json
|
||||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
from access.models import Organization
|
||||
|
||||
from ..models.device import Device, DeviceSoftware, DeviceOperatingSystem
|
||||
from ..models.software import Software
|
||||
from itam.forms.device_softwareadd import SoftwareAdd
|
||||
from itam.forms.device_softwareupdate import SoftwareUpdate
|
||||
|
||||
@ -53,6 +55,9 @@ class View(OrganizationPermission, generic.UpdateView):
|
||||
|
||||
context_object_name = "device"
|
||||
|
||||
paginate_by = 10
|
||||
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@ -74,7 +79,9 @@ class View(OrganizationPermission, generic.UpdateView):
|
||||
context['operating_system'] = OperatingSystemForm(prefix='operating_system')
|
||||
|
||||
|
||||
softwares = DeviceSoftware.objects.filter(device=self.kwargs['pk'])
|
||||
softwares = DeviceSoftware.objects.filter(device=self.kwargs['pk'])[:50]
|
||||
context['installed_software'] = len(DeviceSoftware.objects.filter(device=self.kwargs['pk']))
|
||||
|
||||
context['softwares'] = softwares
|
||||
|
||||
config = self.object.get_configuration(self.kwargs['pk'])
|
||||
@ -134,6 +141,7 @@ class SoftwareView(OrganizationPermission, generic.UpdateView):
|
||||
|
||||
form.instance.organization_id = device.organization.id
|
||||
form.instance.device_id = self.kwargs['device_id']
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
@ -141,6 +149,14 @@ class SoftwareView(OrganizationPermission, generic.UpdateView):
|
||||
|
||||
return f"/itam/device/{self.kwargs['device_id']}/"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Edit Software Action'
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
||||
class Add(PermissionRequiredMixin, OrganizationPermission, generic.CreateView):
|
||||
@ -194,7 +210,25 @@ class SoftwareAdd(PermissionRequiredMixin, OrganizationPermission, generic.Creat
|
||||
device = Device.objects.get(pk=self.kwargs['pk'])
|
||||
form.instance.organization_id = device.organization.id
|
||||
form.instance.device_id = self.kwargs['pk']
|
||||
return super().form_valid(form)
|
||||
|
||||
software = Software.objects.get(pk=form.instance.software.id)
|
||||
|
||||
if DeviceSoftware.objects.get(device=device, software=software):
|
||||
|
||||
|
||||
software_version = DeviceSoftware.objects.get(
|
||||
device=device,
|
||||
software=software
|
||||
)
|
||||
|
||||
software_version.action = form.instance.action
|
||||
software_version.save()
|
||||
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
else:
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def get_form_kwargs(self):
|
||||
@ -212,7 +246,7 @@ class SoftwareAdd(PermissionRequiredMixin, OrganizationPermission, generic.Creat
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
context['content_title'] = 'Add Device'
|
||||
context['content_title'] = 'Add Software Action'
|
||||
|
||||
return context
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.db.models import Q
|
||||
from django.db.models import Count, Q
|
||||
from django.views import generic
|
||||
|
||||
from access.mixin import OrganizationPermission
|
||||
@ -43,16 +43,33 @@ class View(OrganizationPermission, generic.UpdateView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
software_versions = SoftwareVersion.objects.filter(software=self.kwargs['pk'])
|
||||
software_versions = SoftwareVersion.objects.filter(
|
||||
software=self.kwargs['pk']
|
||||
).annotate(
|
||||
installs=Count("installedversion")
|
||||
)
|
||||
|
||||
context['software_versions'] = software_versions
|
||||
|
||||
context['content_title'] = self.object.name
|
||||
|
||||
if self.request.user.is_superuser:
|
||||
context['device_software'] = DeviceSoftware.objects.filter(software=self.kwargs['pk']).order_by('device', 'organization')
|
||||
|
||||
context['device_software'] = DeviceSoftware.objects.filter(
|
||||
software=self.kwargs['pk']
|
||||
).order_by(
|
||||
'device',
|
||||
'organization'
|
||||
)
|
||||
|
||||
elif not self.request.user.is_superuser:
|
||||
context['device_software'] = DeviceSoftware.objects.filter(Q(device__in=self.user_organizations(), software=self.kwargs['pk'])).order_by('name', 'organization')
|
||||
context['device_software'] = DeviceSoftware.objects.filter(
|
||||
Q(device__in=self.user_organizations(),
|
||||
software=self.kwargs['pk'])
|
||||
).order_by(
|
||||
'name',
|
||||
'organization'
|
||||
)
|
||||
|
||||
return context
|
||||
|
||||
|
@ -54,6 +54,33 @@ span.icon-text {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span.icon-text a {
|
||||
text-decoration: unset;
|
||||
color: inherit;
|
||||
|
||||
}
|
||||
|
||||
span.icon-text a:visited {
|
||||
text-decoration: unset;
|
||||
color: inherit;
|
||||
|
||||
}
|
||||
|
||||
span.add {
|
||||
color: #315f9c;
|
||||
}
|
||||
|
||||
span.icon-add {
|
||||
fill: #315f9c;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin: 0px;
|
||||
margin-bottom: -7px;
|
||||
padding: 0px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span.success {
|
||||
color: #319c3a;
|
||||
}
|
||||
@ -69,7 +96,6 @@ span.icon-success {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
span.cross {
|
||||
color: #9c3131;
|
||||
}
|
||||
@ -101,9 +127,9 @@ span.icon-change {
|
||||
}
|
||||
|
||||
|
||||
/* span.issue {
|
||||
span.issue {
|
||||
color: #fc6d26;
|
||||
} */
|
||||
}
|
||||
|
||||
span.icon-issue {
|
||||
fill: #fc6d26;
|
||||
|
12
app/templates/icons/add_link.html.j2
Normal file
12
app/templates/icons/add_link.html.j2
Normal file
@ -0,0 +1,12 @@
|
||||
<span class="icon-text add">
|
||||
<span class="icon-add">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px">
|
||||
<path d="M440-280h80v-160h160v-80H520v-160h-80v160H280v80h160v160Zm40 200q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"/>
|
||||
</svg>
|
||||
</span>
|
||||
{% if icon_link %}
|
||||
<a href="{{ icon_link }}">{{ icon_text }}</a>
|
||||
{% else %}
|
||||
{{ icon_text }}
|
||||
{% endif %}
|
||||
</span>
|
@ -4,5 +4,9 @@
|
||||
<path d="m480-429 116 116q11 11 25.5 10.5T647-314q11-11 11-25.5t-11-25.46L531-480.5l116-115.54q11-10.96 11-25.46T647-647q-11-11-25.5-11T596-647L480-531 364-647q-11-11-25-11t-25 11q-11 11-11 25.5t10.91 25.5L429-480 313-364q-11 11-10.5 25t11.5 25q11 11 25.5 11t25.41-10.91L480-429Zm.28 333Q401-96 331-126t-122.5-82.5Q156-261 126-330.96t-30-149.5Q96-560 126-629.5q30-69.5 82.5-122T330.96-834q69.96-30 149.5-30t149.04 30q69.5 30 122 82.5T834-629.28q30 69.73 30 149Q864-401 834-331t-82.5 122.5Q699-156 629.28-126q-69.73 30-149 30Z" />
|
||||
</svg>
|
||||
</span>
|
||||
{{ icon_text }}
|
||||
{% if icon_link %}
|
||||
<a href="{{ icon_link }}">{{ icon_text }}</a>
|
||||
{% else %}
|
||||
{{ icon_text }}
|
||||
{% endif %}
|
||||
</span>
|
@ -4,5 +4,9 @@
|
||||
<path class="tick" d="m424-408-86-86q-11-11-28-11t-28 11q-11 11-11 28t11 28l114 114q12 12 28 12t28-12l226-226q11-11 11-28t-11-28q-11-11-28-11t-28 11L424-408Zm56 328q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
{{ icon_text }}
|
||||
{% if icon_link %}
|
||||
<a href="{{ icon_link }}">{{ icon_text }}</a>
|
||||
{% else %}
|
||||
{{ icon_text }}
|
||||
{% endif %}
|
||||
</span>
|
Reference in New Issue
Block a user