new structure

This commit is contained in:
Cayo Puigdefabregas 2024-07-18 17:21:22 +02:00
parent e135fd9d28
commit b1440fddfa
32 changed files with 471 additions and 1210 deletions

View File

@ -5,6 +5,7 @@ from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from device.models import Device from device.models import Device
from lot.models import LotTag
class Http403(PermissionDenied): class Http403(PermissionDenied):
@ -37,6 +38,7 @@ class DashboardView(LoginRequiredMixin):
'section': self.section, 'section': self.section,
'path': resolve(self.request.path).url_name, 'path': resolve(self.request.path).url_name,
'user': self.request.user, 'user': self.request.user,
'lot_tags': LotTag.objects.filter(owner=self.request.user)
}) })
return context return context

View File

@ -103,21 +103,13 @@
{% trans 'Lots' %} {% trans 'Lots' %}
</a> </a>
<ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_lots" data-bs-parent="#sidebarMenu"> <ul class="flex-column mb-2 ul_sidebar accordion-collapse {% if section == 'People' %}expanded{% else %}collapse{% endif %}" id="ul_lots" data-bs-parent="#sidebarMenu">
{% for tag in lot_tags %}
<li class="nav-items"> <li class="nav-items">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'lot:lots_incoming' %}"> <a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'lot:tag' tag.id %}">
{% trans 'Incoming ' %} {{ tag.name }}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{% url 'lot:lots_outgoing' %}">
{% trans 'Outgoing ' %}
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if path == 'admin_people_new' %} active2{% endif %}" href="{% url 'lot:lots_temporal' %}">
{% trans 'Temporal ' %}
</a> </a>
</li> </li>
{% endfor %}
</ul> </ul>
</li> </li>
<li class="nav-item"> <li class="nav-item">

View File

@ -6,7 +6,7 @@ from dashboard.mixins import InventaryMixin, DetailsMixin
from device.models import Device from device.models import Device
from snapshot.xapian import search from snapshot.xapian import search
from snapshot.models import Annotation from snapshot.models import Annotation
from lot.models import Lot from lot.models import Lot, LotTag
class UnassignedDevicesView(InventaryMixin): class UnassignedDevicesView(InventaryMixin):
@ -17,30 +17,14 @@ class UnassignedDevicesView(InventaryMixin):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
annotations = Annotation.objects.filter( devices = Device.objects.filter(
owner=self.request.user).filter( owner=self.request.user
key="hidalgo1").order_by('created') ).annotate(num_lots=Count('lot')).filter(num_lots=0)
# 'created').distinct('value')
# ).annotate(num_lots=Count('lot')).filter(num_lots=0)
hids = {}
ids = []
for x in annotations:
if not hids.get(x.key):
hids[x.key] = x.uuid
ids.append(str(x.uuid))
devices = []
for xa in search(ids):
# import pdb; pdb.set_trace()
snap = json.loads(xa.document.get_data())
dev = snap.get("device", {})
dev["id"] = snap["uuid"]
devices.append(dev)
context.update({ context.update({
'devices': devices 'devices': devices,
}) })
return context return context

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-06-11 09:20 # Generated by Django 5.0.6 on 2024-07-17 14:57
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -26,31 +26,7 @@ class Migration(migrations.Migration):
verbose_name="ID", verbose_name="ID",
), ),
), ),
("created", models.DateTimeField(auto_now_add=True)), ("type", models.CharField(blank=True, max_length=64, null=True)),
("updated", models.DateTimeField(auto_now=True)),
("type", models.CharField(max_length=32)),
("model", models.CharField(blank=True, max_length=64, null=True)),
(
"manufacturer",
models.CharField(blank=True, max_length=64, null=True),
),
(
"serial_number",
models.CharField(blank=True, max_length=64, null=True),
),
("part_number", models.CharField(blank=True, max_length=64, null=True)),
("brand", models.TextField(blank=True, null=True)),
("generation", models.SmallIntegerField(blank=True, null=True)),
("version", models.TextField(blank=True, null=True)),
("production_date", models.DateTimeField(blank=True, null=True)),
("variant", models.TextField(blank=True, null=True)),
("devicehub_id", models.TextField(blank=True, null=True, unique=True)),
("dhid_bk", models.CharField(blank=True, max_length=64, null=True)),
("phid_bk", models.CharField(blank=True, max_length=64, null=True)),
("family", models.CharField(blank=True, max_length=64, null=True)),
("hid", models.CharField(blank=True, max_length=64, null=True)),
("chid", models.CharField(blank=True, max_length=64, null=True)),
("active", models.BooleanField(default=True)),
( (
"owner", "owner",
models.ForeignKey( models.ForeignKey(
@ -60,345 +36,4 @@ class Migration(migrations.Migration):
), ),
], ],
), ),
migrations.CreateModel(
name="Component",
fields=[
(
"device",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="device.device",
),
),
(
"type",
models.CharField(
choices=[
("GraphicCard", "Graphiccard"),
("DataStorage", "Datastorage"),
("Motherboard", "Motherboard"),
("NetworkAdapter", "Networkadapter"),
("Processor", "Processor"),
("RamModule", "Rammodule"),
("SoundCard", "Soundcard"),
("Display", "Display"),
("Battery", "Battery"),
("Camera", "Camera"),
],
max_length=32,
),
),
],
),
migrations.CreateModel(
name="Computer",
fields=[
(
"device",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="device.device",
),
),
("chassis", models.TextField(blank=True, null=True)),
("system_uuid", models.UUIDField()),
("sku", models.TextField(blank=True, null=True)),
(
"type",
models.CharField(
choices=[
("Desktop", "Desktop"),
("Laptop", "Laptop"),
("Server", "Server"),
],
default="Laptop",
max_length=32,
),
),
],
),
migrations.CreateModel(
name="PhysicalProperties",
fields=[
(
"device",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
serialize=False,
to="device.device",
),
),
("weight", models.FloatField(blank=True, null=True)),
("width", models.FloatField(blank=True, null=True)),
("height", models.FloatField(blank=True, null=True)),
("depth", models.FloatField(blank=True, null=True)),
("color", models.CharField(blank=True, max_length=20, null=True)),
("image", models.CharField(blank=True, max_length=64, null=True)),
],
),
migrations.CreateModel(
name="SoundCard",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="RamModule",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("size", models.IntegerField(blank=True, null=True)),
("speed", models.SmallIntegerField(blank=True, null=True)),
(
"interface",
models.CharField(
choices=[
("SDRAM", "Sdram"),
("DDR SDRAM", "Ddr"),
("DDR2 SDRAM", "Ddr2"),
("DDR3 SDRAM", "Ddr3"),
("DDR4 SDRAM", "Ddr4"),
("DDR5 SDRAM", "Ddr5"),
("DDR6 SDRAM", "Ddr6"),
("LPDDR3", "Lpddr3"),
],
max_length=32,
),
),
(
"format",
models.CharField(
choices=[("DIMM", "Dimm"), ("SODIMM", "Sodimm")], max_length=32
),
),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="Processor",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("speed", models.FloatField(blank=True, null=True)),
("cores", models.SmallIntegerField(blank=True, null=True)),
("threads", models.SmallIntegerField(blank=True, null=True)),
("address", models.SmallIntegerField(blank=True, null=True)),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="NetworkAdapter",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("speed", models.IntegerField(blank=True, null=True)),
("wireless", models.BooleanField(default=False)),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="Motherboard",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("slots", models.SmallIntegerField(blank=True, null=True)),
("usb", models.SmallIntegerField(blank=True, null=True)),
("firewire", models.SmallIntegerField(blank=True, null=True)),
("serial", models.SmallIntegerField(blank=True, null=True)),
("pcmcia", models.SmallIntegerField(blank=True, null=True)),
("bios_date", models.DateTimeField()),
("ram_slots", models.SmallIntegerField(blank=True, null=True)),
("ram_max_size", models.IntegerField(blank=True, null=True)),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="GraphicCard",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("memory", models.IntegerField(blank=True, null=True)),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="Display",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="DataStorage",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("size", models.IntegerField(blank=True, null=True)),
(
"interface",
models.CharField(
choices=[
("ATA", "Ata"),
("USB", "Usb"),
("PCI", "Pci"),
("NVME", "Nvme"),
],
max_length=32,
),
),
(
"type",
models.CharField(
choices=[
("HardDrive", "Harddrive"),
("SolidStateDrive", "Solidstatedrive"),
],
max_length=32,
),
),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.CreateModel(
name="Battery",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"component",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to="device.component",
),
),
],
),
migrations.AddField(
model_name="component",
name="computer",
field=models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="device.computer",
),
),
] ]

