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.views.generic.base import TemplateView
from device.models import Device
from lot.models import LotTag
class Http403(PermissionDenied):
@ -37,6 +38,7 @@ class DashboardView(LoginRequiredMixin):
'section': self.section,
'path': resolve(self.request.path).url_name,
'user': self.request.user,
'lot_tags': LotTag.objects.filter(owner=self.request.user)
})
return context

View file

@ -103,21 +103,13 @@
{% trans 'Lots' %}
</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">
{% for tag in lot_tags %}
<li class="nav-items">
<a class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'lot:lots_incoming' %}">
{% trans 'Incoming ' %}
</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 class="nav-link{% if path == 'admin_people_list' %} active2{% endif %}" href="{% url 'lot:tag' tag.id %}">
{{ tag.name }}
</a>
</li>
{% endfor %}
</ul>
</li>
<li class="nav-item">

View file

@ -6,7 +6,7 @@ from dashboard.mixins import InventaryMixin, DetailsMixin
from device.models import Device
from snapshot.xapian import search
from snapshot.models import Annotation
from lot.models import Lot
from lot.models import Lot, LotTag
class UnassignedDevicesView(InventaryMixin):
@ -17,30 +17,14 @@ class UnassignedDevicesView(InventaryMixin):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
annotations = Annotation.objects.filter(
owner=self.request.user).filter(
key="hidalgo1").order_by('created')
# '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)
devices = Device.objects.filter(
owner=self.request.user
).annotate(num_lots=Count('lot')).filter(num_lots=0)
context.update({
'devices': devices
'devices': devices,
})
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
from django.conf import settings
@ -26,31 +26,7 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
("created", models.DateTimeField(auto_now_add=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)),
("type", models.CharField(blank=True, max_length=64, null=True)),
(
"owner",
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 utils.constants import STR_SM_SIZE, STR_SIZE, STR_EXTEND_SIZE, ALGOS
from snapshot.models import Annotation, Snapshot
from user.models import User
from utils.constants import STR_SM_SIZE, STR_SIZE
class Device(models.Model):
@ -20,155 +22,59 @@ class Device(models.Model):
BATTERY = "Battery"
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)
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):
try:
if self.physicalproperties:
return True
else:
return False
except Exception:
return False
def __init__(self, *args, **kwargs):
self.annotations = []
self.hids = []
self.uuids = []
self.snapshots = []
super().__init__(*args, **kwargs)
def initial(self):
self.get_annotations()
self.get_uuids()
self.get_hids()
self.get_snapshots()
def get_annotations(self):
self.annotations = Annotation.objects.filter(
device=self,
owner=self.owner
).order_by("-created")
def get_uuids(self):
for a in self.annotations:
if not a.uuid in self.uuids:
self.uuids.append(a.uuid)
class PhysicalProperties(models.Model):
device = models.OneToOneField(Device, models.CASCADE, primary_key=True)
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(max_length=20, blank=True, null=True)
image = models.CharField(max_length=STR_SIZE, blank=True, null=True)
def get_hids(self):
if not self.annotations:
self.get_annotations()
self.hids = self.annotations.filter(
type=Annotation.Type.SYSTEM,
key__in=ALGOS.keys(),
).values_list("value", flat=True)
class Computer(models.Model):
class Chassis(models.TextChoices):
TOWER = 'Tower'
ALLINONE = 'All in one'
MICROTOWER = 'Microtower'
NETBOOK = 'Netbook'
LAPTOP = 'Laptop'
TABLER = 'Tablet'
SERVER = "Server"
VIRTUAL = 'Non-physical device'
def get_snapshots(self):
if not self.uuids:
self.get_uuids()
self.snapshots = [Snapshot(u) for u in self.uuids]
device = models.OneToOneField(Device, models.CASCADE, primary_key=True)
chassis = models.CharField(
blank=True,
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)
def get_last_snapshot(self):
if not self.snapshots:
self.get_snapshots()
if self.snapshots:
return self.snapshots[0]
class Component(models.Model):
device = models.OneToOneField(Device, models.CASCADE, primary_key=True)
computer = models.ForeignKey(Computer, on_delete=models.CASCADE, null=True)
class GraphicCard(models.Model):
component = models.OneToOneField(Component, models.CASCADE)
memory = models.IntegerField(blank=True, null=True)
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)
@classmethod
def get_unassigned(cls, user):
return cls.objects.filter(
owner=user
).annotate(num_lots=models.Count('lot')).filter(num_lots=0)

