This commit is contained in:
Marwan Alwali 2025-02-13 11:11:32 +03:00
parent 0733ea3669
commit 709b3359f3
13 changed files with 201 additions and 87 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ db.sqlite
db.sqlite3
media
car_inventory/settings.py
scripts/dsrpipe.py
# Backup files #
*.bak

View File

@ -1,6 +1,6 @@
from openai import OpenAI
from django.core.management.base import BaseCommand
from inventory.models import CarSerie, CarModel, CarMake, CarTrim, CarOption, CarSpecificationValue
from inventory.models import CarSerie, CarModel, CarMake, CarTrim, CarOption, CarSpecification
from django.conf import settings
@ -9,28 +9,35 @@ class Command(BaseCommand):
def handle(self, *args, **kwargs):
client = OpenAI(api_key=settings.OPENAI_API_KEY)
car_option = CarOption.objects.all()[10300:]
total = car_option.count()
en_value = CarModel.objects.all()
total = en_value.count()
print(f'Translating {total} names...')
for index, car_option in enumerate(car_option, start=1):
try:
completion = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": "You are an assistant that translates English to Arabic."
},
{
"role": "user",
"content": car_option.name
}
],
temperature=0.2,
)
translation = completion.choices[0].message.content.strip()
car_option.arabic_name = translation
car_option.save()
print(f"[{index}/{total}] Translated '{car_option.name}' to '{translation}'")
except Exception as e:
print(f"Error translating '{car_option.name}': {e}")
for index, en_value in enumerate(en_value, start=1):
if not en_value.arabic_name:
try:
completion = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": (
"You are an assistant that translates English to Arabic."
"You are an assistant specialized in cars and automotive terms."
"If the model name is a number just write it as is"
"You can get the arabic names for makes, models, series, trims, options, and specifications."
)
},
{
"role": "user",
"content": en_value.name
}
],
temperature=0.2,
)
translation = completion.choices[0].message.content.strip()
en_value.arabic_name = translation
en_value.save()
print(f"[{index}/{total}] .. Done")
except Exception as e:
print(f"Error translating '{en_value.name}': {e}")

View File

@ -2675,7 +2675,7 @@ class PaymentRequest(LoginRequiredMixin, DetailView):
return context
class EstimatePreviewView(LoginRequiredMixin, DetailView):
class EstimatePreviewView(DetailView):
model = EstimateModel
context_object_name = "estimate"
template_name = "sales/estimates/estimate_preview.html"
@ -2697,30 +2697,31 @@ class EstimatePreviewView(LoginRequiredMixin, DetailView):
@login_required
def estimate_mark_as(request, pk):
estimate = get_object_or_404(EstimateModel, pk=pk)
entity = estimate.entity
dealer = get_user_type(request.user)
entity = dealer.entity
mark = request.GET.get("mark")
if mark:
if mark == "review":
if not estimate.can_review():
messages.error(request, "Estimate is not ready for review")
messages.error(request, _("Estimate is not ready for review"))
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_review()
elif mark == "approved":
if not estimate.can_approve():
messages.error(request, "Estimate is not ready for approval")
messages.error(request, _("Estimate is not ready for approval"))
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_approved()
messages.success(request, "Estimate approved successfully.")
messages.success(request, _("Estimate approved successfully."))
elif mark == "rejected":
if not estimate.can_cancel():
messages.error(request, "Estimate is not ready for rejection")
messages.error(request, _("Estimate is not ready for rejection"))
return redirect("estimate_detail", pk=estimate.pk)
estimate.mark_as_canceled()
messages.success(request, "Estimate canceled successfully.")
messages.success(request, _("Estimate canceled successfully."))
elif mark == "completed":
if not estimate.can_complete():
messages.error(request, "Estimate is not ready for completion")
messages.error(request, _("Estimate is not ready for completion"))
return redirect("estimate_detail", pk=estimate.pk)
estimate.save()
messages.success(request, "Estimate marked as " + mark.upper())
@ -3257,7 +3258,7 @@ def send_lead_email(request, pk):
request.POST.get("subject"),
request.POST.get("message"),
)
messages.success(request, "Email sent successfully!")
messages.success(request, _("Email sent successfully!"))
return redirect("lead_list")
msg = f"""
السلام عليكم
@ -3848,7 +3849,7 @@ def send_email_view(request, pk):
# messages.error(request, "Estimate is not ready for review")
# return redirect("estimate_detail", pk=estimate.pk)
if not estimate.get_itemtxs_data()[0]:
messages.error(request, "Estimate has no items")
messages.error(request, _("Estimate has no items"))
return redirect("estimate_detail", pk=estimate.pk)
send_email(
@ -3858,7 +3859,7 @@ def send_email_view(request, pk):
request.POST.get("message"),
)
estimate.mark_as_review()
messages.success(request, "Email sent successfully!")
messages.success(request, _("Email sent successfully!"))
return redirect("estimate_detail", pk=estimate.pk)
link = reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk})
msg = f"""