View File

@ -1,33 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-03 11:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="device",
name="brand",
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name="device",
name="devicehub_id",
field=models.CharField(blank=True, max_length=64, null=True, unique=True),
),
migrations.AlterField(
model_name="device",
name="variant",
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name="device",
name="version",
field=models.CharField(blank=True, max_length=64, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2024-07-18 09:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="device",
name="model",
field=models.CharField(blank=True, max_length=256, null=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2024-07-18 09:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0002_device_model"),
]
operations = [
migrations.AddField(
model_name="device",
name="manufacturer",
field=models.CharField(blank=True, max_length=256, null=True),
),
]

View File

@ -1,73 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-03 12:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0002_alter_device_brand_alter_device_devicehub_id_and_more"),
]
operations = [
migrations.RemoveField(
model_name="component",
name="type",
),
migrations.RemoveField(
model_name="computer",
name="type",
),
migrations.RemoveField(
model_name="datastorage",
name="type",
),
migrations.AlterField(
model_name="computer",
name="chassis",
field=models.CharField(
blank=True,
choices=[
("Tower", "Tower"),
("All in one", "Allinone"),
("Microtower", "Microtower"),
("Netbook", "Netbook"),
("Laptop", "Laptop"),
("Tablet", "Tabler"),
("Server", "Server"),
("Non-physical device", "Virtual"),
],
max_length=32,
null=True,
),
),
migrations.AlterField(
model_name="computer",
name="sku",
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.AlterField(
model_name="device",
name="type",
field=models.CharField(
choices=[
("Desktop", "Desktop"),
("Laptop", "Laptop"),
("Server", "Server"),
("GraphicCard", "Graphiccard"),
("HardDrive", "Harddrive"),
("SolidStateDrive", "Solidstatedrive"),
("Motherboard", "Motherboard"),
("NetworkAdapter", "Networkadapter"),
("Processor", "Processor"),
("RamModule", "Rammodule"),
("SoundCard", "Soundcard"),
("Display", "Display"),
("Battery", "Battery"),
("Camera", "Camera"),
],
default="Laptop",
max_length=32,
),
),
]

View File

@ -1,46 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-11 13:58
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("device", "0003_remove_component_type_remove_computer_type_and_more"),
]
operations = [
migrations.RemoveField(
model_name="device",
name="dhid_bk",
),
migrations.RemoveField(
model_name="device",
name="phid_bk",
),
migrations.AddField(
model_name="computer",
name="erasure_server",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="device",
name="reliable",
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name="component",
name="computer",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="device.computer",
),
),
migrations.AlterField(
model_name="computer",
name="system_uuid",
field=models.UUIDField(blank=True, null=True),
),
]

View File

