added score system, range unit characteristics, minor improvements

This commit is contained in:
Alexander Karpov 2022-10-22 16:14:43 +03:00
parent c68dfce10a
commit faaccff7dc
7 changed files with 93 additions and 29 deletions

View File

@ -1,9 +1,15 @@
from django.urls import path
from search.api.views import SearchApi, HintApi, AutoCompleteApi
from search.api.views import (
SearchApi,
HintApi,
AutoCompleteApi,
IncreaseProductScoreApi,
)
urlpatterns = [
path("search", SearchApi.as_view(), name="search_api"),
path("hint", HintApi.as_view(), name="hint api"),
path('autocomplete_schema', AutoCompleteApi.as_view(), name='autocomplete api')
path("hint", HintApi.as_view(), name="hint_api"),
path("autocomplete_schema", AutoCompleteApi.as_view(), name="autocomplete_api"),
path("score/<int:pk>", IncreaseProductScoreApi.as_view(), name="score_api"),
]

View File

@ -58,7 +58,7 @@ class HintResponseSerializer(serializers.Serializer):
class AutoCompleteRequestSerializer(serializers.Serializer):
content = serializers.CharField(validators=[MinLengthValidator(3)])
exclude = serializers.ListSerializer(child=QueryFilterSerializer())
exclude = serializers.ListSerializer(child=QueryFilterSerializer(), default=[])
def create(self, validated_data):
raise NotImplementedError

View File

@ -1,6 +1,7 @@
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.generics import get_object_or_404
from rest_framework.response import Response
from rest_framework.views import APIView
from search.api.serializers import HintRequestSerializer
@ -12,6 +13,7 @@ from search.api.serializers import (
AutoCompleteRequestSerializer,
AutoCompleteResponseSerializer,
)
from search.models import Product
from search.services.search import process_search
from search.services.autocomplete_schema import autocomplete_schema
@ -64,6 +66,28 @@ class AutoCompleteApi(APIView):
serializer = AutoCompleteRequestSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(
{"nodes": autocomplete_schema(serializer.data["content"], serializer.data["exclude"])},
{
"nodes": autocomplete_schema(
serializer.data["content"], serializer.data["exclude"]
)
},
status=status.HTTP_200_OK,
)
class IncreaseProductScoreApi(APIView):
@swagger_auto_schema(
manual_parameters=[
openapi.Parameter(
"id",
openapi.IN_PATH,
description="Product id",
type=openapi.TYPE_INTEGER,
)
]
)
def post(self, request, pk):
product = get_object_or_404(Product, id=pk)
product.score += 1
product.save(update_fields=["score"])
return Response({"score": product.score}, status=status.HTTP_200_OK)

View File

