diff --git a/car_inventory/__pycache__/settings.cpython-311.pyc b/car_inventory/__pycache__/settings.cpython-311.pyc index 572eb5dc..825dd1b5 100644 Binary files a/car_inventory/__pycache__/settings.cpython-311.pyc and b/car_inventory/__pycache__/settings.cpython-311.pyc differ diff --git a/car_inventory/__pycache__/urls.cpython-311.pyc b/car_inventory/__pycache__/urls.cpython-311.pyc index 3189c91a..924bb9fc 100644 Binary files a/car_inventory/__pycache__/urls.cpython-311.pyc and b/car_inventory/__pycache__/urls.cpython-311.pyc differ diff --git a/car_inventory/urls.py b/car_inventory/urls.py index af9c4925..5aa86968 100644 --- a/car_inventory/urls.py +++ b/car_inventory/urls.py @@ -1,5 +1,5 @@ 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 import settings from django.conf.urls.i18n import i18n_patterns diff --git a/haikalbot/chatbot_logic.py b/haikalbot/chatbot_logic.py index 2aa03e14..6b9cd7aa 100644 --- a/haikalbot/chatbot_logic.py +++ b/haikalbot/chatbot_logic.py @@ -1,83 +1,45 @@ -import re -from nltk.tokenize import word_tokenize -from django.db.models import Q -from inventory.models import Car # Import your car-related models -import nltk -# Download required NLTK resources -try: +from openai import OpenAI +from inventory import models +from car_inventory import settings - nltk.download("punkt") -except ImportError: - raise ImportError("Ensure nltk is installed by running 'pip install nltk'.") +def fetch_data(dealer): + try: + # 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): - """ - Clean and tokenize user input. - """ - user_input = user_input.lower() - user_input = re.sub(r"[^\w\s]", "", user_input) # Remove punctuation - return word_tokenize(user_input) +def get_gpt4_response(user_input, dealer): + dealer = dealer + client = OpenAI(api_key=settings.OPENAI_API_KEY) -def classify_input(tokens): - """ - Classify user intent based on tokens. - """ - if any(word in tokens for word in ["hello", "hi", "hey"]): - return "greet" - elif any(word in tokens for word in ["inventory", "cars", "check"]): - return "inventory_check" - elif any(word in tokens for word in ["sell", "sold", "process"]): - return "sell_process" - elif any(word in tokens for word in ["transfer", "branch", "display"]): - return "transfer_process" - elif any(word in tokens for word in ["bye", "goodbye", "exit"]): - return "bye" - elif any(word in tokens for word in ["price", "cost", "value"]): - return "car_price" - else: - return "unknown" - -def get_dynamic_response(intent, tokens): - """ - Generate dynamic responses by querying the database. - """ - 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?" \ No newline at end of file + if "سيارة في المخزون" in user_input.lower(): + # cars = user_input.split("how many cars")[-1].strip() + car_data = fetch_data(dealer) + user_input += f" Relevant car data: {car_data}" + try: + completion = client.chat.completions.create( + model="gpt-4o", + messages=[ + { + "role": "system", + "content": ( + "You are an assistant specialized in car inventory management for the Haikal system. " + "You can answer questions about the inventory, sales process, car transfers, invoices, " + "and other application-specific functionalities. Always provide responses aligned " + "with the Haikal system's structure and features." + ) + }, + {"role": "user", "content": user_input}, + ], + ) + return completion.choices[0].message.content.strip() + except Exception as e: + return f"An error occurred while generating the response: {str(e)}" diff --git a/haikalbot/views.py b/haikalbot/views.py index 0328a328..7a0af10a 100644 --- a/haikalbot/views.py +++ b/haikalbot/views.py @@ -1,34 +1,25 @@ +from django.contrib.auth.mixins import LoginRequiredMixin from django.views import View from django.shortcuts import render from django.http import JsonResponse -from .chatbot_logic import get_response +from .chatbot_logic import get_gpt4_response import json -class ChatbotView(View): - """ - Class-based view to handle chatbot template rendering (GET) - and GPT-4-based chatbot API responses (POST). - """ +class ChatbotView(LoginRequiredMixin, View): def get(self, request): - """ - Render the chatbot interface template. - """ return render(request, "haikalbot/chatbot.html") def post(self, request): - """ - Handle chatbot API requests and return responses as JSON. - """ + dealer = request.user.dealer + try: - # Parse JSON payload from the request body data = json.loads(request.body) user_message = data.get("message", "").strip() if not user_message: return JsonResponse({"error": "Message cannot be empty."}, status=400) - # Get GPT-4 response - chatbot_response = get_response(user_message) + chatbot_response = get_gpt4_response(user_message, dealer) return JsonResponse({"response": chatbot_response}, status=200) except json.JSONDecodeError: diff --git a/inventory/__pycache__/admin.cpython-311.pyc b/inventory/__pycache__/admin.cpython-311.pyc index 8464c550..a4f2bd4d 100644 Binary files a/inventory/__pycache__/admin.cpython-311.pyc and b/inventory/__pycache__/admin.cpython-311.pyc differ diff --git a/inventory/__pycache__/forms.cpython-311.pyc b/inventory/__pycache__/forms.cpython-311.pyc index 1580ed63..6ff0a66d 100644 Binary files a/inventory/__pycache__/forms.cpython-311.pyc and b/inventory/__pycache__/forms.cpython-311.pyc differ diff --git a/inventory/__pycache__/models.cpython-311.pyc b/inventory/__pycache__/models.cpython-311.pyc index 06a9e577..b9a21243 100644 Binary files a/inventory/__pycache__/models.cpython-311.pyc and b/inventory/__pycache__/models.cpython-311.pyc differ diff --git a/inventory/__pycache__/services.cpython-311.pyc b/inventory/__pycache__/services.cpython-311.pyc index 210b6a29..77ff391b 100644 Binary files a/inventory/__pycache__/services.cpython-311.pyc and b/inventory/__pycache__/services.cpython-311.pyc differ diff --git a/inventory/__pycache__/urls.cpython-311.pyc b/inventory/__pycache__/urls.cpython-311.pyc index 903da3dd..f9d89f13 100644 Binary files a/inventory/__pycache__/urls.cpython-311.pyc and b/inventory/__pycache__/urls.cpython-311.pyc differ diff --git a/inventory/__pycache__/utils.cpython-311.pyc b/inventory/__pycache__/utils.cpython-311.pyc index 75684cf6..01aa93e2 100644 Binary files a/inventory/__pycache__/utils.cpython-311.pyc and b/inventory/__pycache__/utils.cpython-311.pyc differ diff --git a/inventory/__pycache__/views.cpython-311.pyc b/inventory/__pycache__/views.cpython-311.pyc index 1b6d330e..73d26b03 100644 Binary files a/inventory/__pycache__/views.cpython-311.pyc and b/inventory/__pycache__/views.cpython-311.pyc differ diff --git a/inventory/management/commands/__pycache__/translate.cpython-311.pyc b/inventory/management/commands/__pycache__/translate.cpython-311.pyc index 58dc434a..29351a10 100644 Binary files a/inventory/management/commands/__pycache__/translate.cpython-311.pyc and b/inventory/management/commands/__pycache__/translate.cpython-311.pyc differ diff --git a/inventory/management/commands/translate.py b/inventory/management/commands/translate.py index 38aa5e62..e2c2c3de 100644 --- a/inventory/management/commands/translate.py +++ b/inventory/management/commands/translate.py @@ -9,28 +9,31 @@ class Command(BaseCommand): def handle(self, *args, **kwargs): client = OpenAI(api_key=settings.OPENAI_API_KEY) - car_make = CarMake.objects.all() - total = car_make.count() + car_model = CarModel.objects.all()[0:1000] + total = car_model.count() 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: completion = client.chat.completions.create( - model="gpt-4", + model="gpt-4o", messages=[ { "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", - "content": car_make.name + "content": car_model.name } ], - temperature=0.3, + temperature=0.2, ) translation = completion.choices[0].message.content.strip() - car_make.arabic_name = translation - car_make.save() - print(f"[{index}/{total}] Translated '{car_make.name}' to '{translation}'") + car_model.arabic_name = translation + car_model.save() + print(f"[{index}/{total}] Translated '{car_model.name}' to '{translation}'") except Exception as e: - print(f"Error translating '{car_make.name}': {e}") + print(f"Error translating '{car_model.name}': {e}") diff --git a/inventory/urls.py b/inventory/urls.py index c55dcf6e..219d69ba 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -47,6 +47,9 @@ urlpatterns = [ # Car URLs + path('cars/inventory/', + views.CarInventory.as_view(), + name='car_inventory_all'), path('cars/inventory////', views.CarInventory.as_view(), name='car_inventory'), diff --git a/inventory/views.py b/inventory/views.py index 27a1f1c7..54012fe8 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -296,11 +296,11 @@ class CarInventory(LoginRequiredMixin, ListView): ordering = ["receiving_date"] def get_queryset(self, *args, **kwargs): - query = self.request.GET.get("q") - make_id = self.kwargs["make_id"] - model_id = self.kwargs["model_id"] - trim_id = self.kwargs["trim_id"] - + query = self.request.GET.get('q') + make_id = self.kwargs['make_id'] + model_id = self.kwargs['model_id'] + trim_id = self.kwargs['trim_id'] + cars = models.Car.objects.filter( dealer=self.request.user.dealer.get_root_dealer, id_car_make=make_id, @@ -355,32 +355,28 @@ def inventory_stats_view(request): ) ) - # Prepare the nested structure inventory = {} for car in cars: - # Make Level make = car.id_car_make if make.id_car_make not in inventory: inventory[make.id_car_make] = { - "make_id": make.id_car_make, - "make_name": make.get_local_name(), - "total_cars": 0, - "models": {}, + 'make_id': make.id_car_make, + 'make_name': make.get_local_name(), + 'total_cars': 0, + 'models': {} } inventory[make.id_car_make]["total_cars"] += 1 - # Model Level model = car.id_car_model - 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] = { - "model_id": model.id_car_model, - "model_name": model.get_local_name(), - "total_cars": 0, - "trims": {}, + 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] = { + 'model_id': model.id_car_model, + 'model_name': model.get_local_name(), + 'total_cars': 0, + 'trims': {} } inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1 - # Trim Level trim = car.id_car_trim if ( trim @@ -394,15 +390,14 @@ def inventory_stats_view(request): trim.id_car_trim ]["total_cars"] += 1 - # Convert to a list for easier template rendering result = { "total_cars": cars.count(), "makes": [ { - "make_id": make_data["make_id"], - "make_name": make_data["make_name"], - "total_cars": make_data["total_cars"], - "models": [ + 'make_id': make_data['make_id'], + 'make_name': make_data['make_name'], + 'total_cars': make_data['total_cars'], + 'models': [ { "model_id": model_data["model_id"], "model_name": model_data["model_name"], diff --git a/static/.DS_Store b/static/.DS_Store index 804c8de6..439c8fd0 100644 Binary files a/static/.DS_Store and b/static/.DS_Store differ diff --git a/static/images/.DS_Store b/static/images/.DS_Store index 5884e201..5cd0001f 100644 Binary files a/static/images/.DS_Store and b/static/images/.DS_Store differ diff --git a/static/images/car_make/.DS_Store b/static/images/car_make/.DS_Store index 08ce7ad3..799f2d61 100644 Binary files a/static/images/car_make/.DS_Store and b/static/images/car_make/.DS_Store differ diff --git a/static/images/car_make/Ford2.webp b/static/images/car_make/Ford2.webp deleted file mode 100644 index 60c76bc7..00000000 Binary files a/static/images/car_make/Ford2.webp and /dev/null differ diff --git a/static/images/car_make/Lincoln.webp b/static/images/car_make/Lincoln.webp deleted file mode 100644 index 68ec1168..00000000 Binary files a/static/images/car_make/Lincoln.webp and /dev/null differ diff --git a/static/images/logos/.DS_Store b/static/images/logos/.DS_Store index 08b72d29..e7c24729 100644 Binary files a/static/images/logos/.DS_Store and b/static/images/logos/.DS_Store differ diff --git a/templates/haikalbot/chatbot.html b/templates/haikalbot/chatbot.html index 620211da..97890b8d 100644 --- a/templates/haikalbot/chatbot.html +++ b/templates/haikalbot/chatbot.html @@ -12,7 +12,6 @@ padding: 10px; height: 200px; overflow-y: scroll; - background-color: #f9f9f9; } diff --git a/templates/inventory/car_form.html b/templates/inventory/car_form.html index 5877f9a8..8d8ded4c 100644 --- a/templates/inventory/car_form.html +++ b/templates/inventory/car_form.html @@ -346,7 +346,7 @@