xapian #1
|
@ -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>
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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 = {}
|
||||||
|
|
|
@ -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",
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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):
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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("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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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",
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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):
|
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
|
|
||||||
|
|
|
@ -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"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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(
|
|
@ -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):
|
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)
|
||||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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("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"),
|
||||||
]
|
]
|
||||||
|
|
93
lot/views.py
93
lot/views.py
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue