Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

SECRET_KEY=change-me
DEBUG=True
ALLOWED_HOSTS=
ALLOWED_HOSTS
HS_CODE_SEARCH_THRESHOLD=0.1

# db
DATABASE_URL=postgres://hsuser:hspass@localhost:5432/hsdb
34 changes: 18 additions & 16 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.db import models
from django.contrib.auth.models import AbstractUser

# from django.contrib.postgres.indexes import GinIndex
# from django.contrib.postgres.operations import TrigramExtension
from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.operations import TrigramExtension


class User(AbstractUser):
Expand Down Expand Up @@ -31,17 +31,19 @@ class Meta:
models.UniqueConstraint(fields=["hs_code"], name="unique_hs_code")
]


# class Meta:
# indexes = [
# GinIndex(
# fields=["hs_code"],
# opclasses=["gin_trgm_ops"],
# name="hscode_hs_code_trgm_idx"
# ),
# GinIndex(
# fields=["description"],
# opclasses=["gin_trgm_ops"],
# name="hscode_description_trgm_idx"
# ),
# ]
class Meta:
constraints = [
models.UniqueConstraint(fields=["hs_code"], name="unique_hs_code")
]
indexes = [
GinIndex(
fields=["hs_code"],
opclasses=["gin_trgm_ops"],
name="hscode_hs_code_trgm_idx",
),
GinIndex(
fields=["description"],
opclasses=["gin_trgm_ops"],
name="hscode_description_trgm_idx",
),
]
4 changes: 2 additions & 2 deletions app/services/file_upload_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import io
from dataclasses import dataclass

from .models import HsCode, HsCodeFile, Category
from category_service import HS_CHAPTER_CATEGORIES, get_or_create_category_for_hs_code
from app.models import HsCode, HsCodeFile, Category
from .category_service import HS_CHAPTER_CATEGORIES, get_or_create_category_for_hs_code


@dataclass
Expand Down
7 changes: 6 additions & 1 deletion app/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from django.urls import path
from .views import HsCodeUploadView
from .views import HsCodeUploadView, HsCodeSearchView

urlpatterns = [
path(
"hs-codes/",
HsCodeSearchView.as_view(),
name="hs-code-search",
),
path("hs-codes/upload/", HsCodeUploadView.as_view(), name="hscode-upload"),
]
37 changes: 36 additions & 1 deletion app/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
from dataclasses import asdict

from django.conf import settings
from django.contrib.postgres.search import TrigramSimilarity

from rest_framework import generics
from rest_framework.exceptions import ValidationError

from .models import HsCode

from rest_framework import status
from rest_framework.parsers import MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView

from .permissions import IsAdminOrStaff
from .serializers import HsCodeUploadSerializer
from .serializers import HsCodeUploadSerializer, HsCodeSerializer
from .services.file_upload_service import process_hs_code_csv


Expand All @@ -27,3 +35,30 @@ def post(self, request):
return Response({"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST)

return Response(asdict(result), status=status.HTTP_201_CREATED)


class HsCodeSearchView(generics.ListAPIView):
serializer_class = HsCodeSerializer

def get_queryset(self):
q = self.request.query_params.get("q")

if not q:
raise ValidationError({"q": ["This query parameter is required."]})

threshold = getattr(
settings,
"HS_CODE_SEARCH_THRESHOLD",
0.1,
)

return (
HsCode.objects.annotate(
similarity=(
TrigramSimilarity("description", q) * 2
+ TrigramSimilarity("hs_code", q)
)
)
.filter(similarity__gte=threshold)
.order_by("-similarity")
)
7 changes: 7 additions & 0 deletions core/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.postgres",
# third_party
"rest_framework",
# custom
Expand Down Expand Up @@ -108,3 +109,9 @@
"user": "300/minute",
},
}

HS_CODE_SEARCH_THRESHOLD = config(
"HS_CODE_SEARCH_THRESHOLD",
default=0.1,
cast=float,
)
15 changes: 15 additions & 0 deletions logfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
2026-06-01 15:25:18.444 CAT [15141] LOG: starting PostgreSQL 18.2 on aarch64-unknown-linux-android, compiled by Android (13989888, +pgo, +bolt, +lto, +mlgo, based on r563880c) clang version 21.0.0 (https://android.googlesource.com/toolchain/llvm-project 5e96669f06077099aa41290cdb4c5e6fa0f59349), 64-bit
2026-06-01 15:25:18.445 CAT [15141] LOG: listening on IPv4 address "127.0.0.1", port 5432
2026-06-01 15:25:18.446 CAT [15141] LOG: listening on Unix socket "/data/data/com.termux/files/usr/tmp/.s.PGSQL.5432"
2026-06-01 15:25:18.468 CAT [15149] LOG: database system was shut down at 2026-06-01 15:24:53 CAT
2026-06-01 15:25:18.497 CAT [15141] LOG: database system is ready to accept connections
2026-06-01 15:26:48.604 CAT [15454] ERROR: database "hsdb" already exists
2026-06-01 15:26:48.604 CAT [15454] STATEMENT: CREATE DATABASE hsdb OWNER hsuser;
2026-06-01 15:27:05.786 CAT [15147] LOG: checkpoint starting: immediate force wait
2026-06-01 15:27:05.979 CAT [15147] LOG: checkpoint complete: wrote 52 buffers (0.3%), wrote 3 SLRU buffers; 1 WAL file(s) added, 0 removed, 0 recycled; write=0.022 s, sync=0.008 s, total=0.193 s; sync files=21, longest=0.001 s, average=0.001 s; distance=4745 kB, estimate=4745 kB; lsn=0/1C06188, redo lsn=0/1C06130
2026-06-01 15:27:12.309 CAT [15573] ERROR: role "hsuser" already exists
2026-06-01 15:27:12.309 CAT [15573] STATEMENT: CREATE USER hsuser WITH PASSWORD 'hspass';
2026-06-01 15:32:06.078 CAT [15147] LOG: checkpoint starting: time
2026-06-01 15:33:46.511 CAT [15147] LOG: checkpoint complete: wrote 991 buffers (6.0%), wrote 1 SLRU buffers; 0 WAL file(s) added, 0 removed, 1 recycled; write=100.306 s, sync=0.084 s, total=100.433 s; sync files=310, longest=0.004 s, average=0.001 s; distance=4696 kB, estimate=4740 kB; lsn=0/209C228, redo lsn=0/209C198
2026-06-01 15:42:06.686 CAT [15147] LOG: checkpoint starting: time
2026-06-01 15:42:22.571 CAT [15147] LOG: checkpoint complete: wrote 157 buffers (1.0%), wrote 1 SLRU buffers; 0 WAL file(s) added, 0 removed, 0 recycled; write=15.837 s, sync=0.015 s, total=15.886 s; sync files=122, longest=0.005 s, average=0.001 s; distance=941 kB, estimate=4360 kB; lsn=0/2193BC8, redo lsn=0/2187958