179 lines
5.3 KiB
Python
179 lines
5.3 KiB
Python
import shlex
|
|
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.postgres.search import SearchVector, TrigramSimilarity
|
|
from django.http import Http404, JsonResponse, HttpResponse
|
|
from django.contrib.admin.models import LogEntry
|
|
from django_select2.views import AutoResponseView
|
|
from django.db import connection
|
|
from django.db.models import Q
|
|
|
|
from storage.models import Item, Label
|
|
|
|
from django.contrib.auth.decorators import login_required
|
|
from rest_framework.authtoken.models import Token
|
|
|
|
|
|
def apply_smart_search(query, objects):
|
|
general_term = []
|
|
|
|
filters = {}
|
|
|
|
for prop in shlex.split(query):
|
|
if ":" not in prop:
|
|
general_term.append(prop)
|
|
else:
|
|
key, value = prop.split(":", 1)
|
|
if key in ["owner", "taken_by"]:
|
|
filters[key + "__username"] = value
|
|
elif hasattr(Item, key):
|
|
filters[key + "__search"] = value
|
|
elif key == "ancestor":
|
|
objects = Item.objects.get(pk=value).get_children()
|
|
elif key == "prop" or value:
|
|
if key == "prop":
|
|
key, _, value = value.partition(":")
|
|
if not value:
|
|
filters["props__isnull"] = {key: False}
|
|
else:
|
|
filters["props__contains"] = {key: value}
|
|
else:
|
|
# "Whatever:"
|
|
general_term.append(prop)
|
|
|
|
objects = objects.filter(**filters)
|
|
|
|
if not general_term:
|
|
return objects
|
|
general_term = " ".join(general_term)
|
|
|
|
objects = (
|
|
objects.annotate(
|
|
search=SearchVector("name", "description", "props", config="simple"),
|
|
similarity=TrigramSimilarity("name", general_term),
|
|
)
|
|
.filter(Q(similarity__gte=0.15) | Q(search__contains=general_term))
|
|
.order_by("-similarity")
|
|
)
|
|
return objects
|
|
|
|
|
|
def index(request):
|
|
return render(request, "results.html", {"results": Item.objects.filter_roots()})
|
|
|
|
|
|
def search(request):
|
|
query = request.GET.get("q", "")
|
|
|
|
results = apply_smart_search(query, Item.objects).all()
|
|
|
|
if results and (len(results) == 1 or getattr(results[0], "similarity", 0) == 1):
|
|
return redirect(results[0])
|
|
|
|
return render(
|
|
request,
|
|
"results.html",
|
|
{
|
|
"query": query,
|
|
"results": results,
|
|
},
|
|
)
|
|
|
|
|
|
def item_display(request, pk):
|
|
if not pk:
|
|
return index(request)
|
|
item = get_object_or_404(Item, pk=pk)
|
|
|
|
labels = item.labels.all()
|
|
has_one_label = len(labels) == 1
|
|
|
|
return render(
|
|
request,
|
|
"item.html",
|
|
{
|
|
"title": item.name,
|
|
"item": item,
|
|
"categories": item.categories.all(),
|
|
"props": sorted(item.props.items()),
|
|
"images": item.images.all(),
|
|
"labels": labels,
|
|
"has_one_label": has_one_label,
|
|
"history": LogEntry.objects.filter(object_id=item.pk),
|
|
"ancestors": item.get_ancestors(),
|
|
"children": item.get_children().prefetch_related("categories"),
|
|
},
|
|
)
|
|
|
|
|
|
def label_lookup(request, pk):
|
|
try:
|
|
label = Label.objects.get(pk=pk)
|
|
return redirect(label.item)
|
|
except Label.DoesNotExist:
|
|
try:
|
|
# look up by short id
|
|
item = Item.objects.get(uuid__startswith=pk)
|
|
return redirect(item)
|
|
except Item.DoesNotExist:
|
|
raise Http404("Very sad to say, I could not find this thing")
|
|
|
|
|
|
@login_required
|
|
def apitoken(request):
|
|
print(Token)
|
|
token, created = Token.objects.get_or_create(user=request.user)
|
|
return HttpResponse(token.key, content_type="text/plain")
|
|
|
|
|
|
class ItemSelectView(AutoResponseView):
|
|
def get(self, request, *args, **kwargs):
|
|
self.widget = self.get_widget_or_404()
|
|
self.term = kwargs.get("term", request.GET.get("term", ""))
|
|
self.object_list = apply_smart_search(self.term, Item.objects)
|
|
context = self.get_context_data()
|
|
return JsonResponse(
|
|
{
|
|
"results": [
|
|
{
|
|
"text": obj.name,
|
|
"path": [o.name for o in obj.get_ancestors()],
|
|
"id": obj.pk,
|
|
}
|
|
for obj in context["object_list"]
|
|
],
|
|
"more": context["page_obj"].has_next(),
|
|
}
|
|
)
|
|
|
|
|
|
class PropSelectView(AutoResponseView):
|
|
def get(self, request, *args, **kwargs):
|
|
# self.widget = self.get_widget_or_404()
|
|
self.term = kwargs.get("term", request.GET.get("term", ""))
|
|
# context = self.get_context_data()
|
|
with connection.cursor() as c:
|
|
c.execute(
|
|
"""
|
|
SELECT key, count(*) FROM
|
|
(SELECT (each(props)).key FROM storage_item) AS stat
|
|
WHERE key like %s
|
|
GROUP BY key
|
|
ORDER BY count DESC, key
|
|
limit 10;
|
|
""",
|
|
["%" + self.term + "%"],
|
|
)
|
|
props = [e[0] for e in c.fetchall()]
|
|
return JsonResponse(
|
|
{
|
|
"results": [
|
|
{
|
|
"text": p,
|
|
"id": p,
|
|
}
|
|
for p in props
|
|
],
|
|
}
|
|
)
|