feat: add empty_weigh and full_weight fields for auto and mechanic rvalution models #142
@@ -1,33 +0,0 @@
|
|||||||
from datetime import date
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
CBU_URL = "https://cbu.uz/oz/arkhiv-kursov-valyut/json/{code}/{date}/"
|
|
||||||
TIMEOUT_SECONDS = 5
|
|
||||||
CURRENCY_CODES = ("USD", "EUR", "RUB")
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_rates(target_date):
|
|
||||||
"""CBU.uz dan berilgan sanaga oid USD, EUR, RUB kurslarini olish.
|
|
||||||
|
|
||||||
Tarmoq xatosi yoki notogri javob bolsa bosh dict qaytadi.
|
|
||||||
"""
|
|
||||||
if target_date is None:
|
|
||||||
target_date = date.today()
|
|
||||||
date_str = target_date.strftime("%Y-%m-%d")
|
|
||||||
rates = {}
|
|
||||||
for code in CURRENCY_CODES:
|
|
||||||
try:
|
|
||||||
resp = requests.get(
|
|
||||||
CBU_URL.format(code=code, date=date_str),
|
|
||||||
timeout=TIMEOUT_SECONDS,
|
|
||||||
)
|
|
||||||
resp.raise_for_status()
|
|
||||||
data = resp.json()
|
|
||||||
if isinstance(data, list) and data:
|
|
||||||
rate_value = data[0].get("Rate")
|
|
||||||
if rate_value:
|
|
||||||
rates[code] = rate_value
|
|
||||||
except (requests.RequestException, ValueError):
|
|
||||||
continue
|
|
||||||
return rates
|
|
||||||
@@ -12,7 +12,7 @@ from weasyprint import HTML
|
|||||||
from core.apps.evaluation.models import AutoEvaluationModel
|
from core.apps.evaluation.models import AutoEvaluationModel
|
||||||
from core.apps.evaluation.choices.auto import ObjectOwnerType
|
from core.apps.evaluation.choices.auto import ObjectOwnerType
|
||||||
from core.apps.documents.serializers.contract import ContractPDFRequestSerializer
|
from core.apps.documents.serializers.contract import ContractPDFRequestSerializer
|
||||||
from core.apps.documents.services.cbu_rates import fetch_rates
|
from core.services import CurrencyService
|
||||||
|
|
||||||
|
|
||||||
UZ_MONTHS = {
|
UZ_MONTHS = {
|
||||||
@@ -301,8 +301,8 @@ class ValuationReportPDFView(APIView):
|
|||||||
"tech_passport": tech_passport_value,
|
"tech_passport": tech_passport_value,
|
||||||
"fuel_type": fuel_type_value,
|
"fuel_type": fuel_type_value,
|
||||||
"engine_power": "",
|
"engine_power": "",
|
||||||
"full_weight": "",
|
"full_weight": auto.full_weight if auto.full_weight is not None else "",
|
||||||
"empty_weight": "",
|
"empty_weight": auto.empty_weight if auto.empty_weight is not None else "",
|
||||||
}
|
}
|
||||||
|
|
||||||
def _customer_context(self, user):
|
def _customer_context(self, user):
|
||||||
@@ -365,7 +365,7 @@ class ValuationReportPDFView(APIView):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _rates_context(self, target_date):
|
def _rates_context(self, target_date):
|
||||||
rates = fetch_rates(target_date)
|
rates = CurrencyService.get_rates(target_date)
|
||||||
return {
|
return {
|
||||||
"rur": rates.get("RUB", ""),
|
"rur": rates.get("RUB", ""),
|
||||||
"usd": rates.get("USD", ""),
|
"usd": rates.get("USD", ""),
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("evaluation", "0046_mechanicauto_fields_and_multi_tex_passport"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="autoevaluationmodel",
|
||||||
|
name="full_weight",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="full weight"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="autoevaluationmodel",
|
||||||
|
name="empty_weight",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="empty weight"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="mechanicautoevaluationmodel",
|
||||||
|
name="full_weight",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="full weight"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="mechanicautoevaluationmodel",
|
||||||
|
name="empty_weight",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True, verbose_name="empty weight"),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -260,6 +260,16 @@ class AutoEvaluationModel(AbstractBaseModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
full_weight = models.PositiveIntegerField(
|
||||||
|
verbose_name=_("full weight"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
empty_weight = models.PositiveIntegerField(
|
||||||
|
verbose_name=_("empty weight"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
|
||||||
# ── Natija ───────────────────────────────────────────────────────
|
# ── Natija ───────────────────────────────────────────────────────
|
||||||
rating_goal = models.CharField(
|
rating_goal = models.CharField(
|
||||||
|
|||||||
@@ -256,6 +256,16 @@ class MechanicAutoEvaluationModel(AbstractBaseModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
full_weight = models.PositiveIntegerField(
|
||||||
|
verbose_name=_("full weight"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
empty_weight = models.PositiveIntegerField(
|
||||||
|
verbose_name=_("empty weight"),
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
|
||||||
# ── Natija ───────────────────────────────────────────────────────
|
# ── Natija ───────────────────────────────────────────────────────
|
||||||
rating_goal = models.CharField(
|
rating_goal = models.CharField(
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class BaseAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"car_number",
|
"car_number",
|
||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -214,6 +216,8 @@ class UpdateAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -361,6 +365,8 @@ class CreateAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -493,6 +499,8 @@ class AutoEvaluationModelSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class BaseMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"car_number",
|
"car_number",
|
||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -213,6 +215,8 @@ class UpdateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -363,6 +367,8 @@ class CreateMechanicAutoevaluationSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
@@ -480,6 +486,8 @@ class MechanicAutoEvaluationModelSerializer(serializers.ModelSerializer):
|
|||||||
"manufacture_year",
|
"manufacture_year",
|
||||||
"car_dvigatel_number",
|
"car_dvigatel_number",
|
||||||
"car_color",
|
"car_color",
|
||||||
|
"full_weight",
|
||||||
|
"empty_weight",
|
||||||
"distance_covered",
|
"distance_covered",
|
||||||
"car_position",
|
"car_position",
|
||||||
"body_type",
|
"body_type",
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ from .sms import * # noqa
|
|||||||
from .user import * # noqa
|
from .user import * # noqa
|
||||||
from .didox import * # noqa
|
from .didox import * # noqa
|
||||||
from .tech_passport import * # noqa
|
from .tech_passport import * # noqa
|
||||||
|
from .currency import * # noqa
|
||||||
33
core/services/currency.py
Normal file
33
core/services/currency.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from datetime import date
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CurrencyService:
|
||||||
|
BASE_URL = "https://cbu.uz/oz/arkhiv-kursov-valyut/json"
|
||||||
|
CODES = ("USD", "EUR", "RUB")
|
||||||
|
TIMEOUT_SECONDS = 5
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_rates(cls, target_date=None) -> dict:
|
||||||
|
if target_date is None:
|
||||||
|
target_date = date.today()
|
||||||
|
date_str = target_date.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
rates = {}
|
||||||
|
for code in cls.CODES:
|
||||||
|
url = f"{cls.BASE_URL}/{code}/{date_str}/"
|
||||||
|
try:
|
||||||
|
response = requests.get(url, timeout=cls.TIMEOUT_SECONDS)
|
||||||
|
response.raise_for_status()
|
||||||
|
payload = response.json()
|
||||||
|
if isinstance(payload, list) and payload:
|
||||||
|
rate_value = payload[0].get("Rate")
|
||||||
|
if rate_value:
|
||||||
|
rates[code] = rate_value
|
||||||
|
except (requests.RequestException, ValueError) as e:
|
||||||
|
logger.exception(f"CBU API error for {code}: {e}")
|
||||||
|
return rates
|
||||||
58
task.txt
58
task.txt
@@ -1,41 +1,17 @@
|
|||||||
object_type => Bu hozirda select. O’shanga value qo’shish kerak: bus, moto
|
#Done 1. Valyutalarni kursini olish uchun API topib, malumotlarni pdfga yozish kerak
|
||||||
|
2. Foydalanuvchi ma'lumotlarni jshir orqali Didox apidan olib uni saqlab, keyin pdfga yozish kerak
|
||||||
|
3. Bajaruvchi ma'lumotlarni qoshish, tahrishlash uchun model yozishimiz kerak, va uni pdfga yozish kerak
|
||||||
|
4. Son korinishidan yozuv korinishiga o'tkazadigan funksiya yozish kerak, misol uchun 30 000 000 => "o'ttiz million"
|
||||||
car_position => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman
|
5. Transport ma'lumotlarni tex passport orqali API dan olib uni saqlab, keyin pdfga yozish kerak
|
||||||
|
6. Obyektning textnik xolatini serializer orqali tanlaydigan qilish kerak va html filedagi yozilgan tabledagi malumotlarga quyidagicha ozgartirish kerak:
|
||||||
|
Har bir agregat bo'yicha ko'rik ro'yxati:
|
||||||
|
- Shinalar ko'rigi: tanlangan xolat
|
||||||
distance_covered => Qo’shish kerak. Number. Bosib o’tilgan masofasi
|
- Dvigatel ishlashini ko'rik: tanlangan xolat
|
||||||
|
- Yurish qismining ko'rigi: tanlangan xolat
|
||||||
|
- Transmissiya ko'rigi: tanlangan xolat
|
||||||
|
- Kuzov ko'rigi: tanlangan xolat
|
||||||
body_type => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman
|
7. Baholanishi kerak bolgan mashinaning yangisini malumotlarni kerak. Shuning uchun yangi model ochib, kerakli malumotlarni toldiradigan qilish kerak
|
||||||
|
8. Eskirish koeffitsiyentlarini belgilash yani qanchadan qanchagacha necha foiz olishini yozish uchun model kerak
|
||||||
|
9. Solishtirma tahlil jadvali uchun analoglarni olib kelish kerak uzxariddan
|
||||||
|
10. Solishtirma tahlil jadvali uchun analoglar topilmasa, analoglarni qolda qoshadigan qilish kerak, keyin analoglar bilan birga rasmlar ham yuklay olishi kerak
|
||||||
fuel_type => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman
|
11. QR code generate qilib pdfning har bir sahifasining ong tomon pastki qismiga joylashtirish kerak va https://{domain}/q/auto-basic/:id manashu urlga redirect qilishi kerak
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
state_car => Qo’shish kerak. Bu hozirda select. /api/v1/reference-item/ api’dan value yuboraman
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
car_type => manashu field ni olib tashlash kerak
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tex_passport_file => multiple qilish kerak
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
assessment_task_type => Baholash vazifasi. Qo’shish kerak. Select bo’ladi. /api/v1/reference-item/ api’dan value yuboraman
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object_owner_residence => Obyekt egasi yashash joyi. Qo’shish kerak. string bo’ladi.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
manashu fieldlarni detail apidagi serializerga qoshish kerak auto-evaluationda ham va mechnic-auto-evaluationda ham
|
|
||||||
|
|||||||
Reference in New Issue
Block a user