api contact

This commit is contained in:
javad committed 2026-09-30 08:39:26 +03:30
1 parent 0b56766f96
commit 04d2bc957f
14 files changed
+595 -1

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
import re
from django.db.models import Case, IntegerField, Q, Value, When
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from apps.customers.models import CustomerProfile
from .models import PhoneContactApiKey
_PHONE_DIGITS = str.maketrans("۰۱۲۳۴۵۶۷۸۹٠١٢٣٤٥٦٧٨٩", "01234567890123456789")
def _dialable_number(value):
number = re.sub(r"[\s().-]", "", str(value or "").translate(_PHONE_DIGITS))
return number if 0 < len(number) <= 64 and re.fullmatch(r"[0-9+*#]+", number) else ""
def _json(data, status=200):
response = JsonResponse(data, status=status, json_dumps_params={"ensure_ascii": False})
response["Cache-Control"] = "no-store"
return response
@require_GET
def phone_contacts(request):
authorization = request.headers.get("Authorization", "")
scheme, separator, token = authorization.partition(" ")
if not separator or scheme.lower() != "bearer" or not token or token != token.strip():
return _json({"error": "unauthorized"}, 401)
credential = PhoneContactApiKey.objects.filter(pk=1, is_active=True).first()
if not credential or not credential.accepts(token):
return _json({"error": "unauthorized"}, 401)
query = request.GET.get("search", "").strip()[:80]
if len(query) < 2:
return _json({"contacts": []})
try:
limit = min(50, max(1, int(request.GET.get("limit", "50"))))
except ValueError:
return _json({"error": "invalid limit"}, 400)
query = query.translate(str.maketrans({"ي": "ی", "ك": "ک", "۰": "0", "۱": "1", "۲": "2", "۳": "3", "۴": "4", "۵": "5", "۶": "6", "۷": "7", "۸": "8", "۹": "9"}))
contacts = CustomerProfile.objects.select_related("user").filter(
user__account_type="CUSTOMER", user__is_active=True, user__mobile__isnull=False
).exclude(user__mobile="").filter(
Q(first_name__gt="") | Q(last_name__gt="") | Q(organization_name__gt="") |
Q(user__first_name__gt="") | Q(user__last_name__gt="")
)
for word in re.split(r"\s+", query):
contacts = contacts.filter(
Q(first_name__icontains=word) | Q(last_name__icontains=word) |
Q(organization_name__icontains=word) | Q(user__first_name__icontains=word) |
Q(user__last_name__icontains=word) | Q(user__mobile__icontains=word)
)
preferred = (
Q(first_name__iexact=query) | Q(last_name__iexact=query) |
Q(organization_name__iexact=query) | Q(user__first_name__iexact=query) |
Q(user__last_name__iexact=query)
)
starts_with = (
Q(first_name__istartswith=query) | Q(last_name__istartswith=query) |
Q(organization_name__istartswith=query) | Q(user__first_name__istartswith=query) |
Q(user__last_name__istartswith=query)
)
contacts = contacts.annotate(_search_rank=Case(
When(preferred, then=Value(0)),
When(starts_with, then=Value(1)),
default=Value(2), output_field=IntegerField(),
)).order_by("_search_rank", "pk")
results = []
for profile in contacts[:limit]:
name = (f"{profile.first_name} {profile.last_name}".strip() or
f"{profile.user.first_name} {profile.user.last_name}".strip() or
profile.organization_name.strip())[:120]
number = _dialable_number(profile.user.local_mobile)
if name and number:
results.append({"name": name, "phones": [{"type": "همراه", "number": number}]})
return _json({"contacts": results})