add annotations in device and in lots

This commit is contained in:
Cayo Puigdefabregas 2024-07-30 13:37:08 +02:00
parent 91780be94b
commit 0136a98ff5
27 changed files with 502 additions and 208 deletions

View File

@ -7,21 +7,17 @@
<h3>{{ subtitle }}</h3> <h3>{{ subtitle }}</h3>
</div> </div>
<div class="col text-center"> <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> <i class="bi bi-folder2"></i>
{% trans 'Lots' %} {% trans 'Documents' %}
</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' %}
</a> </a>
<a href="{# url 'idhub:admin_people_activate' object.id #}" type="button" class="btn btn-green-admin"> <a href="{# url 'idhub:admin_people_activate' object.id #}" type="button" class="btn btn-green-admin">
<i class="bi bi-reply"></i> <i class="bi bi-reply"></i>
{% trans 'Exports' %} {% trans 'Exports' %}
</a> </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> <i class="bi bi-tag"></i>
{% trans 'Labels' %} {% trans 'Annotations' %}
</a> </a>
</div> </div>
</div> </div>

View File

@ -1,12 +1,7 @@
import json
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db.models import Count
from dashboard.mixins import InventaryMixin, DetailsMixin from dashboard.mixins import InventaryMixin, DetailsMixin
from device.models import Device from device.models import Device
from evidence.xapian import search from lot.models import Lot
from evidence.models import Annotation
from lot.models import Lot, LotTag
class UnassignedDevicesView(InventaryMixin): class UnassignedDevicesView(InventaryMixin):

View File

@ -4,8 +4,8 @@ import hashlib
import datetime import datetime
from django import forms from django import forms
from snapshot.models import Annotation from evidence.models import Annotation
from snapshot.xapian import search, index from evidence.xapian import search, index
DEVICE_TYPES = [ DEVICE_TYPES = [
("Desktop", "Desktop"), ("Desktop", "Desktop"),
@ -27,13 +27,19 @@ DEVICE_TYPES = [
class DeviceForm(forms.Form): class DeviceForm(forms.Form):
type = forms.ChoiceField(choices = DEVICE_TYPES, required=False) 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) tag = forms.CharField(required=False)
name = forms.CharField(required=False) name = forms.CharField(required=False)
value = forms.CharField(required=False) value = forms.CharField(required=False)
class BaseDeviceFormSet(forms.BaseFormSet): 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): def save(self, user, commit=True):
self.user = user self.user = user
doc = {} doc = {}

View File

@ -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),
),
]

View File

@ -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),
),
]

View File

@ -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",
),
]

View File

@ -57,9 +57,31 @@ class Device:
return self.annotations 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): def get_uuids(self):
for a in self.get_annotations(): 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) self.uuids.append(a.uuid)
def get_hids(self): def get_hids(self):

View File

@ -90,7 +90,7 @@
</a> </a>
</div> </div>
<h5 class="card-title">Annotations</h5> <h5 class="card-title mt-2">Annotations</h5>
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -102,8 +102,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for a in object.annotations %} {% for a in object.get_user_annotations %}
{% if a.is_user_annotation %}
<tr> <tr>
<td>{{ a.key }}</td> <td>{{ a.key }}</td>
<td>{{ a.value }}</td> <td>{{ a.value }}</td>
@ -111,7 +110,6 @@
<td></td> <td></td>
<td></td> <td></td>
</tr> </tr>
{% endif %}
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -137,26 +135,35 @@
<div class="tab-pane fade profile-overview" id="documents"> <div class="tab-pane fade profile-overview" id="documents">
<div class="btn-group dropdown ml-1 mt-1" uib-dropdown=""> <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> <i class="bi bi-plus"></i>
Add new document Add new document
<span class="caret"></span> <span class="caret"></span>
</a> </a>
</div> </div>
<h5 class="card-title">Documents</h5> <h5 class="card-title mt-2">Documents</h5>
<table class="table"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th scope="col">File</th> <th scope="col">Key</th>
<th scope="col">Type</th> <th scope="col">Value</th>
<th scope="col">Description</th> <th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Created on</th>
<th scope="col" data-type="date" data-format="YYYY-MM-DD hh:mm">Uploaded on</th>
<th></th> <th></th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <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> </tbody>
</table> </table>

View 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 %}

View File

@ -8,4 +8,5 @@ urlpatterns = [
path("edit/<str:pk>/", views.EditDeviceView.as_view(), name="edit"), path("edit/<str:pk>/", views.EditDeviceView.as_view(), name="edit"),
path("<str:pk>/", views.DetailsView.as_view(), name="details"), path("<str:pk>/", views.DetailsView.as_view(), name="details"),
path("<str:pk>/annotation/add", views.AddAnnotationView.as_view(), name="add_annotation"), path("<str:pk>/annotation/add", views.AddAnnotationView.as_view(), name="add_annotation"),
path("<str:pk>/document/add", views.AddDocumentView.as_view(), name="add_document"),
] ]

View File

