Merge branch 'main' of http://10.10.1.120:3000/tenhal_admin/haikal
This commit is contained in:
commit
faf5f14881
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include, re_path
|
from django.urls import path, include
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.i18n import i18n_patterns
|
from django.conf.urls.i18n import i18n_patterns
|
||||||
|
|||||||
@ -1,83 +1,45 @@
|
|||||||
import re
|
from openai import OpenAI
|
||||||
from nltk.tokenize import word_tokenize
|
from inventory import models
|
||||||
from django.db.models import Q
|
from car_inventory import settings
|
||||||
from inventory.models import Car # Import your car-related models
|
|
||||||
import nltk
|
|
||||||
# Download required NLTK resources
|
|
||||||
try:
|
|
||||||
|
|
||||||
nltk.download("punkt")
|
def fetch_data(dealer):
|
||||||
except ImportError:
|
try:
|
||||||
raise ImportError("Ensure nltk is installed by running 'pip install nltk'.")
|
# Annotate total cars by make, model, and trim
|
||||||
|
cars = models.Car.objects.filter(dealer=dealer).count()
|
||||||
|
print(cars)
|
||||||
|
if cars:
|
||||||
|
return f"إجمالي عدد السيارات في المخزون الخاص بـ {dealer.get_local_name}) هو {cars}"
|
||||||
|
# return f"The total cars in {dealer} inventory is ( {cars} )."
|
||||||
|
else:
|
||||||
|
return "No cars found in the inventory."
|
||||||
|
except Exception as e:
|
||||||
|
return f"An error occurred while fetching car data: {str(e)}"
|
||||||
|
|
||||||
# Static responses for predefined intents
|
|
||||||
RESPONSES = {
|
|
||||||
"greet": ["Hello! How can I assist you today with Haikal Car Inventory?"],
|
|
||||||
"inventory_check": ["You can check the car inventory in the Cars section. Do you want to search for a specific car?"],
|
|
||||||
"car_status": ["If a car is sold, it will either be marked as SOLD or removed from the inventory, as per your preferences."],
|
|
||||||
"sell_process": ["To sell a car, the process involves creating a Sell Order, adding the customer, confirming payment, generating an invoice, and finally delivering the car."],
|
|
||||||
"transfer_process": ["Dealers can transfer cars to other branches or dealers for display or sale. This is handled through Sell Orders as well."],
|
|
||||||
"bye": ["Goodbye! If you need further assistance, just ask!"],
|
|
||||||
}
|
|
||||||
|
|
||||||
def clean_input(user_input):
|
def get_gpt4_response(user_input, dealer):
|
||||||
"""
|
dealer = dealer
|
||||||
Clean and tokenize user input.
|
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
||||||
"""
|
|
||||||
user_input = user_input.lower()
|
|
||||||
user_input = re.sub(r"[^\w\s]", "", user_input) # Remove punctuation
|
|
||||||
return word_tokenize(user_input)
|
|
||||||
|
|
||||||
def classify_input(tokens):
|
if "سيارة في المخزون" in user_input.lower():
|
||||||
"""
|
# cars = user_input.split("how many cars")[-1].strip()
|
||||||
Classify user intent based on tokens.
|
car_data = fetch_data(dealer)
|
||||||
"""
|
user_input += f" Relevant car data: {car_data}"
|
||||||
if any(word in tokens for word in ["hello", "hi", "hey"]):
|
try:
|
||||||
return "greet"
|
completion = client.chat.completions.create(
|
||||||
elif any(word in tokens for word in ["inventory", "cars", "check"]):
|
model="gpt-4o",
|
||||||
return "inventory_check"
|
messages=[
|
||||||
elif any(word in tokens for word in ["sell", "sold", "process"]):
|
{
|
||||||
return "sell_process"
|
"role": "system",
|
||||||
elif any(word in tokens for word in ["transfer", "branch", "display"]):
|
"content": (
|
||||||
return "transfer_process"
|
"You are an assistant specialized in car inventory management for the Haikal system. "
|
||||||
elif any(word in tokens for word in ["bye", "goodbye", "exit"]):
|
"You can answer questions about the inventory, sales process, car transfers, invoices, "
|
||||||
return "bye"
|
"and other application-specific functionalities. Always provide responses aligned "
|
||||||
elif any(word in tokens for word in ["price", "cost", "value"]):
|
"with the Haikal system's structure and features."
|
||||||
return "car_price"
|
)
|
||||||
else:
|
},
|
||||||
return "unknown"
|
{"role": "user", "content": user_input},
|
||||||
|
],
|
||||||
def get_dynamic_response(intent, tokens):
|
)
|
||||||
"""
|
return completion.choices[0].message.content.strip()
|
||||||
Generate dynamic responses by querying the database.
|
except Exception as e:
|
||||||
"""
|
return f"An error occurred while generating the response: {str(e)}"
|
||||||
if intent == "car_price":
|
|
||||||
# Extract car name from tokens
|
|
||||||
car_name = " ".join([word.capitalize() for word in tokens])
|
|
||||||
try:
|
|
||||||
car = Car.objects.filter(Q(make__icontains=car_name) | Q(model__icontains=car_name)).first()
|
|
||||||
if car:
|
|
||||||
return f"The price of {car_name} is {car.finance.selling_price}."
|
|
||||||
return f"Sorry, no car matching '{car_name}' was found in the inventory."
|
|
||||||
except Exception as e:
|
|
||||||
return f"An error occurred while retrieving the car price: {str(e)}"
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_response(user_input):
|
|
||||||
"""
|
|
||||||
Generate a response based on the user's input.
|
|
||||||
"""
|
|
||||||
tokens = clean_input(user_input)
|
|
||||||
intent = classify_input(tokens)
|
|
||||||
|
|
||||||
# Check for a dynamic response
|
|
||||||
dynamic_response = get_dynamic_response(intent, tokens)
|
|
||||||
if dynamic_response:
|
|
||||||
return dynamic_response
|
|
||||||
|
|
||||||
# Return a static response if available
|
|
||||||
if intent in RESPONSES:
|
|
||||||
return RESPONSES[intent][0]
|
|
||||||
|
|
||||||
# Default response for unknown intents
|
|
||||||
return "I'm sorry, I didn't understand that. Could you rephrase your question about the Haikal system?"
|
|
||||||
|
|||||||
@ -1,34 +1,25 @@
|
|||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from .chatbot_logic import get_response
|
from .chatbot_logic import get_gpt4_response
|
||||||
import json
|
import json
|
||||||
|
|
||||||
class ChatbotView(View):
|
class ChatbotView(LoginRequiredMixin, View):
|
||||||
"""
|
|
||||||
Class-based view to handle chatbot template rendering (GET)
|
|
||||||
and GPT-4-based chatbot API responses (POST).
|
|
||||||
"""
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""
|
|
||||||
Render the chatbot interface template.
|
|
||||||
"""
|
|
||||||
return render(request, "haikalbot/chatbot.html")
|
return render(request, "haikalbot/chatbot.html")
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
dealer = request.user.dealer
|
||||||
Handle chatbot API requests and return responses as JSON.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
# Parse JSON payload from the request body
|
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
user_message = data.get("message", "").strip()
|
user_message = data.get("message", "").strip()
|
||||||
|
|
||||||
if not user_message:
|
if not user_message:
|
||||||
return JsonResponse({"error": "Message cannot be empty."}, status=400)
|
return JsonResponse({"error": "Message cannot be empty."}, status=400)
|
||||||
|
|
||||||
# Get GPT-4 response
|
chatbot_response = get_gpt4_response(user_message, dealer)
|
||||||
chatbot_response = get_response(user_message)
|
|
||||||
|
|
||||||
return JsonResponse({"response": chatbot_response}, status=200)
|
return JsonResponse({"response": chatbot_response}, status=200)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -9,28 +9,31 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
def handle(self, *args, **kwargs):
|
def handle(self, *args, **kwargs):
|
||||||
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
||||||
car_make = CarMake.objects.all()
|
car_model = CarModel.objects.all()[0:1000]
|
||||||
total = car_make.count()
|
total = car_model.count()
|
||||||
print(f'Translating {total} names...')
|
print(f'Translating {total} names...')
|
||||||
for index, car_make in enumerate(car_make, start=1):
|
for index, car_model in enumerate(car_model, start=1):
|
||||||
try:
|
try:
|
||||||
completion = client.chat.completions.create(
|
completion = client.chat.completions.create(
|
||||||
model="gpt-4",
|
model="gpt-4o",
|
||||||
messages=[
|
messages=[
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
"content": "You are a translation assistant that translates English to Arabic."
|
"content": "You are an assistant that finds the Arabic Names for car models."
|
||||||
|
"Do not translate just write the arabic names."
|
||||||
|
"If the name is a number just write it as is."
|
||||||
|
"If you can't find the arabic name just write -"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": car_make.name
|
"content": car_model.name
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
temperature=0.3,
|
temperature=0.2,
|
||||||
)
|
)
|
||||||
translation = completion.choices[0].message.content.strip()
|
translation = completion.choices[0].message.content.strip()
|
||||||
car_make.arabic_name = translation
|
car_model.arabic_name = translation
|
||||||
car_make.save()
|
car_model.save()
|
||||||
print(f"[{index}/{total}] Translated '{car_make.name}' to '{translation}'")
|
print(f"[{index}/{total}] Translated '{car_model.name}' to '{translation}'")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error translating '{car_make.name}': {e}")
|
print(f"Error translating '{car_model.name}': {e}")
|
||||||
|
|||||||
@ -47,6 +47,9 @@ urlpatterns = [
|
|||||||
|
|
||||||
|
|
||||||
# Car URLs
|
# Car URLs
|
||||||
|
path('cars/inventory/',
|
||||||
|
views.CarInventory.as_view(),
|
||||||
|
name='car_inventory_all'),
|
||||||
path('cars/inventory/<int:make_id>/<int:model_id>/<int:trim_id>/',
|
path('cars/inventory/<int:make_id>/<int:model_id>/<int:trim_id>/',
|
||||||
views.CarInventory.as_view(),
|
views.CarInventory.as_view(),
|
||||||
name='car_inventory'),
|
name='car_inventory'),
|
||||||
|
|||||||
@ -296,10 +296,10 @@ class CarInventory(LoginRequiredMixin, ListView):
|
|||||||
ordering = ["receiving_date"]
|
ordering = ["receiving_date"]
|
||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
def get_queryset(self, *args, **kwargs):
|
||||||
query = self.request.GET.get("q")
|
query = self.request.GET.get('q')
|
||||||
make_id = self.kwargs["make_id"]
|
make_id = self.kwargs['make_id']
|
||||||
model_id = self.kwargs["model_id"]
|
model_id = self.kwargs['model_id']
|
||||||
trim_id = self.kwargs["trim_id"]
|
trim_id = self.kwargs['trim_id']
|
||||||
|
|
||||||
cars = models.Car.objects.filter(
|
cars = models.Car.objects.filter(
|
||||||
dealer=self.request.user.dealer.get_root_dealer,
|
dealer=self.request.user.dealer.get_root_dealer,
|
||||||
@ -355,32 +355,28 @@ def inventory_stats_view(request):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Prepare the nested structure
|
|
||||||
inventory = {}
|
inventory = {}
|
||||||
for car in cars:
|
for car in cars:
|
||||||
# Make Level
|
|
||||||
make = car.id_car_make
|
make = car.id_car_make
|
||||||
if make.id_car_make not in inventory:
|
if make.id_car_make not in inventory:
|
||||||
inventory[make.id_car_make] = {
|
inventory[make.id_car_make] = {
|
||||||
"make_id": make.id_car_make,
|
'make_id': make.id_car_make,
|
||||||
"make_name": make.get_local_name(),
|
'make_name': make.get_local_name(),
|
||||||
"total_cars": 0,
|
'total_cars': 0,
|
||||||
"models": {},
|
'models': {}
|
||||||
}
|
}
|
||||||
inventory[make.id_car_make]["total_cars"] += 1
|
inventory[make.id_car_make]["total_cars"] += 1
|
||||||
|
|
||||||
# Model Level
|
|
||||||
model = car.id_car_model
|
model = car.id_car_model
|
||||||
if model and model.id_car_model not in inventory[make.id_car_make]["models"]:
|
if model and model.id_car_model not in inventory[make.id_car_make]['models']:
|
||||||
inventory[make.id_car_make]["models"][model.id_car_model] = {
|
inventory[make.id_car_make]['models'][model.id_car_model] = {
|
||||||
"model_id": model.id_car_model,
|
'model_id': model.id_car_model,
|
||||||
"model_name": model.get_local_name(),
|
'model_name': model.get_local_name(),
|
||||||
"total_cars": 0,
|
'total_cars': 0,
|
||||||
"trims": {},
|
'trims': {}
|
||||||
}
|
}
|
||||||
inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1
|
inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1
|
||||||
|
|
||||||
# Trim Level
|
|
||||||
trim = car.id_car_trim
|
trim = car.id_car_trim
|
||||||
if (
|
if (
|
||||||
trim
|
trim
|
||||||
@ -394,15 +390,14 @@ def inventory_stats_view(request):
|
|||||||
trim.id_car_trim
|
trim.id_car_trim
|
||||||
]["total_cars"] += 1
|
]["total_cars"] += 1
|
||||||
|
|
||||||
# Convert to a list for easier template rendering
|
|
||||||
result = {
|
result = {
|
||||||
"total_cars": cars.count(),
|
"total_cars": cars.count(),
|
||||||
"makes": [
|
"makes": [
|
||||||
{
|
{
|
||||||
"make_id": make_data["make_id"],
|
'make_id': make_data['make_id'],
|
||||||
"make_name": make_data["make_name"],
|
'make_name': make_data['make_name'],
|
||||||
"total_cars": make_data["total_cars"],
|
'total_cars': make_data['total_cars'],
|
||||||
"models": [
|
'models': [
|
||||||
{
|
{
|
||||||
"model_id": model_data["model_id"],
|
"model_id": model_data["model_id"],
|
||||||
"model_name": model_data["model_name"],
|
"model_name": model_data["model_name"],
|
||||||
|
|||||||
BIN
static/.DS_Store
vendored
BIN
static/.DS_Store
vendored
Binary file not shown.
BIN
static/images/.DS_Store
vendored
BIN
static/images/.DS_Store
vendored
Binary file not shown.
BIN
static/images/car_make/.DS_Store
vendored
BIN
static/images/car_make/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.0 KiB |
BIN
static/images/logos/.DS_Store
vendored
BIN
static/images/logos/.DS_Store
vendored
Binary file not shown.
@ -12,7 +12,6 @@
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
{% for make in inventory.makes %}
|
{% for make in inventory.makes %}
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<p class="accordion-header" id="heading{{ make.make_id }}">
|
<p class="accordion-header" id="heading{{ make.make_id }}">
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="accordion-button"
|
class="accordion-button"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user