140
scripts/dsrpipe.py Normal file
View File

@ -0,0 +1,140 @@
"""
title: Deepseek R1 Reasoner and Chat with Realtime Thinking Preview
authors: Ethan Copping
author_url: https://github.com/CoppingEthan
version: 0.3.0
required_open_webui_version: 0.5.5
license: MIT
# Acknowledgments
Code used from MCode-Team & Zgccrui
"""
import json
import httpx
from typing import AsyncGenerator, Callable, Awaitable
from pydantic import BaseModel, Field
class Pipe:
class Valves(BaseModel):
DEEPSEEK_API_BASE_URL: str = Field(
default="https://api.deepseek.com/v1", description="Base API endpoint URL"
)
DEEPSEEK_API_KEY: str = Field(
default="", description="Authentication key for API access"
)
def __init__(self):
self.valves = self.Valves()
self.thinking = -1
self._emitter = None
self.data_prefix = "data: "
def pipes(self):
try:
headers = {"Authorization": f"Bearer {self.valves.DEEPSEEK_API_KEY}"}
resp = httpx.get(
f"{self.valves.DEEPSEEK_API_BASE_URL}/models",
headers=headers,
timeout=10,
)
if resp.status_code == 200:
return [
{"id": m["id"], "name": m["id"]}
for m in resp.json().get("data", [])
]
except Exception:
pass
return [
{"id": "deepseek-chat", "name": "deepseek-chat"},
{"id": "deepseek-reasoner", "name": "deepseek-reasoner"},
]
async def pipe(
self, body: dict, __event_emitter__: Callable[[dict], Awaitable[None]] = None
) -> AsyncGenerator[str, None]:
self.thinking = -1
self._emitter = __event_emitter__
if not self.valves.DEEPSEEK_API_KEY:
yield json.dumps({"error": "Missing API credentials"})
return
req_headers = {
"Authorization": f"Bearer {self.valves.DEEPSEEK_API_KEY}",
"Content-Type": "application/json",
}
try:
request_data = body.copy()
model_id = request_data["model"].split(".", 1)[-1]
request_data["model"] = model_id
is_reasoner = "reasoner" in model_id.lower()
messages = request_data["messages"]
for i in reversed(range(1, len(messages))):
if messages[i - 1]["role"] == messages[i]["role"]:
alt_role = (
"user" if messages[i]["role"] == "assistant" else "assistant"
)
messages.insert(
i, {"role": alt_role, "content": "[Unfinished thinking]"}
)
async with httpx.AsyncClient(http2=True) as client:
async with client.stream(
"POST",
f"{self.valves.DEEPSEEK_API_BASE_URL}/chat/completions",
json=request_data,
headers=req_headers,
timeout=20,
) as resp:
if resp.status_code != 200:
error_content = (await resp.aread()).decode()[:200]
yield json.dumps(
{"error": f"API error {resp.status_code}: {error_content}"}
)
return
async for line in resp.aiter_lines():
if not line.startswith(self.data_prefix):
continue
stream_data = json.loads(line[6:])
choice = stream_data.get("choices", [{}])[0]
if choice.get("finish_reason"):
return
delta = choice.get("delta", {})
if is_reasoner:
state_marker = self._handle_state(delta)
if state_marker:
yield state_marker
if state_marker == "<think>":
yield "\n"
content = delta.get("reasoning_content", "") or delta.get(
"content", ""
)
if content:
yield content
else:
content = delta.get("content", "")
if content:
yield content
except Exception as e:
yield json.dumps({"error": f"{type(e).__name__}: {str(e)}"})
def _handle_state(self, delta: dict) -> str:
if self.thinking == -1 and delta.get("reasoning_content"):
self.thinking = 0
return "<think>"
if self.thinking == 0 and not delta.get("reasoning_content"):
self.thinking = 1
return "\n</think>\n\n"
return ""

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}{{ _("View Estimate") }}{% endblock title %}
{% block title %}{{ _("View Quotation") }}{% endblock title %}
{% block content %}
<div class="modal fade" id="confirmModal" tabindex="-1" aria-labelledby="confirmModalLabel" aria-hidden="true">
@ -21,8 +21,8 @@
<form id="confirmForm" method="POST" action="{% url 'estimate_mark_as' estimate.pk %}?mark=approved" class="form">
{% csrf_token %}
<div class="container-fluid m-0 p-0">
<button type="button" class="btn btn-danger btn-sm w-100" data-bs-dismiss="modal">{% trans 'No' %}</button>
<button type="submit" class="btn btn-success btn-sm w-100">{% trans "Yes" %}</button>
<button type="button" class="btn btn-danger btn-sm" data-bs-dismiss="modal">{% trans 'No' %}</button>
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
</div>
</form>
</div>

