first commit
This commit is contained in:
commit
997b3cef10
BIN
.idea/.DS_Store
generated
vendored
Normal file
BIN
.idea/.DS_Store
generated
vendored
Normal file
Binary file not shown.
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
7
.idea/JetClient/state.xml
generated
Normal file
7
.idea/JetClient/state.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JetClientState">
|
||||
<option name="id" value="9643c603-9e26-490a-916b-222ca66b0b6e" />
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
||||
37
.idea/car_inventory.iml
generated
Normal file
37
.idea/car_inventory.iml
generated
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="car_inventory/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="jquery-3.5.1" level="application" />
|
||||
<orderEntry type="library" name="sweetalert2" level="application" />
|
||||
<orderEntry type="library" name="jquery" level="application" />
|
||||
<orderEntry type="library" name="bootstrap-icons" level="application" />
|
||||
<orderEntry type="library" name="quagga" level="application" />
|
||||
<orderEntry type="library" name="@zxing" level="application" />
|
||||
<orderEntry type="library" name="tesseract.js" level="application" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
||||
17
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
17
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="Django" />
|
||||
<item index="1" class="java.lang.String" itemvalue="django-admin-volt" />
|
||||
<item index="2" class="java.lang.String" itemvalue="faker_food" />
|
||||
<item index="3" class="java.lang.String" itemvalue="typing_extensions" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="file://$PROJECT_DIR$" libraries="{@zxing, bootstrap-icons, jquery, jquery-3.5.1, quagga, sweetalert2, tesseract.js}" />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/material_theme_project_new.xml
generated
Normal file
12
.idea/material_theme_project_new.xml
generated
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="-4d33890d:18fe9fd09b1:-7ffe" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11 (car_inventory)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (car_inventory)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/car_inventory.iml" filepath="$PROJECT_DIR$/.idea/car_inventory.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
BIN
api/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
api/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/admin.cpython-311.pyc
Normal file
BIN
api/__pycache__/admin.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/apps.cpython-311.pyc
Normal file
BIN
api/__pycache__/apps.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/models.cpython-311.pyc
Normal file
BIN
api/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/serializers.cpython-311.pyc
Normal file
BIN
api/__pycache__/serializers.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/services.cpython-311.pyc
Normal file
BIN
api/__pycache__/services.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/urls.cpython-311.pyc
Normal file
BIN
api/__pycache__/urls.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/__pycache__/views.cpython-311.pyc
Normal file
BIN
api/__pycache__/views.cpython-311.pyc
Normal file
Binary file not shown.
5
api/admin.py
Normal file
5
api/admin.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from . import models
|
||||
|
||||
|
||||
admin.site.register(models.CarVIN)
|
||||
6
api/apps.py
Normal file
6
api/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
||||
35
api/consumers.py
Normal file
35
api/consumers.py
Normal file
@ -0,0 +1,35 @@
|
||||
# from channels.generic.websocket import AsyncWebsocketConsumer
|
||||
# from api.services import get_car_data
|
||||
# import json
|
||||
#
|
||||
#
|
||||
# class VINScanConsumer(AsyncWebsocketConsumer):
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(args, kwargs)
|
||||
# self.group_name = None
|
||||
#
|
||||
# async def connect(self):
|
||||
# self.group_name = 'vin_scan_group'
|
||||
# await self.channel_layer.group_add(
|
||||
# self.group_name,
|
||||
# self.channel_name
|
||||
# )
|
||||
# await self.accept()
|
||||
#
|
||||
# async def disconnect(self, close_code):
|
||||
# await self.channel_layer.group_discard(
|
||||
# self.group_name,
|
||||
# self.channel_name
|
||||
# )
|
||||
#
|
||||
# async def receive(self, text_data, **kwargs):
|
||||
# data = json.loads(text_data)
|
||||
# vin = data['vin']
|
||||
# get_car_data(vin)
|
||||
# # Process the VIN as needed, e.g., save to the database
|
||||
#
|
||||
# # Send data back to the WebSocket
|
||||
# await self.send(text_data=json.dumps({
|
||||
# 'message': 'VIN received',
|
||||
# 'vin': vin
|
||||
# }))
|
||||
22
api/migrations/0001_initial.py
Normal file
22
api/migrations/0001_initial.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.0.6 on 2024-08-21 11:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CarVIN',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('vin', models.CharField(max_length=17, verbose_name='VIN')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='created')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
api/migrations/__init__.py
Normal file
0
api/migrations/__init__.py
Normal file
BIN
api/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
api/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
BIN
api/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
api/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
11
api/models.py
Normal file
11
api/models.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class CarVIN(models.Model):
|
||||
vin = models.CharField(max_length=17, verbose_name=_("VIN"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name="created")
|
||||
|
||||
def __str__(self):
|
||||
return self.vin
|
||||
|
||||
6
api/routing.py
Normal file
6
api/routing.py
Normal file
@ -0,0 +1,6 @@
|
||||
# from django.urls import re_path
|
||||
# from api import consumers
|
||||
#
|
||||
# websocket_urlpatterns = [
|
||||
# re_path(r'ws/vin_scan/$', consumers.VINScanConsumer.as_asgi()),
|
||||
# ]
|
||||
14
api/serializers.py
Normal file
14
api/serializers.py
Normal file
@ -0,0 +1,14 @@
|
||||
from rest_framework import serializers
|
||||
from . import models
|
||||
|
||||
|
||||
class CarVINSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.CarVIN
|
||||
fields = ['vin']
|
||||
|
||||
def create(self, validated_data):
|
||||
vin = validated_data.pop('vin')
|
||||
return models.CarVIN.objects.create(vin=vin, **validated_data)
|
||||
|
||||
|
||||
63
api/services.py
Normal file
63
api/services.py
Normal file
@ -0,0 +1,63 @@
|
||||
import hashlib
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
def get_bearer():
|
||||
|
||||
api_token = "f5204a00-6f31-4de2-96d8-ed998e0d230c"
|
||||
api_secret = "8c11320781a5b8f4f327b6937e6f8241"
|
||||
|
||||
url = "https://carapi.app/api/auth/login"
|
||||
headers = {
|
||||
"accept": "text/plain",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
data = {
|
||||
"api_token": api_token,
|
||||
"api_secret": api_secret
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, json=data)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.text
|
||||
else:
|
||||
print("the api key or secret is not valid")
|
||||
return None
|
||||
|
||||
|
||||
def get_car_data(vin):
|
||||
|
||||
url = f"https://carapi.app/api/vin/{vin}?verbose=no&all_trims=no"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {get_bearer()}"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status() # Raises an HTTPError for bad responses (4XX, 5XX)
|
||||
|
||||
response = response.json()
|
||||
print(response)
|
||||
|
||||
except requests.exceptions.HTTPError as http_err:
|
||||
print(f"HTTP error occurred: {http_err}")
|
||||
except requests.exceptions.RequestException as err:
|
||||
print(f"Error occurred: {err}")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
|
||||
def get_from_cardatabase(vin):
|
||||
vin = vin
|
||||
url = "https://api.vehicledatabases.com/premium/vin-decode/{vin}"
|
||||
|
||||
payload = {}
|
||||
headers = {
|
||||
'x-AuthKey': '3cefdfd4272445f1929b5801c55d8fa5'
|
||||
}
|
||||
|
||||
response = requests.request("GET", url, headers=headers, data=payload)
|
||||
|
||||
print(response.text)
|
||||
797
api/temp.txt
Normal file
797
api/temp.txt
Normal file
@ -0,0 +1,797 @@
|
||||
# url = f"https://carapi.app/api/vin/{vin}?verbose=no&all_trims=no"
|
||||
# headers = {
|
||||
# "Authorization": f"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjYXJhcGkuYXBwIiwic3ViIjoiYjU1OGYzMDMtODI0Ni00NjgzLTkwYTQtZmYwMGQxYWNmNGU3IiwiYXVkIjoiYjU1OGYzMDMtODI0Ni00NjgzLTkwYTQtZmYwMGQxYWNmNGU3IiwiZXhwIjoxNzI0MzgyODMzLCJpYXQiOjE3MjM3NzgwMzMsImp0aSI6IjA3ZDUzM2Y5LWRmNzEtNDM1My1hMTBhLTNiYmQ2MDljNWM0YiIsInVzZXIiOnsic3Vic2NyaWJlZCI6dHJ1ZSwic3Vic2NyaXB0aW9uIjoic3RhcnRlciIsInJhdGVfbGltaXRfdHlwZSI6ImhhcmQiLCJhZGRvbnMiOnsiYW50aXF1ZV92ZWhpY2xlcyI6ZmFsc2UsImRhdGFfZmVlZCI6ZmFsc2V9fX0.KnaH-dOGcmJAF3WqzrZ_c_eFtkSjPVEjR15G7KFxi2s"
|
||||
# }
|
||||
#
|
||||
# try:
|
||||
# response = requests.get(url, headers=headers)
|
||||
# response.raise_for_status() # Raises an HTTPError for bad responses (4XX, 5XX)
|
||||
#
|
||||
# response = response.json()
|
||||
# print(response)
|
||||
#
|
||||
# except requests.exceptions.HTTPError as http_err:
|
||||
# print(f"HTTP error occurred: {http_err}")
|
||||
# except requests.exceptions.RequestException as err:
|
||||
# print(f"Error occurred: {err}")
|
||||
# except Exception as e:
|
||||
# print(f"An error occurred: {e}")
|
||||
|
||||
|
||||
# class TokenAuthenticationMiddleware(MiddlewareMixin):
|
||||
#
|
||||
# def process_request(self, request, *args, **kwargs):
|
||||
#
|
||||
# exempt_paths = ['/login/', '/signup/', '/']
|
||||
# if request.path in exempt_paths:
|
||||
# return None
|
||||
#
|
||||
# token_key = request.META.get('HTTP_AUTHORIZATION')
|
||||
# if token_key:
|
||||
# token_key = token_key.split(' ')[1] if ' ' in token_key else token_key
|
||||
# try:
|
||||
# token = Token.objects.get(key=token_key)
|
||||
# request.user = token.user
|
||||
# except Token.DoesNotExist:
|
||||
# return HttpResponseForbidden("Invalid token")
|
||||
#
|
||||
# if not request.user.is_authenticated:
|
||||
# return HttpResponseForbidden("You are not authorized to view this page")
|
||||
#
|
||||
# return None
|
||||
|
||||
|
||||
# def vin_decode(vin):
|
||||
# apiKey = "67b751455994"
|
||||
# secretKey = "2ca9c09960"
|
||||
# apiPrefix = "https://api.vindecoder.eu/3.2"
|
||||
# id = "decode"
|
||||
#
|
||||
# controlSum = hashlib.sha1((vin.upper() + "|" + id + "|" + apiKey + "|" + secretKey).encode('utf-8')).hexdigest()[
|
||||
# :10]
|
||||
# url = apiPrefix + "/" + apiKey + "/" + controlSum + "/decode/" + vin.upper() + ".json"
|
||||
# print(url)
|
||||
# json_data = requests.get(url).json()
|
||||
# print(json_data)
|
||||
# return json_data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"year": 2007,
|
||||
"make": "CHEVROLET",
|
||||
"model": "TrailBlazer",
|
||||
"trim": "1/2 Ton",
|
||||
"specs": {
|
||||
"active_safety_system_note": null,
|
||||
"adaptive_cruise_control_acc": null,
|
||||
"adaptive_driving_beam_adb": null,
|
||||
"anti_lock_braking_system_abs": null,
|
||||
"auto_reverse_system_for_windows_and_sunroofs": null,
|
||||
"automatic_crash_notification_acn_advanced_automatic_crash_notification_aacn": null,
|
||||
"automatic_pedestrian_alerting_sound_for_hybrid_and_ev_only": null,
|
||||
"axle_configuration": null,
|
||||
"axles": null,
|
||||
"backup_camera": null,
|
||||
"base_price": null,
|
||||
"battery_current_amps_from": null,
|
||||
"battery_current_amps_to": null,
|
||||
"battery_energy_kwh_from": null,
|
||||
"battery_energy_kwh_to": null,
|
||||
"battery_type": null,
|
||||
"battery_voltage_volts_from": null,
|
||||
"battery_voltage_volts_to": null,
|
||||
"bed_length_inches": null,
|
||||
"bed_type": null,
|
||||
"blind_spot_intervention_bsi": null,
|
||||
"blind_spot_warning_bsw": null,
|
||||
"body_class": "Sport Utility Vehicle (SUV)/Multi-Purpose Vehicle (MPV)",
|
||||
"brake_system_description": null,
|
||||
"brake_system_type": "Hydraulic",
|
||||
"bus_floor_configuration_type": "Not Applicable",
|
||||
"bus_length_feet": null,
|
||||
"bus_type": "Not Applicable",
|
||||
"cab_type": null,
|
||||
"charger_level": null,
|
||||
"charger_power_kw": null,
|
||||
"cooling_type": "Water",
|
||||
"crash_imminent_braking_cib": null,
|
||||
"curb_weight_pounds": null,
|
||||
"curtain_air_bag_locations": null,
|
||||
"custom_motorcycle_type": "Not Applicable",
|
||||
"daytime_running_light_drl": null,
|
||||
"destination_market": null,
|
||||
"displacement_cc": "4200.0",
|
||||
"displacement_ci": "256.29972519787",
|
||||
"displacement_l": "4.2",
|
||||
"doors": "4",
|
||||
"drive_type": "4x2",
|
||||
"dynamic_brake_support_dbs": null,
|
||||
"electrification_level": null,
|
||||
"electronic_stability_control_esc": null,
|
||||
"engine_brake_hp_from": null,
|
||||
"engine_brake_hp_to": null,
|
||||
"engine_configuration": "In-Line",
|
||||
"engine_manufacturer": "GM",
|
||||
"engine_model": "LL8",
|
||||
"engine_number_of_cylinders": "6",
|
||||
"engine_power_kw": null,
|
||||
"engine_stroke_cycles": null,
|
||||
"entertainment_system": null,
|
||||
"ev_drive_unit": null,
|
||||
"event_data_recorder_edr": null,
|
||||
"forward_collision_warning_fcw": null,
|
||||
"front_air_bag_locations": null,
|
||||
"fuel_delivery_fuel_injection_type": "Multipoint Fuel Injection (MPFI)",
|
||||
"fuel_type_primary": "Gasoline",
|
||||
"fuel_type_secondary": null,
|
||||
"gross_combination_weight_rating_from": null,
|
||||
"gross_combination_weight_rating_to": null,
|
||||
"gross_vehicle_weight_rating_from": "Class 1D: 5,001 - 6,000 lb (2,268 - 2,722 kg)",
|
||||
"gross_vehicle_weight_rating_to": null,
|
||||
"headlamp_light_source": null,
|
||||
"keyless_ignition": null,
|
||||
"knee_air_bag_locations": null,
|
||||
"lane_centering_assistance": null,
|
||||
"lane_departure_warning_ldw": null,
|
||||
"lane_keeping_assistance_lka": null,
|
||||
"manufacturer_name": "GENERAL MOTORS LLC",
|
||||
"motorcycle_chassis_type": "Not Applicable",
|
||||
"motorcycle_suspension_type": "Not Applicable",
|
||||
"ncsa_body_type": "Compact Utility (Utility Vehicle Categories \"Small\" and \"Midsize\")",
|
||||
"ncsa_make": "Chevrolet",
|
||||
"ncsa_model": "TrailBlazer (2003 on; for 2002 model, see 401)",
|
||||
"ncsa_note": null,
|
||||
"non_land_use": null,
|
||||
"note": null,
|
||||
"number_of_battery_cells_per_module": null,
|
||||
"number_of_battery_modules_per_pack": null,
|
||||
"number_of_battery_packs_per_vehicle": null,
|
||||
"number_of_seat_rows": null,
|
||||
"number_of_seats": null,
|
||||
"number_of_wheels": null,
|
||||
"other_battery_info": null,
|
||||
"other_bus_info": null,
|
||||
"other_engine_info": "MFI, DOHC, Aluminium",
|
||||
"other_motorcycle_info": null,
|
||||
"other_restraint_system_info": null,
|
||||
"other_trailer_info": null,
|
||||
"parking_assist": null,
|
||||
"pedestrian_automatic_emergency_braking_paeb": null,
|
||||
"plant_city": "MORAINE",
|
||||
"plant_company_name": "GM Truck Group",
|
||||
"plant_country": "UNITED STATES (USA)",
|
||||
"plant_state": "OHIO",
|
||||
"possible_values": "",
|
||||
"pretensioner": null,
|
||||
"rear_automatic_emergency_braking": null,
|
||||
"rear_cross_traffic_alert": null,
|
||||
"sae_automation_level_from": null,
|
||||
"sae_automation_level_to": null,
|
||||
"seat_belt_type": null,
|
||||
"seat_cushion_air_bag_locations": null,
|
||||
"semiautomatic_headlamp_beam_switching": null,
|
||||
"series": "1/2 Ton",
|
||||
"series2": null,
|
||||
"side_air_bag_locations": null,
|
||||
"steering_location": null,
|
||||
"suggested_vin": "",
|
||||
"tire_pressure_monitoring_system_tpms_type": null,
|
||||
"top_speed_mph": null,
|
||||
"track_width_inches": null,
|
||||
"traction_control": null,
|
||||
"trailer_body_type": "Not Applicable",
|
||||
"trailer_length_feet": null,
|
||||
"trailer_type_connection": "Not Applicable",
|
||||
"transmission_speeds": null,
|
||||
"transmission_style": null,
|
||||
"trim": null,
|
||||
"trim2": null,
|
||||
"turbo": null,
|
||||
"valve_train_design": "Dual Overhead Cam (DOHC)",
|
||||
"vehicle_descriptor": "1GNDS13S*72",
|
||||
"vehicle_type": "MULTIPURPOSE PASSENGER VEHICLE (MPV)",
|
||||
"wheel_base_inches_from": null,
|
||||
"wheel_base_inches_to": null,
|
||||
"wheel_base_type": null,
|
||||
"wheel_size_front_inches": null,
|
||||
"wheel_size_rear_inches": null,
|
||||
"windows": null
|
||||
},
|
||||
"trims": [
|
||||
{
|
||||
"id": 40805,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "LS",
|
||||
"description": "LS 4dr SUV (4.2L 6cyl 4A)",
|
||||
"msrp": 25045,
|
||||
"invoice": 23417,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40805,
|
||||
"make_model_trim_id": 40805,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 16,
|
||||
"epa_city_mpg": 14,
|
||||
"epa_highway_mpg": 20,
|
||||
"range_city": 308,
|
||||
"range_highway": 440,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40805,
|
||||
"make_model_trim_id": 40805,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "regular unleaded",
|
||||
"cylinders": "I6",
|
||||
"size": "4.2",
|
||||
"horsepower_hp": 291,
|
||||
"horsepower_rpm": 6000,
|
||||
"torque_ft_lbs": 277,
|
||||
"torque_rpm": 4800,
|
||||
"valves": 24,
|
||||
"valve_timing": "Variable",
|
||||
"cam_type": "Double overhead cam (DOHC)",
|
||||
"drive_type": "rear wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40805,
|
||||
"make_model_trim_id": 40805,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "72.5",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4356,
|
||||
"gross_weight": 5550,
|
||||
"max_payload": 1194,
|
||||
"max_towing_capacity": 6800
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40806,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "LS",
|
||||
"description": "LS 4dr SUV 4WD (4.2L 6cyl 4A)",
|
||||
"msrp": 27340,
|
||||
"invoice": 25563,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40806,
|
||||
"make_model_trim_id": 40806,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 16,
|
||||
"epa_city_mpg": 14,
|
||||
"epa_highway_mpg": 20,
|
||||
"range_city": 308,
|
||||
"range_highway": 440,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40806,
|
||||
"make_model_trim_id": 40806,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "regular unleaded",
|
||||
"cylinders": "I6",
|
||||
"size": "4.2",
|
||||
"horsepower_hp": 291,
|
||||
"horsepower_rpm": 6000,
|
||||
"torque_ft_lbs": 277,
|
||||
"torque_rpm": 4800,
|
||||
"valves": 24,
|
||||
"valve_timing": "Variable",
|
||||
"cam_type": "Double overhead cam (DOHC)",
|
||||
"drive_type": "four wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40806,
|
||||
"make_model_trim_id": 40806,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "72.5",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4523,
|
||||
"gross_weight": 5750,
|
||||
"max_payload": 1227,
|
||||
"max_towing_capacity": 6600
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40807,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "LT",
|
||||
"description": "LT 4dr SUV (4.2L 6cyl 4A)",
|
||||
"msrp": 27970,
|
||||
"invoice": 26152,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40807,
|
||||
"make_model_trim_id": 40807,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 16,
|
||||
"epa_city_mpg": 14,
|
||||
"epa_highway_mpg": 20,
|
||||
"range_city": 308,
|
||||
"range_highway": 440,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40807,
|
||||
"make_model_trim_id": 40807,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "regular unleaded",
|
||||
"cylinders": "I6",
|
||||
"size": "4.2",
|
||||
"horsepower_hp": 291,
|
||||
"horsepower_rpm": 6000,
|
||||
"torque_ft_lbs": 277,
|
||||
"torque_rpm": 4800,
|
||||
"valves": 24,
|
||||
"valve_timing": "Variable",
|
||||
"cam_type": "Double overhead cam (DOHC)",
|
||||
"drive_type": "rear wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40807,
|
||||
"make_model_trim_id": 40807,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": null,
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4356,
|
||||
"gross_weight": 5550,
|
||||
"max_payload": 1194,
|
||||
"max_towing_capacity": 6800
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40808,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "LT",
|
||||
"description": "LT 4dr SUV 4WD (4.2L 6cyl 4A)",
|
||||
"msrp": 30210,
|
||||
"invoice": 28246,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40808,
|
||||
"make_model_trim_id": 40808,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 16,
|
||||
"epa_city_mpg": 14,
|
||||
"epa_highway_mpg": 20,
|
||||
"range_city": 308,
|
||||
"range_highway": 440,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40808,
|
||||
"make_model_trim_id": 40808,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "regular unleaded",
|
||||
"cylinders": "I6",
|
||||
"size": "4.2",
|
||||
"horsepower_hp": 291,
|
||||
"horsepower_rpm": 6000,
|
||||
"torque_ft_lbs": 277,
|
||||
"torque_rpm": 4800,
|
||||
"valves": 24,
|
||||
"valve_timing": "Variable",
|
||||
"cam_type": "Double overhead cam (DOHC)",
|
||||
"drive_type": "four wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40808,
|
||||
"make_model_trim_id": 40808,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": null,
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4523,
|
||||
"gross_weight": 5750,
|
||||
"max_payload": 1227,
|
||||
"max_towing_capacity": 6600
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40809,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "SS",
|
||||
"description": "SS 4dr SUV (6.0L 8cyl 4A)",
|
||||
"msrp": 31320,
|
||||
"invoice": 29284,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40809,
|
||||
"make_model_trim_id": 40809,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 15,
|
||||
"epa_city_mpg": 13,
|
||||
"epa_highway_mpg": 17,
|
||||
"range_city": 286,
|
||||
"range_highway": 374,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40809,
|
||||
"make_model_trim_id": 40809,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "premium unleaded (required)",
|
||||
"cylinders": "V8",
|
||||
"size": "6.0",
|
||||
"horsepower_hp": 395,
|
||||
"horsepower_rpm": 5400,
|
||||
"torque_ft_lbs": 400,
|
||||
"torque_rpm": 4400,
|
||||
"valves": 16,
|
||||
"valve_timing": null,
|
||||
"cam_type": "Overhead valves (OHV)",
|
||||
"drive_type": "rear wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40809,
|
||||
"make_model_trim_id": 40809,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "67.8",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4496,
|
||||
"gross_weight": 6001,
|
||||
"max_payload": 1505,
|
||||
"max_towing_capacity": 6800
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40810,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "SS",
|
||||
"description": "SS 4dr SUV AWD (6.0L 8cyl 4A)",
|
||||
"msrp": 33620,
|
||||
"invoice": 31435,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40810,
|
||||
"make_model_trim_id": 40810,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 14,
|
||||
"epa_city_mpg": 12,
|
||||
"epa_highway_mpg": 16,
|
||||
"range_city": 264,
|
||||
"range_highway": 352,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40810,
|
||||
"make_model_trim_id": 40810,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "premium unleaded (required)",
|
||||
"cylinders": "V8",
|
||||
"size": "6.0",
|
||||
"horsepower_hp": 395,
|
||||
"horsepower_rpm": 5400,
|
||||
"torque_ft_lbs": 400,
|
||||
"torque_rpm": 4400,
|
||||
"valves": 16,
|
||||
"valve_timing": null,
|
||||
"cam_type": "Overhead valves (OHV)",
|
||||
"drive_type": "all wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40810,
|
||||
"make_model_trim_id": 40810,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "67.8",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4663,
|
||||
"gross_weight": 6001,
|
||||
"max_payload": 1338,
|
||||
"max_towing_capacity": 6600
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40812,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "SS",
|
||||
"description": "SS 4dr SUV AWD w/3SS (6.0L 8cyl 4A)",
|
||||
"msrp": 37125,
|
||||
"invoice": 34712,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40812,
|
||||
"make_model_trim_id": 40812,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 14,
|
||||
"epa_city_mpg": 12,
|
||||
"epa_highway_mpg": 16,
|
||||
"range_city": 264,
|
||||
"range_highway": 352,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40812,
|
||||
"make_model_trim_id": 40812,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "premium unleaded (required)",
|
||||
"cylinders": "V8",
|
||||
"size": "6.0",
|
||||
"horsepower_hp": 395,
|
||||
"horsepower_rpm": 5400,
|
||||
"torque_ft_lbs": 400,
|
||||
"torque_rpm": 4400,
|
||||
"valves": 16,
|
||||
"valve_timing": null,
|
||||
"cam_type": "Overhead valves (OHV)",
|
||||
"drive_type": "all wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40812,
|
||||
"make_model_trim_id": 40812,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "67.8",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4663,
|
||||
"gross_weight": 6001,
|
||||
"max_payload": 1338,
|
||||
"max_towing_capacity": 6600
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 40811,
|
||||
"make_model_id": 33,
|
||||
"year": 2007,
|
||||
"name": "SS",
|
||||
"description": "SS 4dr SUV w/3SS (6.0L 8cyl 4A)",
|
||||
"msrp": 34885,
|
||||
"invoice": 32617,
|
||||
"created": "2023-06-29T21:13:16-04:00",
|
||||
"modified": "2023-06-29T21:13:16-04:00",
|
||||
"make_model_trim_mileage": {
|
||||
"id": 40811,
|
||||
"make_model_trim_id": 40811,
|
||||
"fuel_tank_capacity": "22.0",
|
||||
"combined_mpg": 15,
|
||||
"epa_city_mpg": 13,
|
||||
"epa_highway_mpg": 17,
|
||||
"range_city": 286,
|
||||
"range_highway": 374,
|
||||
"battery_capacity_electric": null,
|
||||
"epa_time_to_charge_hr_240v_electric": null,
|
||||
"epa_kwh_100_mi_electric": null,
|
||||
"range_electric": null,
|
||||
"epa_highway_mpg_electric": null,
|
||||
"epa_city_mpg_electric": null,
|
||||
"epa_combined_mpg_electric": null
|
||||
},
|
||||
"make_model_trim_engine": {
|
||||
"id": 40811,
|
||||
"make_model_trim_id": 40811,
|
||||
"engine_type": "gas",
|
||||
"fuel_type": "premium unleaded (required)",
|
||||
"cylinders": "V8",
|
||||
"size": "6.0",
|
||||
"horsepower_hp": 395,
|
||||
"horsepower_rpm": 5400,
|
||||
"torque_ft_lbs": 400,
|
||||
"torque_rpm": 4400,
|
||||
"valves": 16,
|
||||
"valve_timing": null,
|
||||
"cam_type": "Overhead valves (OHV)",
|
||||
"drive_type": "rear wheel drive",
|
||||
"transmission": "4-speed automatic"
|
||||
},
|
||||
"make_model_trim_body": {
|
||||
"id": 40811,
|
||||
"make_model_trim_id": 40811,
|
||||
"type": "SUV",
|
||||
"doors": 5,
|
||||
"length": "191.8",
|
||||
"width": "74.7",
|
||||
"seats": 5,
|
||||
"height": "67.8",
|
||||
"wheel_base": "113.0",
|
||||
"front_track": "62.7",
|
||||
"rear_track": "62.0",
|
||||
"ground_clearance": "7.8",
|
||||
"cargo_capacity": "43.7",
|
||||
"max_cargo_capacity": "80.1",
|
||||
"curb_weight": 4496,
|
||||
"gross_weight": 6001,
|
||||
"max_payload": 1505,
|
||||
"max_towing_capacity": 6800
|
||||
},
|
||||
"make_model": {
|
||||
"id": 33,
|
||||
"make_id": 6,
|
||||
"name": "TrailBlazer",
|
||||
"make": {
|
||||
"id": 6,
|
||||
"name": "Chevrolet"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
3
api/tests.py
Normal file
3
api/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
8
api/urls.py
Normal file
8
api/urls.py
Normal file
@ -0,0 +1,8 @@
|
||||
from django.urls import path
|
||||
from api import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('cars/vin/', views.CarVINViewSet.as_view(), name='car_vin'),
|
||||
path('login/', views.LoginView.as_view(), name='login'),
|
||||
]
|
||||
53
api/views.py
Normal file
53
api/views.py
Normal file
@ -0,0 +1,53 @@
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from rest_framework import permissions, status, viewsets
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from django.contrib.auth import authenticate
|
||||
from django.shortcuts import render
|
||||
from . import models, serializers
|
||||
from .services import get_car_data, get_from_cardatabase
|
||||
from rest_framework.authtoken.models import Token
|
||||
# from inventory.models import CustomUser
|
||||
from django.utils.decorators import method_decorator
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
permission_classes = [permissions.AllowAny,]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
username = request.data.get('username')
|
||||
password = request.data.get('password')
|
||||
|
||||
if username is None or password is None:
|
||||
return Response({'error': 'Please provide both username and password.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = authenticate(username=username, password=password)
|
||||
|
||||
if not user:
|
||||
return Response({'error': 'Invalid credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
token, created = Token.objects.get_or_create(user=user)
|
||||
return Response({'token': token.key, 'user_id': user.id, 'username': user.username}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class CarVINViewSet(APIView):
|
||||
queryset = models.CarVIN.objects.all().order_by('-created')
|
||||
serializer_class = serializers.CarVINSerializer
|
||||
|
||||
def get(self, request):
|
||||
vin = models.CarVIN.objects.all()
|
||||
serializer = serializers.CarVINSerializer(vin, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def post(self, request):
|
||||
serializer = serializers.CarVINSerializer(data=request.data)
|
||||
if serializer.is_valid(raise_exception=True):
|
||||
for key, value in serializer.validated_data.items():
|
||||
print(key, value)
|
||||
get_car_data(value)
|
||||
# get_from_cardatabase(value)
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
0
car_inventory/__init__.py
Normal file
0
car_inventory/__init__.py
Normal file
BIN
car_inventory/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
car_inventory/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
car_inventory/__pycache__/settings.cpython-311.pyc
Normal file
BIN
car_inventory/__pycache__/settings.cpython-311.pyc
Normal file
Binary file not shown.
BIN
car_inventory/__pycache__/urls.cpython-311.pyc
Normal file
BIN
car_inventory/__pycache__/urls.cpython-311.pyc
Normal file
Binary file not shown.
BIN
car_inventory/__pycache__/wsgi.cpython-311.pyc
Normal file
BIN
car_inventory/__pycache__/wsgi.cpython-311.pyc
Normal file
Binary file not shown.
27
car_inventory/asgi.py
Normal file
27
car_inventory/asgi.py
Normal file
@ -0,0 +1,27 @@
|
||||
"""
|
||||
ASGI config for car_inventory project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
# asgi.py
|
||||
|
||||
import os
|
||||
from django.core.asgi import get_asgi_application
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
from api import routing
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'car_inventory.settings')
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
"http": get_asgi_application(),
|
||||
"websocket": AuthMiddlewareStack(
|
||||
URLRouter(
|
||||
routing.websocket_urlpatterns
|
||||
)
|
||||
),
|
||||
})
|
||||
255
car_inventory/settings.py
Normal file
255
car_inventory/settings.py
Normal file
@ -0,0 +1,255 @@
|
||||
"""
|
||||
Django settings for car_inventory project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.0.6.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-gc9bh4*3=b6hihdnaom0edjsbxh$5t)aap@e8p&340r7)*)qb8'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['10.10.1.109', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.sites',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'inventory.apps.InventoryConfig',
|
||||
'api.apps.ApiConfig',
|
||||
|
||||
# Other Apps to select what we need before deploy
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'allauth.socialaccount.providers.google',
|
||||
'debug_toolbar',
|
||||
'silk',
|
||||
'django_prometheus',
|
||||
'django_tables2',
|
||||
"django_bootstrap5",
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
"phonenumber_field",
|
||||
"rest_framework",
|
||||
'rest_framework.authtoken',
|
||||
'django_extensions',
|
||||
'djangoviz',
|
||||
'django_ledger',
|
||||
'djmoney',
|
||||
'sslserver',
|
||||
|
||||
]
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django_prometheus.middleware.PrometheusBeforeMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware',
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
'silk.middleware.SilkyMiddleware',
|
||||
'django_prometheus.middleware.PrometheusAfterMiddleware',
|
||||
|
||||
]
|
||||
ROOT_URLCONF = 'car_inventory.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(BASE_DIR, 'templates')],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"dj_shop_cart.context_processors.cart",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'car_inventory.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django_prometheus.db.backends.postgresql",
|
||||
"NAME": "secondhaikal",
|
||||
"USER": "f95166",
|
||||
"PASSWORD": "Kfsh&rc9788",
|
||||
"HOST": "localhost",
|
||||
"PORT": 5432,
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
# Authentications
|
||||
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
|
||||
ACCOUNT_EMAIL_VERIFICATION = "none"
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"allauth.account.auth_backends.AuthenticationBackend",
|
||||
]
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
# 'rest_framework.authentication.SessionAuthentication',
|
||||
# 'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
|
||||
],
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
# 'rest_framework.permissions.IsAuthenticated',
|
||||
'rest_framework.permissions.AllowAny',
|
||||
],
|
||||
}
|
||||
|
||||
# REST_AUTH = {
|
||||
# 'USE_JWT': True,
|
||||
# 'JWT_AUTH_COOKIE': 'jwt-auth',
|
||||
# }
|
||||
|
||||
# Email backend
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/images/'
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static'
|
||||
]
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images')
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||
|
||||
|
||||
LOCALE_PATHS = (
|
||||
os.path.join(BASE_DIR, 'locale'),
|
||||
)
|
||||
|
||||
LANGUAGE_COOKIE_NAME = 'django_language'
|
||||
|
||||
LANGUAGE_CODE = "en"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
LANGUAGES = [
|
||||
('en', _('English')),
|
||||
('ar', _('Arabic')),
|
||||
]
|
||||
|
||||
TIME_ZONE = "Asia/Riyadh"
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||
|
||||
SILKY_PYTHON_PROFILER = True
|
||||
|
||||
DJANGO_TABLES2_TEMPLATE = "django_tables2/bootstrap5-responsive.html"
|
||||
DJANGO_TABLES2_TABLE_ATTRS = {
|
||||
'class': 'table table-sm table-hover table-responsive-sm',
|
||||
'thead': {
|
||||
'class': 'table-secondary',
|
||||
},
|
||||
'tbody': {
|
||||
'class': 'fw-light',
|
||||
}
|
||||
}
|
||||
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
|
||||
# DEFAULT_CURRENCY = 'SAR'
|
||||
|
||||
# API KEYS
|
||||
|
||||
OPENAI_API_KEY = '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'
|
||||
APP_ID = '367974ed'
|
||||
APP_KEY = '046b0412c1b4d3f8c39ec6375d6f3030'
|
||||
CLIENT_ID = '94142c27-2536-47e9-8e28-9ca7728b9442'
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'django': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO', # Change to WARNING or ERROR as needed
|
||||
},
|
||||
'openai': {
|
||||
'handlers': ['console'],
|
||||
'level': 'WARNING', # Suppress detailed logs from OpenAI client
|
||||
},
|
||||
},
|
||||
}
|
||||
27
car_inventory/urls.py
Normal file
27
car_inventory/urls.py
Normal file
@ -0,0 +1,27 @@
|
||||
from django.contrib import admin
|
||||
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
|
||||
from inventory import views
|
||||
import debug_toolbar
|
||||
|
||||
urlpatterns = [
|
||||
path('__debug__/', include(debug_toolbar.urls)),
|
||||
path('silk/', include('silk.urls', namespace='silk')),
|
||||
path('api-auth/', include('rest_framework.urls')),
|
||||
path('api/', include('api.urls')),
|
||||
path('dj-rest-auth/', include('dj_rest_auth.urls')),
|
||||
|
||||
]
|
||||
urlpatterns += i18n_patterns(
|
||||
path('admin/', admin.site.urls),
|
||||
path('switch_language/', views.switch_language, name='switch_language'),
|
||||
path('accounts/', include('allauth.urls')),
|
||||
path('prometheus/', include('django_prometheus.urls')),
|
||||
path('', include('inventory.urls')),
|
||||
path('ledger/', include('django_ledger.urls', namespace='django_ledger')),
|
||||
|
||||
)
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
16
car_inventory/wsgi.py
Normal file
16
car_inventory/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for car_inventory project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'car_inventory.settings')
|
||||
|
||||
application = get_wsgi_application()
|
||||
4012
carmake_backup.json
Normal file
4012
carmake_backup.json
Normal file
File diff suppressed because it is too large
Load Diff
45641
carmodel_backup.json
Normal file
45641
carmodel_backup.json
Normal file
File diff suppressed because it is too large
Load Diff
141095
carserie_backup.json
Normal file
141095
carserie_backup.json
Normal file
File diff suppressed because it is too large
Load Diff
848
carspecification_backup.json
Normal file
848
carspecification_backup.json
Normal file
@ -0,0 +1,848 @@
|
||||
[
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"name": "Bodywork",
|
||||
"arabic_name": "هيكل السيارة",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"name": "Body type",
|
||||
"arabic_name": "نوع الهيكل",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Number of doors",
|
||||
"arabic_name": "عدد الأبواب",
|
||||
"id_parent": 1549
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "Number of seater",
|
||||
"arabic_name": "عدد المقاعد",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Length",
|
||||
"arabic_name": "الطول",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "Width",
|
||||
"arabic_name": "العرض",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"name": "Height",
|
||||
"arabic_name": "الارتفاع",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"name": "Wheelbase",
|
||||
"arabic_name": "قاعدة العجلات",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"name": "Front track",
|
||||
"arabic_name": "المسار الأمامي",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"name": "Rear track",
|
||||
"arabic_name": "المسار الخلفي",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"name": "Engine",
|
||||
"arabic_name": "المحرك",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"name": "Engine type",
|
||||
"arabic_name": "نوع المحرك",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"name": "Capacity",
|
||||
"arabic_name": "السعة",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"name": "Engine power",
|
||||
"arabic_name": "قوة المحرك",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"name": "Max power at RPM",
|
||||
"arabic_name": "القوة القصوى عند دورة في الدقيقة",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"name": "Maximum torque",
|
||||
"arabic_name": "العزم الأقصى",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"name": "Injection type",
|
||||
"arabic_name": "نوع الحقن",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"name": "Overhead camshaft",
|
||||
"arabic_name": "عمود الحدبات العلوي",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"name": "Cylinder layout",
|
||||
"arabic_name": "تخطيط الأسطوانة",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"name": "Number of cylinders",
|
||||
"arabic_name": "عدد الأسطوانات",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"name": "Compression ratio",
|
||||
"arabic_name": "نسبة الضغط",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"name": "Fuel",
|
||||
"arabic_name": "الوقود",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"name": "Gearbox",
|
||||
"arabic_name": "(جيربوكس) علبة التروس",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"name": "Gearbox type",
|
||||
"arabic_name": "نوع علبة التروس",
|
||||
"id_parent": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"name": "Number of gear",
|
||||
"arabic_name": "عدد السرعات",
|
||||
"id_parent": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 27,
|
||||
"fields": {
|
||||
"name": "Drive wheels",
|
||||
"arabic_name": "عجلات القيادة",
|
||||
"id_parent": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 29,
|
||||
"fields": {
|
||||
"name": "Front brakes",
|
||||
"arabic_name": "الفرامل الأمامية",
|
||||
"id_parent": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 30,
|
||||
"fields": {
|
||||
"name": "Rear brakes",
|
||||
"arabic_name": "الفرامل الخلفية",
|
||||
"id_parent": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 31,
|
||||
"fields": {
|
||||
"name": "Operating characteristics",
|
||||
"arabic_name": "خصائص التشغيل",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 32,
|
||||
"fields": {
|
||||
"name": "Max speed",
|
||||
"arabic_name": "السرعة القصوى",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 33,
|
||||
"fields": {
|
||||
"name": "Acceleration (0-100 km/h)",
|
||||
"arabic_name": "التسارع (من ٠ إلى ١٠٠ كم/ساعة)",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 34,
|
||||
"fields": {
|
||||
"name": "Curb weight",
|
||||
"arabic_name": "وزن الرصيف",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 35,
|
||||
"fields": {
|
||||
"name": "Fuel tank capacity",
|
||||
"arabic_name": "سعة خزان الوقود",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 36,
|
||||
"fields": {
|
||||
"name": "Wheel size",
|
||||
"arabic_name": "حجم العجلة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 37,
|
||||
"fields": {
|
||||
"name": "Emission standards",
|
||||
"arabic_name": "معايير الانبعاثات",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 38,
|
||||
"fields": {
|
||||
"name": "Ground clearance",
|
||||
"arabic_name": "المسافة الفارغة عن الأرض",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 39,
|
||||
"fields": {
|
||||
"name": "Valves per cylinder",
|
||||
"arabic_name": "صمامات لكل اسطوانة",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 40,
|
||||
"fields": {
|
||||
"name": "Suspension and brakes",
|
||||
"arabic_name": "التعليق والفرامل",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 41,
|
||||
"fields": {
|
||||
"name": "Front suspension",
|
||||
"arabic_name": "تعليق أمامي",
|
||||
"id_parent": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 42,
|
||||
"fields": {
|
||||
"name": "Back suspension",
|
||||
"arabic_name": "تعليق الخلفي",
|
||||
"id_parent": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 44,
|
||||
"fields": {
|
||||
"name": "Max trunk capacity",
|
||||
"arabic_name": "الحد الأقصى لسعة صندوق السيارة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 45,
|
||||
"fields": {
|
||||
"name": "Min trunk capacity",
|
||||
"arabic_name": "الحد الأدنى لسعة صندوق السيارة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 46,
|
||||
"fields": {
|
||||
"name": "Boost type",
|
||||
"arabic_name": "نوع الدفع",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 47,
|
||||
"fields": {
|
||||
"name": "Cylinder bore",
|
||||
"arabic_name": "قطر الأسطوانة",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 48,
|
||||
"fields": {
|
||||
"name": "Stroke cycle",
|
||||
"arabic_name": "دورة الشوط",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 49,
|
||||
"fields": {
|
||||
"name": "Bore/stroke ratio",
|
||||
"arabic_name": "نسبة القطر إلى الشوط",
|
||||
"id_parent": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 50,
|
||||
"fields": {
|
||||
"name": "City driving fuel consumption per 100 km",
|
||||
"arabic_name": "استهلاك الوقود داخل المدينة لكل ١٠٠ كيلومتر",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 51,
|
||||
"fields": {
|
||||
"name": "Highway driving fuel consumption per 100 km",
|
||||
"arabic_name": "استهلاك الوقود على الطريق السريع لكل ١٠٠ كيلومتر",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 52,
|
||||
"fields": {
|
||||
"name": "Mixed driving fuel consumption per 100 km",
|
||||
"arabic_name": "استهلاك الوقود لكل ١٠٠ كيلومتر في القيادة المختطلة",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 53,
|
||||
"fields": {
|
||||
"name": "Steering",
|
||||
"arabic_name": "التوجيه",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 54,
|
||||
"fields": {
|
||||
"name": "Steering type",
|
||||
"arabic_name": "نوع التوجيه",
|
||||
"id_parent": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 55,
|
||||
"fields": {
|
||||
"name": "Engine model",
|
||||
"arabic_name": "نموذج المحرك",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 56,
|
||||
"fields": {
|
||||
"name": "Electric motor power",
|
||||
"arabic_name": "قوة المحرك الكهربائي",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 57,
|
||||
"fields": {
|
||||
"name": "Turning circle",
|
||||
"arabic_name": "دائرة الالتفاف",
|
||||
"id_parent": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 58,
|
||||
"fields": {
|
||||
"name": "Full weight",
|
||||
"arabic_name": "الوزن الكامل",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 59,
|
||||
"fields": {
|
||||
"name": "Disc size",
|
||||
"arabic_name": "حجم القرص",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 60,
|
||||
"fields": {
|
||||
"name": "Total power output",
|
||||
"arabic_name": "إجمالي قوة الإخراج",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 61,
|
||||
"fields": {
|
||||
"name": "Engine placement",
|
||||
"arabic_name": "موضع المحرك",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 62,
|
||||
"fields": {
|
||||
"name": "Cruising range",
|
||||
"arabic_name": "مدى السير",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 63,
|
||||
"fields": {
|
||||
"name": "Full cycle charge",
|
||||
"arabic_name": "شحن دورة كاملة",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 66,
|
||||
"fields": {
|
||||
"name": "Car width with mirrors",
|
||||
"arabic_name": "عرض السيارة مع المرايا",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1521,
|
||||
"fields": {
|
||||
"name": "Cylinder bore and stroke cycle",
|
||||
"arabic_name": "دورة حجم الاسطوانة والشوط",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1549,
|
||||
"fields": {
|
||||
"name": "General information",
|
||||
"arabic_name": "معلومات عامة",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1550,
|
||||
"fields": {
|
||||
"name": "Volume and weight",
|
||||
"arabic_name": "الحجم والوزن",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1551,
|
||||
"fields": {
|
||||
"name": "Security",
|
||||
"arabic_name": "الأمان",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1552,
|
||||
"fields": {
|
||||
"name": "Country",
|
||||
"arabic_name": "الدولة",
|
||||
"id_parent": 1549
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1553,
|
||||
"fields": {
|
||||
"name": "Car class",
|
||||
"arabic_name": "فئة السيارة",
|
||||
"id_parent": 1549
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1554,
|
||||
"fields": {
|
||||
"name": "Clearance",
|
||||
"arabic_name": "إرتفاع السيارة عن الارض",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1555,
|
||||
"fields": {
|
||||
"name": "Front track width",
|
||||
"arabic_name": "عرض المسار الأمامي للسيارة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1556,
|
||||
"fields": {
|
||||
"name": "Back track width",
|
||||
"arabic_name": "عرض المسار الخلفي للسيارة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1558,
|
||||
"fields": {
|
||||
"name": "Max power (h.p.)",
|
||||
"arabic_name": "القوة القصوى (حصان)",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1559,
|
||||
"fields": {
|
||||
"name": "Max power (kW)",
|
||||
"arabic_name": "القوة القصوى (كيلوواط)",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1560,
|
||||
"fields": {
|
||||
"name": "Model assembly",
|
||||
"arabic_name": "تجميع الطراز",
|
||||
"id_parent": 1549
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1561,
|
||||
"fields": {
|
||||
"name": "CO2 emissions",
|
||||
"arabic_name": "انبعاثات ثاني أكسيد الكربون",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1562,
|
||||
"fields": {
|
||||
"name": "Safety assessment",
|
||||
"arabic_name": "تقييم السلامة",
|
||||
"id_parent": 1551
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1563,
|
||||
"fields": {
|
||||
"name": "Rating name",
|
||||
"arabic_name": "اسم التقييم",
|
||||
"id_parent": 1551
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1564,
|
||||
"fields": {
|
||||
"name": "Turnover of maximum torque",
|
||||
"arabic_name": "تحول العزم الأقصى",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1565,
|
||||
"fields": {
|
||||
"name": "Payload",
|
||||
"arabic_name": "الحمولة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1566,
|
||||
"fields": {
|
||||
"name": "Presence of intercooler",
|
||||
"arabic_name": "وجود مبرد بيني",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1567,
|
||||
"fields": {
|
||||
"name": "Trailer load (with brakes)",
|
||||
"arabic_name": "حمولة المقطورة (مع الفرامل)",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1568,
|
||||
"fields": {
|
||||
"name": "Front/rear axle load",
|
||||
"arabic_name": "حمولة محور العجلات الأمامي / الخلفي",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1569,
|
||||
"fields": {
|
||||
"name": "Loading height",
|
||||
"arabic_name": "ارتفاع التحميل",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1570,
|
||||
"fields": {
|
||||
"name": "Cargo compartment (Length x Width x Height)",
|
||||
"arabic_name": "حجرة البضائع (الطول x العرض x الارتفاع)",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1571,
|
||||
"fields": {
|
||||
"name": "Cargo compartment volume",
|
||||
"arabic_name": "حجم حيز التحميل في السيارة",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1631,
|
||||
"fields": {
|
||||
"name": "Accumulator battery",
|
||||
"arabic_name": "البطارية المجمعة",
|
||||
"id_parent": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1632,
|
||||
"fields": {
|
||||
"name": "Battery capacity",
|
||||
"arabic_name": "سعة البطارية",
|
||||
"id_parent": 1631
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1634,
|
||||
"fields": {
|
||||
"name": "Electric power reserve",
|
||||
"arabic_name": "احتياطي الطاقة الكهربائية",
|
||||
"id_parent": 1631
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1635,
|
||||
"fields": {
|
||||
"name": "Charging time",
|
||||
"arabic_name": "وقت الشحن",
|
||||
"id_parent": 1631
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1636,
|
||||
"fields": {
|
||||
"name": "Fuel consumption city/highway/mixed, l",
|
||||
"arabic_name": "استهلاك الوقود في المدينة / الطريق السريع / المختلط، لتر",
|
||||
"id_parent": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1641,
|
||||
"fields": {
|
||||
"name": "Rudder location",
|
||||
"arabic_name": "موقع الدفة",
|
||||
"id_parent": 53
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1642,
|
||||
"fields": {
|
||||
"name": "Dimensions",
|
||||
"arabic_name": "الأبعاد",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1644,
|
||||
"fields": {
|
||||
"name": "Pitch Circle Diameter",
|
||||
"arabic_name": "قطر دائرة التركيب",
|
||||
"id_parent": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1645,
|
||||
"fields": {
|
||||
"name": "Engine code",
|
||||
"arabic_name": "كود المحرك",
|
||||
"id_parent": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "inventory.carspecification",
|
||||
"pk": 1646,
|
||||
"fields": {
|
||||
"name": "Disc sizes",
|
||||
"arabic_name": "أحجام الأقراص",
|
||||
"id_parent": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
BIN
carspecificationvalue_backup.json
Normal file
BIN
carspecificationvalue_backup.json
Normal file
Binary file not shown.
940678
cartrim_backup.json
Normal file
940678
cartrim_backup.json
Normal file
File diff suppressed because it is too large
Load Diff
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
28
docs/source/conf.py
Normal file
28
docs/source/conf.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'Haikal'
|
||||
copyright = '2024, Marwan Alwali'
|
||||
author = 'Marwan Alwali'
|
||||
release = '01/11/2024'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = []
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = []
|
||||
|
||||
language = '[en,ar]'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'alabaster'
|
||||
html_static_path = ['_static']
|
||||
17
docs/source/index.rst
Normal file
17
docs/source/index.rst
Normal file
@ -0,0 +1,17 @@
|
||||
.. Haikal documentation master file, created by
|
||||
sphinx-quickstart on Mon Nov 25 16:28:43 2024.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Haikal documentation
|
||||
====================
|
||||
|
||||
Add your content using ``reStructuredText`` syntax. See the
|
||||
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
|
||||
documentation for details.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
1565
haikalna.py
Normal file
1565
haikalna.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
inventory/.DS_Store
vendored
Normal file
BIN
inventory/.DS_Store
vendored
Normal file
Binary file not shown.
0
inventory/__init__.py
Normal file
0
inventory/__init__.py
Normal file
BIN
inventory/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/admin.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/admin.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/apps.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/apps.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/context_processors.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/context_processors.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/filters.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/filters.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/forms.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/middleware.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/middleware.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/mixins.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/mixins.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/models.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/services.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/services.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/tables.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/tables.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/urls.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/urls.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/utils.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/utils.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/views.cpython-311.pyc
Normal file
BIN
inventory/__pycache__/views.cpython-311.pyc
Normal file
Binary file not shown.
76
inventory/admin.py
Normal file
76
inventory/admin.py
Normal file
@ -0,0 +1,76 @@
|
||||
from django.contrib import admin
|
||||
from . import models
|
||||
|
||||
|
||||
admin.site.register(models.Dealer)
|
||||
admin.site.register(models.Vendor)
|
||||
admin.site.register(models.Customer)
|
||||
admin.site.register(models.Car)
|
||||
admin.site.register(models.CarFinance)
|
||||
admin.site.register(models.CarColors)
|
||||
admin.site.register(models.CarRegistration)
|
||||
admin.site.register(models.CustomCard)
|
||||
admin.site.register(models.CarSpecificationValue)
|
||||
|
||||
|
||||
@admin.register(models.CarMake)
|
||||
class CarMakeAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'is_sa_import')
|
||||
search_fields = ('name', 'arabic_name')
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Make"
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
@admin.register(models.CarModel)
|
||||
class CarModelAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'id_car_make', 'get_is_sa_import')
|
||||
search_fields = ('name', 'arabic_name')
|
||||
list_filter = ('id_car_make__is_sa_import', 'id_car_make')
|
||||
sortable_by = ['name', 'arabic_name', 'id_car_make']
|
||||
|
||||
def get_is_sa_import(self, obj):
|
||||
return obj.id_car_make.is_sa_import
|
||||
get_is_sa_import.boolean = True
|
||||
get_is_sa_import.short_description = 'Is SA Import'
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Model"
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
@admin.register(models.CarSerie)
|
||||
class CarSeriesAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'id_car_model')
|
||||
search_fields = ('name', 'id_car_model__name')
|
||||
list_filter = ('id_car_model__id_car_make__is_sa_import',
|
||||
'id_car_model__id_car_make__name',)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Series"
|
||||
|
||||
|
||||
@admin.register(models.CarTrim)
|
||||
class CarTrimAdmin(admin.ModelAdmin):
|
||||
list_display = ('name',
|
||||
'id_car_serie__name',
|
||||
'id_car_serie__id_car_model__name',
|
||||
'id_car_serie__id_car_model__id_car_make__name')
|
||||
search_fields = ('name', 'arabic_name', 'id_car_serie__id_car_model__name')
|
||||
list_filter = ('id_car_serie__id_car_model__id_car_make__is_sa_import',
|
||||
'id_car_serie__id_car_model__id_car_make__name')
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Trim"
|
||||
|
||||
|
||||
@admin.register(models.CarSpecification)
|
||||
class CarSpecificationAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'arabic_name', 'id_parent')
|
||||
search_fields = ('name', 'id_parent')
|
||||
list_filter = ('id_parent',)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Car Specification"
|
||||
|
||||
6
inventory/apps.py
Normal file
6
inventory/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class InventoryConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'inventory'
|
||||
9
inventory/context_processors.py
Normal file
9
inventory/context_processors.py
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
def breadcrumbs(request):
|
||||
breadcrumbs = []
|
||||
path = request.path.strip('/').split('/')
|
||||
for i in range(len(path)):
|
||||
url = '/' + '/'.join(path[:i+1]) + '/'
|
||||
breadcrumbs.append({'name': path[i].capitalize(), 'url': url})
|
||||
return {'breadcrumbs': breadcrumbs}
|
||||
1
inventory/filters.py
Normal file
1
inventory/filters.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
163
inventory/forms.py
Normal file
163
inventory/forms.py
Normal file
@ -0,0 +1,163 @@
|
||||
from django import forms
|
||||
from .mixins import AddClassMixin
|
||||
from .models import (
|
||||
Dealer,
|
||||
# Branch,
|
||||
Vendor,
|
||||
Customer,
|
||||
Car,
|
||||
CarFinance,
|
||||
CustomCard,
|
||||
CarRegistration,
|
||||
CarColors
|
||||
)
|
||||
from django.contrib.contenttypes.forms import generic_inlineformset_factory
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
# Dealer Form
|
||||
class DealerForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Dealer
|
||||
fields = ['crn', 'vrn', 'arabic_name', 'name', 'phone_number', 'address', 'logo']
|
||||
|
||||
|
||||
# Customer Form
|
||||
class CustomerForm(forms.ModelForm, AddClassMixin):
|
||||
class Meta:
|
||||
model = Customer
|
||||
fields = [
|
||||
'first_name', 'middle_name', 'last_name', 'email',
|
||||
'national_id', 'phone_number', 'address'
|
||||
]
|
||||
|
||||
|
||||
class CarForm(forms.ModelForm, AddClassMixin, ):
|
||||
class Meta:
|
||||
model = Car
|
||||
fields = [
|
||||
'vin', 'id_car_make', 'id_car_model',
|
||||
'year', 'id_car_serie', 'id_car_trim',
|
||||
'stock_type', 'remarks', 'mileage', 'receiving_date', 'vendor'
|
||||
]
|
||||
widgets = {
|
||||
'receiving_date': forms.DateTimeInput(attrs={'type': 'datetime-local'}),
|
||||
'remarks': forms.Textarea(attrs={'rows': 2}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dealer = kwargs.pop('dealer', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
# if dealer:
|
||||
# self.fields['branch'].queryset = Branch.objects.filter(dealer=dealer)
|
||||
|
||||
if 'id_car_make' in self.fields:
|
||||
queryset = self.fields['id_car_make'].queryset
|
||||
self.fields['id_car_make'].choices = [
|
||||
(obj.id_car_make, obj.get_local_name()) for obj in queryset
|
||||
]
|
||||
|
||||
|
||||
class CarUpdateForm(forms.ModelForm, AddClassMixin):
|
||||
class Meta:
|
||||
model = Car
|
||||
fields = ['vendor', 'status', 'stock_type', 'mileage', 'receiving_date', 'remarks']
|
||||
|
||||
widgets = {
|
||||
'receiving_date': forms.DateTimeInput(attrs={'type': 'datetime-local'}),
|
||||
'remarks': forms.Textarea(attrs={'rows': 2}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
dealer = kwargs.pop('dealer', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# if dealer and 'branch' in self.fields:
|
||||
# self.fields['branch'].queryset = Branch.objects.filter(dealer=dealer)
|
||||
# self.fields['branch'].choices = [
|
||||
# (branch.id, branch.get_local_name()) for branch in self.fields['branch'].queryset
|
||||
# ]
|
||||
|
||||
if 'vendor' in self.fields:
|
||||
queryset = self.fields['vendor'].queryset
|
||||
if queryset:
|
||||
self.fields['vendor'].choices = [
|
||||
(vendor.id, vendor.get_local_name()) for vendor in queryset
|
||||
]
|
||||
|
||||
|
||||
class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
||||
profit_margin_percentage = forms.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
label="Profit Margin",
|
||||
required=True,
|
||||
widget=forms.NumberInput(attrs={'min': '0', 'max': '100', 'step': '0.01'})
|
||||
)
|
||||
vat_rate_percentage = forms.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=2,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
label="Vat Rate",
|
||||
required=True,
|
||||
widget=forms.NumberInput(attrs={'min': '0', 'max': '100', 'step': '0.01'})
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CarFinance
|
||||
fields = ['cost_price']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CarFinanceForm, self).__init__(*args, **kwargs)
|
||||
if self.instance and self.instance.pk:
|
||||
# Convert profit_margin from decimal to percentage for initial display
|
||||
self.fields['profit_margin_percentage'].initial = self.instance.profit_margin * 100
|
||||
self.fields['vat_rate_percentage'].initial = self.instance.vat_rate * 100
|
||||
|
||||
def clean_profit_margin_percentage(self):
|
||||
profit_margin_percentage = self.cleaned_data['profit_margin_percentage']
|
||||
if not (0 <= profit_margin_percentage <= 100):
|
||||
raise forms.ValidationError('Profit margin must be between 0 and 100.')
|
||||
return profit_margin_percentage
|
||||
|
||||
def clean_vat_rate_percentage(self):
|
||||
vat_rate_percentage = self.cleaned_data['vat_rate_percentage']
|
||||
if not (0 <= vat_rate_percentage <= 100):
|
||||
raise forms.ValidationError('vat rate must be between 0 and 100.')
|
||||
return vat_rate_percentage
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super(CarFinanceForm, self).save(commit=False)
|
||||
profit_margin_percentage = self.cleaned_data['profit_margin_percentage']
|
||||
vat_rate_percentage = self.cleaned_data['vat_rate_percentage']
|
||||
instance.profit_margin = profit_margin_percentage / 100
|
||||
instance.vat_rate = vat_rate_percentage / 100
|
||||
if commit:
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
# Custom Card Form
|
||||
class CustomCardForm(forms.ModelForm):
|
||||
custom_date = forms.DateTimeField(
|
||||
widget=forms.DateInput(attrs={'type': 'date'}),
|
||||
label=_("Custom Date"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CustomCard
|
||||
fields = ['custom_number', 'custom_date']
|
||||
|
||||
|
||||
# Car Registration Form
|
||||
class CarRegistrationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CarRegistration
|
||||
fields = [
|
||||
'car', 'plate_number', 'text1', 'text2', 'text3', 'registration_date'
|
||||
]
|
||||
|
||||
|
||||
BIN
inventory/management/.DS_Store
vendored
Normal file
BIN
inventory/management/.DS_Store
vendored
Normal file
Binary file not shown.
0
inventory/management/__init__.py
Normal file
0
inventory/management/__init__.py
Normal file
BIN
inventory/management/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
inventory/management/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
inventory/management/commands/.DS_Store
vendored
Normal file
BIN
inventory/management/commands/.DS_Store
vendored
Normal file
Binary file not shown.
0
inventory/management/commands/__init__.py
Normal file
0
inventory/management/commands/__init__.py
Normal file
BIN
inventory/management/commands/__pycache__/.DS_Store
vendored
Normal file
BIN
inventory/management/commands/__pycache__/.DS_Store
vendored
Normal file
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.
0
inventory/middleware.py
Normal file
0
inventory/middleware.py
Normal file
293
inventory/migrations/0001_initial.py
Normal file
293
inventory/migrations/0001_initial.py
Normal file
@ -0,0 +1,293 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-04 23:43
|
||||
|
||||
import django.db.models.deletion
|
||||
import inventory.mixins
|
||||
import phonenumber_field.modelfields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Car',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('vin', models.CharField(max_length=17, unique=True, verbose_name='VIN')),
|
||||
('year', models.IntegerField(verbose_name='Year')),
|
||||
('status', models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged')], default='available', max_length=10, verbose_name='Status')),
|
||||
('stock_type', models.CharField(choices=[('new', 'New'), ('used', 'Used')], default='new', max_length=10, verbose_name='Stock Type')),
|
||||
('remarks', models.TextField(blank=True, null=True, verbose_name='Remarks')),
|
||||
('mileage', models.IntegerField(blank=True, null=True, verbose_name='Mileage')),
|
||||
('receiving_date', models.DateTimeField(verbose_name='Receiving Date')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Car',
|
||||
'verbose_name_plural': 'Cars',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarMake',
|
||||
fields=[
|
||||
('id_car_make', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='car_make', verbose_name='logo')),
|
||||
('is_sa_import', models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Make',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarFinance',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cost_price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Cost Price')),
|
||||
('profit_margin', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Profit Margin')),
|
||||
('selling_price', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Selling Price')),
|
||||
('vat_rate', models.DecimalField(decimal_places=2, default=0.15, max_digits=10, verbose_name='VAT Rate')),
|
||||
('vat_amount', models.DecimalField(decimal_places=2, editable=False, max_digits=12, verbose_name='VAT Amount')),
|
||||
('total', models.DecimalField(decimal_places=2, editable=False, max_digits=14, verbose_name='Total Amount')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='finances', to='inventory.car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Car Financial Details',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_make',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_make', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake', verbose_name='Make'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarModel',
|
||||
fields=[
|
||||
('id_car_model', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_car_make', models.ForeignKey(db_column='id_car_make', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmake')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Model',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_model',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_model', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel', verbose_name='Model'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarRegistration',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('plate_number', models.IntegerField(verbose_name='Plate Number')),
|
||||
('text1', models.CharField(max_length=1, verbose_name='Text 1')),
|
||||
('text2', models.CharField(max_length=1, verbose_name='Text 2')),
|
||||
('text3', models.CharField(max_length=1, verbose_name='Text 3')),
|
||||
('registration_date', models.DateTimeField(verbose_name='Registration Date')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='registrations', to='inventory.car', verbose_name='Car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Registration',
|
||||
'verbose_name_plural': 'Registrations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSerie',
|
||||
fields=[
|
||||
('id_car_serie', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_car_model', models.ForeignKey(db_column='id_car_model', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carmodel')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Series',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_serie',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_serie', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie', verbose_name='Series'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSpecification',
|
||||
fields=[
|
||||
('id_car_specification', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('id_parent', models.ForeignKey(blank=True, db_column='id_parent', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Specification',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarTrim',
|
||||
fields=[
|
||||
('id_car_trim', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('arabic_name', models.CharField(max_length=255)),
|
||||
('start_production_year', models.IntegerField(blank=True, null=True)),
|
||||
('end_production_year', models.IntegerField(blank=True, null=True)),
|
||||
('id_car_serie', models.ForeignKey(db_column='id_car_serie', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carserie')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Trim',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarSpecificationValue',
|
||||
fields=[
|
||||
('id_car_specification_value', models.AutoField(primary_key=True, serialize=False)),
|
||||
('value', models.CharField(max_length=500)),
|
||||
('unit', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('id_car_specification', models.ForeignKey(db_column='id_car_specification', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.carspecification')),
|
||||
('id_car_trim', models.ForeignKey(db_column='id_car_trim', on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Specification Value',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='id_car_trim',
|
||||
field=models.ForeignKey(blank=True, db_column='id_car_trim', null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='inventory.cartrim', verbose_name='Trim'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomCard',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('custom_number', models.CharField(max_length=255, verbose_name='Custom Number')),
|
||||
('custom_date', models.DateTimeField(verbose_name='Custom Date')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_cards', to='inventory.car', verbose_name='Car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Custom Card',
|
||||
'verbose_name_plural': 'Custom Cards',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dealer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(max_length=10, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('logo', models.ImageField(blank=True, null=True, upload_to='logos/users', verbose_name='Logo')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='dealer', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Dealer',
|
||||
'verbose_name_plural': 'Dealers',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Customer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=50, verbose_name='First Name')),
|
||||
('middle_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='Middle Name')),
|
||||
('last_name', models.CharField(max_length=50, verbose_name='Last Name')),
|
||||
('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')),
|
||||
('national_id', models.CharField(max_length=10, unique=True, verbose_name='National ID')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', unique=True, verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customers', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Customer',
|
||||
'verbose_name_plural': 'Customers',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='dealer',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.dealer', verbose_name='Dealer'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ExteriorColors',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='exteriorcolor', to='inventory.car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Exterior Color',
|
||||
'verbose_name_plural': 'Exterior Colors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InteriorColors',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interiorcolor', to='inventory.car', verbose_name='Car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Interior Color',
|
||||
'verbose_name_plural': 'Interior Colors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Vendor',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('crn', models.CharField(max_length=10, unique=True, verbose_name='Commercial Registration Number')),
|
||||
('vrn', models.CharField(max_length=15, unique=True, verbose_name='VAT Registration Number')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('name', models.CharField(max_length=255, verbose_name='English Name')),
|
||||
('contact_person', models.CharField(max_length=100, verbose_name='Contact Person')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, region='SA', verbose_name='Phone Number')),
|
||||
('address', models.CharField(blank=True, max_length=200, null=True, verbose_name='Address')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vendors', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Vendor',
|
||||
'verbose_name_plural': 'Vendors',
|
||||
},
|
||||
bases=(models.Model, inventory.mixins.LocalizedNameMixin),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='car',
|
||||
name='vendor',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='cars', to='inventory.vendor', verbose_name='Vendor'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarReservation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('reserved_at', models.DateTimeField(auto_now_add=True)),
|
||||
('reserved_until', models.DateTimeField()),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to='inventory.car')),
|
||||
('reserved_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-reserved_at'],
|
||||
'unique_together': {('car', 'reserved_until')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,39 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-06 14:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='interiorcolors',
|
||||
name='car',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CarColors',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||
('arabic_name', models.CharField(max_length=255, verbose_name='Arabic Name')),
|
||||
('rgb', models.CharField(blank=True, max_length=24, null=True, verbose_name='RGB')),
|
||||
('color_type', models.CharField(choices=[('exterior', 'Exterior'), ('interior', 'Interior')], default='exterior', max_length=10, verbose_name='Color Type')),
|
||||
('car', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='inventory.car')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Color',
|
||||
'verbose_name_plural': 'Colors',
|
||||
},
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ExteriorColors',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='InteriorColors',
|
||||
),
|
||||
]
|
||||
18
inventory/migrations/0003_alter_customcard_custom_date.py
Normal file
18
inventory/migrations/0003_alter_customcard_custom_date.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.4 on 2024-12-08 08:10
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0002_remove_interiorcolors_car_carcolors_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='customcard',
|
||||
name='custom_date',
|
||||
field=models.DateField(verbose_name='Custom Date'),
|
||||
),
|
||||
]
|
||||
0
inventory/migrations/__init__.py
Normal file
0
inventory/migrations/__init__.py
Normal file
BIN
inventory/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
inventory/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
inventory/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
inventory/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
34
inventory/mixins.py
Normal file
34
inventory/mixins.py
Normal file
@ -0,0 +1,34 @@
|
||||
from django import forms
|
||||
from django.utils.translation import get_language
|
||||
|
||||
|
||||
class AddClassMixin:
|
||||
"""
|
||||
Mixin for adding classes to a model.
|
||||
"""
|
||||
def add_class_to_fields(self):
|
||||
"""
|
||||
Adds the class to the fields of the model.
|
||||
:return: class names form-control or form-select
|
||||
"""
|
||||
for field_name, field in self.fields.items():
|
||||
if isinstance(field.widget, forms.Select):
|
||||
existing_classes = field.widget.attrs.get('class', '')
|
||||
field.widget.attrs['class'] = f"{existing_classes} form-select form-select-sm".strip()
|
||||
else:
|
||||
existing_classes = field.widget.attrs.get('class', '')
|
||||
field.widget.attrs['class'] = f"{existing_classes} form-control form-control-sm".strip()
|
||||
|
||||
|
||||
class LocalizedNameMixin:
|
||||
"""
|
||||
Mixin to provide a reusable get_localized_name method.
|
||||
"""
|
||||
def get_local_name(self):
|
||||
"""
|
||||
Returns the localized name based on the current language.
|
||||
"""
|
||||
if get_language() == 'ar':
|
||||
return getattr(self, 'arabic_name', None)
|
||||
return getattr(self, 'name', None)
|
||||
|
||||
503
inventory/models.py
Normal file
503
inventory/models.py
Normal file
@ -0,0 +1,503 @@
|
||||
from uuid import uuid4
|
||||
from django.db import models, transaction
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_ledger.models import (
|
||||
VendorModel,
|
||||
EntityModel,
|
||||
EntityUnitModel,
|
||||
ItemModel,
|
||||
AccountModel,
|
||||
ItemModelAbstract,
|
||||
UnitOfMeasureModel,
|
||||
CustomerModel,
|
||||
ItemModelQuerySet,
|
||||
|
||||
)
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from decimal import Decimal
|
||||
from django.utils.timezone import now
|
||||
from .mixins import LocalizedNameMixin
|
||||
|
||||
|
||||
class CarMake(models.Model, LocalizedNameMixin):
|
||||
id_car_make = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
logo = models.ImageField(_('logo'), upload_to='car_make', blank=True, null=True)
|
||||
is_sa_import = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Make"
|
||||
|
||||
|
||||
class CarModel(models.Model, LocalizedNameMixin):
|
||||
id_car_model = models.AutoField(primary_key=True)
|
||||
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column='id_car_make')
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Model"
|
||||
|
||||
|
||||
class CarSerie(models.Model, LocalizedNameMixin):
|
||||
id_car_serie = models.AutoField(primary_key=True)
|
||||
id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model')
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Series"
|
||||
|
||||
|
||||
class CarTrim(models.Model, LocalizedNameMixin):
|
||||
id_car_trim = models.AutoField(primary_key=True)
|
||||
id_car_serie = models.ForeignKey(CarSerie, models.DO_NOTHING, db_column='id_car_serie')
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
start_production_year = models.IntegerField(blank=True, null=True)
|
||||
end_production_year = models.IntegerField(blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Trim"
|
||||
|
||||
|
||||
class CarSpecification(models.Model, LocalizedNameMixin):
|
||||
id_car_specification = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=255)
|
||||
arabic_name = models.CharField(max_length=255)
|
||||
id_parent = models.ForeignKey('self', models.DO_NOTHING, db_column='id_parent', blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Specification"
|
||||
|
||||
|
||||
class CarSpecificationValue(models.Model):
|
||||
id_car_specification_value = models.AutoField(primary_key=True)
|
||||
id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column='id_car_trim')
|
||||
id_car_specification = models.ForeignKey(CarSpecification, models.DO_NOTHING, db_column='id_car_specification')
|
||||
value = models.CharField(max_length=500)
|
||||
unit = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.id_car_specification.name}: {self.value} {self.unit}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Specification Value"
|
||||
|
||||
|
||||
# Car Model
|
||||
class CarStatusChoices(models.TextChoices):
|
||||
AVAILABLE = 'available', _('Available')
|
||||
SOLD = 'sold', _('Sold')
|
||||
HOLD = 'hold', _('Hold')
|
||||
DAMAGED = 'damaged', _('Damaged')
|
||||
|
||||
|
||||
class CarStockTypeChoices(models.TextChoices):
|
||||
NEW = 'new', _('New')
|
||||
USED = 'used', _('Used')
|
||||
|
||||
|
||||
class Car(models.Model):
|
||||
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
||||
dealer = models.ForeignKey(
|
||||
"Dealer",
|
||||
models.DO_NOTHING,
|
||||
related_name='cars',
|
||||
verbose_name=_("Dealer")
|
||||
)
|
||||
|
||||
vendor = models.ForeignKey(
|
||||
"Vendor",
|
||||
models.DO_NOTHING,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='cars',
|
||||
verbose_name=_("Vendor")
|
||||
)
|
||||
id_car_make = models.ForeignKey(
|
||||
CarMake,
|
||||
models.DO_NOTHING,
|
||||
db_column='id_car_make',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Make")
|
||||
)
|
||||
id_car_model = models.ForeignKey(
|
||||
CarModel,
|
||||
models.DO_NOTHING,
|
||||
db_column='id_car_model',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Model")
|
||||
)
|
||||
year = models.IntegerField(verbose_name=_("Year"))
|
||||
id_car_serie = models.ForeignKey(
|
||||
CarSerie,
|
||||
models.DO_NOTHING,
|
||||
db_column='id_car_serie',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Series")
|
||||
)
|
||||
id_car_trim = models.ForeignKey(
|
||||
CarTrim,
|
||||
models.DO_NOTHING,
|
||||
db_column='id_car_trim',
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("Trim")
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=10,
|
||||
choices=CarStatusChoices.choices,
|
||||
default=CarStatusChoices.AVAILABLE,
|
||||
verbose_name=_("Status")
|
||||
)
|
||||
stock_type = models.CharField(
|
||||
max_length=10,
|
||||
choices=CarStockTypeChoices.choices,
|
||||
default=CarStockTypeChoices.NEW,
|
||||
verbose_name=_("Stock Type")
|
||||
)
|
||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
||||
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Car")
|
||||
verbose_name_plural = _("Cars")
|
||||
|
||||
def __str__(self):
|
||||
make = self.id_car_make.name if self.id_car_make else "Unknown Make"
|
||||
model = self.id_car_model.name if self.id_car_model else "Unknown Model"
|
||||
trim = self.id_car_trim.name if self.id_car_trim else "Unknown Trim"
|
||||
return f"{self.year} - {make} - {model} - {trim}"
|
||||
|
||||
def is_reserved(self):
|
||||
active_reservations = self.reservations.filter(reserved_until__gt=now())
|
||||
return active_reservations.exists()
|
||||
|
||||
@property
|
||||
def selling_price(self):
|
||||
finance = self.finances.first()
|
||||
return finance.selling_price if finance else Decimal('0.00')
|
||||
|
||||
@property
|
||||
def vat_amount(self):
|
||||
finance = self.finances.first()
|
||||
return finance.vat_amount if finance else Decimal('0.00')
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
finance = self.finances.first()
|
||||
return finance.total if finance else Decimal('0.00')
|
||||
|
||||
|
||||
class CarReservation(models.Model):
|
||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations')
|
||||
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
reserved_at = models.DateTimeField(auto_now_add=True)
|
||||
reserved_until = models.DateTimeField()
|
||||
|
||||
def is_active(self):
|
||||
return self.reserved_until > now()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('car', 'reserved_until')
|
||||
ordering = ['-reserved_at']
|
||||
|
||||
|
||||
# Car Finance Model
|
||||
class CarFinance(models.Model):
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='finances')
|
||||
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
|
||||
profit_margin = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_("Profit Margin"))
|
||||
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"), editable=False)
|
||||
vat_rate = models.DecimalField(max_digits=10, decimal_places=2, default=0.15, verbose_name=_("VAT Rate"))
|
||||
vat_amount = models.DecimalField(max_digits=12, decimal_places=2, verbose_name=_("VAT Amount"), editable=False)
|
||||
total = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Total Amount"), editable=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Car Financial Details")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
self.selling_price = self.cost_price * (1 + self.profit_margin)
|
||||
self.vat_amount = self.selling_price * self.vat_rate
|
||||
self.total = self.selling_price + self.vat_amount
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f"Car Financial Details for {self.car}: Selling Price {self.selling_price}"
|
||||
|
||||
|
||||
# Colors Model
|
||||
class CarColors(models.Model):
|
||||
class ColorType(models.TextChoices):
|
||||
EXTERIOR = 'exterior', _("Exterior")
|
||||
INTERIOR = 'interior', _("Interior")
|
||||
|
||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='colors')
|
||||
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
|
||||
color_type = models.CharField(
|
||||
max_length=10,
|
||||
choices=ColorType.choices,
|
||||
default=ColorType.EXTERIOR,
|
||||
verbose_name=_("Color Type")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Color")
|
||||
verbose_name_plural = _("Colors")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_color_type_display()} - {self.name} ({self.rgb})"
|
||||
|
||||
|
||||
# Custom Card Model
|
||||
class CustomCard(models.Model):
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
|
||||
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
|
||||
custom_date = models.DateField(verbose_name=_("Custom Date"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Custom Card")
|
||||
verbose_name_plural = _("Custom Cards")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.car} - {self.custom_number}"
|
||||
|
||||
|
||||
# Car Registration Model
|
||||
class CarRegistration(models.Model):
|
||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car"))
|
||||
plate_number = models.IntegerField(verbose_name=_("Plate Number"))
|
||||
text1 = models.CharField(max_length=1, verbose_name=_("Text 1"))
|
||||
text2 = models.CharField(max_length=1, verbose_name=_("Text 2"))
|
||||
text3 = models.CharField(max_length=1, verbose_name=_("Text 3"))
|
||||
registration_date = models.DateTimeField(verbose_name=_("Registration Date"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Registration")
|
||||
verbose_name_plural = _("Registrations")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.plate_number} - {self.text1} {self.text2} {self.text3}"
|
||||
|
||||
|
||||
# TimestampedModel Abstract Class
|
||||
class TimestampedModel(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("Updated"))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
# Dealer Model
|
||||
class Dealer(models.Model, LocalizedNameMixin):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='dealer')
|
||||
crn = models.CharField(max_length=10, verbose_name=_("Commercial Registration Number"))
|
||||
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
||||
logo = models.ImageField(upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Dealer")
|
||||
verbose_name_plural = _("Dealers")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# Vendor Model
|
||||
class Vendor(models.Model, LocalizedNameMixin):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='vendors')
|
||||
crn = models.CharField(max_length=10, unique=True, verbose_name=_("Commercial Registration Number"))
|
||||
vrn = models.CharField(max_length=15, unique=True, verbose_name=_("VAT Registration Number"))
|
||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
|
||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Vendor")
|
||||
verbose_name_plural = _("Vendors")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# Customer Model
|
||||
class Customer(models.Model):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='customers')
|
||||
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
|
||||
middle_name = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Middle Name"))
|
||||
last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
|
||||
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
||||
national_id = models.CharField(max_length=10, unique=True, verbose_name=_("National ID"))
|
||||
phone_number = PhoneNumberField(region='SA', unique=True, verbose_name=_("Phone Number"))
|
||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Customer")
|
||||
verbose_name_plural = _("Customers")
|
||||
|
||||
def __str__(self):
|
||||
middle = f" {self.middle_name}" if self.middle_name else ''
|
||||
return f"{self.first_name}{middle} {self.last_name}"
|
||||
|
||||
|
||||
# # Create Entity
|
||||
# @receiver(post_save, sender=Dealer)
|
||||
# def create_ledger_entity(sender, instance, created, **kwargs):
|
||||
# if created:
|
||||
# entity = EntityModel.objects.create(
|
||||
# name=instance.name,
|
||||
# admin=instance.user,
|
||||
# address_1=instance.address,
|
||||
# fy_start_month=1,
|
||||
# accrual_method=True,
|
||||
# depth=0,
|
||||
# )
|
||||
#
|
||||
# default_coa = entity.create_chart_of_accounts(assign_as_default=True,
|
||||
# commit=True,
|
||||
# coa_name=_("Chart of Accounts"))
|
||||
# if default_coa:
|
||||
# entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
|
||||
# print(f"Ledger entity created for Dealer: {instance.name}")
|
||||
#
|
||||
#
|
||||
# # # Create Vendor
|
||||
# @receiver(post_save, sender=Vendor)
|
||||
# def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||
#
|
||||
# if created:
|
||||
# entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||
#
|
||||
# vendor = VendorModel.objects.create(
|
||||
# entity_model=entity,
|
||||
# vendor_name=instance.name,
|
||||
# vendor_number=instance.crn,
|
||||
# address_1=instance.address,
|
||||
# phone=instance.phone_number,
|
||||
# tax_id_number=instance.vrn,
|
||||
# active=True,
|
||||
# hidden=False,
|
||||
# additional_info={
|
||||
# "arabic_name": instance.arabic_name,
|
||||
# "contact_person": instance.contact_person,
|
||||
# },
|
||||
# )
|
||||
#
|
||||
# print(f"VendorModel created for Vendor: {instance.name}")
|
||||
#
|
||||
#
|
||||
# @receiver(post_save, sender=Customer)
|
||||
# def create_customer(sender, instance, created, **kwargs):
|
||||
#
|
||||
# if created:
|
||||
# entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||
# name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
||||
#
|
||||
# customer = CustomerModel.objects.create(
|
||||
# entity_model=entity,
|
||||
# customer_name=name,
|
||||
# customer_number=instance.national_id,
|
||||
# address_1=instance.address,
|
||||
# phone=instance.phone_number,
|
||||
# email=instance.email,
|
||||
# sales_tax_rate=0.15,
|
||||
# )
|
||||
#
|
||||
# print(f"Customer created: {name}")
|
||||
#
|
||||
#
|
||||
# # Create Item
|
||||
# @receiver(post_save, sender=Car)
|
||||
# def create_item_model(sender, instance, created, **kwargs):
|
||||
# item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
|
||||
# uom_name = _("Car")
|
||||
# unit_abbr = _("C")
|
||||
#
|
||||
# uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
|
||||
# name=uom_name,
|
||||
# unit_abbr=unit_abbr
|
||||
# )
|
||||
#
|
||||
# if uom_created:
|
||||
# print(f"UOM created: {uom_name}")
|
||||
# else:
|
||||
# print(f"Using existing UOM: {uom_name}")
|
||||
#
|
||||
# entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||
#
|
||||
# inventory_account = AccountModel.objects.first()
|
||||
# cogs_account = AccountModel.objects.first()
|
||||
# earnings_account = AccountModel.objects.first()
|
||||
#
|
||||
# item = ItemModel.objects.create(
|
||||
# entity=entity,
|
||||
# uom=uom,
|
||||
# name=item_name,
|
||||
# item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
|
||||
# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
|
||||
# item_id=instance.vin,
|
||||
# sold_as_unit=True,
|
||||
# inventory_received=1.00,
|
||||
# inventory_received_value=0.00,
|
||||
# inventory_account=inventory_account,
|
||||
# for_inventory=True,
|
||||
# is_product_or_service=True,
|
||||
# cogs_account=cogs_account,
|
||||
# earnings_account=earnings_account,
|
||||
# is_active=True,
|
||||
# additional_info={
|
||||
# "remarks": instance.remarks,
|
||||
# "status": instance.status,
|
||||
# "stock_type": instance.stock_type,
|
||||
# "mileage": instance.mileage,
|
||||
# },
|
||||
# )
|
||||
#
|
||||
# print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
|
||||
#
|
||||
#
|
||||
# # update price - CarFinance
|
||||
# @receiver(post_save, sender=CarFinance)
|
||||
# def update_item_model_cost(sender, instance, created, **kwargs):
|
||||
#
|
||||
# ItemModel.objects.filter(item_id=instance.car.vin).update(
|
||||
# inventory_received_value=instance.cost_price,
|
||||
# default_amount=instance.cost_price,
|
||||
# )
|
||||
# print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
||||
257
inventory/services.py
Normal file
257
inventory/services.py
Normal file
@ -0,0 +1,257 @@
|
||||
"""
|
||||
Services module
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
from .utils import get_jwt_token
|
||||
from pyvin import VIN
|
||||
from django.conf import settings
|
||||
from openai import OpenAI
|
||||
from .models import Car
|
||||
|
||||
|
||||
def normalize_name(name):
|
||||
return name.replace(' ', '').replace('-', '').lower()
|
||||
|
||||
|
||||
def decode_vin_pyvin(vin):
|
||||
vehicle = VIN(vin)
|
||||
|
||||
data = {
|
||||
'Make': vehicle.Make,
|
||||
'Model': vehicle.Model,
|
||||
'ModelYear': vehicle.ModelYear,
|
||||
}
|
||||
print(data)
|
||||
return data
|
||||
|
||||
|
||||
# vehicle-info
|
||||
# c2729afb
|
||||
# 6d397471920412d672af1b8a02ca52ea
|
||||
|
||||
# option-info
|
||||
# 367974ed
|
||||
# 046b0412c1b4d3f8c39ec6375d6f3030
|
||||
def elm(vin):
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
'app-id': 'c2729afb',
|
||||
'app-key': '6d397471920412d672af1b8a02ca52ea',
|
||||
'client-id': '94142c27-2536-47e9-8e28-9ca7728b9442',
|
||||
}
|
||||
url = 'https://vehicle-maintenance.api.elm.sa/api/v1/vehicles/vehicle-info?vin='+vin
|
||||
|
||||
payload = {}
|
||||
response = requests.request("GET", url, headers=headers, data=payload)
|
||||
car_info = json.loads(response.text)
|
||||
return car_info
|
||||
|
||||
|
||||
def get_unique_colors(api_response):
|
||||
print(api_response)
|
||||
colors = api_response.get("data", [])
|
||||
print(colors)
|
||||
|
||||
unique_colors = {}
|
||||
for color in colors:
|
||||
color_name = color.get("name")
|
||||
rgb = color.get("rgb")
|
||||
if color_name not in unique_colors:
|
||||
unique_colors[color_name] = rgb
|
||||
|
||||
return [{"name": name, "rgb": rgb} for name, rgb in unique_colors.items()]
|
||||
|
||||
|
||||
def fetch_colors(car_data):
|
||||
|
||||
car_colors = {
|
||||
"data": [
|
||||
{"rgb": "192, 192, 192", "name": "Silver Metallic"},
|
||||
{"rgb": "0, 0, 0", "name": "Jet Black"},
|
||||
{"rgb": "255, 255, 255", "name": "Bright White"},
|
||||
{"rgb": "128, 128, 128", "name": "Graphite Gray"},
|
||||
{"rgb": "80, 80, 80", "name": "Gunmetal Gray"},
|
||||
{"rgb": "255, 0, 0", "name": "Racing Red"},
|
||||
{"rgb": "255, 69, 0", "name": "Inferno Orange"},
|
||||
{"rgb": "0, 0, 255", "name": "Deep Blue Pearl"},
|
||||
{"rgb": "75, 0, 130", "name": "Indigo Night"},
|
||||
{"rgb": "255, 215, 0", "name": "Solar Gold"},
|
||||
{"rgb": "34, 139, 34", "name": "Emerald Green"},
|
||||
{"rgb": "60, 179, 113", "name": "Forest Mist Green"},
|
||||
{"rgb": "255, 140, 0", "name": "Burnt Amber"},
|
||||
{"rgb": "160, 82, 45", "name": "Copper Brown"},
|
||||
{"rgb": "128, 0, 0", "name": "Crimson Maroon"},
|
||||
{"rgb": "245, 245, 220", "name": "Beige Champagne"},
|
||||
{"rgb": "169, 169, 169", "name": "Shadow Gray"},
|
||||
{"rgb": "255, 250, 205", "name": "Lemon Pearl"},
|
||||
{"rgb": "220, 220, 220", "name": "Platinum Silver"},
|
||||
{"rgb": "105, 105, 105", "name": "Charcoal Metallic"},
|
||||
{"rgb": "128, 0, 128", "name": "Royal Purple"},
|
||||
{"rgb": "210, 105, 30", "name": "Sunset Bronze"},
|
||||
{"rgb": "0, 128, 128", "name": "Teal Lagoon"},
|
||||
{"rgb": "72, 61, 139", "name": "Midnight Blue"},
|
||||
{"rgb": "255, 20, 147", "name": "Blazing Pink"},
|
||||
{"rgb": "192, 57, 43", "name": "Crimson Flame"},
|
||||
{"rgb": "255, 228, 196", "name": "Cream Sand"},
|
||||
{"rgb": "112, 128, 144", "name": "Steel Gray"},
|
||||
{"rgb": "0, 100, 0", "name": "Hunter Green"},
|
||||
{"rgb": "255, 223, 0", "name": "Bright Yellow"},
|
||||
{"rgb": "85, 107, 47", "name": "Olive Metallic"},
|
||||
{"rgb": "128, 128, 0", "name": "Mustard Gold"},
|
||||
{"rgb": "139, 69, 19", "name": "Cocoa Brown"},
|
||||
{"rgb": "255, 165, 0", "name": "Tangerine Flame"},
|
||||
{"rgb": "0, 0, 139", "name": "Navy Sapphire"},
|
||||
{"rgb": "70, 130, 180", "name": "Skyline Blue"},
|
||||
{"rgb": "220, 20, 60", "name": "Crimson Passion"},
|
||||
{"rgb": "189, 183, 107", "name": "Khaki Dune"},
|
||||
{"rgb": "50, 205, 50", "name": "Lime Essence"},
|
||||
{"rgb": "139, 0, 139", "name": "Amethyst Glow"},
|
||||
{"rgb": "255, 215, 180", "name": "Rosé Gold"},
|
||||
{"rgb": "46, 139, 87", "name": "Moss Green"},
|
||||
{"rgb": "72, 209, 204", "name": "Caribbean Aqua"},
|
||||
{"rgb": "255, 240, 245", "name": "Pearl Blush"},
|
||||
{"rgb": "244, 164, 96", "name": "Sierra Sunset"},
|
||||
{"rgb": "139, 0, 0", "name": "Crimson Ruby"},
|
||||
{"rgb": "192, 192, 192", "name": "Chrome"},
|
||||
{"rgb": "255, 105, 180", "name": "Hot Magenta"},
|
||||
{"rgb": "0, 255, 255", "name": "Ice Blue"},
|
||||
{"rgb": "184, 134, 11", "name": "Golden Bronze"},
|
||||
{"rgb": "128, 128, 64", "name": "Bronze Olive"},
|
||||
{"rgb": "245, 222, 179", "name": "Wheat Cream"}
|
||||
]
|
||||
}
|
||||
|
||||
jwt_token = get_jwt_token()
|
||||
if not jwt_token:
|
||||
print("Failed to retrieve JWT token.")
|
||||
return None
|
||||
|
||||
year = car_data['year']
|
||||
make = car_data['make']
|
||||
model = car_data['model']
|
||||
|
||||
url = "https://carapi.app/api/exterior-colors?year={}&make={}&model={}".format(year, make, model)
|
||||
params = {
|
||||
'limit': '1000',
|
||||
'sort': 'name',
|
||||
'direction': 'asc',
|
||||
'verbose': 'no',
|
||||
'all_trims': 'no',
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': f'Bearer {jwt_token}',
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
color_info = response.json()
|
||||
|
||||
if not color_info["data"] == []:
|
||||
return get_unique_colors(color_info)
|
||||
else:
|
||||
return car_colors["data"]
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching color information: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def fetch_interior_colors(car_data):
|
||||
|
||||
car_colors = {
|
||||
"data": [
|
||||
{"rgb": "0, 0, 0", "name": "Jet Black"},
|
||||
{"rgb": "54, 69, 79", "name": "Charcoal Black"},
|
||||
{"rgb": "255, 255, 255", "name": "Bright White"},
|
||||
{"rgb": "245, 245, 220", "name": "Off-White"},
|
||||
{"rgb": "210, 180, 140", "name": "Beige"},
|
||||
{"rgb": "205, 133, 63", "name": "Tan"},
|
||||
{"rgb": "128, 128, 128", "name": "Gray"},
|
||||
{"rgb": "80, 80, 80", "name": "Graphite Gray"},
|
||||
{"rgb": "112, 128, 144", "name": "Gunmetal Gray"},
|
||||
{"rgb": "192, 192, 192", "name": "Silver Metallic"},
|
||||
{"rgb": "139, 69, 19", "name": "Cognac Brown"},
|
||||
{"rgb": "149, 94, 55", "name": "Chestnut Brown"},
|
||||
{"rgb": "97, 63, 43", "name": "Espresso Brown"},
|
||||
{"rgb": "72, 40, 34", "name": "Dark Chocolate"},
|
||||
{"rgb": "139, 69, 19", "name": "Saddle Brown"},
|
||||
{"rgb": "124, 79, 58", "name": "Mocha"},
|
||||
{"rgb": "193, 154, 107", "name": "Camel Tan"},
|
||||
{"rgb": "128, 0, 32", "name": "Burgundy"},
|
||||
{"rgb": "128, 0, 0", "name": "Maroon"},
|
||||
{"rgb": "139, 0, 0", "name": "Deep Red"},
|
||||
{"rgb": "0, 0, 128", "name": "Navy Blue"},
|
||||
{"rgb": "65, 105, 225", "name": "Royal Blue"},
|
||||
{"rgb": "34, 139, 34", "name": "Forest Green"},
|
||||
{"rgb": "80, 200, 120", "name": "Emerald Green"},
|
||||
{"rgb": "255, 255, 240", "name": "Ivory"},
|
||||
{"rgb": "242, 242, 242", "name": "Pearl White"},
|
||||
{"rgb": "169, 169, 169", "name": "Stone Gray"},
|
||||
{"rgb": "112, 128, 144", "name": "Slate Gray"},
|
||||
{"rgb": "150, 111, 51", "name": "Ash Brown"},
|
||||
{"rgb": "128, 128, 0", "name": "Olive Green"},
|
||||
{"rgb": "25, 25, 112", "name": "Midnight Blue"},
|
||||
{"rgb": "72, 60, 50", "name": "Taupe"}
|
||||
]
|
||||
}
|
||||
|
||||
jwt_token = get_jwt_token()
|
||||
if not jwt_token:
|
||||
print("Failed to retrieve JWT token.")
|
||||
return None
|
||||
|
||||
year = car_data['year']
|
||||
make = car_data['make']
|
||||
model = car_data['model']
|
||||
|
||||
url = "https://carapi.app/api/interior-colors?year={}&make={}&model={}".format(year, make, model)
|
||||
params = {
|
||||
'limit': '100',
|
||||
'sort': 'name',
|
||||
'direction': 'asc',
|
||||
'verbose': 'no',
|
||||
'all_trims': 'no',
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': f'Bearer {jwt_token}',
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, params=params)
|
||||
color_info = response.json()
|
||||
|
||||
if not color_info["data"] == []:
|
||||
return get_unique_colors(color_info)
|
||||
else:
|
||||
return car_colors["data"]
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching color information: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def translate(content, *args, **kwargs):
|
||||
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
||||
completion = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a translation assistant that translates English to Arabic."},
|
||||
{"role": "user", "content": content}
|
||||
],
|
||||
temperature=0.3,
|
||||
)
|
||||
translation = completion.choices[0].message.content.strip()
|
||||
return translation
|
||||
|
||||
|
||||
def calculate_stock_value():
|
||||
cars = Car.objects.all()
|
||||
total_value = sum(car.selling_price for car in cars)
|
||||
return total_value
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user