mirror of
https://github.com/magnum-opus-tender-hack/backend.git
synced 2025-02-22 04:00:35 +03:00
added score system, range unit characteristics, minor improvements
This commit is contained in:
parent
c68dfce10a
commit
faaccff7dc
|
@ -1,9 +1,15 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from search.api.views import SearchApi, HintApi, AutoCompleteApi
|
from search.api.views import (
|
||||||
|
SearchApi,
|
||||||
|
HintApi,
|
||||||
|
AutoCompleteApi,
|
||||||
|
IncreaseProductScoreApi,
|
||||||
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("search", SearchApi.as_view(), name="search_api"),
|
path("search", SearchApi.as_view(), name="search_api"),
|
||||||
path("hint", HintApi.as_view(), name="hint api"),
|
path("hint", HintApi.as_view(), name="hint_api"),
|
||||||
path('autocomplete_schema', AutoCompleteApi.as_view(), name='autocomplete api')
|
path("autocomplete_schema", AutoCompleteApi.as_view(), name="autocomplete_api"),
|
||||||
|
path("score/<int:pk>", IncreaseProductScoreApi.as_view(), name="score_api"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -58,7 +58,7 @@ class HintResponseSerializer(serializers.Serializer):
|
||||||
|
|
||||||
class AutoCompleteRequestSerializer(serializers.Serializer):
|
class AutoCompleteRequestSerializer(serializers.Serializer):
|
||||||
content = serializers.CharField(validators=[MinLengthValidator(3)])
|
content = serializers.CharField(validators=[MinLengthValidator(3)])
|
||||||
exclude = serializers.ListSerializer(child=QueryFilterSerializer())
|
exclude = serializers.ListSerializer(child=QueryFilterSerializer(), default=[])
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
from rest_framework.generics import get_object_or_404
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from search.api.serializers import HintRequestSerializer
|
from search.api.serializers import HintRequestSerializer
|
||||||
|
@ -12,6 +13,7 @@ from search.api.serializers import (
|
||||||
AutoCompleteRequestSerializer,
|
AutoCompleteRequestSerializer,
|
||||||
AutoCompleteResponseSerializer,
|
AutoCompleteResponseSerializer,
|
||||||
)
|
)
|
||||||
|
from search.models import Product
|
||||||
from search.services.search import process_search
|
from search.services.search import process_search
|
||||||
from search.services.autocomplete_schema import autocomplete_schema
|
from search.services.autocomplete_schema import autocomplete_schema
|
||||||
|
|
||||||
|
@ -64,6 +66,28 @@ class AutoCompleteApi(APIView):
|
||||||
serializer = AutoCompleteRequestSerializer(data=request.data)
|
serializer = AutoCompleteRequestSerializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
return Response(
|
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,
|
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)
|
||||||
|
|
|
@ -18,14 +18,22 @@ class Characteristic(models.Model):
|
||||||
class UnitCharacteristic(models.Model):
|
class UnitCharacteristic(models.Model):
|
||||||
name = models.TextField("Имя", blank=False)
|
name = models.TextField("Имя", blank=False)
|
||||||
value = 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)
|
unit = models.TextField("Размерность", blank=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
|
|
||||||
def serialize_self(self):
|
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:
|
class Meta:
|
||||||
db_table = "unit_characteristic"
|
db_table = "unit_characteristic"
|
||||||
|
@ -50,14 +58,16 @@ class Product(models.Model):
|
||||||
Category, related_name="products", on_delete=models.CASCADE
|
Category, related_name="products", on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
|
|
||||||
# score = models.IntegerField(default=0)
|
score = models.IntegerField(default=0)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
|
|
||||||
def serialize_self(self) -> dict:
|
def serialize_self(self) -> dict:
|
||||||
return {
|
return {
|
||||||
|
"id": self.id,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
|
"score": self.score,
|
||||||
"characteristic": [
|
"characteristic": [
|
||||||
x.characteristic.serialize_self() for x in self.characteristics.all()
|
x.characteristic.serialize_self() for x in self.characteristics.all()
|
||||||
]
|
]
|
||||||
|
@ -69,7 +79,7 @@ class Product(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "product"
|
db_table = "product"
|
||||||
# ordering = ["score"]
|
ordering = ["-score"]
|
||||||
|
|
||||||
|
|
||||||
class ProductCharacteristic(models.Model):
|
class ProductCharacteristic(models.Model):
|
||||||
|
|
|
@ -18,9 +18,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
|
||||||
"value": product["name"],
|
"value": product["name"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for product in Product.objects.filter(
|
for product in Product.objects.filter(name__unaccent__icontains=val)[
|
||||||
name__unaccent__icontains=val
|
:20
|
||||||
).values("name")
|
].values("name")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if not category_exclude:
|
if not category_exclude:
|
||||||
|
@ -30,9 +30,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
|
||||||
"coordinate": cat["name"].lower().index(val.lower()),
|
"coordinate": cat["name"].lower().index(val.lower()),
|
||||||
"value": {"type": "Category", "value": cat["name"]},
|
"value": {"type": "Category", "value": cat["name"]},
|
||||||
}
|
}
|
||||||
for cat in Category.objects.filter(
|
for cat in Category.objects.filter(name__unaccent__icontains=val)[
|
||||||
name__unaccent__icontains=val
|
:20
|
||||||
).values("name")
|
].values("name")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
schema.extend(
|
schema.extend(
|
||||||
|
@ -41,9 +41,9 @@ def autocomplete_schema(val: str, exclude: List[Dict]):
|
||||||
"coordinate": char["value"].lower().index(val.lower()),
|
"coordinate": char["value"].lower().index(val.lower()),
|
||||||
"value": {"type": char["name"], "value": char["value"]},
|
"value": {"type": char["name"], "value": char["value"]},
|
||||||
}
|
}
|
||||||
for char in Characteristic.objects.filter(
|
for char in Characteristic.objects.filter(value__unaccent__icontains=val)[
|
||||||
value__unaccent__icontains=val
|
:20
|
||||||
).values("name", "value")
|
].values("name", "value")
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
return schema
|
return schema
|
||||||
|
|
|
@ -64,12 +64,23 @@ def load_excel():
|
||||||
|
|
||||||
def process_unit_character():
|
def process_unit_character():
|
||||||
for el in UnitCharacteristic.objects.all():
|
for el in UnitCharacteristic.objects.all():
|
||||||
nums = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", el.value)
|
nums = re.findall(
|
||||||
if len(nums) != 1:
|
"[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", el.value
|
||||||
el.delete()
|
)
|
||||||
else:
|
if len(nums) == 1:
|
||||||
try:
|
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()
|
el.save()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
el.delete()
|
el.delete()
|
||||||
|
|
|
@ -16,15 +16,26 @@ from search.services.translate import translate_en_ru, translate_ru_en
|
||||||
|
|
||||||
def process_unit_operation(unit: ProductUnitCharacteristic.objects, operation: str):
|
def process_unit_operation(unit: ProductUnitCharacteristic.objects, operation: str):
|
||||||
if operation.startswith("<=") or operation.startswith("=<"):
|
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(">="):
|
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(">"):
|
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("<"):
|
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("="):
|
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
|
return unit
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,7 +44,9 @@ def apply_qs_search(qs: Product.objects, text: str):
|
||||||
text = text.replace(st, " ")
|
text = text.replace(st, " ")
|
||||||
text = text.split()
|
text = text.split()
|
||||||
for word in text:
|
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
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user