View File

@ -2,7 +2,7 @@
{% load crispy_forms_filters %}
{% load i18n static %}
{% block title %}{{ _("Quotations") }}{% endblock title %}
{% block title %}{{ _("Quotation") }}{% endblock title %}
{% block content %}
<div class="card email-content">
@ -30,7 +30,5 @@
</form>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -106,24 +106,26 @@
<th>نوع السيارة</th>
<th>اللون الخارجي</th>
<th>اللون الداخلي</th>
<th>السعر</th>
<th>ملاحظات</th>
</tr>
</thead>
<tbody>
{% for car in cars %}
<tr>
<td>{{ car.year }} - {{ car.id_car_make.arabic_name }} - {{ car.id_car_model.arabic_name }} - {{ car.id_car_trim.arabic_name }}</td>
<td>{{ car.colors.first.exterior.arabic_name }}</td>
<td>{{ car.colors.first.interior.arabic_name }}</td>
<td>{{ car.finances.first.cost_price }}</td>
<td>{{ car. }}</td>
<td>{{ car.year }} - {{ car.id_car_make.name }} - {{ car.id_car_model.name }} - {{ car.id_car_trim.name }}</td>
<td>{{ car.colors.first.exterior.name }}</td>
<td>{{ car.colors.first.interior.name }}</td>
<td>{{ car.finances.selling_price }}</td>
<td>{{ car.get_specifications }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>حمولة المركبة (<input type="text">) سنة الصنع (<input type="text">) (جديد / مستعملة) كلم/ميل</p>
<p>حمولة المركبة )<input type="text">) سنة الصنع (<input type="text">) (جديد / مستعملة) كلم/ميل</p>
<p>مستوى اقتصاد الوقود (<input type="text">) رقم الشاسيه (في حال كانت السيارة مستعملة فقط) (<input type="text">)</p>
<p>مستوى اقتصاد الوقود )<input type="text">) رقم الهيكل (في حال كانت السيارة مستعملة فقط) (<input type="text">)</p>
<p>مواصفات أخرى:</p>
<input type="text">

View File

@ -238,7 +238,9 @@
<div class="d-flex flex-wrap align-items-center justify-content-between py-3 pe-0 fs-9 border-bottom border-translucent">
<div class="d-flex">
{% if is_paginated %}
{% include 'partials/pagination.html' %}
{% endif %}
</div>
</div>

View File

@ -114,7 +114,6 @@
<div class="avatar avatar-xl me-3"><img class="rounded-circle" src="{% static 'images/icons/picture.svg' %}" alt="" />
{% endif %}
</div>
<div><a class="fs-8 fw-bold" href="">{{ vendor.vendor_name }}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.vendor_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span>
@ -147,43 +146,7 @@
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
<!-- Optional: Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination mb-0">
{% if page_obj.has_previous %}
<li class="page-item py-0">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true"><span class="fas fa-chevron-left"></span></span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Previous">
<span aria-hidden="true"><span class="fas fa-chevron-left"></span></span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true"><span class="fas fa-chevron-right"></span></span>
</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-label="Next">
<span aria-hidden="true"><span class="fas fa-chevron-right"></span></span>
</a>
</li>
{% endif %}
</ul>
</nav>
{% include 'partials/pagination.html' %}
{% endif %}
</div>
</div>