Merge pull request 'search' (#8) from search into main

Reviewed-on: #8
This commit is contained in:
cayop 2024-10-09 16:04:01 +00:00
commit 0a9596d33b
12 changed files with 194 additions and 85 deletions

View File

@ -39,7 +39,7 @@ class DashboardView(LoginRequiredMixin):
'section': self.section, 'section': self.section,
'path': resolve(self.request.path).url_name, 'path': resolve(self.request.path).url_name,
'user': self.request.user, 'user': self.request.user,
'lot_tags': LotTag.objects.filter(owner=self.request.user) 'lot_tags': LotTag.objects.filter(owner=self.request.user.institution)
}) })
return context return context
@ -58,7 +58,11 @@ class DetailsMixin(DashboardView, TemplateView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.pk = kwargs['pk'] self.pk = kwargs['pk']
self.object = get_object_or_404(self.model, pk=self.pk, owner=self.request.user) self.object = get_object_or_404(
self.model,
pk=self.pk,
owner=self.request.user.institution
)
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -72,14 +76,17 @@ class DetailsMixin(DashboardView, TemplateView):
class InventaryMixin(DashboardView, TemplateView): class InventaryMixin(DashboardView, TemplateView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
dev_ids = dict(self.request.POST).get("devices", []) post = dict(self.request.POST)
self.request.session["devices"] = dev_ids url = post.get("url")
url = self.request.POST.get("url")
if url: if url:
dev_ids = post.get("devices", [])
self.request.session["devices"] = dev_ids
try: try:
resource = resolve(url) resource = resolve(url[0])
if resource and dev_ids: if resource and dev_ids:
return redirect(url) return redirect(url[0])
except Exception: except Exception:
pass pass
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)

View File

@ -179,6 +179,17 @@
{% endblock messages %} {% endblock messages %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2">
<h1 class="h2">{{ title }}</h1> <h1 class="h2">{{ title }}</h1>
<form method="post" action="{% url 'dashboard:search' %}">
{% csrf_token %}
<div class="input-group rounded">
<input type="search" name="search" class="form-control rounded" placeholder="Search your device..." aria-label="Search" aria-describedby="search-addon" />
<span class="input-group-text border-0" id="search-addon">
<i class="fas fa-search"></i>
</span>
</div>
</form>
</div> </div>
<div class="row border-bottom mb-3"> <div class="row border-bottom mb-3">
<div class="col"> <div class="col">

View File

@ -6,4 +6,5 @@ app_name = 'dashboard'
urlpatterns = [ urlpatterns = [
path("", views.UnassignedDevicesView.as_view(), name="unassigned_devices"), path("", views.UnassignedDevicesView.as_view(), name="unassigned_devices"),
path("<int:pk>/", views.LotDashboardView.as_view(), name="lot"), path("<int:pk>/", views.LotDashboardView.as_view(), name="lot"),
path("search", views.SearchView.as_view(), name="search"),
] ]

View File

@ -1,7 +1,13 @@
import json
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import FormView
from django.shortcuts import Http404 from django.shortcuts import Http404
from django.db.models import Q
from dashboard.mixins import InventaryMixin, DetailsMixin from dashboard.mixins import InventaryMixin, DetailsMixin
from evidence.models import Annotation
from evidence.xapian import search
from device.models import Device from device.models import Device
from lot.models import Lot from lot.models import Lot
@ -32,6 +38,68 @@ class LotDashboardView(InventaryMixin, DetailsMixin):
return context return context
def get_devices(self, user, offset, limit): def get_devices(self, user, offset, limit):
chids = self.object.devicelot_set.all().values_list("device_id", flat=True).distinct() chids = self.object.devicelot_set.all().values_list(
"device_id", flat=True
).distinct()
chids_page = chids[offset:offset+limit] chids_page = chids[offset:offset+limit]
return [Device(id=x) for x in chids_page], chids.count() return [Device(id=x) for x in chids_page], chids.count()
class SearchView(InventaryMixin):
template_name = "unassigned_devices.html"
section = "Search"
title = _("Search Devices")
breadcrumb = "Devices / Search Devices"
def get_devices(self, user, offset, limit):
post = dict(self.request.POST)
query = post.get("search")
if not query:
return [], 0
matches = search(
self.request.user.institution,
query[0],
offset,
limit
)
if not matches.size():
return self.search_hids(query, offset, limit)
annotations = []
for x in matches:
annotations.extend(self.get_annotations(x))
devices = [Device(id=x) for x in set(annotations)]
count = matches.size()
return devices, count
def get_annotations(self, xp):
snap = xp.document.get_data()
uuid = json.loads(snap).get('uuid')
return Annotation.objects.filter(
type=Annotation.Type.SYSTEM,
owner=self.request.user.institution,
uuid=uuid
).values_list("value", flat=True).distinct()
def search_hids(self, query, offset, limit):
qry = Q()
for i in query[0].split(" "):
if i:
qry |= Q(value__startswith=i)
chids = Annotation.objects.filter(
type=Annotation.Type.SYSTEM,
owner=self.request.user.institution
).filter(
qry
).values_list("value", flat=True).distinct()
chids_page = chids[offset:offset+limit]
return [Device(id=x) for x in chids_page], chids.count()

View File

@ -98,7 +98,7 @@ class DetailsView(DashboardView, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
self.object.initial() self.object.initial()
lot_tags = LotTag.objects.filter(owner=self.request.user) lot_tags = LotTag.objects.filter(owner=self.request.user.institution)
context.update({ context.update({
'object': self.object, 'object': self.object,
'snapshot': self.object.get_last_evidence(), 'snapshot': self.object.get_last_evidence(),

View File

@ -18,7 +18,6 @@ def search(institution, qs, offset=0, limit=10):
qp.set_stemmer(xapian.Stem("english")) qp.set_stemmer(xapian.Stem("english"))
qp.set_stemming_strategy(xapian.QueryParser.STEM_SOME) qp.set_stemming_strategy(xapian.QueryParser.STEM_SOME)
qp.add_prefix("uuid", "uuid") qp.add_prefix("uuid", "uuid")
# qp.add_prefix("snapshot", "snapshot")
query = qp.parse_query(qs) query = qp.parse_query(qs)
institution_term = "U{}".format(institution.id) institution_term = "U{}".format(institution.id)
final_query = xapian.Query( final_query = xapian.Query(

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.6 on 2024-07-27 16:23 # Generated by Django 5.0.6 on 2024-10-09 14:41
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -10,6 +10,7 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
("user", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
@ -36,6 +37,15 @@ class Migration(migrations.Migration):
"owner", "owner",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="user.institution",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL, to=settings.AUTH_USER_MODEL,
), ),
), ),
@ -62,6 +72,51 @@ class Migration(migrations.Migration):
), ),
], ],
), ),
migrations.CreateModel(
name="LotAnnotation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("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(
on_delete=django.db.models.deletion.CASCADE,
to="user.institution",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.CreateModel( migrations.CreateModel(
name="LotTag", name="LotTag",
fields=[ fields=[
@ -79,6 +134,15 @@ class Migration(migrations.Migration):
"owner", "owner",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="user.institution",
),
),
(
"user",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL, to=settings.AUTH_USER_MODEL,
), ),
), ),

View File

@ -1,52 +0,0 @@
# Generated by Django 5.0.6 on 2024-07-29 15:37
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("lot", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="LotAnnotation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("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(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@ -6,14 +6,15 @@ from utils.constants import (
STR_EXTEND_SIZE, STR_EXTEND_SIZE,
) )
from user.models import User from user.models import User, Institution
# from device.models import Device # from device.models import Device
from evidence.models import Annotation from evidence.models import Annotation
class LotTag(models.Model): class LotTag(models.Model):
name = models.CharField(max_length=STR_SIZE, blank=False, null=False) name = models.CharField(max_length=STR_SIZE, blank=False, null=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self): def __str__(self):
return self.name return self.name
@ -31,7 +32,8 @@ class Lot(models.Model):
code = models.CharField(max_length=STR_SIZE, blank=True, null=True) code = models.CharField(max_length=STR_SIZE, blank=True, null=True)
description = models.CharField(max_length=STR_SIZE, blank=True, null=True) description = models.CharField(max_length=STR_SIZE, blank=True, null=True)
closed = models.BooleanField(default=True) closed = models.BooleanField(default=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
type = models.ForeignKey(LotTag, on_delete=models.CASCADE) type = models.ForeignKey(LotTag, on_delete=models.CASCADE)
def add(self, v): def add(self, v):
@ -52,7 +54,8 @@ class LotAnnotation(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
lot = models.ForeignKey(Lot, on_delete=models.CASCADE) lot = models.ForeignKey(Lot, on_delete=models.CASCADE)
owner = models.ForeignKey(User, on_delete=models.CASCADE) owner = models.ForeignKey(Institution, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
type = models.SmallIntegerField(choices=Type) type = models.SmallIntegerField(choices=Type)
key = models.CharField(max_length=STR_EXTEND_SIZE) key = models.CharField(max_length=STR_EXTEND_SIZE)
value = models.CharField(max_length=STR_EXTEND_SIZE) value = models.CharField(max_length=STR_EXTEND_SIZE)

View File

@ -28,7 +28,8 @@ class NewLotView(DashboardView, CreateView):
) )
def form_valid(self, form): def form_valid(self, form):
form.instance.owner = self.request.user form.instance.owner = self.request.user.institution
form.instance.user = self.request.user
response = super().form_valid(form) response = super().form_valid(form)
return response return response
@ -68,7 +69,11 @@ class EditLotView(DashboardView, UpdateView):
def get_form_kwargs(self): def get_form_kwargs(self):
pk = self.kwargs.get('pk') pk = self.kwargs.get('pk')
self.object = get_object_or_404(self.model, pk=pk) self.object = get_object_or_404(
self.model,
owner=self.request.user.institution,
pk=pk,
)
# self.success_url = reverse_lazy('dashbiard:lot', args=[pk]) # self.success_url = reverse_lazy('dashbiard:lot', args=[pk])
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return kwargs return kwargs
@ -83,8 +88,8 @@ class AddToLotView(DashboardView, FormView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
lots = Lot.objects.filter(owner=self.request.user) lots = Lot.objects.filter(owner=self.request.user.institution)
lot_tags = LotTag.objects.filter(owner=self.request.user) lot_tags = LotTag.objects.filter(owner=self.request.user.institution)
context.update({ context.update({
'lots': lots, 'lots': lots,
'lot_tags':lot_tags, 'lot_tags':lot_tags,
@ -93,7 +98,7 @@ class AddToLotView(DashboardView, FormView):
def get_form(self): def get_form(self):
form = super().get_form() form = super().get_form()
form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user) form.fields["lots"].queryset = Lot.objects.filter(owner=self.request.user.institution)
return form return form
def form_valid(self, form): def form_valid(self, form):
@ -123,10 +128,10 @@ class LotsTagsView(DashboardView, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk') self.pk = kwargs.get('pk')
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
tag = get_object_or_404(LotTag, owner=self.request.user, id=self.pk) tag = get_object_or_404(LotTag, owner=self.request.user.institution, id=self.pk)
self.title += " {}".format(tag.name) self.title += " {}".format(tag.name)
self.breadcrumb += " {}".format(tag.name) self.breadcrumb += " {}".format(tag.name)
lots = Lot.objects.filter(owner=self.request.user).filter(type=tag) lots = Lot.objects.filter(owner=self.request.user.institution).filter(type=tag)
context.update({ context.update({
'lots': lots, 'lots': lots,
'title': self.title, 'title': self.title,
@ -144,7 +149,8 @@ class LotAddDocumentView(DashboardView, CreateView):
fields = ("key", "value") fields = ("key", "value")
def form_valid(self, form): def form_valid(self, form):
form.instance.owner = self.request.user form.instance.owner = self.request.user.institution
form.instance.user = self.request.user
form.instance.lot = self.lot form.instance.lot = self.lot
form.instance.type = LotAnnotation.Type.DOCUMENT form.instance.type = LotAnnotation.Type.DOCUMENT
response = super().form_valid(form) response = super().form_valid(form)
@ -152,7 +158,7 @@ class LotAddDocumentView(DashboardView, CreateView):
def get_form_kwargs(self): def get_form_kwargs(self):
pk = self.kwargs.get('pk') pk = self.kwargs.get('pk')
self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user) self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user.institution)
self.success_url = reverse_lazy('lot:documents', args=[pk]) self.success_url = reverse_lazy('lot:documents', args=[pk])
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return kwargs return kwargs
@ -166,10 +172,10 @@ class LotDocumentsView(DashboardView, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk') self.pk = kwargs.get('pk')
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk) lot = get_object_or_404(Lot, owner=self.request.user.institution, id=self.pk)
documents = LotAnnotation.objects.filter( documents = LotAnnotation.objects.filter(
lot=lot, lot=lot,
owner=self.request.user, owner=self.request.user.institution,
type=LotAnnotation.Type.DOCUMENT, type=LotAnnotation.Type.DOCUMENT,
) )
context.update({ context.update({
@ -189,10 +195,10 @@ class LotAnnotationsView(DashboardView, TemplateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.pk = kwargs.get('pk') self.pk = kwargs.get('pk')
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
lot = get_object_or_404(Lot, owner=self.request.user, id=self.pk) lot = get_object_or_404(Lot, owner=self.request.user.institution, id=self.pk)
annotations = LotAnnotation.objects.filter( annotations = LotAnnotation.objects.filter(
lot=lot, lot=lot,
owner=self.request.user, owner=self.request.user.institution,
type=LotAnnotation.Type.USER, type=LotAnnotation.Type.USER,
) )
context.update({ context.update({
@ -213,7 +219,8 @@ class LotAddAnnotationView(DashboardView, CreateView):
fields = ("key", "value") fields = ("key", "value")
def form_valid(self, form): def form_valid(self, form):
form.instance.owner = self.request.user form.instance.owner = self.request.user.institution
form.instance.user = self.request.user
form.instance.lot = self.lot form.instance.lot = self.lot
form.instance.type = LotAnnotation.Type.USER form.instance.type = LotAnnotation.Type.USER
response = super().form_valid(form) response = super().form_valid(form)
@ -221,7 +228,7 @@ class LotAddAnnotationView(DashboardView, CreateView):
def get_form_kwargs(self): def get_form_kwargs(self):
pk = self.kwargs.get('pk') pk = self.kwargs.get('pk')
self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user) self.lot = get_object_or_404(Lot, pk=pk, owner=self.request.user.institution)
self.success_url = reverse_lazy('lot:annotations', args=[pk]) self.success_url = reverse_lazy('lot:annotations', args=[pk])
kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
return kwargs return kwargs

View File

@ -43,5 +43,6 @@ class Command(BaseCommand):
for tag in tags: for tag in tags:
LotTag.objects.create( LotTag.objects.create(
name=tag, name=tag,
owner=self.u owner=self.u.institution,
user=self.u
) )

View File

@ -87,4 +87,4 @@ def create_index(doc, user):
_uuid = doc['uuid'] _uuid = doc['uuid']
ev = json.dumps(doc) ev = json.dumps(doc)
index(user, _uuid, ev) index(user.institution, _uuid, ev)