@ -1,6 +1,8 @@
from django.db import models from django.db import models
from utils.constants import STR_SM_SIZE, STR_SIZE, STR_EXTEND_SIZE, ALGOS
from snapshot.models import Annotation, Snapshot
from user.models import User from user.models import User
from utils.constants import STR_SM_SIZE, STR_SIZE
class Device(models.Model): class Device(models.Model):
@ -20,155 +22,59 @@ class Device(models.Model):
BATTERY = "Battery" BATTERY = "Battery"
CAMERA = "Camera" CAMERA = "Camera"
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
type = models.CharField(max_length=STR_SM_SIZE, choices=Types, default=Types.LAPTOP)
model = models.CharField(max_length=STR_SIZE, blank=True, null=True)
manufacturer = models.CharField(max_length=STR_SIZE, blank=True, null=True)
serial_number = models.CharField(max_length=STR_SIZE, blank=True, null=True)
part_number = models.CharField(max_length=STR_SIZE, blank=True, null=True)
brand = models.CharField(max_length=STR_SIZE, blank=True, null=True)
generation = models.SmallIntegerField(blank=True, null=True)
version = models.CharField(max_length=STR_SIZE, blank=True, null=True)
production_date = models.DateTimeField(blank=True, null=True)
variant = models.CharField(max_length=STR_SIZE, blank=True, null=True)
devicehub_id = models.CharField(max_length=STR_SIZE, unique=True, blank=True, null=True)
family = models.CharField(max_length=STR_SIZE, blank=True, null=True)
hid = models.CharField(max_length=STR_SIZE, blank=True, null=True)
chid = models.CharField(max_length=STR_SIZE, blank=True, null=True)
active = models.BooleanField(default=True)
reliable = models.BooleanField(default=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.CharField(max_length=STR_SIZE, blank=True, null=True)
manufacturer = models.CharField(max_length=STR_EXTEND_SIZE, blank=True, null=True)
model = models.CharField(max_length=STR_EXTEND_SIZE, blank=True, null=True)
def has_physical_properties(self): def __init__(self, *args, **kwargs):
try: self.annotations = []
if self.physicalproperties: self.hids = []
return True self.uuids = []
else: self.snapshots = []
return False super().__init__(*args, **kwargs)
except Exception:
return False
def initial(self):
self.get_annotations()
self.get_uuids()
self.get_hids()
self.get_snapshots()
class PhysicalProperties(models.Model): def get_annotations(self):
device = models.OneToOneField(Device, models.CASCADE, primary_key=True) self.annotations = Annotation.objects.filter(
weight = models.FloatField(blank=True, null=True) device=self,
width = models.FloatField(blank=True, null=True) owner=self.owner
height = models.FloatField(blank=True, null=True) ).order_by("-created")
depth = models.FloatField(blank=True, null=True)
color = models.CharField(max_length=20, blank=True, null=True)
image = models.CharField(max_length=STR_SIZE, blank=True, null=True)
def get_uuids(self):
for a in self.annotations:
if not a.uuid in self.uuids:
self.uuids.append(a.uuid)
class Computer(models.Model): def get_hids(self):
class Chassis(models.TextChoices): if not self.annotations:
TOWER = 'Tower' self.get_annotations()
ALLINONE = 'All in one'
MICROTOWER = 'Microtower'
NETBOOK = 'Netbook'
LAPTOP = 'Laptop'
TABLER = 'Tablet'
SERVER = "Server"
VIRTUAL = 'Non-physical device'
self.hids = self.annotations.filter(
type=Annotation.Type.SYSTEM,
key__in=ALGOS.keys(),
).values_list("value", flat=True)
device = models.OneToOneField(Device, models.CASCADE, primary_key=True) def get_snapshots(self):
chassis = models.CharField( if not self.uuids:
blank=True, self.get_uuids()
null=True,
max_length=STR_SM_SIZE,
choices=Chassis
)
system_uuid = models.UUIDField(blank=True, null=True)
sku = models.CharField(max_length=STR_SM_SIZE, blank=True, null=True)
erasure_server = models.BooleanField(default=False)
self.snapshots = [Snapshot(u) for u in self.uuids]
class Component(models.Model): def get_last_snapshot(self):
device = models.OneToOneField(Device, models.CASCADE, primary_key=True) if not self.snapshots:
computer = models.ForeignKey(Computer, on_delete=models.CASCADE, null=True) self.get_snapshots()
if self.snapshots:
return self.snapshots[0]
class GraphicCard(models.Model): @classmethod
component = models.OneToOneField(Component, models.CASCADE) def get_unassigned(cls, user):
memory = models.IntegerField(blank=True, null=True) return cls.objects.filter(
owner=user
).annotate(num_lots=models.Count('lot')).filter(num_lots=0)
class DataStorage(models.Model):
class Interface(models.TextChoices):
ATA = 'ATA'
USB = 'USB'
PCI = 'PCI'
NVME = 'NVME'
size = models.IntegerField(blank=True, null=True)
interface = models.CharField(max_length=STR_SM_SIZE, choices=Interface)
component = models.OneToOneField(Component, models.CASCADE)
class Motherboard(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
slots = models.SmallIntegerField(blank=True, null=True)
usb = models.SmallIntegerField(blank=True, null=True)
firewire = models.SmallIntegerField(blank=True, null=True)
serial = models.SmallIntegerField(blank=True, null=True)
pcmcia = models.SmallIntegerField(blank=True, null=True)
bios_date = models.DateTimeField()
ram_slots = models.SmallIntegerField(blank=True, null=True)
ram_max_size = models.IntegerField(blank=True, null=True)
class NetworkAdapter(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
speed = models.IntegerField(blank=True, null=True)
wireless = models.BooleanField(default=False)
def __format__(self, format_spec):
v = super().__format__(format_spec)
if 's' in format_spec:
v += ' {} Mbps'.format(self.speed)
return v
class Processor(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
speed = models.FloatField(blank=True, null=True)
cores = models.SmallIntegerField(blank=True, null=True)
threads = models.SmallIntegerField(blank=True, null=True)
address = models.SmallIntegerField(blank=True, null=True)
class RamModule(models.Model):
class Interface(models.TextChoices):
SDRAM = 'SDRAM'
DDR = 'DDR SDRAM'
DDR2 = 'DDR2 SDRAM'
DDR3 = 'DDR3 SDRAM'
DDR4 = 'DDR4 SDRAM'
DDR5 = 'DDR5 SDRAM'
DDR6 = 'DDR6 SDRAM'
LPDDR3 = 'LPDDR3'
class Format(models.TextChoices):
DIMM = 'DIMM'
SODIMM = 'SODIMM'
component = models.OneToOneField(Component, models.CASCADE)
size = models.IntegerField(blank=True, null=True)
interface = models.CharField(max_length=STR_SM_SIZE, choices=Interface)
speed = models.SmallIntegerField(blank=True, null=True)
interface = models.CharField(max_length=STR_SM_SIZE, choices=Interface)
format = models.CharField(max_length=STR_SM_SIZE, choices=Format)
class SoundCard(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
class Display(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
class Battery(models.Model):
component = models.OneToOneField(Component, models.CASCADE)

View File

@ -4,18 +4,18 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h3>{{ object.uuid }}</h3> <h3>{{ object.id }}</h3>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="nav nav-tabs nav-tabs-bordered"> <ul class="nav nav-tabs nav-tabs-bordered">
<li class="nav-items"> <li class="nav-items">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#details">General details</button> <button class="nav-link active" data-bs-toggle="tab" data-bs-target="#details">General details</button>
</li> </li>
<li class="nav-items"> <li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#physicalproperties">Physical properties</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#annotations">User annotations</button>
</li> </li>
<li class="nav-items"> <li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">Documents</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#documents">Documents</button>
@ -23,20 +23,19 @@
<li class="nav-items"> <li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#lots">Lots</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#lots">Lots</button>
</li> </li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#status">Status</button>
</li>
<li class="nav-items"> <li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#components">Components</button>
</li> </li>
<li class="nav-items"> <li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceabiliy">Traceability log</button> <button class="nav-link" data-bs-toggle="tab" data-bs-target="#traceabiliy">Traceability log</button>
</li> </li>
<li class="nav-items">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#snapshots">Snapshots</button>
</li>
<li class="nav-items"> <li class="nav-items">
<a class="nav-link" href="">Web</a> <a class="nav-link" href="">Web</a>
</li> </li>
</ul>
</div>
</div> </div>
</div> </div>
<div class="tab-content pt-2"> <div class="tab-content pt-2">
@ -45,7 +44,7 @@
<h5 class="card-title">Details</h5> <h5 class="card-title">Details</h5>
<div class="row mb-3"> <div class="row mb-3">
<div class="col-lg-3 col-md-4 label "> <div class="col-lg-3 col-md-4 label ">
(<a href="{% url 'device:edit' object.uuid %}">Edit Device</a>) (<a href="{% url 'device:edit' object.id %}">Edit Device</a>)
</div> </div>
<div class="col-lg-9 col-md-8"> <div class="col-lg-9 col-md-8">
{% if object.hid %}Snapshot{% else %}Placeholder{% endif %} {% if object.hid %}Snapshot{% else %}Placeholder{% endif %}
@ -54,7 +53,7 @@
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label ">Phid</div> <div class="col-lg-3 col-md-4 label ">Phid</div>
<div class="col-lg-9 col-md-8">{{ object.uuid }}</div> <div class="col-lg-9 col-md-8">{{ object.id }}</div>
</div> </div>
<div class="row"> <div class="row">
@ -64,101 +63,85 @@
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label ">Type</div> <div class="col-lg-3 col-md-4 label ">Type</div>
<div class="col-lg-9 col-md-8">{{ object.type }}</div> <div class="col-lg-9 col-md-8">{{ snapshot.doc.device.type }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Manufacturer</div> <div class="col-lg-3 col-md-4 label">Manufacturer</div>
<div class="col-lg-9 col-md-8">{{ object.device.manufacturer|default:"" }}</div> <div class="col-lg-9 col-md-8">{{ snapshot.doc.device.manufacturer|default:"" }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Model</div> <div class="col-lg-3 col-md-4 label">Model</div>
<div class="col-lg-9 col-md-8">{{ object.device.model|default:"" }}</div> <div class="col-lg-9 col-md-8">{{ snapshot.doc.device.model|default:"" }}</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label">Serial Number</div> <div class="col-lg-3 col-md-4 label">Serial Number</div>
<div class="col-lg-9 col-md-8">{{ object.device.serialNumber|default:"" }}</div> <div class="col-lg-9 col-md-8">{{ snapshot.doc.device.serialNumber|default:"" }}</div>
</div> </div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Identifiers</div>
</div>
{% for chid in object.hids %}
<div class="row">
<div class="col">{{ chid |default:"" }}</div>
</div>
{% endfor %}
</div> </div>
<div class="tab-pane fade profile-overview" id="physicalproperties"> <div class="tab-pane fade profile-overview" id="annotations">
<h5 class="card-title">Physical Properties</h5> <div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
<div class="row mb-3"> <a href="{% url 'device:add_annotation' object.pk %}" class="btn btn-primary">
<div class="col-lg-3 col-md-4 label "> <i class="bi bi-plus"></i>
(<a href="{% url 'device:physical_edit' object.uuid %}">Edit Physical Properties</a>) Add new annotation
</div> <span class="caret"></span>
</a>
</div> </div>
{% if object.has_physical_properties %} <h5 class="card-title">Annotations</h5>
<div class="row"> <table class="table table-striped">
<div class="col-lg-3 col-md-4 label "> <thead>
Weight: <tr>
</div> <th scope="col">Key</th>
<div class="col-lg-9 col-md-8"> <th scope="col">Value</th>
{{ object.physicalproperties.weight }} <th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Created on</th>
</div> <th></th>
</div> <th></th>
</tr>
</thead>
<tbody>
{% for a in object.annotations %}
{% if a.is_user_annotation %}
<tr>
<td>{{ a.key }}</td>
<td>{{ a.value }}</td>
<td>{{ a.created }}</td>
<td></td>
<td></td>
</tr>
{% endif %}
{% endfor %}
<div class="row"> </tbody>
<div class="col-lg-3 col-md-4 label ">width:</div> </table>
<div class="col-lg-9 col-md-8">
{{ object.physicalproperties.width }}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">height:</div>
<div class="col-lg-9 col-md-8">
{{ object.physicalproperties.height }}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">depth:</div>
<div class="col-lg-9 col-md-8">
{{ object.physicalproperties.depth }}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">color:</div>
<div class="col-lg-9 col-md-8">
{{ object.physicalproperties.color }}
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label ">image:</div>
<div class="col-lg-9 col-md-8">
{% if object.physicalproperties.image %}
<img width="200px" src="{{ object.physicalproperties.image }}" />
{% endif %}
</div>
</div>
{% endif %}
</div> </div>
<div class="tab-pane fade profile-overview" id="lots"> <div class="tab-pane fade profile-overview" id="lots">
<h5 class="card-title">Incoming Lots</h5> {% for tag in lot_tags %}
<h5 class="card-title">{{ tag }}</h5>
{% for lot in object.lot_set.filter %}
{% if lot.type == tag %}
<div class="row"> <div class="row">
<div class="col">
</div> <a href="{% url 'dashboard:lot' lot.id %}">{{ lot.name }}</a>
</div>
<h5 class="card-title">Outgoing Lots</h5>
<div class="row">
</div>
<h5 class="card-title">Temporary Lots</h5>
<div class="row">
</div> </div>
{% endif %}
{% endfor %}
{% endfor %}
</div> </div>
<div class="tab-pane fade profile-overview" id="documents"> <div class="tab-pane fade profile-overview" id="documents">
@ -188,28 +171,6 @@
</table> </table>
</div> </div>
<div class="tab-pane fade profile-overview" id="status">
<h5 class="card-title">Status Details</h5>
<div class="row">
<div class="col-lg-3 col-md-4 label">Physical State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Lifecycle State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
<div class="row">
<div class="col-lg-3 col-md-4 label">Allocated State</div>
<div class="col-lg-9 col-md-8">
</div>
</div>
</div>
<div class="tab-pane fade profile-overview" id="traceability"> <div class="tab-pane fade profile-overview" id="traceability">
<h5 class="card-title">Traceability log Details</h5> <h5 class="card-title">Traceability log Details</h5>
<div class="list-group col-6"> <div class="list-group col-6">
@ -233,35 +194,42 @@
</div> </div>
<div class="tab-pane fade profile-overview" id="components"> <div class="tab-pane fade profile-overview" id="components">
<h5 class="card-title">Components Snapshot</h5> <h5 class="card-title">Components last snapshot</h5>
<div class="list-group col-6"> <div class="list-group col-6">
{% for c in snapshot.components %}
<div class="list-group-item"> <div class="list-group-item">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Motherboard</h5> <h5 class="mb-1">{{ c.type }}</h5>
<small class="text-muted">14:07 23-06-2024</small> <small class="text-muted">{{ snapshot.created }}</small>
</div> </div>
<p class="mb-1"> <p class="mb-1">
hp<br /> {{ c.manufacturer }}<br />
890e<br /> {{ c.model }}<br />
{{ c.serialNumber }}<br />
</p> </p>
<small class="text-muted"> <small class="text-muted">
</small> </small>
</div> </div>
{% endfor %}
</div>
</div>
<div class="tab-pane fade profile-overview" id="snapshots">
<h5 class="card-title">List of snapshots</h5>
<div class="list-group col-6">
{% for snap in object.snapshots %}
<div class="list-group-item"> <div class="list-group-item">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">NetworkAdapter</h5> <h5 class="mb-1"></h5>
<small class="text-muted">14:07 23-06-2024</small> <small class="text-muted">{{ snap.created }}</small>
</div> </div>
<p class="mb-1"> <p class="mb-1">
realtek semiconductor co., ltd.<br /> {{ snap.uuid }}<br />
rtl8852ae 802.11ax pcie wireless network adapter<br />
</p> </p>
<small class="text-muted"> <small class="text-muted">
</small> </small>
</div> </div>
{% endfor %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ app_name = 'device'
urlpatterns = [ urlpatterns = [
path("add/", views.NewDeviceView.as_view(), name="add"), path("add/", views.NewDeviceView.as_view(), name="add"),
path("edit/<uuid:pk>/", views.EditDeviceView.as_view(), name="edit"), path("edit/<int:pk>/", views.EditDeviceView.as_view(), name="edit"),
path("<uuid:pk>/", views.DetailsView.as_view(), name="details"), path("<int:pk>/", views.DetailsView.as_view(), name="details"),
path("physical/<uuid:pk>/", views.PhysicalView.as_view(), name="physical_edit"), path("<int:pk>/annotation/add", views.AddAnnotationView.as_view(), name="add_annotation"),
] ]

View File

@ -11,7 +11,8 @@ from django.views.generic.base import TemplateView
from dashboard.mixins import DashboardView, DetailsMixin from dashboard.mixins import DashboardView, DetailsMixin
from snapshot.models import Annotation from snapshot.models import Annotation
from snapshot.xapian import search from snapshot.xapian import search
from device.models import Device, PhysicalProperties from lot.models import LotTag
from device.models import Device
class NewDeviceView(DashboardView, CreateView): class NewDeviceView(DashboardView, CreateView):
@ -20,24 +21,10 @@ class NewDeviceView(DashboardView, CreateView):
breadcrumb = "Device / New Device" breadcrumb = "Device / New Device"
success_url = reverse_lazy('dashboard:unassigned_devices') success_url = reverse_lazy('dashboard:unassigned_devices')
model = Device model = Device
fields = (
'type',
"model",
"manufacturer",
"serial_number",
"part_number",
"brand",
"generation",
"version",
"production_date",
"variant",
"family",
)
def form_valid(self, form): def form_valid(self, form):
form.instance.owner = self.request.user form.instance.owner = self.request.user
response = super().form_valid(form) response = super().form_valid(form)
PhysicalProperties.objects.create(device=form.instance)
return response return response
@ -47,19 +34,6 @@ class EditDeviceView(DashboardView, UpdateView):
breadcrumb = "Device / Update Device" breadcrumb = "Device / Update Device"
success_url = reverse_lazy('dashboard:unassigned_devices') success_url = reverse_lazy('dashboard:unassigned_devices')
model = Device model = Device
fields = (
'type',
"model",
"manufacturer",
"serial_number",
"part_number",
"brand",
"generation",
"version",
"production_date",
"variant",
"family",
)
def get_form_kwargs(self): def get_form_kwargs(self):
pk = self.kwargs.get('pk') pk = self.kwargs.get('pk')
@ -69,71 +43,51 @@ class EditDeviceView(DashboardView, UpdateView):
return kwargs return kwargs
class DetailsView2(DetailsMixin): class DetailsView(DetailsMixin):
template_name = "details.html" template_name = "details.html"
title = _("Device") title = _("Device")
breadcrumb = "Device / Details" breadcrumb = "Device / Details"
model = Device model = Device
class DetailsView(DashboardView, TemplateView):
template_name = "details.html"
title = _("Device")
breadcrumb = "Device / Details"
def get(self, request, *args, **kwargs):
# import pdb; pdb.set_trace()
self.pk = kwargs['pk']
annotation = get_object_or_404(Annotation, owner=self.request.user, uuid=self.pk)
for xa in search([str(self.pk)]):
self.object = json.loads(xa.document.get_data())
return super().get(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
self.object.initial()
lot_tags = LotTag.objects.filter(owner=self.request.user)
context.update({ context.update({
'object': self.object, 'snapshot': self.object.get_last_snapshot(),
'lot_tags': lot_tags,
}) })
return context return context
class AddAnnotationView(DashboardView, CreateView):
class PhysicalView(DashboardView, UpdateView): template_name = "new_device.html"
template_name = "physical_properties.html" title = _("New annotation")
title = _("Physical Properties") breadcrumb = "Device / New annotation"
breadcrumb = "Device / Physical properties"
success_url = reverse_lazy('dashboard:unassigned_devices') success_url = reverse_lazy('dashboard:unassigned_devices')
model = PhysicalProperties model = Annotation
fields = ( fields = ("key", "value")
"weight",
"width",
"height",
"depth",
"color",
"image",
)
def get_context_data(self, **kwargs): def form_valid(self, form):
context = super().get_context_data(**kwargs) self.device.get_annotations()
context.update({ self.device.get_uuids()
'device': self.device, form.instance.owner = self.request.user
}) form.instance.device = self.device
return context form.instance.uuid = self.device.uuids[0]
form.instance.type = Annotation.Type.USER
response = super().form_valid(form)
return response
def get_form_kwargs(self): def get_form_kwargs(self):
pk = self.kwargs.get('pk') pk = self.kwargs.get('pk')
self.device = get_object_or_404(Device, pk=pk) self.device = get_object_or_404(Device, pk=pk)
try: self.success_url = reverse_lazy('device:details', args=[pk])
self.object = self.device.physicalproperties
except Exception:
self.object = PhysicalProperties.objects.create(device=self.device)
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return kwargs return kwargs
def form_valid(self, form): def get_success_url(self):
self.success_url = reverse_lazy('device:details', args=[self.device.id]) url = super().get_success_url()
response = super().form_valid(form) import pdb; pdb.set_trace()
return response return url

1
example/snapshot1.json Normal file
View File

@ -0,0 +1 @@
{"type": "Snapshot", "components": [{"type": "SoundCard", "model": "Xeon E3-1200 V3/4th Gen Core Processor Hd Audio Controller", "manufacturer": "Intel Corporation"}, {"type": "SoundCard", "model": "Hp Hd Webcam", "manufacturer": "Chicony Electronics Co.,ltd.", "serialNumber": "200901010001"}, {"type": "SoundCard", "model": "8 Series/c220 Series Chipset High Definition Audio Controller", "manufacturer": "Intel Corporation"}, {"type": "RamModule", "model": "M471b5273ch0-Yk0", "manufacturer": "Samsung", "serialNumber": "96C0FA89", "size": 4096, "speed": 1600, "interface": "DDR3", "format": "SODIMM"}, {"type": "Processor", "actions": [{"type": "BenchmarkProcessor", "elapsed": 0, "rate": 19951.48}, {"type": "BenchmarkProcessorSysbench", "elapsed": 15, "rate": 14.6652}], "model": "Intel Core I5-4200m Cpu @ 2.50ghz", "manufacturer": "Intel Corp.", "brand": "Core i5", "generation": 4, "speed": 2.524414, "cores": 2, "threads": 4, "address": 64}, {"type": "NetworkAdapter", "model": "Ethernet Connection I217-V", "manufacturer": "Intel Corporation", "serialNumber": "FC:15:B4:E7:5D:D7", "variant": "04", "speed": 1000, "wireless": false}, {"type": "NetworkAdapter", "model": "Bcm43228 802.11a/b/g/n", "manufacturer": "Broadcom Inc. and Subsidiaries", "variant": "00", "wireless": false}, {"type": "SolidStateDrive", "actions": [{"type": "BenchmarkDataStorage", "elapsed": 2, "readSpeed": 487, "writeSpeed": 179}], "model": "Emtec X150 120gb", "serialNumber": "LDS645R002202", "variant": "5.0", "size": 120034.123776, "interface": "ATA"}, {"type": "Display", "model": "Lcd Monitor", "manufacturer": "Auo", "productionDate": "2012-01-01T00:00:00", "size": 15.529237982414482, "technology": "LCD", "resolutionWidth": 1366, "resolutionHeight": 768, "refreshRate": 60}, {"type": "GraphicCard", "model": "4th Gen Core Processor Integrated Graphics Controller", "manufacturer": "Intel Corporation"}, {"type": "Motherboard", "model": "1993", "manufacturer": "Hewlett-Packard", "serialNumber": "PEBJK001X5ZI3Z", "version": "L77 Ver. 01.05", "slots": 2, "usb": 3, "firewire": 0, "serial": 1, "pcmcia": 0, "biosDate": "2014-04-28T22:00:00.000Z", "ramSlots": 2, "ramMaxSize": 16}], "device": {"type": "Laptop", "actions": [{"type": "BenchmarkRamSysbench", "elapsed": 1, "rate": 0.731}], "model": "Hp Probook 650 G1", "manufacturer": "Hewlett-Packard", "serialNumber": "CNU406CLGR", "version": "A3009DD10303", "sku": "H5G75EA#ABE", "chassis": "Netbook"}, "closed": true, "endTime": "2022-06-09T12:10:50.809Z", "uuid": "7928afeb-e6a4-464a-a842-0c3de0d01677", "software": "Workbench", "version": "12.0b0", "elapsed": 34}

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-07-08 13:55 # Generated by Django 5.0.6 on 2024-07-17 14:57
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -10,11 +10,33 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("device", "0003_remove_component_type_remove_computer_type_and_more"), ("device", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel(
name="LotTag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=64)),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel( migrations.CreateModel(
name="Lot", name="Lot",
fields=[ fields=[
@ -29,18 +51,6 @@ class Migration(migrations.Migration):
), ),
("created", models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)), ("updated", models.DateTimeField(auto_now=True)),
(
"type",
models.CharField(
choices=[
("Incoming", "Incoming"),
("Outgoing", "Outgoing"),
("Temporal", "Temporal"),
],
default="Temporal",
max_length=32,
),
),
("name", models.CharField(blank=True, max_length=64, null=True)), ("name", models.CharField(blank=True, max_length=64, null=True)),
("code", models.CharField(blank=True, max_length=64, null=True)), ("code", models.CharField(blank=True, max_length=64, null=True)),
("description", models.CharField(blank=True, max_length=64, null=True)), ("description", models.CharField(blank=True, max_length=64, null=True)),
@ -53,6 +63,12 @@ class Migration(migrations.Migration):
to=settings.AUTH_USER_MODEL, to=settings.AUTH_USER_MODEL,
), ),
), ),
(
"type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="lot.lottag"
),
),
], ],
), ),
] ]

View File

@ -1,41 +1,31 @@
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from utils.constants import STR_SM_SIZE, STR_SIZE from utils.constants import (
STR_SM_SIZE,
STR_SIZE,
STR_EXTEND_SIZE,
)
from user.models import User from user.models import User
from device.models import Device from device.models import Device
from snapshot.models import Annotation
class LotTag(models.Model):
name = models.CharField(max_length=STR_SIZE, blank=False, null=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Lot(models.Model): class Lot(models.Model):
class Types(models.TextChoices):
INCOMING = "Incoming"
OUTGOING = "Outgoing"
TEMPORAL = "Temporal"
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)
type = models.CharField(max_length=STR_SM_SIZE, choices=Types, default=Types.TEMPORAL)
name = models.CharField(max_length=STR_SIZE, blank=True, null=True) name = models.CharField(max_length=STR_SIZE, blank=True, null=True)
code = models.CharField(max_length=STR_SIZE, blank=True, null=True) code = models.CharField(max_length=STR_SIZE, blank=True, null=True)
description = models.CharField(max_length=STR_SIZE, blank=True, null=True) description = models.CharField(max_length=STR_SIZE, blank=True, null=True)
closed = models.BooleanField(default=True) closed = models.BooleanField(default=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.ForeignKey(LotTag, on_delete=models.CASCADE)
devices = models.ManyToManyField(Device) devices = models.ManyToManyField(Device)
@property
def is_incoming(self):
if self.type == self.Types.INCOMING:
return True
return False
@property
def is_outgoing(self):
if self.type == self.Types.OUTGOING:
return True
return False
@property
def is_temporal(self):
if self.type == self.Types.TEMPORAL:
return True
return False

View File

@ -11,50 +11,21 @@
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{% if incoming %} {% for tag in lot_tags %}
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label ">Incoming Lots</div> <div class="col-lg-3 col-md-4 label ">{{ tag }}</div>
</div> </div>
{% for lot in lots %} {% for lot in lots %}
{% if lot.is_incoming %} {% if lot.type == tag %}
<div class="row"> <div class="row">
<div class="col-lg-3 col-md-4 label "><input type="checkbox" name="lots" value="{{ lot.id }}" /></div> <div class="col-lg-3 col-md-4 label "><input type="checkbox" name="lots" value="{{ lot.id }}" /></div>
<div class="col-lg-3 col-md-4 label ">{{ lot.name }}</div> <div class="col-lg-3 col-md-4 label ">{{ lot.name }}</div>
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %}
{% if outgoing %}
<div class="row">
<div class="col-lg-3 col-md-4 label ">Outgoing Lots</div>
</div>
{% for lot in lots %}
{% if lot.is_outgoing %}
<div class="row">
<div class="col-lg-3 col-md-4 label "><input type="checkbox" name="lots" value="{{ lot.id }}" /></div>
<div class="col-lg-3 col-md-4 label ">{{ lot.name }}</div>
</div>
{% endif %}
{% endfor %} {% endfor %}
{% endif %}
{% if temporal %}
<div class="row">
<div class="col-lg-3 col-md-4 label ">Temporary Lots</div>
</div>
{% for lot in lots %}
{% if lot.is_temporal %}
<div class="row">
<div class="col-lg-3 col-md-4 label "><input type="checkbox" name="lots" value="{{ lot.id }}" /></div>
<div class="col-lg-3 col-md-4 label ">{{ lot.name }}</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
<button type="submit">Save</button> <button type="submit">Save</button>
</form> </form>

