update
This commit is contained in:
parent
2b74f92ab6
commit
152518ebdc
Binary file not shown.
Binary file not shown.
@ -61,6 +61,7 @@ INSTALLED_APPS = [
|
||||
'django_ledger',
|
||||
'djmoney',
|
||||
'sslserver',
|
||||
'haikalbot',
|
||||
|
||||
]
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ urlpatterns = [
|
||||
path('api/', include('api.urls')),
|
||||
path('dj-rest-auth/', include('dj_rest_auth.urls')),
|
||||
|
||||
|
||||
]
|
||||
urlpatterns += i18n_patterns(
|
||||
path('admin/', admin.site.urls),
|
||||
@ -21,7 +22,7 @@ urlpatterns += i18n_patterns(
|
||||
path('prometheus/', include('django_prometheus.urls')),
|
||||
path('', include('inventory.urls')),
|
||||
path('ledger/', include('django_ledger.urls', namespace='django_ledger')),
|
||||
|
||||
path("haikalbot/", include("haikalbot.urls")),
|
||||
)
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
0
haikalbot/__init__.py
Normal file
0
haikalbot/__init__.py
Normal file
3
haikalbot/admin.py
Normal file
3
haikalbot/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
haikalbot/apps.py
Normal file
6
haikalbot/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HaikalbotConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'haikalbot'
|
||||
83
haikalbot/chatbot_logic.py
Normal file
83
haikalbot/chatbot_logic.py
Normal file
@ -0,0 +1,83 @@
|
||||
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:
|
||||
|
||||
nltk.download("punkt")
|
||||
except ImportError:
|
||||
raise ImportError("Ensure nltk is installed by running 'pip install nltk'.")
|
||||
|
||||
# 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 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?"
|
||||
26
haikalbot/migrations/0001_initial.py
Normal file
26
haikalbot/migrations/0001_initial.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-18 16:49
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0030_alter_carfinance_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ChatLog',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user_message', models.TextField()),
|
||||
('chatbot_response', models.TextField()),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chatlogs', to='inventory.dealer')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
haikalbot/migrations/__init__.py
Normal file
0
haikalbot/migrations/__init__.py
Normal file
12
haikalbot/models.py
Normal file
12
haikalbot/models.py
Normal file
@ -0,0 +1,12 @@
|
||||
from django.db import models
|
||||
from inventory.models import Dealer
|
||||
|
||||
|
||||
class ChatLog(models.Model):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='chatlogs')
|
||||
user_message = models.TextField()
|
||||
chatbot_response = models.TextField()
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.user_message
|
||||
3
haikalbot/tests.py
Normal file
3
haikalbot/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
7
haikalbot/urls.py
Normal file
7
haikalbot/urls.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.ChatbotView.as_view(), name="chatbot"),
|
||||
]
|
||||
35
haikalbot/views.py
Normal file
35
haikalbot/views.py
Normal file
@ -0,0 +1,35 @@
|
||||
from django.views import View
|
||||
from django.shortcuts import render
|
||||
from django.http import JsonResponse
|
||||
from .chatbot_logic import get_response
|
||||
import json
|
||||
|
||||
class ChatbotView(View):
|
||||
"""
|
||||
Class-based view to handle chatbot template rendering (GET)
|
||||
and GPT-4-based chatbot API responses (POST).
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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)
|
||||
|
||||
return JsonResponse({"response": chatbot_response}, status=200)
|
||||
except json.JSONDecodeError:
|
||||
return JsonResponse({"error": "Invalid JSON format."}, status=400)
|
||||
Binary file not shown.
@ -962,5 +962,3 @@ class RepresentativeDeleteView(LoginRequiredMixin, DeleteView):
|
||||
template_name = 'representatives/representative_confirm_delete.html'
|
||||
success_url = reverse_lazy('representative_list')
|
||||
success_message = "Representative deleted successfully."
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,30 @@
|
||||
alabaster==1.0.0
|
||||
annotated-types==0.7.0
|
||||
anyio==4.6.2.post1
|
||||
anyio==4.7.0
|
||||
arabic-reshaper==3.0.0
|
||||
asgiref==3.8.1
|
||||
astroid==3.3.5
|
||||
astroid==3.3.6
|
||||
attrs==23.2.0
|
||||
autopep8==2.3.1
|
||||
babel==2.16.0
|
||||
certifi==2024.8.30
|
||||
beautifulsoup4==4.12.3
|
||||
bleach==6.2.0
|
||||
blinker==1.9.0
|
||||
certifi==2024.12.14
|
||||
cffi==1.17.1
|
||||
chardet==5.2.0
|
||||
charset-normalizer==3.4.0
|
||||
click==8.1.7
|
||||
colorama==0.4.6
|
||||
commonmark==0.9.1
|
||||
crispy-bootstrap5==2024.10
|
||||
cryptography==44.0.0
|
||||
desert==2020.11.18
|
||||
dill==0.3.9
|
||||
distro==1.9.0
|
||||
dj-rest-auth==7.0.0
|
||||
dj-shop-cart==7.1.1
|
||||
Django
|
||||
Django==5.1.4
|
||||
django-allauth==65.3.0
|
||||
django-autoslug==1.9.9
|
||||
django-bootstrap5==24.3
|
||||
@ -29,83 +37,133 @@ django-filter==24.3
|
||||
django-formtools==2.5.1
|
||||
django-ledger==0.7.0
|
||||
django-money==3.5.3
|
||||
django-nine==0.2.7
|
||||
django-nonefield==0.4
|
||||
django-phonenumber-field==8.0.0
|
||||
django-prometheus==2.3.1
|
||||
django-sekizai==4.1.0
|
||||
django-silk==5.3.1
|
||||
django-silk==5.3.2
|
||||
django-sslserver==0.22
|
||||
django-tables2==2.7.0
|
||||
django-treebeard==4.7.1
|
||||
django-view-breadcrumbs==2.5.1
|
||||
djangocms-admin-style==3.3.1
|
||||
djangorestframework==3.15.2
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
djangoviz==0.1.1
|
||||
docutils==0.21.2
|
||||
easy-thumbnails==2.10
|
||||
et_xmlfile==2.0.0
|
||||
Faker==33.1.0
|
||||
Flask==3.1.0
|
||||
gprof2dot==2024.6.6
|
||||
graphqlclient==0.2.4
|
||||
h11==0.14.0
|
||||
httpcore==1.0.7
|
||||
httpx==0.28.0
|
||||
httpx==0.28.1
|
||||
idna==3.10
|
||||
imagesize==1.4.1
|
||||
iso4217==1.12.20240625
|
||||
isodate==0.7.2
|
||||
isort==5.13.2
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.4
|
||||
jiter==0.8.0
|
||||
jiter==0.8.2
|
||||
joblib==1.4.2
|
||||
ledger==1.0.1
|
||||
lxml==5.3.0
|
||||
Markdown==3.7
|
||||
MarkupSafe==3.0.2
|
||||
marshmallow==3.23.2
|
||||
mccabe==0.7.0
|
||||
newrelic==10.3.1
|
||||
MouseInfo==0.1.3
|
||||
mypy-extensions==1.0.0
|
||||
newrelic==10.4.0
|
||||
nltk==3.9.1
|
||||
numpy==2.2.0
|
||||
oauthlib==3.2.2
|
||||
ofxtools==0.9.5
|
||||
openai==1.56.2
|
||||
openai==1.58.1
|
||||
openpyxl==3.1.5
|
||||
outcome==1.3.0.post0
|
||||
packaging==24.2
|
||||
pandas==2.2.3
|
||||
pdfkit==1.0.0
|
||||
phonenumbers==8.13.51
|
||||
phonenumbers==8.13.52
|
||||
pillow==11.0.0
|
||||
platformdirs==4.3.6
|
||||
prometheus_client==0.21.1
|
||||
psycopg==3.2.3
|
||||
psycopg-binary==3.2.3
|
||||
psycopg-c==3.2.3
|
||||
py-moneyed==3.0
|
||||
PyAutoGUI==0.9.54
|
||||
pycodestyle==2.12.1
|
||||
pycparser==2.22
|
||||
pydantic==2.10.3
|
||||
pydantic_core==2.27.1
|
||||
pydantic==2.10.4
|
||||
pydantic_core==2.27.2
|
||||
pydotplus==2.0.2
|
||||
PyGetWindow==0.0.9
|
||||
Pygments==2.18.0
|
||||
PyJWT==2.10.1
|
||||
pylint==3.3.2
|
||||
PyMsgBox==1.0.9
|
||||
PyMySQL==1.1.1
|
||||
pyobjc-core==10.3.2
|
||||
pyobjc-framework-Cocoa==10.3.2
|
||||
pyobjc-framework-Quartz==10.3.2
|
||||
pyparsing==3.2.0
|
||||
pyperclip==1.9.0
|
||||
pypng==0.20220715.0
|
||||
PyRect==0.2.0
|
||||
PyScreeze==1.0.1
|
||||
pyserial==3.5
|
||||
PySocks==1.7.1
|
||||
python-bidi==0.6.3
|
||||
python-dateutil==2.9.0.post0
|
||||
python-openid==2.2.5
|
||||
python3-saml==1.16.0
|
||||
pytweening==1.2.0
|
||||
pytz==2024.2
|
||||
pyvin==0.0.2
|
||||
pywa==2.4.0
|
||||
pywhat==5.1.0
|
||||
pywhatkit==5.4
|
||||
qrcode==8.0
|
||||
regex==2024.11.6
|
||||
reportlab==4.2.5
|
||||
requests==2.32.3
|
||||
requests-oauthlib==2.0.0
|
||||
six==1.16.0
|
||||
rich==10.16.2
|
||||
rubicon-objc==0.4.9
|
||||
scikit-learn==1.6.0
|
||||
scipy==1.14.1
|
||||
selenium==4.27.1
|
||||
six==1.17.0
|
||||
sniffio==1.3.1
|
||||
snowballstemmer==2.2.0
|
||||
sqlparse==0.5.2
|
||||
sortedcontainers==2.4.0
|
||||
soupsieve==2.6
|
||||
SQLAlchemy==2.0.36
|
||||
sqlparse==0.5.3
|
||||
tablib==3.7.0
|
||||
threadpoolctl==3.5.0
|
||||
tomlkit==0.13.2
|
||||
tqdm==4.67.1
|
||||
trio==0.27.0
|
||||
trio-websocket==0.11.1
|
||||
typing-inspect==0.9.0
|
||||
typing_extensions==4.12.2
|
||||
tzdata==2024.2
|
||||
Unidecode==1.3.8
|
||||
upgrade-requirements==1.7.0
|
||||
urllib3==2.2.3
|
||||
vin==0.6.2
|
||||
vininfo==1.8.0
|
||||
vishap==0.1.5
|
||||
vpic-api==0.7.4
|
||||
webencodings==0.5.1
|
||||
websocket-client==1.8.0
|
||||
Werkzeug==3.1.3
|
||||
wikipedia==1.4.0
|
||||
wsproto==1.2.0
|
||||
xmlsec==1.3.14
|
||||
|
||||
90
templates/haikalbot/chatbot.html
Normal file
90
templates/haikalbot/chatbot.html
Normal file
@ -0,0 +1,90 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block title %}{{ _("HaikalBot") }}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
|
||||
#chatbox {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
height: 200px;
|
||||
overflow-y: scroll;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="container p-2">
|
||||
<div class="card shadow-sm rounded shadow">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{% trans 'HaikalBot' %}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div id="chatbox">
|
||||
<p><b>{% trans 'HaikalBot' %}:</b> {% trans 'Hello! How can I assist you today?' %}</p>
|
||||
</div>
|
||||
<label for="userMessage"></label>
|
||||
<input class="form-control form-control-sm"
|
||||
type="text" id="userMessage"
|
||||
placeholder="{% trans 'Type your message here...' %}" />
|
||||
<button class="btn btn-sm btn-success m-2" onclick="sendMessage()">{% trans 'Send' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Script to send to api -->
|
||||
<script>
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let cookie of cookies) {
|
||||
cookie = cookie.trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
const csrfToken = getCookie('csrftoken');
|
||||
|
||||
|
||||
async function sendMessage() {
|
||||
const userMessage = document.getElementById("userMessage").value;
|
||||
|
||||
if (!userMessage.trim()) {
|
||||
alert("Please enter a message.");
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch("", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": csrfToken,
|
||||
},
|
||||
body: JSON.stringify({ message: userMessage }),
|
||||
});
|
||||
|
||||
if (response) {
|
||||
const data = await response.json();
|
||||
const chatbox = document.getElementById("chatbox");
|
||||
chatbox.innerHTML += `<p><b>{% trans 'You' %}:</b> ${userMessage}</p>`;
|
||||
chatbox.innerHTML += `<p><b>{% trans 'HaikalBot' %}:</b> ${data.response}</p>`;
|
||||
document.getElementById("userMessage").value = "";
|
||||
chatbox.scrollTop = chatbox.scrollHeight;
|
||||
} else {
|
||||
alert("An error occurred.");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock content %}
|
||||
67
whatsapp_bot.py
Normal file
67
whatsapp_bot.py
Normal file
@ -0,0 +1,67 @@
|
||||
import re
|
||||
import arabic_reshaper
|
||||
from bidi.algorithm import get_display
|
||||
from pywa import WhatsApp
|
||||
|
||||
# Initialize the WhatsApp bot
|
||||
bot = WhatsApp(
|
||||
phone_id="00966535521547",
|
||||
token="c446eba6-fcfe-437c-923a-a554c25578dd"
|
||||
)
|
||||
|
||||
# Pre-defined responses in English and Arabic
|
||||
responses = {
|
||||
"greet": {
|
||||
"en": "Hello! How can I assist you with Haikal Car Inventory?",
|
||||
"ar": get_display(arabic_reshaper.reshape("مرحباً! كيف يمكنني مساعدتك في نظام هيكل لإدارة السيارات؟"))
|
||||
},
|
||||
"sell_process": {
|
||||
"en": "To sell a car: Create a Sell Order, add a customer, confirm payment, generate an invoice, and deliver the car.",
|
||||
"ar": get_display(arabic_reshaper.reshape("لبيع السيارة: قم بإنشاء طلب بيع، إضافة العميل، تأكيد الدفع، إنشاء الفاتورة، وتسليم السيارة."))
|
||||
},
|
||||
"inventory_check": {
|
||||
"en": "You can check available cars in the inventory. Do you want details on any specific car?",
|
||||
"ar": get_display(arabic_reshaper.reshape("يمكنك التحقق من السيارات المتوفرة في المخزون. هل تريد تفاصيل عن سيارة معينة؟"))
|
||||
},
|
||||
"bye": {
|
||||
"en": "Goodbye! Let me know if you need further help.",
|
||||
"ar": get_display(arabic_reshaper.reshape("وداعاً! أخبرني إذا كنت بحاجة إلى مزيد من المساعدة."))
|
||||
},
|
||||
"unknown": {
|
||||
"en": "I'm sorry, I didn't understand that. Can you rephrase?",
|
||||
"ar": get_display(arabic_reshaper.reshape("عذراً، لم أفهم ذلك. هل يمكنك إعادة صياغة السؤال؟"))
|
||||
}
|
||||
}
|
||||
|
||||
# Function to classify user input
|
||||
def classify_message(message):
|
||||
message = message.lower()
|
||||
if re.search(r"hello|hi|مرحب|السلام", message):
|
||||
return "greet"
|
||||
elif re.search(r"sell|بيع", message):
|
||||
return "sell_process"
|
||||
elif re.search(r"inventory|cars|المخزون|السيارات", message):
|
||||
return "inventory_check"
|
||||
elif re.search(r"bye|وداع|إلى اللقاء", message):
|
||||
return "bye"
|
||||
else:
|
||||
return "unknown"
|
||||
|
||||
# Send response based on the intent
|
||||
@bot.on_message()
|
||||
def handle_message(message):
|
||||
user_input = message.body.strip()
|
||||
intent = classify_message(user_input)
|
||||
|
||||
# Detect language and respond
|
||||
if re.search("[\u0600-\u06FF]", user_input): # Arabic characters
|
||||
response = responses[intent]["ar"]
|
||||
else: # Default to English
|
||||
response = responses[intent]["en"]
|
||||
|
||||
message.reply(response)
|
||||
|
||||
# Start the bot
|
||||
if __name__ == "__main__":
|
||||
print("Starting the Haikal Car Inventory WhatsApp Bot...")
|
||||
bot.run()
|
||||
Loading…
x
Reference in New Issue
Block a user