first push

This commit is contained in:
2026-02-01 16:40:43 +03:30
commit 06014b267f
22 changed files with 656 additions and 0 deletions

0
chat/__init__.py Normal file
View File

3
chat/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
chat/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class ChatConfig(AppConfig):
name = 'chat'

View File

3
chat/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

319
chat/schema.py Normal file
View File

@@ -0,0 +1,319 @@
from openai import OpenAI
import json
import re
API_KEY = "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" # ⚠️ جایگزین با کلیدت کن
client = OpenAI(api_key=API_KEY, timeout=60)
MODELS_SCHEMA = {
"Poultry": {
"fields": {
"UserName": "string",
"Password": "string",
"FirstName": "string",
"LastName": "string",
"UserGroupName": "string",
"UserRoleName": "string",
"UserGroupId": "string",
"UserRoleId": "string",
"Mobile": "string",
"Email": "string",
"UserIsActive": "boolean",
"UserIsActiveDescription": "string",
"RegDate": "string",
"RegDateShamsi": "string",
"RegDateShamsiWithTime": "string",
"RegDateShamsiOnlyTime": "string",
"StringId": "string",
"IsPersisted": "boolean",
"AllowInsert": "boolean",
"AllowUpdate": "boolean",
"ModalCss": "string",
"GridContainerParametersModel": "string",
"MenuUserAccess": "string",
"MenuUserAccessId": "string",
"LogTableName": "string",
"LogTableAlias": "string",
"PageTitle": "string",
"UnitName": "string",
"SystemCode": "string",
"TrackingCode": "string",
"EpidemiologicCode": "string",
"PartIdCode": "string",
"PostalCode": "string",
"UnitId": "string",
"UnitTypeId": "string",
"UnitTypeName": "string",
"LocationIdProvince": "string",
"LocationIdCity": "string",
"LocationNameProvince": "string",
"LocationNameCity": "string",
"UnitIsActive": "boolean",
"UnitIsActiveDescription": "string",
"PId": "string",
"Province": "string",
"City": "string",
}
},
"Hatching": {
"fields": {
"poultry": "ForeignKey(Poultry)",
"Date": "datetime",
"ArchiveDate": "datetime",
"BroilerFlockRequestId": "integer",
"InsertDate": "string",
"LastChangeStatusDate": "string",
"LastChangeStatusDateShamsi": "string",
"FlockRequestUnitName": "string",
"PedigreeName": "string",
"StatusId": "integer",
"Status": "integer",
"StatusName": "string",
"PedigreeType": "integer",
"BroilerPedigreeTypeName": "string",
"StatusColor": "string",
"SystemRevocationDate": "string",
"RemindDays": "integer",
"PartyCount": "integer",
"GoodCount": "integer",
"ShowButtons": "boolean",
"HasSync": "boolean",
"BroilerFlockRequestExpireStatus": "integer",
"IdWithFormat": "string",
"ProvinceName": "string",
"CityName": "string",
"Address": "string",
"UnitTel": "string",
"UnitPostalCode": "string",
"UnitName": "string",
"SystemCode": "string",
"CapacityFemale": "integer",
"EpidemiologicCode": "string",
"RequestCode": "string",
"RequestDate": "string",
"RequestDateFa": "string",
"RequestCount": "integer",
"DeliverDate": "string",
"DeliverDateFa": "string",
"UnionName": "string",
"PersonTypeId": "integer",
"PersonType": "integer",
"PersonTypeName": "string",
"PersonFullName": "string",
"NationalCode": "string",
"InteractType": "integer",
"InteractTypeName": "string",
"UnionTypeId": "integer",
"UnionTypeName": "string",
"SendDate": "string",
"SendDateFa": "string",
"ChickCountSum": "integer",
"CalculatedDate": "string",
"CalculatedDateFa": "string",
"PartIdCode": "string",
"CertId": "string",
"StartDate": "string",
"StartDateFa": "string",
"EndDate": "string",
"EndDateFa": "string",
"RemainCredit": "integer",
"StrRemainCredit": "string",
"ShowStatus": "string",
"ValidStatus": "string",
"ValidStatusName": "string",
"RegDate": "string",
"RegDateShamsi": "string",
"RegDateShamsiWithTime": "string",
"RegDateShamsiOnlyTime": "string",
"HatchingId": "string",
"StringId": "string",
"IsPersisted": "boolean",
"AllowInsert": "boolean",
"AllowUpdate": "boolean",
"ModalCss": "string",
"GridContainerParametersModel": "string",
"MenuUserAccess": "string",
"MenuUserAccessId": "integer",
"LogTableName": "string",
"LogTableAlias": "string",
"PageTitle": "string",
"Evacuation": "integer",
"Age": "integer",
"KillingAve": "integer",
"Period": "integer",
"LeftOver": "integer",
"samasat_discharge_percentage": "integer",
"GoodSum": "integer",
}
}
}
from datetime import datetime, timedelta
from django.utils import timezone
def apply_date_filter(queryset, date_filter):
if not date_filter:
return queryset
field = date_filter.get("field", "Date")
filter_type = date_filter.get("type")
value = date_filter.get("value")
now = timezone.now()
# امروز
if filter_type == "today":
start = now.replace(hour=0, minute=0, second=0, microsecond=0)
end = start + timedelta(days=1)
return queryset.filter(
**{f"{field}__gte": start, f"{field}__lt": end}
)
# دیروز
if filter_type == "yesterday":
start = (now - timedelta(days=1)).replace(
hour=0, minute=0, second=0, microsecond=0
)
end = start + timedelta(days=1)
return queryset.filter(
**{f"{field}__gte": start, f"{field}__lt": end}
)
# n روز گذشته
if filter_type == "last_n_days" and value:
start = now - timedelta(days=int(value))
return queryset.filter(**{f"{field}__gte": start})
# این هفته
if filter_type == "this_week":
start = now - timedelta(days=now.weekday())
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
return queryset.filter(**{f"{field}__gte": start})
# این ماه
if filter_type == "this_month":
start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
return queryset.filter(**{f"{field}__gte": start})
# n ماه گذشته
if filter_type == "last_n_month" and value:
start = now
for _ in range(int(value)):
start = (start.replace(day=1) - timedelta(days=1)).replace(day=1)
start = start.replace(hour=0, minute=0, second=0, microsecond=0)
return queryset.filter(**{f"{field}__gte": start})
# این سال
if filter_type == "this_year":
start = now.replace(
month=1, day=1, hour=0, minute=0, second=0, microsecond=0
)
return queryset.filter(**{f"{field}__gte": start})
# n سال گذشته
if filter_type == "last_n_year" and value:
start = now.replace(
year=now.year - int(value),
month=1,
day=1,
hour=0,
minute=0,
second=0,
microsecond=0
)
return queryset.filter(**{f"{field}__gte": start})
return queryset
def clean_gpt_json(text):
"""
بک‌تیک‌ها و پیشوندهای GPT را پاک می‌کند
"""
# پاک کردن ```json یا ``` یا ```text
text = re.sub(r"^```(?:json)?\s*", "", text)
text = re.sub(r"\s*```$", "", text)
return text.strip()
def get_filters_from_question(question):
prompt = f"""
شما مدل‌های زیر را دارید و فیلدهایشان را می‌دانید:
{MODELS_SCHEMA}
سوال کاربر: "{question}"
لطفاً یک JSON بازگردانید که فیلترهای Django ORM را نشان می‌دهد.
JSON باید فقط شامل کلید زیر باشد:
- "models": لیستی از مدل‌هایی که برای پاسخ به سوال کاربر نیاز است.
هر آیتم داخل "models" باید شامل این کلیدها باشد:
- "model": نام مدل (مثلا "Hatching" یا "Poultry")
- "filters": دیکشنری فیلدها و مقادیر برای filter(**filters)
- "aggregations":
- اگر کاربر عملیات آماری خواسته، لیستی از نام عملیات‌ها مثل ["count", "sum"]
- اگر عملیات آماری ندارد، مقدار null
- "fields_to_return":
- اگر کاربر اطلاعات توصیفی خواسته، لیستی از فیلدها
- در غیر این صورت null
اگر سوال شامل زمان نسبی بود (مثل امروز، دیروز، دو روز پیش، یک هفته پیش و ...):
- به جای مقدار مستقیم تاریخ،
- یک key به نام "date_filter" اضافه کن.
ساختار date_filter باید این باشد:
{{
"field": "Date",
"type": یکی از این مقادیر:
"today"
"yesterday"
"last_n_days"
"this_week"
"this_month",
"last_n_month"
"this_year",
"last_n_year",
"value": فقط در صورتی که لازم بود
(مثلا برای last_n_days عدد روز)
}}
❗️هرگز تاریخ میلادی یا شمسی ننویس.
❗️هرگز از today یا now به عنوان مقدار فیلتر استفاده نکن.
قوانین بسیار مهم:
- اگر سوال کاربر ترکیبی است، باید چند مدل در لیست "models" بازگردانده شود.
- اگر فقط یک مدل لازم است، باز هم آن را داخل لیست قرار دهید.
- مقدار aggregations فقط نام ساده عملیات‌ها باشد (بدون ":"، بدون نام فیلد، بدون دیکشنری).
- هیچ کلید اضافه‌ای ننویسید.
- خروجی فقط JSON معتبر باشد، بدون توضیح اضافی.
"""
try:
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[{"role": "user", "content": prompt}]
)
gpt_output = response.choices[0].message.content.strip()
gpt_output = clean_gpt_json(gpt_output) # پاک کردن بک‌تیک‌ها و متن اضافی
try:
filters_json = json.loads(gpt_output)
except json.JSONDecodeError:
print("GPT output is not valid JSON after cleaning:", gpt_output)
filters_json = {}
except Exception as e:
print("Error calling OpenAI API:", e)
filters_json = {}
return filters_json