View File

@ -9,7 +9,5 @@ urlpatterns = [
path("edit/<int:pk>/", views.EditLotView.as_view(), name="edit"), path("edit/<int:pk>/", views.EditLotView.as_view(), name="edit"),
path("add/devices/", views.AddToLotView.as_view(), name="add_devices"), path("add/devices/", views.AddToLotView.as_view(), name="add_devices"),
path("del/devices/", views.DelToLotView.as_view(), name="del_devices"), path("del/devices/", views.DelToLotView.as_view(), name="del_devices"),
path("temporal/", views.LotsTemporalView.as_view(), name="lots_temporal"), path("tag/<int:pk>/", views.LotsTagsView.as_view(), name="tag"),
path("outgoing/", views.LotsOutgoingView.as_view(), name="lots_outgoing"),
path("incoming/", views.LotsIncomingView.as_view(), name="lots_incoming"),
] ]

View File

@ -9,7 +9,7 @@ from django.views.generic.edit import (
FormView FormView
) )
from dashboard.mixins import DashboardView from dashboard.mixins import DashboardView
from lot.models import Lot from lot.models import Lot, LotTag
from lot.forms import LotsForm from lot.forms import LotsForm
@ -84,20 +84,15 @@ class AddToLotView(DashboardView, FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
lots = Lot.objects.filter(owner=self.request.user) lots = Lot.objects.filter(owner=self.request.user)
lots_incoming = lots.filter(type=Lot.Types.INCOMING).exists() lot_tags = LotTag.objects.filter(owner=self.request.user)
lots_outgoing = lots.filter(type=Lot.Types.OUTGOING).exists()
lots_temporal = lots.filter(type=Lot.Types.TEMPORAL).exists()
context.update({ context.update({
'lots': lots, 'lots': lots,
'incoming': lots_incoming, 'lot_tags':lot_tags,
'outgoing': lots_outgoing,
'temporal': lots_temporal
}) })
return context return context
def get_form(self): def get_form(self):
form = super().get_form() form = super().get_form()
# import pdb; pdb.set_trace()
form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user) form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user)
return form return form
@ -119,28 +114,23 @@ class DelToLotView(AddToLotView):
return response return response
class LotsTemporalView(DashboardView, TemplateView): class LotsTagsView(DashboardView, TemplateView):
template_name = "lots.html" template_name = "lots.html"
title = _("Temporal lots") title = _("lots")
breadcrumb = "lot / temporal lots" breadcrumb = _("lots") + " /"
success_url = reverse_lazy('dashboard:unassigned_devices') success_url = reverse_lazy('dashboard:unassigned_devices')
lot_type = Lot.Types.TEMPORAL
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk')
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
lots = Lot.objects.filter(owner=self.request.user) tag = get_object_or_404(LotTag, owner=self.request.user, id=self.pk)
self.title += " {}".format(tag.name)
self.breadcrumb += " {}".format(tag.name)
lots = Lot.objects.filter(owner=self.request.user).filter(type=tag)
context.update({ context.update({
'lots': lots.filter(type=self.lot_type), 'lots': lots,
'title': self.title,
'breadcrumb': self.breadcrumb
}) })
return context return context
class LotsOutgoingView(LotsTemporalView):
title = _("Outgoing lots")
breadcrumb = "lot / outging lots"
lot_type = Lot.Types.OUTGOING
class LotsIncomingView(LotsTemporalView):
title = _("Incoming lots")
breadcrumb = "lot / Incoming lots"
lot_type = Lot.Types.INCOMING

