This commit is contained in:
gitea 2024-12-22 15:18:14 +00:00
commit faf5f14881
25 changed files with 102 additions and 148 deletions

View File

@ -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

View File

@ -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?"

View File

@ -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:

View File

@ -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}")

View File

@ -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'),

View File

@ -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

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

View File

@ -12,7 +12,6 @@
padding: 10px; padding: 10px;
height: 200px; height: 200px;
overflow-y: scroll; overflow-y: scroll;
background-color: #f9f9f9;
} }
</style> </style>

View File

@ -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"