View file

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

View file

@ -5,7 +5,7 @@ app_name = 'device'
urlpatterns = [
path("add/", views.NewDeviceView.as_view(), name="add"),
path("edit/<uuid:pk>/", views.EditDeviceView.as_view(), name="edit"),
path("<uuid:pk>/", views.DetailsView.as_view(), name="details"),
path("physical/<uuid:pk>/", views.PhysicalView.as_view(), name="physical_edit"),
path("edit/<int:pk>/", views.EditDeviceView.as_view(), name="edit"),
path("<int:pk>/", views.DetailsView.as_view(), name="details"),
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 snapshot.models import Annotation
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):
@ -20,26 +21,12 @@ class NewDeviceView(DashboardView, CreateView):
breadcrumb = "Device / New Device"
success_url = reverse_lazy('dashboard:unassigned_devices')
model = Device
fields = (
'type',
"model",
"manufacturer",
"serial_number",
"part_number",
"brand",
"generation",
"version",
"production_date",
"variant",
"family",
)
def form_valid(self, form):
form.instance.owner = self.request.user
response = super().form_valid(form)
PhysicalProperties.objects.create(device=form.instance)
return response
class EditDeviceView(DashboardView, UpdateView):
template_name = "new_device.html"
@ -47,19 +34,6 @@ class EditDeviceView(DashboardView, UpdateView):
breadcrumb = "Device / Update Device"
success_url = reverse_lazy('dashboard:unassigned_devices')
model = Device
fields = (
'type',
"model",
"manufacturer",
"serial_number",
"part_number",
"brand",
"generation",
"version",
"production_date",
"variant",
"family",
)
def get_form_kwargs(self):
pk = self.kwargs.get('pk')
@ -67,73 +41,53 @@ class EditDeviceView(DashboardView, UpdateView):
self.success_url = reverse_lazy('device:details', args=[pk])
kwargs = super().get_form_kwargs()
return kwargs
class DetailsView2(DetailsMixin):
class DetailsView(DetailsMixin):
template_name = "details.html"
title = _("Device")
breadcrumb = "Device / Details"
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):
context = super().get_context_data(**kwargs)
self.object.initial()
lot_tags = LotTag.objects.filter(owner=self.request.user)
context.update({
'object': self.object,
'snapshot': self.object.get_last_snapshot(),
'lot_tags': lot_tags,
})
return context
class PhysicalView(DashboardView, UpdateView):
template_name = "physical_properties.html"
title = _("Physical Properties")
breadcrumb = "Device / Physical properties"
class AddAnnotationView(DashboardView, CreateView):
template_name = "new_device.html"
title = _("New annotation")
breadcrumb = "Device / New annotation"
success_url = reverse_lazy('dashboard:unassigned_devices')
model = PhysicalProperties
fields = (
"weight",
"width",
"height",
"depth",
"color",
"image",
)
model = Annotation
fields = ("key", "value")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'device': self.device,
})
return context
def form_valid(self, form):
self.device.get_annotations()
self.device.get_uuids()
form.instance.owner = self.request.user
form.instance.device = self.device
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):
pk = self.kwargs.get('pk')
self.device = get_object_or_404(Device, pk=pk)
try:
self.object = self.device.physicalproperties
except Exception:
self.object = PhysicalProperties.objects.create(device=self.device)
self.success_url = reverse_lazy('device:details', args=[pk])
kwargs = super().get_form_kwargs()
return kwargs
def form_valid(self, form):
self.success_url = reverse_lazy('device:details', args=[self.device.id])
response = super().form_valid(form)
return response
def get_success_url(self):
url = super().get_success_url()
import pdb; pdb.set_trace()
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
from django.conf import settings
@ -10,11 +10,33 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
("device", "0003_remove_component_type_remove_computer_type_and_more"),
("device", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
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(
name="Lot",
fields=[
@ -29,18 +51,6 @@ class Migration(migrations.Migration):
),
("created", models.DateTimeField(auto_now_add=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)),
("code", 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,
),
),
(
"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.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 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 Types(models.TextChoices):
INCOMING = "Incoming"
OUTGOING = "Outgoing"
TEMPORAL = "Temporal"
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.TEMPORAL)
name = 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)
closed = models.BooleanField(default=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.ForeignKey(LotTag, on_delete=models.CASCADE)
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">
{% csrf_token %}
{% if incoming %}
{% for tag in lot_tags %}
<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>
{% for lot in lots %}
{% if lot.is_incoming %}
{% if lot.type == tag %}
<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 %}
{% 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 %}
{% 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>
</form>

View file

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

View file

@ -9,7 +9,7 @@ from django.views.generic.edit import (
FormView
)
from dashboard.mixins import DashboardView
from lot.models import Lot
from lot.models import Lot, LotTag
from lot.forms import LotsForm
@ -84,20 +84,15 @@ class AddToLotView(DashboardView, FormView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
lots = Lot.objects.filter(owner=self.request.user)
lots_incoming = lots.filter(type=Lot.Types.INCOMING).exists()
lots_outgoing = lots.filter(type=Lot.Types.OUTGOING).exists()
lots_temporal = lots.filter(type=Lot.Types.TEMPORAL).exists()
lot_tags = LotTag.objects.filter(owner=self.request.user)
context.update({
'lots': lots,
'incoming': lots_incoming,
'outgoing': lots_outgoing,
'temporal': lots_temporal
'lot_tags':lot_tags,
})
return context
def get_form(self):
form = super().get_form()
# import pdb; pdb.set_trace()
form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user)
return form
@ -119,28 +114,23 @@ class DelToLotView(AddToLotView):
return response
class LotsTemporalView(DashboardView, TemplateView):
class LotsTagsView(DashboardView, TemplateView):
template_name = "lots.html"
title = _("Temporal lots")
breadcrumb = "lot / temporal lots"
title = _("lots")
breadcrumb = _("lots") + " /"
success_url = reverse_lazy('dashboard:unassigned_devices')
lot_type = Lot.Types.TEMPORAL
def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk')
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({
'lots': lots.filter(type=self.lot_type),
'lots': lots,
'title': self.title,
'breadcrumb': self.breadcrumb
})
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
from django.conf import settings
@ -16,7 +16,7 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name="Snapshot",
name="Annotation",
fields=[
(
"id",
@ -28,39 +28,17 @@ class Migration(migrations.Migration):
),
),
("created", models.DateTimeField(auto_now_add=True)),
(
"software",
models.CharField(
choices=[("Workbench", "Workbench")],
default="Workbench",
max_length=32,
),
),
("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",
models.SmallIntegerField(
choices=[
(0, "Info"),
(1, "Notice"),
(2, "Warning"),
(3, "Error"),
],
default=0,
),
"type",
models.SmallIntegerField(choices=[(0, "System"), (1, "User")]),
),
("start_time", models.DateTimeField()),
("end_time", models.DateTimeField()),
("components", models.ManyToManyField(to="device.component")),
("key", models.CharField(max_length=256)),
("value", models.CharField(max_length=256)),
(
"computer",
"device",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="device.computer",
on_delete=django.db.models.deletion.CASCADE, to="device.device"
),
),
(
@ -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 utils.constants import STR_SM_SIZE, STR_EXTEND_SIZE
from snapshot.xapian import search
from user.models import User
from device.models import Computer, Component
# Create your models here.
class Snapshot(models.Model):
class SoftWare(models.TextChoices):
WORKBENCH= "Workbench"
class Snapshot:
def __init__(self, uuid):
self.uuid = uuid
self.owner = None
self.doc = None
self.created = None
self.annotations = []
class Severity(models.IntegerChoices):
Info = 0, "Info"
Notice = 1, "Notice"
Warning = 2, "Warning"
Error = 3, "Error"
self.get_owner()
self.get_time()
created = models.DateTimeField(auto_now_add=True)
software = models.CharField(max_length=STR_SM_SIZE, choices=SoftWare, default=SoftWare.WORKBENCH)
uuid = models.UUIDField(unique=True)
version = models.CharField(max_length=STR_SM_SIZE)
sid = models.CharField(max_length=STR_SM_SIZE)
settings_version = models.CharField(max_length=STR_SM_SIZE)
is_server_erase = models.BooleanField(default=False)
severity = models.SmallIntegerField(choices=Severity, default=Severity.Info)
end_time = models.DateTimeField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
computer = models.ForeignKey(Computer, on_delete=models.CASCADE)
components = models.ManyToManyField(Component)
def get_annotations(self):
self.annotations = Annotation.objects.filter(
uuid=self.uuid
).order_by("created")
def get_owner(self):
if not self.annotations:
self.get_annotations()
a = self.annotations.first()
if a:
self.owner = a.owner
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 Type(models.IntegerChoices):
SYSTEM= 0, "System"
USER = 1, "User"
created = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField(unique=True)
uuid = models.UUIDField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.SmallIntegerField(choices=Type)
key = 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
from datetime import datetime
from snapshot.xapian import search, index
from snapshot.models import Snapshot, Annotation
from snapshot.xapian import search, indexer, database
HID_ALGO1 = [
"manufacturer",
"model",
"chassis",
"serialNumber",
"sku"
]
from device.models import Device
from utils.constants import ALGOS
class Build:
def __init__(self, snapshot_json, user):
self.json = snapshot_json
self.uuid = self.json['uuid']
self.user = user
self.hid = None
self.index()
self.create_annotation()
self.create_annotations()
def index(self):
matches = search(self.json['uuid'], limit=1)
if matches.size() > 0:
return
snap = json.dumps(self.json)
doc = xapian.Document()
doc.set_data(snap)
indexer.set_document(doc)
indexer.index_text(snap)
# Add the document to the database.
database.add_document(doc)
index(self.uuid, snap)
def get_hid_14(self):
device = self.json['device']
@ -52,15 +35,35 @@ class Build:
hid = f"{manufacturer}{model}{chassis}{serial_number}{sku}"
return hashlib.sha3_256(hid.encode()).hexdigest()
def create_annotation(self):
uuid = self.json['uuid']
owner = self.user
key = 'hidalgo1'
value = self.get_hid_14()
Annotation.objects.create(
uuid=uuid,
owner=owner,
key=key,
value=value
)
def create_annotations(self):
algorithms = {
'hidalgo1': self.get_hid_14(),
}
annotation = Annotation.objects.filter(
owner=self.user,
type=Annotation.Type.SYSTEM,
key='hidalgo1',
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.edit import FormView
from django.urls import reverse_lazy
from django.views.generic.edit import (
CreateView,
UpdateView,
)
from dashboard.mixins import DashboardView
from snapshot.models import Snapshot
from snapshot.models import Snapshot, Annotation
# from snapshot.forms import UploadForm
# from django.shortcuts import render
# from rest_framework import viewsets
@ -24,7 +28,8 @@ class ListSnapshotsView(DashboardView, TemplateView):
def get_context_data(self, **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({
'snapshots': snapshots,
})

View file

@ -1,22 +1,51 @@
import xapian
database = xapian.WritableDatabase("db", xapian.DB_CREATE_OR_OPEN)
# database = xapian.WritableDatabase("db", xapian.DB_CREATE_OR_OPEN)
indexer = xapian.TermGenerator()
stemmer = xapian.Stem("english")
indexer.set_stemmer(stemmer)
# Read Only
# database = xapian.Database("db")
# indexer = xapian.TermGenerator()
# stemmer = xapian.Stem("english")
# indexer.set_stemmer(stemmer)
def search(qs, offset=0, limit=10):
query_string = str.join(' ', qs)
database = xapian.Database("db")
qp = xapian.QueryParser()
qp.set_stemmer(stemmer)
qp.set_database(database)
qp.set_stemmer(xapian.Stem("english"))
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.set_query(query)
matches = enquire.get_mset(offset, limit)
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.contrib.auth import get_user_model
from lot.models import LotTag
User = get_user_model()
@ -16,8 +17,21 @@ class Command(BaseCommand):
email = kwargs['email']
password = kwargs['password']
self.create_user(email, password)
self.create_lot_tags()
def create_user(self, email, password):
u = User.objects.create(email=email, password=password)
u.set_password(password)
u.save()
self.u = User.objects.create(email=email, password=password)
self.u.set_password(password)
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

View file

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