4
reset.sh Normal file
View File

@ -0,0 +1,4 @@
rm db/*
./manage.py migrate
./manage.py add_user user@example.org 1234
./manage.py up_snapshots example/ user@example.org

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-06-11 09:20 # Generated by Django 5.0.6 on 2024-07-17 14:57
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -16,7 +16,7 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="Snapshot", name="Annotation",
fields=[ fields=[
( (
"id", "id",
@ -28,39 +28,17 @@ class Migration(migrations.Migration):
), ),
), ),
("created", models.DateTimeField(auto_now_add=True)), ("created", models.DateTimeField(auto_now_add=True)),
(
"software",
models.CharField(
choices=[("Workbench", "Workbench")],
default="Workbench",
max_length=32,
),
),
("uuid", models.UUIDField()), ("uuid", models.UUIDField()),
("version", models.CharField(max_length=32)),
("sid", models.CharField(max_length=32)),
("settings_version", models.CharField(max_length=32)),
("is_server_erase", models.BooleanField(default=False)),
( (
"severity", "type",
models.SmallIntegerField( models.SmallIntegerField(choices=[(0, "System"), (1, "User")]),
choices=[
(0, "Info"),
(1, "Notice"),
(2, "Warning"),
(3, "Error"),
],
default=0,
),
), ),
("start_time", models.DateTimeField()), ("key", models.CharField(max_length=256)),
("end_time", models.DateTimeField()), ("value", models.CharField(max_length=256)),
("components", models.ManyToManyField(to="device.component")),
( (
"computer", "device",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE, to="device.device"
to="device.computer",
), ),
), ),
( (
@ -72,4 +50,10 @@ class Migration(migrations.Migration):
), ),
], ],
), ),
migrations.AddConstraint(
model_name="annotation",
constraint=models.UniqueConstraint(
fields=("type", "key", "uuid"), name="unique_type_key_uuid"
),
),
] ]

View File

@ -1,18 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-11 14:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("snapshot", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="snapshot",
name="uuid",
field=models.UUIDField(unique=True),
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-11 14:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("snapshot", "0002_alter_snapshot_uuid"),
]
operations = [
migrations.RemoveField(
model_name="snapshot",
name="start_time",
),
]

View File

@ -1,41 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-15 09:20
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("snapshot", "0003_remove_snapshot_start_time"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="anotation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("uuid", models.UUIDField(unique=True)),
("key", models.CharField(max_length=256)),
("value", models.CharField(max_length=256)),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@ -1,44 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-15 09:21
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("snapshot", "0004_anotation"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="Annotation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(auto_now_add=True)),
("uuid", models.UUIDField(unique=True)),
("key", models.CharField(max_length=256)),
("value", models.CharField(max_length=256)),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.DeleteModel(
name="anotation",
),
]

View File

@ -1,39 +1,76 @@
import json
from django.db import models from django.db import models
from utils.constants import STR_SM_SIZE, STR_EXTEND_SIZE from utils.constants import STR_SM_SIZE, STR_EXTEND_SIZE
from snapshot.xapian import search
from user.models import User from user.models import User
from device.models import Computer, Component
# Create your models here.
class Snapshot(models.Model): class Snapshot:
class SoftWare(models.TextChoices): def __init__(self, uuid):
WORKBENCH= "Workbench" self.uuid = uuid
self.owner = None
self.doc = None
self.created = None
self.annotations = []
class Severity(models.IntegerChoices): self.get_owner()
Info = 0, "Info" self.get_time()
Notice = 1, "Notice"
Warning = 2, "Warning"
Error = 3, "Error"
created = models.DateTimeField(auto_now_add=True) def get_annotations(self):
software = models.CharField(max_length=STR_SM_SIZE, choices=SoftWare, default=SoftWare.WORKBENCH) self.annotations = Annotation.objects.filter(
uuid = models.UUIDField(unique=True) uuid=self.uuid
version = models.CharField(max_length=STR_SM_SIZE) ).order_by("created")
sid = models.CharField(max_length=STR_SM_SIZE)
settings_version = models.CharField(max_length=STR_SM_SIZE) def get_owner(self):
is_server_erase = models.BooleanField(default=False) if not self.annotations:
severity = models.SmallIntegerField(choices=Severity, default=Severity.Info) self.get_annotations()
end_time = models.DateTimeField() a = self.annotations.first()
owner = models.ForeignKey(User, on_delete=models.CASCADE) if a:
computer = models.ForeignKey(Computer, on_delete=models.CASCADE) self.owner = a.owner
components = models.ManyToManyField(Component)
def get_doc(self):
self.doc = {}
qry = 'uuid:"{}"'.format(self.uuid)
matches = search(qry, limit=1)
if matches.size() < 0:
return
for xa in matches:
self.doc = json.loads(xa.document.get_data())
def get_time(self):
if not self.doc:
self.get_doc()
self.created = self.doc.get("endTime")
if not self.created:
self.created = self.annotations.last().created
def components(self):
return self.doc.get('components', [])
class Annotation(models.Model): class Annotation(models.Model):
class Type(models.IntegerChoices):
SYSTEM= 0, "System"
USER = 1, "User"
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField(unique=True) uuid = models.UUIDField()
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.SmallIntegerField(choices=Type)
key = models.CharField(max_length=STR_EXTEND_SIZE) key = models.CharField(max_length=STR_EXTEND_SIZE)
value = models.CharField(max_length=STR_EXTEND_SIZE) value = models.CharField(max_length=STR_EXTEND_SIZE)
device = models.ForeignKey('device.Device', on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=["type", "key", "uuid"], name="unique_type_key_uuid")
]
def is_user_annotation(self):
if self.type == self.Type.USER:
return True
return False

View File

@ -5,42 +5,25 @@ import xapian
import hashlib import hashlib
from datetime import datetime from datetime import datetime
from snapshot.xapian import search, index
from snapshot.models import Snapshot, Annotation from snapshot.models import Snapshot, Annotation
from snapshot.xapian import search, indexer, database from device.models import Device
from utils.constants import ALGOS
HID_ALGO1 = [
"manufacturer",
"model",
"chassis",
"serialNumber",
"sku"
]
class Build: class Build:
def __init__(self, snapshot_json, user): def __init__(self, snapshot_json, user):
self.json = snapshot_json self.json = snapshot_json
self.uuid = self.json['uuid']
self.user = user self.user = user
self.hid = None self.hid = None
self.index() self.index()
self.create_annotation() self.create_annotations()
def index(self): def index(self):
matches = search(self.json['uuid'], limit=1)
if matches.size() > 0:
return
snap = json.dumps(self.json) snap = json.dumps(self.json)
doc = xapian.Document() index(self.uuid, snap)
doc.set_data(snap)
indexer.set_document(doc)
indexer.index_text(snap)
# Add the document to the database.
database.add_document(doc)
def get_hid_14(self): def get_hid_14(self):
device = self.json['device'] device = self.json['device']
@ -52,15 +35,35 @@ class Build:
hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}" hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}"
return hashlib.sha3_256(hid.encode()).hexdigest() return hashlib.sha3_256(hid.encode()).hexdigest()
def create_annotation(self): def create_annotations(self):
uuid = self.json['uuid'] algorithms = {
owner = self.user 'hidalgo1': self.get_hid_14(),
key = 'hidalgo1' }
value = self.get_hid_14()
Annotation.objects.create( annotation = Annotation.objects.filter(
uuid=uuid, owner=self.user,
owner=owner, type=Annotation.Type.SYSTEM,
key=key, key='hidalgo1',
value=value value = algorithms['hidalgo1']
) ).first()
if annotation:
device = annotation.device
else:
device = Device.objects.create(
type=self.json["device"]["type"],
manufacturer=self.json["device"]["manufacturer"],
model=self.json["device"]["model"],
owner=self.user
)
for k, v in algorithms.items():
Annotation.objects.create(
uuid=self.uuid,
owner=self.user,
device=device,
type=Annotation.Type.SYSTEM,
key=k,
value=v
)

View File

@ -2,9 +2,13 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.edit import (
CreateView,
UpdateView,
)
from dashboard.mixins import DashboardView from dashboard.mixins import DashboardView
from snapshot.models import Snapshot from snapshot.models import Snapshot, Annotation
# from snapshot.forms import UploadForm # from snapshot.forms import UploadForm
# from django.shortcuts import render # from django.shortcuts import render
# from rest_framework import viewsets # from rest_framework import viewsets
@ -24,7 +28,8 @@ class ListSnapshotsView(DashboardView, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
snapshots = Snapshot.objects.filter(owner=self.request.user) # snapshots = Snapshot.objects.filter(owner=self.request.user)
snapshots = []
context.update({ context.update({
'snapshots': snapshots, 'snapshots': snapshots,
}) })

View File

@ -1,22 +1,51 @@
import xapian import xapian
database = xapian.WritableDatabase("db", xapian.DB_CREATE_OR_OPEN) # database = xapian.WritableDatabase("db", xapian.DB_CREATE_OR_OPEN)
indexer = xapian.TermGenerator() # Read Only
stemmer = xapian.Stem("english") # database = xapian.Database("db")
indexer.set_stemmer(stemmer)
# indexer = xapian.TermGenerator()
# stemmer = xapian.Stem("english")
# indexer.set_stemmer(stemmer)
def search(qs, offset=0, limit=10): def search(qs, offset=0, limit=10):
query_string = str.join(' ', qs) database = xapian.Database("db")
qp = xapian.QueryParser() qp = xapian.QueryParser()
qp.set_stemmer(stemmer)
qp.set_database(database) qp.set_database(database)
qp.set_stemmer(xapian.Stem("english"))
qp.set_stemming_strategy(xapian.QueryParser.STEM_SOME) qp.set_stemming_strategy(xapian.QueryParser.STEM_SOME)
query = qp.parse_query(query_string) qp.add_prefix("uuid", "uuid")
# qp.add_prefix("snapshot", "snapshot")
query = qp.parse_query(qs)
enquire = xapian.Enquire(database) enquire = xapian.Enquire(database)
enquire.set_query(query) enquire.set_query(query)
matches = enquire.get_mset(offset, limit) matches = enquire.get_mset(offset, limit)
return matches return matches
def index(uuid, snap):
uuid = 'uuid:"{}"'.format(uuid)
try:
matches = search(uuid, limit=1)
if matches.size() > 0:
return
except xapian.DatabaseNotFoundError:
pass
database = xapian.WritableDatabase("db", xapian.DB_CREATE_OR_OPEN)
indexer = xapian.TermGenerator()
stemmer = xapian.Stem("english")
indexer.set_stemmer(stemmer)
doc = xapian.Document()
doc.set_data(snap)
indexer.set_document(doc)
indexer.index_text(snap)
indexer.index_text(uuid, 10, "uuid")
# indexer.index_text(snap, 1, "snapshot")
database.add_document(doc)

View File

@ -1,5 +1,6 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from lot.models import LotTag
User = get_user_model() User = get_user_model()
@ -16,8 +17,21 @@ class Command(BaseCommand):
email = kwargs['email'] email = kwargs['email']
password = kwargs['password'] password = kwargs['password']
self.create_user(email, password) self.create_user(email, password)
self.create_lot_tags()
def create_user(self, email, password): def create_user(self, email, password):
u = User.objects.create(email=email, password=password) self.u = User.objects.create(email=email, password=password)
u.set_password(password) self.u.set_password(password)
u.save() self.u.save()
def create_lot_tags(self):
tags = [
"Entrada",
"Salida",
"Temporal"
]
for tag in tags:
LotTag.objects.create(
name=tag,
owner=self.u
)

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-06-11 09:19 # Generated by Django 5.0.6 on 2024-07-17 14:57
from django.db import migrations, models from django.db import migrations, models

View File

@ -6,3 +6,17 @@ STR_SM_SIZE = 32
STR_SIZE = 64 STR_SIZE = 64
STR_BIG_SIZE = 128 STR_BIG_SIZE = 128
STR_EXTEND_SIZE = 256 STR_EXTEND_SIZE = 256
# Algorithms for build hids
HID_ALGO1 = [
"manufacturer",
"model",
"chassis",
"serialNumber",
"sku"
]
ALGOS = {
"hidalgo1": HID_ALGO1,
}