add annotations in device and in lots
This commit is contained in:
parent
91780be94b
commit
0136a98ff5
|
@ -7,21 +7,17 @@
|
|||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
<a href="{# url 'idhub:admin_people_edit' object.id #}" type="button" class="btn btn-green-admin">
|
||||
<a href="{% url 'lot:documents' object.id %}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-folder2"></i>
|
||||
{% trans 'Lots' %}
|
||||
</a>
|
||||
<a href="{# url 'idhub:admin_people_edit' object.id #}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-plus"></i>
|
||||
{% trans 'Actions' %}
|
||||
{% trans 'Documents' %}
|
||||
</a>
|
||||
<a href="{# url 'idhub:admin_people_activate' object.id #}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-reply"></i>
|
||||
{% trans 'Exports' %}
|
||||
</a>
|
||||
<a href="#" type="button" class="btn btn-green-admin">
|
||||
<a href="{% url 'lot:annotations' object.id %}" type="button" class="btn btn-green-admin">
|
||||
<i class="bi bi-tag"></i>
|
||||
{% trans 'Labels' %}
|
||||
{% trans 'Annotations' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import json
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db.models import Count
|
||||
from dashboard.mixins import InventaryMixin, DetailsMixin
|
||||
from device.models import Device
|
||||
from evidence.xapian import search
|
||||
from evidence.models import Annotation
|
||||
from lot.models import Lot, LotTag
|
||||
from lot.models import Lot
|
||||
|
||||
|
||||
class UnassignedDevicesView(InventaryMixin):
|
||||
|
|
|
@ -4,8 +4,8 @@ import hashlib
|
|||
import datetime
|
||||
|
||||
from django import forms
|
||||
from snapshot.models import Annotation
|
||||
from snapshot.xapian import search, index
|
||||
from evidence.models import Annotation
|
||||
from evidence.xapian import search, index
|
||||
|
||||
DEVICE_TYPES = [
|
||||
("Desktop", "Desktop"),
|
||||
|
@ -27,13 +27,19 @@ DEVICE_TYPES = [
|
|||
|
||||
class DeviceForm(forms.Form):
|
||||
type = forms.ChoiceField(choices = DEVICE_TYPES, required=False)
|
||||
amount = forms.IntegerField(required=True, initial=1)
|
||||
amount = forms.IntegerField(required=False, initial=1)
|
||||
tag = forms.CharField(required=False)
|
||||
name = forms.CharField(required=False)
|
||||
value = forms.CharField(required=False)
|
||||
|
||||
|
||||
class BaseDeviceFormSet(forms.BaseFormSet):
|
||||
def clean(self):
|
||||
for x in self.cleaned_data:
|
||||
if x.get("amount"):
|
||||
return True
|
||||
return False
|
||||
|
||||
def save(self, user, commit=True):
|
||||
self.user = user
|
||||
doc = {}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# 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),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# 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),
|
||||
),
|
||||
]
|
|
@ -1,18 +0,0 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-18 17:30
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("device", "0003_device_manufacturer"),
|
||||
("lot", "0002_remove_lot_devices_devicelot"),
|
||||
("snapshot", "0002_remove_annotation_device"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="Device",
|
||||
),
|
||||
]
|
|
@ -56,10 +56,32 @@ class Device:
|
|||
self.owner = self.annotations[0].owner
|
||||
|
||||
return self.annotations
|
||||
|
||||
def get_user_annotations(self):
|
||||
if not self.uuids:
|
||||
self.get_uuids()
|
||||
|
||||
annotations = Annotation.objects.filter(
|
||||
uuid__in=self.uuids,
|
||||
owner=self.owner,
|
||||
type=Annotation.Type.USER
|
||||
)
|
||||
return annotations
|
||||
|
||||
def get_user_documents(self):
|
||||
if not self.uuids:
|
||||
self.get_uuids()
|
||||
|
||||
annotations = Annotation.objects.filter(
|
||||
uuid__in=self.uuids,
|
||||
owner=self.owner,
|
||||
type=Annotation.Type.DOCUMENT
|
||||
)
|
||||
return annotations
|
||||
|
||||
def get_uuids(self):
|
||||
for a in self.get_annotations():
|
||||
if not a.uuid in self.uuids:
|
||||
if a.uuid not in self.uuids:
|
||||
self.uuids.append(a.uuid)
|
||||
|
||||
def get_hids(self):
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title">Annotations</h5>
|
||||
<h5 class="card-title mt-2">Annotations</h5>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -102,8 +102,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for a in object.annotations %}
|
||||
{% if a.is_user_annotation %}
|
||||
{% for a in object.get_user_annotations %}
|
||||
<tr>
|
||||
<td>{{ a.key }}</td>
|
||||
<td>{{ a.value }}</td>
|
||||
|
@ -111,7 +110,6 @@
|
|||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
|
@ -137,26 +135,35 @@
|
|||
|
||||
<div class="tab-pane fade profile-overview" id="documents">
|
||||
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
|
||||
<a href="/inventory/device/4W8D3/document/add/" class="btn btn-primary">
|
||||
<a href="{% url 'device:add_document' object.pk %}" class="btn btn-primary">
|
||||
|
||||
<i class="bi bi-plus"></i>
|
||||
Add new document
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title">Documents</h5>
|
||||
<table class="table">
|
||||
<h5 class="card-title mt-2">Documents</h5>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">File</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Description</th>
|
||||
<th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Uploaded on</th>
|
||||
<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.get_user_documents %}
|
||||
<tr>
|
||||
<td>{{ a.key }}</td>
|
||||
<td>{{ a.value }}</td>
|
||||
<td>{{ a.created }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
45
device/templates/new_annotation.html
Normal file
45
device/templates/new_annotation.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||
<div class="message">
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ form.management_form }}
|
||||
<div class="container" id="formset-container">
|
||||
<div class="row mb-2">
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
{% for f in form %}
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
{% bootstrap_field f %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="container">
|
||||
<a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -8,4 +8,5 @@ urlpatterns = [
|
|||
path("edit/<str:pk>/", views.EditDeviceView.as_view(), name="edit"),
|
||||
path("<str:pk>/", views.DetailsView.as_view(), name="details"),
|
||||
path("<str:pk>/annotation/add", views.AddAnnotationView.as_view(), name="add_annotation"),
|
||||
path("<str:pk>/document/add", views.AddDocumentView.as_view(), name="add_document"),
|
||||
]
|
||||
|
|
|
@ -10,8 +10,8 @@ from django.views.generic.edit import (
|
|||
)
|
||||
from django.views.generic.base import TemplateView
|
||||
from dashboard.mixins import DashboardView
|
||||
from snapshot.models import Annotation
|
||||
from snapshot.xapian import search
|
||||
from evidence.models import Annotation
|
||||
from evidence.xapian import search
|
||||
from lot.models import LotTag
|
||||
from device.models import Device
|
||||
from device.forms import DeviceFormSet
|
||||
|
@ -95,14 +95,14 @@ class DetailsView(DashboardView, TemplateView):
|
|||
lot_tags = LotTag.objects.filter(owner=self.request.user)
|
||||
context.update({
|
||||
'object': self.object,
|
||||
'snapshot': self.object.get_last_snapshot(),
|
||||
'snapshot': self.object.get_last_evidence(),
|
||||
'lot_tags': lot_tags,
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class AddAnnotationView(DashboardView, CreateView):
|
||||
template_name = "new_device.html"
|
||||
template_name = "new_annotation.html"
|
||||
title = _("New annotation")
|
||||
breadcrumb = "Device / New annotation"
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
|
@ -110,25 +110,46 @@ class AddAnnotationView(DashboardView, CreateView):
|
|||
fields = ("key", "value")
|
||||
|
||||
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.uuid = self.annotation.uuid
|
||||
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)
|
||||
self.annotation = Annotation.objects.filter(
|
||||
owner=self.request.user, value=pk, type=Annotation.Type.SYSTEM
|
||||
).first()
|
||||
if not self.annotation:
|
||||
get_object_or_404(Annotation, pk=0, owner=self.request.user)
|
||||
self.success_url = reverse_lazy('device:details', args=[pk])
|
||||
kwargs = super().get_form_kwargs()
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
url = super().get_success_url()
|
||||
import pdb; pdb.set_trace()
|
||||
return url
|
||||
|
||||
class AddDocumentView(DashboardView, CreateView):
|
||||
template_name = "new_annotation.html"
|
||||
title = _("New Document")
|
||||
breadcrumb = "Device / New document"
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = Annotation
|
||||
fields = ("key", "value")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.owner = self.request.user
|
||||
form.instance.uuid = self.annotation.uuid
|
||||
form.instance.type = Annotation.Type.DOCUMENT
|
||||
response = super().form_valid(form)
|
||||
return response
|
||||
|
||||
def get_form_kwargs(self):
|
||||
pk = self.kwargs.get('pk')
|
||||
self.annotation = Annotation.objects.filter(
|
||||
owner=self.request.user, value=pk, type=Annotation.Type.SYSTEM
|
||||
).first()
|
||||
if not self.annotation:
|
||||
get_object_or_404(Annotation, pk=0, owner=self.request.user)
|
||||
self.success_url = reverse_lazy('device:details', args=[pk])
|
||||
kwargs = super().get_form_kwargs()
|
||||
return kwargs
|
||||
|
|
|
@ -21,7 +21,7 @@ urlpatterns = [
|
|||
# path('api/', include('snapshot.urls')),
|
||||
path("", include("login.urls")),
|
||||
path("dashboard/", include("dashboard.urls")),
|
||||
path("snapshot/", include("snapshot.urls")),
|
||||
path("evidence/", include("evidence.urls")),
|
||||
path("device/", include("device.urls")),
|
||||
path("lot/", include("lot.urls")),
|
||||
]
|
||||
|
|
|
@ -4,7 +4,7 @@ import json
|
|||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.contrib.auth import get_user_model
|
||||
from snapshot.parse import Build
|
||||
from evidence.parse import Build
|
||||
|
||||
|
||||
User = get_user_model()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-17 14:57
|
||||
# Generated by Django 5.0.6 on 2024-07-27 16:23
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
@ -10,7 +10,6 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("device", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
@ -35,12 +34,6 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
("key", models.CharField(max_length=256)),
|
||||
("value", models.CharField(max_length=256)),
|
||||
(
|
||||
"device",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="device.device"
|
||||
),
|
||||
),
|
||||
(
|
||||
"owner",
|
||||
models.ForeignKey(
|
||||
|
|
20
evidence/migrations/0002_alter_annotation_type.py
Normal file
20
evidence/migrations/0002_alter_annotation_type.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-29 14:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("evidence", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="annotation",
|
||||
name="type",
|
||||
field=models.SmallIntegerField(
|
||||
choices=[(0, "System"), (1, "User"), (2, "Document"), (3, "Action")]
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-18 17:30
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("snapshot", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="annotation",
|
||||
name="device",
|
||||
),
|
||||
]
|
20
evidence/migrations/0003_alter_annotation_type.py
Normal file
20
evidence/migrations/0003_alter_annotation_type.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-29 15:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("evidence", "0002_alter_annotation_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="annotation",
|
||||
name="type",
|
||||
field=models.SmallIntegerField(
|
||||
choices=[(0, "System"), (1, "User"), (2, "Document")]
|
||||
),
|
||||
),
|
||||
]
|
|
@ -56,6 +56,7 @@ class Annotation(models.Model):
|
|||
class Type(models.IntegerChoices):
|
||||
SYSTEM= 0, "System"
|
||||
USER = 1, "User"
|
||||
DOCUMENT = 2, "Document"
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
uuid = models.UUIDField()
|
||||
|
@ -68,8 +69,3 @@ class Annotation(models.Model):
|
|||
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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-17 14:57
|
||||
# Generated by Django 5.0.6 on 2024-07-27 16:23
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
@ -10,11 +10,58 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("device", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Lot",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
("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)),
|
||||
("closed", models.BooleanField(default=True)),
|
||||
(
|
||||
"owner",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DeviceLot",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("device_id", models.CharField(max_length=256)),
|
||||
(
|
||||
"lot",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="lot.lot"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="LotTag",
|
||||
fields=[
|
||||
|
@ -37,38 +84,11 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Lot",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
("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)),
|
||||
("closed", models.BooleanField(default=True)),
|
||||
("devices", models.ManyToManyField(to="device.device")),
|
||||
(
|
||||
"owner",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"type",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="lot.lottag"
|
||||
),
|
||||
),
|
||||
],
|
||||
migrations.AddField(
|
||||
model_name="lot",
|
||||
name="type",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="lot.lottag"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-17 14:57
|
||||
# Generated by Django 5.0.6 on 2024-07-29 15:37
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
@ -7,15 +7,14 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("lot", "0001_initial"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Device",
|
||||
name="LotAnnotation",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
|
@ -26,7 +25,21 @@ class Migration(migrations.Migration):
|
|||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("type", models.CharField(blank=True, max_length=64, null=True)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"type",
|
||||
models.SmallIntegerField(
|
||||
choices=[(0, "System"), (1, "User"), (2, "Document")]
|
||||
),
|
||||
),
|
||||
("key", models.CharField(max_length=256)),
|
||||
("value", models.CharField(max_length=256)),
|
||||
(
|
||||
"lot",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="lot.lot"
|
||||
),
|
||||
),
|
||||
(
|
||||
"owner",
|
||||
models.ForeignKey(
|
|
@ -1,39 +0,0 @@
|
|||
# Generated by Django 5.0.6 on 2024-07-18 17:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("lot", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="lot",
|
||||
name="devices",
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="DeviceLot",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("device_id", models.CharField(max_length=256)),
|
||||
(
|
||||
"lot",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="lot.lot"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -43,3 +43,16 @@ class Lot(models.Model):
|
|||
for d in DeviceLot.objects.filter(lot=self, device_id=v):
|
||||
d.delete()
|
||||
|
||||
|
||||
class LotAnnotation(models.Model):
|
||||
class Type(models.IntegerChoices):
|
||||
SYSTEM= 0, "System"
|
||||
USER = 1, "User"
|
||||
DOCUMENT = 2, "Document"
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
lot = models.ForeignKey(Lot, 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)
|
||||
value = models.CharField(max_length=STR_EXTEND_SIZE)
|
||||
|
|
48
lot/templates/annotations.html
Normal file
48
lot/templates/annotations.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Lot {{ lot.name }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="tab-pane fade show active" id="details">
|
||||
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
|
||||
<a href="{% url 'lot:add_annotation' lot.pk %}" class="btn btn-primary">
|
||||
|
||||
<i class="bi bi-plus"></i>
|
||||
Add new annotation
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title mt-2">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 annotations %}
|
||||
<tr>
|
||||
<td>{{ a.key }}</td>
|
||||
<td>{{ a.value }}</td>
|
||||
<td>{{ a.created }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
48
lot/templates/documents.html
Normal file
48
lot/templates/documents.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>Lot {{ lot.name }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="tab-pane fade show active" id="details">
|
||||
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown="">
|
||||
<a href="{% url 'lot:add_document' lot.pk %}" class="btn btn-primary">
|
||||
|
||||
<i class="bi bi-plus"></i>
|
||||
Add new document
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title mt-2">Documents</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 documents %}
|
||||
<tr>
|
||||
<td>{{ a.key }}</td>
|
||||
<td>{{ a.value }}</td>
|
||||
<td>{{ a.created }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
45
lot/templates/new_annotation.html
Normal file
45
lot/templates/new_annotation.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{{ subtitle }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% load django_bootstrap5 %}
|
||||
<form role="form" method="post">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
<div class="alert alert-danger alert-icon alert-icon-border alert-dismissible" role="alert">
|
||||
<div class="icon"><span class="mdi mdi-close-circle-o"></span></div>
|
||||
<div class="message">
|
||||
{% for field, error in form.errors.items %}
|
||||
{{ error }}<br />
|
||||
{% endfor %}
|
||||
<button class="btn-close" type="button" data-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ form.management_form }}
|
||||
<div class="container" id="formset-container">
|
||||
<div class="row mb-2">
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
{% for f in form %}
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
{% bootstrap_field f %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="container">
|
||||
<a class="btn btn-grey" href="{% url 'dashboard:unassigned_devices' %}">{% translate "Cancel" %}</a>
|
||||
<input class="btn btn-green-admin" type="submit" name="submit" value="{% translate 'Save' %}" />
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -10,4 +10,8 @@ urlpatterns = [
|
|||
path("add/devices/", views.AddToLotView.as_view(), name="add_devices"),
|
||||
path("del/devices/", views.DelToLotView.as_view(), name="del_devices"),
|
||||
path("tag/<int:pk>/", views.LotsTagsView.as_view(), name="tag"),
|
||||
path("<int:pk>/document/", views.LotDocumentsView.as_view(), name="documents"),
|
||||
path("<int:pk>/document/add", views.LotAddDocumentView.as_view(), name="add_document"),
|
||||
path("<int:pk>/annotation", views.LotAnnotationsView.as_view(), name="annotations"),
|
||||
path("<int:pk>/annotation/add", views.LotAddAnnotationView.as_view(), name="add_annotation"),
|
||||
]
|
||||
|
|
93
lot/views.py
93
lot/views.py
|
@ -9,7 +9,7 @@ from django.views.generic.edit import (
|
|||
FormView,
|
||||
)
|
||||
from dashboard.mixins import DashboardView
|
||||
from lot.models import Lot, LotTag
|
||||
from lot.models import Lot, LotTag, LotAnnotation
|
||||
from lot.forms import LotsForm
|
||||
|
||||
|
||||
|
@ -134,3 +134,94 @@ class LotsTagsView(DashboardView, TemplateView):
|
|||
})
|
||||
return context
|
||||
|
||||
|
||||
class LotAddDocumentView(DashboardView, CreateView):
|
||||
template_name = "new_annotation.html"
|
||||
title = _("New Document")
|
||||
breadcrumb = "Device / New document"
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = LotAnnotation
|
||||
fields = ("key", "value")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.owner = self.request.user
|
||||
form.instance.lot = self.lot
|
||||
form.instance.type = LotAnnotation.Type.DOCUMENT
|
||||
response = super().form_valid(form)
|
||||
return response
|
||||
|
||||
def get_form_kwargs(self):
|
||||
pk = self.kwargs.get('pk')
|
||||
self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user)
|
||||
self.success_url = reverse_lazy('lot:documents', args=[pk])
|
||||
kwargs = super().get_form_kwargs()
|
||||
return kwargs
|
||||
|
||||
|
||||
class LotDocumentsView(DashboardView, TemplateView):
|
||||
template_name = "documents.html"
|
||||
title = _("New Document")
|
||||
breadcrumb = "Device / New document"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.pk = kwargs.get('pk')
|
||||
context = super().get_context_data(**kwargs)
|
||||
lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk)
|
||||
documents = LotAnnotation.objects.filter(
|
||||
lot=lot,
|
||||
owner=self.request.user,
|
||||
type=LotAnnotation.Type.DOCUMENT,
|
||||
)
|
||||
context.update({
|
||||
'lot': lot,
|
||||
'documents': documents,
|
||||
'title': self.title,
|
||||
'breadcrumb': self.breadcrumb
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class LotAnnotationsView(DashboardView, TemplateView):
|
||||
template_name = "annotations.html"
|
||||
title = _("New Annotation")
|
||||
breadcrumb = "Device / New annotation"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.pk = kwargs.get('pk')
|
||||
context = super().get_context_data(**kwargs)
|
||||
lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk)
|
||||
annotations = LotAnnotation.objects.filter(
|
||||
lot=lot,
|
||||
owner=self.request.user,
|
||||
type=LotAnnotation.Type.USER,
|
||||
)
|
||||
context.update({
|
||||
'lot': lot,
|
||||
'annotations': annotations,
|
||||
'title': self.title,
|
||||
'breadcrumb': self.breadcrumb
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class LotAddAnnotationView(DashboardView, CreateView):
|
||||
template_name = "new_annotation.html"
|
||||
title = _("New Annotation")
|
||||
breadcrumb = "Device / New annotation"
|
||||
success_url = reverse_lazy('dashboard:unassigned_devices')
|
||||
model = LotAnnotation
|
||||
fields = ("key", "value")
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.owner = self.request.user
|
||||
form.instance.lot = self.lot
|
||||
form.instance.type = LotAnnotation.Type.USER
|
||||
response = super().form_valid(form)
|
||||
return response
|
||||
|
||||
def get_form_kwargs(self):
|
||||
pk = self.kwargs.get('pk')
|
||||
self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user)
|
||||
self.success_url = reverse_lazy('lot:annotations', args=[pk])
|
||||
kwargs = super().get_form_kwargs()
|
||||
return kwargs
|
||||
|
|
Loading…
Reference in a new issue