@ -10,8 +10,8 @@ from django.views.generic.edit import (
) )
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from dashboard.mixins import DashboardView from dashboard.mixins import DashboardView
from snapshot.models import Annotation from evidence.models import Annotation
from snapshot.xapian import search from evidence.xapian import search
from lot.models import LotTag from lot.models import LotTag
from device.models import Device from device.models import Device
from device.forms import DeviceFormSet from device.forms import DeviceFormSet
@ -95,14 +95,14 @@ class DetailsView(DashboardView, TemplateView):
lot_tags = LotTag.objects.filter(owner=self.request.user) lot_tags = LotTag.objects.filter(owner=self.request.user)
context.update({ context.update({
'object': self.object, 'object': self.object,
'snapshot': self.object.get_last_snapshot(), 'snapshot': self.object.get_last_evidence(),
'lot_tags': lot_tags, 'lot_tags': lot_tags,
}) })
return context return context
class AddAnnotationView(DashboardView, CreateView): class AddAnnotationView(DashboardView, CreateView):
template_name = "new_device.html" template_name = "new_annotation.html"
title = _("New annotation") title = _("New annotation")
breadcrumb = "Device / New annotation" breadcrumb = "Device / New annotation"
success_url = reverse_lazy('dashboard:unassigned_devices') success_url = reverse_lazy('dashboard:unassigned_devices')
@ -110,25 +110,46 @@ class AddAnnotationView(DashboardView, CreateView):
fields = ("key", "value") fields = ("key", "value")
def form_valid(self, form): def form_valid(self, form):
self.device.get_annotations()
self.device.get_uuids()
form.instance.owner = self.request.user form.instance.owner = self.request.user
form.instance.device = self.device form.instance.uuid = self.annotation.uuid
form.instance.uuid = self.device.uuids[0]
form.instance.type = Annotation.Type.USER form.instance.type = Annotation.Type.USER
response = super().form_valid(form) response = super().form_valid(form)
return response 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.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]) self.success_url = reverse_lazy('device:details', args=[pk])
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return 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

View File

@ -21,7 +21,7 @@ urlpatterns = [
# path('api/', include('snapshot.urls')), # path('api/', include('snapshot.urls')),
path("", include("login.urls")), path("", include("login.urls")),
path("dashboard/", include("dashboard.urls")), path("dashboard/", include("dashboard.urls")),
path("snapshot/", include("snapshot.urls")), path("evidence/", include("evidence.urls")),
path("device/", include("device.urls")), path("device/", include("device.urls")),
path("lot/", include("lot.urls")), path("lot/", include("lot.urls")),
] ]

View File

@ -4,7 +4,7 @@ import json
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 snapshot.parse import Build from evidence.parse import Build
User = get_user_model() User = get_user_model()

View File

@ -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 import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -10,7 +10,6 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("device", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
@ -35,12 +34,6 @@ class Migration(migrations.Migration):
), ),
("key", models.CharField(max_length=256)), ("key", models.CharField(max_length=256)),
("value", 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", "owner",
models.ForeignKey( models.ForeignKey(

View 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")]
),
),
]

View File

@ -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",
),
]

View 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")]
),
),
]

View File

@ -56,6 +56,7 @@ class Annotation(models.Model):
class Type(models.IntegerChoices): class Type(models.IntegerChoices):
SYSTEM= 0, "System" SYSTEM= 0, "System"
USER = 1, "User" USER = 1, "User"
DOCUMENT = 2, "Document"
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField() uuid = models.UUIDField()
@ -68,8 +69,3 @@ class Annotation(models.Model):
constraints = [ constraints = [
models.UniqueConstraint(fields=["type", "key", "uuid"], name="unique_type_key_uuid") 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

@ -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 import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -10,11 +10,58 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("device", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ 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( migrations.CreateModel(
name="LotTag", name="LotTag",
fields=[ fields=[
@ -37,38 +84,11 @@ class Migration(migrations.Migration):
), ),
], ],
), ),
migrations.CreateModel( migrations.AddField(
name="Lot", model_name="lot",
fields=[ name="type",
( field=models.ForeignKey(
"id", on_delete=django.db.models.deletion.CASCADE, to="lot.lottag"
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"
),
),
],
), ),
] ]

View File

@ -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 import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -7,15 +7,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
("lot", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name="Device", name="LotAnnotation",
fields=[ fields=[
( (
"id", "id",
@ -26,7 +25,21 @@ class Migration(migrations.Migration):
verbose_name="ID", 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", "owner",
models.ForeignKey( models.ForeignKey(

View File

@ -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"
),
),
],
),
]

View File

@ -43,3 +43,16 @@ class Lot(models.Model):
for d in DeviceLot.objects.filter(lot=self, device_id=v): for d in DeviceLot.objects.filter(lot=self, device_id=v):
d.delete() 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)

View 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 %}

View 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 %}

View 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 %}

View File

@ -10,4 +10,8 @@ urlpatterns = [
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("tag/<int:pk>/", views.LotsTagsView.as_view(), name="tag"), 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"),
] ]

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, LotTag from lot.models import Lot, LotTag, LotAnnotation
from lot.forms import LotsForm from lot.forms import LotsForm
@ -134,3 +134,94 @@ class LotsTagsView(DashboardView, TemplateView):
}) })
return context 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