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

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

27
.idea/Houshyar.iml generated Normal file
View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="Houshyar/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10 (houshyar_env)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
</component>
</module>

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (houshyar_env)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Houshyar.iml" filepath="$PROJECT_DIR$/.idea/Houshyar.iml" />
</modules>
</component>
</project>

0
Houshyar/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

16
Houshyar/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for Houshyar project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Houshyar.settings')
application = get_asgi_application()

119
Houshyar/settings.py Normal file
View File

@@ -0,0 +1,119 @@
"""
Django settings for Houshyar project.
Generated by 'django-admin startproject' using Django 6.0.1.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-f7wv^3wl@0&d8%)zf7)sl!n5m8kij(21su&_d=_tz&0ge+1+-1'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'chat',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'Houshyar.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'Houshyar.wsgi.application'
# Database
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/6.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = 'static/'

23
Houshyar/urls.py Normal file
View File

@@ -0,0 +1,23 @@
"""
URL configuration for Houshyar project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/6.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('chat.urls')),
]

16
Houshyar/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for Houshyar project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Houshyar.settings')
application = get_wsgi_application()

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
)

22
manage.py Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Houshyar.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()