@ -18,14 +18,22 @@ class Characteristic(models.Model):
class UnitCharacteristic(models.Model):
name = models.TextField("Имя", blank=False)
value = models.TextField("Значение", blank=False)
numeric_value = models.IntegerField(default=0)
numeric_value_min = models.IntegerField(default=0)
numeric_value_max = models.IntegerField(default=0)
unit = models.TextField("Размерность", blank=False)
def __str__(self):
return str(self.name)
def serialize_self(self):
return {"name": self.name, "value": self.numeric_value, "unit": self.unit}
return {
"id": self.id,
"name": self.name,
"value": self.numeric_value_min
if self.numeric_value_min == self.numeric_value_max
else f"{self.numeric_value_min}:{self.numeric_value_max}",
"unit": self.unit,
}
class Meta:
db_table = "unit_characteristic"
@ -50,14 +58,16 @@ class Product(models.Model):
Category, related_name="products", on_delete=models.CASCADE
)
# score = models.IntegerField(default=0)
score = models.IntegerField(default=0)
def __str__(self):
return str(self.name)
def serialize_self(self) -> dict:
return {
"id": self.id,
"name": self.name,
"score": self.score,
"characteristic": [
x.characteristic.serialize_self() for x in self.characteristics.all()
]
@ -69,7 +79,7 @@ class Product(models.Model):
class Meta:
db_table = "product"
# ordering = ["score"]
ordering = ["-score"]
class ProductCharacteristic(models.Model):

View File

@ -18,9 +18,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
"value": product["name"],
},
}
for product in Product.objects.filter(
name__unaccent__icontains=val
).values("name")
for product in Product.objects.filter(name__unaccent__icontains=val)[
:20
].values("name")
]
)
if not category_exclude:
@ -30,9 +30,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
"coordinate": cat["name"].lower().index(val.lower()),
"value": {"type": "Category", "value": cat["name"]},
}
for cat in Category.objects.filter(
name__unaccent__icontains=val
).values("name")
for cat in Category.objects.filter(name__unaccent__icontains=val)[
:20
].values("name")
]
)
schema.extend(
@ -41,9 +41,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
"coordinate": char["value"].lower().index(val.lower()),
"value": {"type": char["name"], "value": char["value"]},
}
for char in Characteristic.objects.filter(
value__unaccent__icontains=val
).values("name", "value")
for char in Characteristic.objects.filter(value__unaccent__icontains=val)[
:20
].values("name", "value")
]
)
return schema

View File

@ -64,12 +64,23 @@ def load_excel():
def process_unit_character():
for el in UnitCharacteristic.objects.all():
nums = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", el.value)
if len(nums) != 1:
el.delete()
else:
nums = re.findall(
"[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", el.value
)
if len(nums) == 1:
try:
el.numeric_value = int(float(nums[0].replace(",", ".")))
el.numeric_value_min = int(float(nums[0].replace(",", ".")))
el.numeric_value_max = int(float(nums[0].replace(",", ".")))
el.save()
except ValueError:
el.delete()
elif len(nums):
try:
nums = [int(float(x.replace(",", "."))) for x in nums]
min_num = min(nums)
max_num = max(nums)
el.numeric_value_min = min_num
el.numeric_value_max = max_num
el.save()
except ValueError:
el.delete()

View File

@ -16,15 +16,26 @@ from search.services.translate import translate_en_ru, translate_ru_en
def process_unit_operation(unit: ProductUnitCharacteristic.objects, operation: str):
if operation.startswith("<=") or operation.startswith("=<"):
return unit.filter(characteristic__numeric_value__lte=int(float(operation[2:])))
return unit.filter(
characteristic__numeric_value_max__lte=int(float(operation[2:]))
)
elif operation.startswith("=>") or operation.startswith(">="):
return unit.filter(characteristic__numeric_value__gte=int(float(operation[2:])))
return unit.filter(
characteristic__numeric_value_min__gte=int(float(operation[2:]))
)
elif operation.startswith(">"):
return unit.filter(characteristic__numeric_value__gt=int(float(operation[1:])))
return unit.filter(
characteristic__numeric_value_min__gt=int(float(operation[1:]))
)
elif operation.startswith("<"):
return unit.filter(characteristic__numeric_value__lt=int(float(operation[1:])))
return unit.filter(
characteristic__numeric_value_max__lt=int(float(operation[1:]))
)
elif operation.startswith("="):
return unit.filter(characteristic__numeric_value=int(float(operation[1:])))
return unit.filter(
characteristic__numeric_value_min__gte=int(float(operation[1:])),
characteristic__numeric_value_max__lte=int(float(operation[1:])),
)
return unit
@ -33,7 +44,9 @@ def apply_qs_search(qs: Product.objects, text: str):
text = text.replace(st, " ")
text = text.split()
for word in text:
qs = qs.filter(name__unaccent__trigram_similar=word) | qs.filter(name__unaccent__icontains=word)
qs = qs.filter(name__unaccent__trigram_similar=word) | qs.filter(
name__unaccent__icontains=word
)
return qs