3
chat/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
chat/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from chat.views import get_ai_response
urlpatterns = [
path("get_ai_response/", get_ai_response),
]

67
chat/views.py Normal file
View File

@@ -0,0 +1,67 @@
import requests
from django.db.models import Sum
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from openai import OpenAI
from schema import get_filters_from_question, apply_date_filter
API_KEY = "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" # ⚠️ جایگزین با کلیدت کن
RSI_URL='https://rsibackend.rasadyar.com/app/get_ai_response/'
client = OpenAI(api_key=API_KEY, timeout=60)
@api_view(['POST'])
@permission_classes([AllowAny])
@csrf_exempt
def get_ai_response(request):
question = request.data.get('question')
if not question:
return Response(
{"error": "Question is required"},
status=status.HTTP_400_BAD_REQUEST
)
filters_json = get_filters_from_question(question)
models_info = filters_json.get("models", [])
req_data={
"models_info":models_info
}
result_data = requests.post(
url=RSI_URL,
json=req_data,
verify=False
)
# تولید پاسخ نهایی با GPT
prompt = f"""
سوال کاربر: "{question}"
داده‌های به‌دست‌آمده:
{result_data}
لطفاً یک پاسخ کاملاً روان، خودمونی و قابل فهم برای کاربر فارسی‌زبان تولید کن.
"""
try:
response_final = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[{"role": "user", "content": prompt}]
)
answer = response_final.choices[0].message.content.strip()
except Exception as e:
print("Error generating GPT response:", e)
answer = "متأسفانه در تولید پاسخ مشکلی پیش آمد."
return Response(
{
"answer": answer,
"data": result_data
},
status=status.HTTP_200_OK
)