import re import requests from django.conf import settings from django.db import models from django.urls import reverse from django.template.defaultfilters import slugify from django.utils.html import format_html from django_ckeditor_5.fields import CKEditor5Field from django.utils import timezone from django.utils.dateformat import DateFormat from filer.fields.folder import FilerFolderField from filer.fields.image import FilerImageField, FilerFileField from easy_thumbnails.files import get_thumbnailer from unidecode import unidecode from googletrans import Translator ReviewSource = ( ('1', 'Яндекс'), ('2', '2ГИС'), ('3', 'VL.ru'), ) TransportCategory = ( ('1', 'Япония'), ('2', 'Корея'), ) Currency = ( ('¥', 'Йены'), ('₩', 'Воны'), ) Import_type = ( ('ЭПТС', 'ЭПТС'), ('ГТД', 'ГТД'), ) class StaticPage(models.Model): title = models.CharField(verbose_name='Заголовок', max_length=255) content = CKEditor5Field(verbose_name='Контент', blank=True, null=True) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') class Meta: verbose_name = 'текстовый раздел' verbose_name_plural = 'текстовые разделы' def __str__(self): return self.title def get_absolute_url(self): return reverse('core:static_page', kwargs={'slug': self.slug}) def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if StaticPage.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class StaticBlock(models.Model): title = models.CharField(verbose_name='Заголовок', max_length=255) content = models.TextField(verbose_name='Контент', null=True, blank=True) class Meta: verbose_name = 'статический блок' verbose_name_plural = 'статические блоки' def __str__(self): return self.title class Category(models.Model): title = models.CharField(verbose_name='Заголовок', max_length=255) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'категория новости' verbose_name_plural = 'категории новостей' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Category.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class Article(models.Model): category = models.ForeignKey(Category, verbose_name='Категория', related_name='articles', blank=True, null=True, on_delete=models.SET_NULL) cover = FilerImageField(verbose_name='Обложка', null=True, on_delete=models.SET_NULL) title = models.CharField(verbose_name='Заголовок', max_length=255) content = CKEditor5Field(verbose_name='Контент') date = models.DateTimeField(verbose_name='Дата') is_active = models.BooleanField(verbose_name='Отображать на сайте?', default=True) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') class Meta: verbose_name = 'статья/новость' verbose_name_plural = 'Статьи/новости' ordering = ['-date'] def __str__(self): return self.title def get_thumbnail(self): image = '-' if self.cover: image = format_html('', get_thumbnailer(self.cover)['article_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Обложка' def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Article.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) @property def get_date(self): return DateFormat(timezone.make_naive(self.date)).format('j E Y') def get_absolute_url(self): return reverse('core:article_details', kwargs={'slug': self.slug}) class Question(models.Model): title = models.CharField(verbose_name='Вопрос', max_length=255) content = CKEditor5Field(verbose_name='Ответ') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) is_active = models.BooleanField(verbose_name='Отображать на сайте?', default=True) class Meta: verbose_name = 'FAQ' verbose_name_plural = 'FAQ' ordering = ('order',) def __str__(self): return self.title class Video(models.Model): title = models.CharField(verbose_name='Название', max_length=255) link = models.CharField(verbose_name='Ссылка', max_length=512) order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) is_active = models.BooleanField(verbose_name='Отображать на сайте?', default=True) on_main = models.BooleanField(verbose_name='На главной странице?', default=True) cover = models.CharField(verbose_name='Обложка', max_length=512, blank=True, null=True, help_text='Оставить пустым для автоматической генерации') player_link = models.CharField(verbose_name='Ссылка плеера', max_length=512, blank=True, null=True, help_text='Оставить пустым для автоматической генерации') class Meta: verbose_name = 'видео' verbose_name_plural = 'видео' ordering = ('order',) def __str__(self): return self.title def _extract_video_params(self): pattern = r"video(-?\d+)_(\d+)" match = re.search(pattern, self.link) if match: owner_id = match.group(1) video_id = match.group(2) return owner_id, video_id else: return None, None def _get_cover(self): owner_id, video_id = self._extract_video_params() cover = None player_link = None if owner_id and video_id: url = settings.VK_VIDEO_ENDPOINT data = { 'videos': f'{owner_id}_{video_id}', 'v': '5.199', 'access_token': settings.VK_TOKEN, } response = requests.get(url, params=data) try: cover = response.json().get('response').get('items')[0].get('image')[-1].get('url') player_link = response.json().get('response').get('items')[0].get('player') except: pass return cover, player_link def save(self, *args, **kwargs) -> None: if self.cover is None: cover, player = self._get_cover() if cover: self.cover = cover if player: self.player_link = player return super().save(*args, **kwargs) class Review(models.Model): name = models.CharField(verbose_name='Фамилия Имя', max_length=255) date = models.DateField(verbose_name='Дата') text = models.TextField(verbose_name='Отзыв') screen = FilerImageField(verbose_name='Скрин', null=True, blank=True, on_delete=models.SET_NULL) is_active = models.BooleanField(verbose_name='Активный', default=True) on_main = models.BooleanField(verbose_name='На главной странице?', default=True) source = models.CharField(verbose_name='Источник', blank=True, null=True, choices=ReviewSource, max_length=50) class Meta: verbose_name = 'отзыв' verbose_name_plural = 'отзывы' ordering = ('-date',) def __str__(self): return self.name def get_thumbnail(self): image = '-' if self.screen: image = format_html('', get_thumbnailer(self.screen)['review_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Скрин' @property def get_date(self): return DateFormat(self.date).format('j E Y') class TransportBrand(models.Model): title = models.CharField(verbose_name='Название', max_length=255) db_id = models.CharField(max_length=255) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'марка' verbose_name_plural = 'марки' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Category.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class TransportModel(models.Model): brand = models.ForeignKey(TransportBrand, verbose_name='Марка', related_name='transport_models', on_delete=models.CASCADE) title = models.CharField(verbose_name='Название', max_length=255) db_id = models.CharField(max_length=255) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) category = models.CharField(verbose_name='Категория', choices=TransportCategory, max_length=50, null=True) class Meta: verbose_name = 'модель' verbose_name_plural = 'модели' ordering = ('order',) def __str__(self): country = 'не определено' if self.category == '1': country = 'Япония' elif self.category == '2': country = 'Корея' return f'{self.title} ({country})' def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Category.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class MachineryBrand(models.Model): title = models.CharField(verbose_name='Название', max_length=255) db_id = models.CharField(max_length=255) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'марка (спец техника)' verbose_name_plural = 'марки (спец техника)' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Category.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class MachineryModel(models.Model): brand = models.ForeignKey(MachineryBrand, verbose_name='Марка', related_name='machinery_models', on_delete=models.CASCADE) title = models.CharField(verbose_name='Название', max_length=255) slug = models.CharField(verbose_name='Уникальный URL', max_length=512, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'модель (спец техника)' verbose_name_plural = 'модели (спец техника)' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if Category.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class TransportExperience(models.Model): category = models.CharField(verbose_name='Категория', choices=TransportCategory, max_length=50) cover = FilerImageField(verbose_name='Обложка', null=True, blank=True, on_delete=models.SET_NULL) slider = FilerFolderField(on_delete=models.CASCADE, verbose_name='Галерея', null=True, blank=True) currency = models.CharField(verbose_name='Валюта', choices=Currency, max_length=50) import_type = models.CharField(verbose_name='Ввоз', choices=Import_type, max_length=50, default='ЭПТС') title = models.CharField(verbose_name='Заголовок', max_length=255, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') brand = models.ForeignKey(TransportBrand, verbose_name='Марка', related_name='transport_experience', on_delete=models.CASCADE) transport_model = models.ForeignKey(TransportModel, verbose_name='Модель', related_name='transport_experience', on_delete=models.SET_NULL, null=True) lot = models.CharField(verbose_name="Лот", max_length=100, null=True, blank=True) auction = models.CharField(verbose_name="Аукцион", max_length=100, null=True, blank=True) year = models.PositiveIntegerField(verbose_name='Год', null=True) mileage = models.PositiveBigIntegerField(verbose_name='Пробег (км)', null=True, blank=True) car_body = models.CharField(verbose_name='Кузов', max_length=255, null=True, blank=True) rate = models.FloatField(verbose_name='Оценка', null=True, blank=True) volume = models.PositiveIntegerField(verbose_name='Объем двигателя (см3)', null=True, blank=True) power = models.PositiveIntegerField(verbose_name='Мощность (л.с.)', null=True, blank=True) drive = models.CharField(verbose_name='Привод', max_length=255, null=True, blank=True) transmission = models.CharField(verbose_name='Трансмиссия', max_length=255, null=True, blank=True) equipment = models.CharField(verbose_name='Комплектация', max_length=255, null=True, blank=True) comment = models.TextField(verbose_name='Комментарий', null=True, blank=True) auction_price = models.PositiveBigIntegerField(verbose_name='Цена на аукционе (валюта)') auction_price_rub = models.PositiveBigIntegerField(verbose_name='Цена на аукционе (руб.)') delivery_price_rub = models.PositiveBigIntegerField(verbose_name='Доставка (руб.)') tax_price_rub = models.PositiveBigIntegerField(verbose_name='Пошлина (руб.)') is_active = models.BooleanField(verbose_name='Активный', default=True) on_main = models.BooleanField(verbose_name='На главной странице?', default=True) order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'привезенное авто' verbose_name_plural = 'привезенные авто' ordering = ('order',) def __str__(self): if self.title: return self.title else: return self.brand.title def save(self, *args, **kwargs) -> None: if self.title is None: title = f'{self.brand.title} {self.transport_model.title}' if self.volume: eng_v = round(self.volume / 1000, 1) title += f', {eng_v} л.' if self.year: title += f', {self.year}' self.title = title return super().save(*args, **kwargs) def get_thumbnail(self): image = '-' if self.cover: image = format_html('', get_thumbnailer(self.cover)['experience_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Обложка' @property def full_title(self): model_name = self.transport_model.title brand_name = self.brand.title full_title = '' if brand_name: full_title += brand_name.capitalize() if model_name: full_title += f' {model_name.capitalize()}' if self.volume: eng_v = round(self.volume / 1000, 1) full_title += f', {eng_v} л.' if self.year: full_title += f', {self.year}' return full_title @property def short_description(self): description = '' if self.volume: description += f'{self.volume} cc' if self.power: description += f' / {self.power} л.с.' if self.transmission: description += f' / {self.transmission}' if self.mileage: description += f' / Пробег {self.mileage} км.' description = description.strip(' / ') return description @property def total_cost_rub(self): return self.auction_price_rub + self.tax_price_rub + self.delivery_price_rub @property def currency_price_readable(self): if self.auction_price_rub: formatted_price = f"{self.auction_price_rub:,}".replace(',', ' ') return f'{formatted_price} {self.currency}' return 'не указана' @property def rub_price_readable(self): if self.auction_price_rub: formatted_price = f"{self.auction_price_rub:,}".replace(',', ' ') return f'{formatted_price} ₽' return 'не указана' @property def get_absolute_url(self): if self.category == '1': return reverse('core:experience_japan_details', kwargs={'pk': self.id}) elif self.category == '2': return reverse('core:experience_korea_details', kwargs={'pk': self.id}) class MachineryExperience(models.Model): cover = FilerImageField(verbose_name='Обложка', null=True, blank=True, on_delete=models.SET_NULL) slider = FilerFolderField(on_delete=models.CASCADE, verbose_name='Галерея', null=True, blank=True) currency = models.CharField(verbose_name='Валюта', choices=Currency, max_length=50) import_type = models.CharField(verbose_name='Ввоз', choices=Import_type, max_length=50, default='ЭПТС') title = models.CharField(verbose_name='Заголовок', max_length=255, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') brand = models.ForeignKey(MachineryBrand, verbose_name='Марка', related_name='transport_experience', on_delete=models.CASCADE) transport_model = models.ForeignKey(MachineryModel, verbose_name='Модель', related_name='transport_experience', on_delete=models.CASCADE) lot = models.CharField(verbose_name="Лот", max_length=100, null=True, blank=True) auction = models.CharField(verbose_name="Аукцион", max_length=100, null=True, blank=True) year = models.PositiveIntegerField(verbose_name='Год', null=True) mileage = models.PositiveBigIntegerField(verbose_name='Пробег (км)', null=True, blank=True) car_body = models.CharField(verbose_name='Кузов', max_length=255, null=True, blank=True) rate = models.FloatField(verbose_name='Оценка', null=True, blank=True) volume = models.PositiveIntegerField(verbose_name='Объем двигателя (см3)', null=True, blank=True) power = models.PositiveIntegerField(verbose_name='Мощность (л.с.)', null=True, blank=True) drive = models.CharField(verbose_name='Привод', max_length=255, null=True, blank=True) transmission = models.CharField(verbose_name='Трансмиссия', max_length=255, null=True, blank=True) equipment = models.CharField(verbose_name='Комплектация', max_length=255, null=True, blank=True) comment = models.TextField(verbose_name='Комментарий', null=True, blank=True) auction_price = models.PositiveBigIntegerField(verbose_name='Цена на аукционе (валюта)') auction_price_rub = models.PositiveBigIntegerField(verbose_name='Цена на аукционе (руб.)') delivery_price_rub = models.PositiveBigIntegerField(verbose_name='Доставка (руб.)') tax_price_rub = models.PositiveBigIntegerField(verbose_name='Пошлина (руб.)') is_active = models.BooleanField(verbose_name='Активный', default=True) on_main = models.BooleanField(verbose_name='На главной странице?', default=True) order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'привезенная (спец техника)' verbose_name_plural = 'привезенные (спец техника)' ordering = ('order',) def __str__(self): if self.title: return self.title else: return self.brand.title def save(self, *args, **kwargs) -> None: if self.title is None: title = f'{self.brand.title} {self.transport_model.title}' if self.volume: title += f', {self.volume} cc' if self.year: title += f', {self.year}' self.title = title return super().save(*args, **kwargs) def get_thumbnail(self): image = '-' if self.cover: image = format_html('', get_thumbnailer(self.cover)['experience_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Обложка' @property def full_title(self): model_name = self.transport_model.title brand_name = self.brand.title full_title = '' if brand_name: full_title += brand_name.capitalize() if model_name: full_title += f' {model_name.capitalize()}' if self.volume: full_title += f', {self.volume} cc' if self.year: full_title += f', {self.year}' return full_title @property def short_description(self): description = '' if self.volume: description += f'{self.volume} cc' if self.power: description += f' / {self.power} л.с.' if self.transmission: description += f' / {self.transmission}' if self.mileage: description += f' / Пробег {self.mileage} км.' description = description.strip(' / ') return description @property def total_cost_rub(self): return self.auction_price_rub + self.tax_price_rub + self.delivery_price_rub @property def currency_price_readable(self): if self.auction_price_rub: formatted_price = f"{self.auction_price_rub:,}".replace(',', ' ') return f'{formatted_price} {self.currency}' return 'не указана' @property def rub_price_readable(self): if self.auction_price_rub: formatted_price = f"{self.auction_price_rub:,}".replace(',', ' ') return f'{formatted_price} ₽' return 'не указана' @property def get_absolute_url(self): return reverse('core:experience_machinery_details', kwargs={'pk': self.id}) class Rate(models.Model): slug = models.CharField(verbose_name='Валютная пара', max_length=15) rate = models.DecimalField(max_digits=10, decimal_places=3, default=0) class Meta: verbose_name = 'курс валют' verbose_name_plural = 'курсы валют' def __str__(self): return str(self.rate) class ColorFilter(models.Model): title = models.CharField(verbose_name='Цвет', max_length=100, unique=True) contain = models.CharField(verbose_name='Название цвета включает', max_length=100, unique=True) order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'фильтр цветы' verbose_name_plural = 'фильтры цветов' ordering = ('order',) def __str__(self): return self.title class MainLot(models.Model): category = models.CharField(max_length=5, default='1') db_id = models.CharField(max_length=100, unique=True) lot = models.CharField(max_length=100, null=True, blank=True) auction_type = models.CharField(max_length=100, null=True, blank=True) auction_date = models.CharField(max_length=100, null=True, blank=True) auction = models.CharField(max_length=100, null=True, blank=True) car_model = models.ForeignKey(TransportModel, related_name='main_lots', null=True, blank=True, on_delete=models.CASCADE) year = models.PositiveIntegerField(null=True, blank=True) town = models.CharField(max_length=100, null=True, blank=True) eng_v = models.PositiveIntegerField(null=True, blank=True) pw = models.PositiveIntegerField(null=True, blank=True) kuzov = models.CharField(max_length=100, null=True, blank=True) grade = models.CharField(max_length=2016, null=True, blank=True) color = models.CharField(max_length=100, null=True, blank=True) kpp = models.CharField(max_length=100, null=True, blank=True) kpp_type = models.CharField(max_length=100, null=True, blank=True) priv = models.CharField(max_length=100, null=True, blank=True) mileage = models.PositiveIntegerField(null=True, blank=True) equip = models.CharField(max_length=4096, null=True, blank=True) rate = models.CharField(max_length=100, null=True, blank=True) start = models.PositiveIntegerField(null=True, blank=True) finish = models.PositiveIntegerField(null=True, blank=True) status = models.CharField(max_length=100, null=True, blank=True) time = models.CharField(max_length=100, null=True, blank=True) avg_price = models.PositiveIntegerField(null=True, blank=True) avg_string = models.CharField(max_length=1000, null=True, blank=True) auction_list = models.CharField(max_length=200, null=True, blank=True) cover = models.CharField(max_length=200, null=True, blank=True) images = models.TextField(null=True, blank=True) serial = models.CharField(max_length=4096, null=True, blank=True) info = models.TextField(max_length=8192, null=True, blank=True) full_info = models.BooleanField(default=False) class Meta: verbose_name = 'лот (Япония)' verbose_name_plural = 'лоты (Япония)' @property def currency_price(self): if self.avg_price and self.avg_price > 0: return self.avg_price if self.start and self.start > 0: return self.start if self.finish and self.finish > 0: return self.finish return 'не указана' @property def currency_price_readable(self): price = self.currency_price if isinstance(price, int): formatted_price = f"{price:,}".replace(',', ' ') return f'{formatted_price} ¥' return price @property def rub_price(self): rate = Rate.objects.get(slug='rub-jpy') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) formatted_price = f"{price:,}".replace(',', ' ') else: return 'не указана' return f'{formatted_price} ₽' @property def rub_price_int(self): rate = Rate.objects.get(slug='rub-jpy') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) return price else: return False @property def full_title(self): if self.car_model: model_name = self.car_model.title brand_name = self.car_model.brand.title else: model_name = None brand_name = None full_title = '' if brand_name: full_title += brand_name.capitalize() if model_name: full_title += f' {model_name.capitalize()}' if self.eng_v: eng_v = round(self.eng_v / 1000, 1) full_title += f', {eng_v} л.' if self.year: full_title += f', {self.year}' return full_title @property def xml_description(self): eng_v = '' pw = '' transmission = '' if self.eng_v: eng_v = round(self.eng_v / 1000, 1) if self.pw: pw = f' ({self.pw} л.с.)' if self.kpp: transmission = f' {self.kpp}' return f'{eng_v}{transmission}{pw}' @property def get_absolute_url(self): brand = '' model = '' try: brand = self.car_model.brand.title.lower().replace(' ', '-') except: pass try: model = self.car_model.title.lower().replace(' ', '-') model = f'-{model}' except: pass if self.year: year = f'-{self.year}' else: year = '' url = f'{brand}{model}{year}-{self.db_id}' url = url.strip('-') return reverse('core:lot_japan', kwargs={'lot_id': url}) @property def get_absolute_truck_url(self): brand = '' model = '' try: brand = self.car_model.brand.title.lower().replace(' ', '-') except: pass try: model = self.car_model.title.lower().replace(' ', '-') model = f'-{model}' except: pass if self.year: year = f'-{self.year}' else: year = '' url = f'{brand}{model}{year}-{self.db_id}' url = url.strip('-') return reverse('core:lot_truck', kwargs={'lot_id': url}) class KoreaLot(models.Model): category = models.CharField(max_length=5, default='2') db_id = models.CharField(max_length=100, unique=True) lot = models.CharField(max_length=100, null=True, blank=True) auction_date = models.CharField(max_length=100, null=True, blank=True) auction = models.CharField(max_length=100, null=True, blank=True) car_model = models.ForeignKey(TransportModel, related_name='korea_lots', null=True, blank=True, on_delete=models.CASCADE) year = models.PositiveIntegerField(null=True, blank=True) eng_v = models.PositiveIntegerField(null=True, blank=True) pw = models.PositiveIntegerField(null=True, blank=True) kuzov = models.CharField(max_length=100, null=True, blank=True) grade = models.CharField(max_length=2016, null=True, blank=True) color = models.CharField(max_length=100, null=True, blank=True) kpp = models.CharField(max_length=100, null=True, blank=True) kpp_type = models.CharField(max_length=100, null=True, blank=True) priv = models.CharField(max_length=100, null=True, blank=True) mileage = models.PositiveIntegerField(null=True, blank=True) equip = models.CharField(max_length=4096, null=True, blank=True) rate = models.CharField(max_length=100, null=True, blank=True) start = models.PositiveIntegerField(null=True, blank=True) finish = models.PositiveIntegerField(null=True, blank=True) status = models.CharField(max_length=100, null=True, blank=True) time = models.CharField(max_length=100, null=True, blank=True) avg_price = models.PositiveIntegerField(null=True, blank=True) avg_string = models.CharField(max_length=1000, null=True, blank=True) cover = models.CharField(max_length=200, null=True, blank=True) images = models.TextField(null=True, blank=True) serial = models.CharField(max_length=4096, null=True, blank=True) info = models.TextField(max_length=8192, null=True, blank=True) info_rus = models.TextField(max_length=8192, null=True, blank=True) full_info = models.BooleanField(default=False) class Meta: verbose_name = 'лот (Корея)' verbose_name_plural = 'лоты (Корея)' @property def currency_price(self): if self.avg_price and self.avg_price > 0: return self.avg_price if self.start and self.start > 0: return self.start if self.finish and self.finish > 0: return self.finish return 'не указана' @property def rub_price_int(self): rate = Rate.objects.get(slug='rub-krw') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) return price else: return False @property def currency_price_readable(self): price = self.currency_price if isinstance(price, int): formatted_price = f"{price:,}".replace(',', ' ') return f'{formatted_price} ₩' return price @property def rub_price(self): rate = Rate.objects.get(slug='rub-krw') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) formatted_price = f"{price:,}".replace(',', ' ') else: return 'не указана' return f'{formatted_price} ₽' @property def full_title(self): try: model_name = self.car_model.title brand_name = self.car_model.brand.title except: model_name = None brand_name = None full_title = '' if brand_name: full_title += brand_name.capitalize() if model_name: full_title += f' {model_name.capitalize()}' if self.eng_v: eng_v = round(self.eng_v / 1000, 1) full_title += f', {eng_v} л.' if self.year: full_title += f', {self.year}' return full_title @property def xml_description(self): eng_v = '' pw = '' transmission = '' if self.eng_v: eng_v = round(self.eng_v / 1000, 1) if self.pw: pw = f' ({self.pw} л.с.)' if self.kpp: transmission = f' {self.kpp}' return f'{eng_v}{transmission}{pw}' @property def get_absolute_url(self): brand = '' model = '' try: brand = self.car_model.brand.title.lower().replace(' ', '-') except: pass try: model = self.car_model.title.lower().replace(' ', '-') model = f'-{model}' except: pass if self.year: year = f'-{self.year}' else: year = '' url = f'{brand}{model}{year}-{self.db_id}' url = url.strip('-') return reverse('core:lot_korea', kwargs={'lot_id': url}) def translate_to_rus(self): try: translator = Translator() text_rus = translator.translate(text=self.info, dest='ru', src='auto') if text_rus.text: return text_rus.text return False except: return False def save(self, *args, **kwargs) -> None: if self.info and self.info_rus is None: info_rus = self.translate_to_rus() if info_rus: self.info_rus = info_rus return super().save(*args, **kwargs) class MachineryLot(models.Model): db_id = models.CharField(max_length=100, unique=True) lot = models.CharField(max_length=100, null=True, blank=True) auction_date = models.CharField(max_length=100, null=True, blank=True) auction = models.CharField(max_length=100, null=True, blank=True) group = models.CharField(max_length=100, null=True, blank=True) category = models.CharField(max_length=100, null=True, blank=True) gg = models.CharField(max_length=100, null=True, blank=True) cc = models.CharField(max_length=100, null=True, blank=True) machinery_model = models.ForeignKey(MachineryModel, related_name='machinery_lots', null=True, blank=True, on_delete=models.CASCADE) year = models.PositiveIntegerField(null=True, blank=True) grade = models.CharField(max_length=2016, null=True, blank=True) kpp = models.CharField(max_length=100, null=True, blank=True) priv = models.CharField(max_length=100, null=True, blank=True) mileage = models.PositiveIntegerField(null=True, blank=True) rate = models.CharField(max_length=100, null=True, blank=True) volume = models.CharField(max_length=100, null=True, blank=True) price = models.PositiveIntegerField(null=True, blank=True) finish = models.PositiveIntegerField(null=True, blank=True) status = models.CharField(max_length=100, null=True, blank=True) cover = models.CharField(max_length=200, null=True, blank=True) auction_list = models.CharField(max_length=200, null=True, blank=True) images = models.TextField(null=True, blank=True) serial = models.CharField(max_length=4096, null=True, blank=True) info = models.TextField(max_length=8192, null=True, blank=True) info_rus = models.TextField(max_length=8192, null=True, blank=True) machinery_filter = models.ForeignKey('MachineryFilter', related_name='machinery_lots', null=True, blank=True, on_delete=models.CASCADE) full_info = models.BooleanField(default=False) class Meta: verbose_name = 'лот (спец техника)' verbose_name_plural = 'лоты (спец техника)' @property def currency_price(self): if self.finish and self.finish > 0: return self.finish if self.price and self.price > 0: return self.price return 'не указана' @property def rub_price_int(self): rate = Rate.objects.get(slug='rub-jpy') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) return price else: return False @property def currency_price_readable(self): price = self.currency_price if isinstance(price, int): formatted_price = f"{price:,}".replace(',', ' ') return f'{formatted_price} ¥' return price @property def rub_price(self): rate = Rate.objects.get(slug='rub-jpy') if isinstance(self.currency_price, int): price = int(self.currency_price / rate.rate) formatted_price = f"{price:,}".replace(',', ' ') else: return 'не указана' return f'{formatted_price} ₽' @property def full_title(self): try: model_name = self.machinery_model.title brand_name = self.machinery_model.brand.title except: model_name = None brand_name = None full_title = '' if brand_name: full_title += brand_name.capitalize() if model_name: full_title += f' {model_name.capitalize()}' if self.cc: full_title += f', {self.cc} cc' if self.year: full_title += f', {self.year}' return full_title @property def get_absolute_url(self): brand = '' model = '' try: brand = self.machinery_model.brand.title.lower().replace(' ', '-') except: pass try: model = self.machinery_model.title.lower().replace(' ', '-') model = f'-{model}' except: pass if self.year: year = f'-{self.year}' else: year = '' url = f'{brand}{model}{year}-{self.db_id}' url = url.strip('-') return reverse('core:lot_machinery', kwargs={'lot_id': url}) def translate_to_rus(self): try: translator = Translator() text_rus = translator.translate(text=self.info, dest='ru', src='auto') if text_rus.text: return text_rus.text return False except: return False def save(self, *args, **kwargs) -> None: if self.info and self.info_rus is None: info_rus = self.translate_to_rus() if info_rus: self.info_rus = info_rus return super().save(*args, **kwargs) class MachineryGroup(models.Model): title = models.CharField(verbose_name='Группа спец. техники', max_length=100, unique=True) class Meta: verbose_name = 'группа спец. техники' verbose_name_plural = 'группы спец. техники' def __str__(self): return self.title class MachineryFilter(models.Model): title = models.CharField(verbose_name='Название фильтра', max_length=100, unique=True) groups = models.ManyToManyField(MachineryGroup, verbose_name='Группы спец. техники', related_name='filters') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) on_main = models.BooleanField(verbose_name='Отображать на главной?', default=True) icon = FilerImageField(verbose_name='Иконка фильтра', null=True, on_delete=models.SET_NULL) class Meta: verbose_name = 'фильтр спец. техники' verbose_name_plural = 'фильтры спец.техники' ordering = ('order',) def __str__(self): return self.title class Banner(models.Model): title = models.CharField(verbose_name='Заголовок', max_length=100) banner = FilerImageField(verbose_name='Баннер', null=True, on_delete=models.SET_NULL) button_text = models.CharField(verbose_name='Текст кнопки', max_length=50) is_active = models.BooleanField(verbose_name='Отображать на сайте?', default=True) link = models.URLField() class Meta: verbose_name = 'баннер' verbose_name_plural = 'баннеры' def get_thumbnail(self): image = '-' if self.banner: image = format_html('', get_thumbnailer(self.banner)['banner_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Баннер' class Document(models.Model): title = models.TextField(verbose_name='Заголовок', null=True, blank=True) file = FilerFileField(verbose_name='Файл', related_name="document_files", on_delete=models.SET_NULL, null=True, blank=True) slug = models.SlugField(verbose_name='Уникальный URL', max_length=255, unique=True, null=True, blank=True) class Meta: verbose_name = 'документ' verbose_name_plural = 'документы' def __str__(self): return f'{self.title}' @property def get_file_size(self): if self.file: size_in_bytes = self.file.size size_in_kb = int(size_in_bytes / 1024) size_in_mb = int(size_in_bytes / (1024 * 1024)) if size_in_mb >= 1: return f'{size_in_mb}МБ' return f'{size_in_kb}КБ' return False class SeoAbove(models.Model): h1 = models.CharField(verbose_name='H1 заголовок', max_length=512, blank=True, null=True) meta_title = models.CharField(verbose_name='meta Title', max_length=512, blank=True, null=True) meta_description = models.CharField(verbose_name='meta Description', max_length=512, blank=True, null=True) meta_keywords = models.CharField(verbose_name='meta Keywords', max_length=512, blank=True, null=True) og_image = FilerImageField(verbose_name='meta og image', null=True, blank=True, on_delete=models.SET_NULL) slug = models.CharField(verbose_name='Слаг необходимой страницы', max_length=255, null=True, blank=True, unique=True, help_text='Без домена.') class Meta: verbose_name = 'принудительное SEO' verbose_name_plural = 'Принудительное SEO' def __str__(self): return f'{self.id} - {self.h1}' class FormRequest(models.Model): name = models.CharField(max_length=256, verbose_name="Имя") phone = models.CharField(max_length=256, verbose_name="Номер") comment = models.TextField(verbose_name="Комментарий", null=True, blank=True) file = models.FileField(verbose_name="Конкурентное предложение", null=True, blank=True, upload_to="uploads/") created_at = models.DateTimeField(verbose_name="Дата", auto_now_add=True) class Meta: verbose_name = 'заявка' verbose_name_plural = 'заявки' ordering = ('-created_at',) def __str__(self): return f'{self.name} - {self.phone}' class StockAuto(models.Model): cover = FilerImageField(verbose_name='Обложка', null=True, blank=True, on_delete=models.SET_NULL) slider = FilerFolderField(on_delete=models.CASCADE, verbose_name='Галерея', null=True, blank=True) city = models.ForeignKey('City', null=True, verbose_name='Город', blank=True, on_delete=models.SET_NULL, related_name='autos_in_stock') title = models.CharField(verbose_name='Заголовок', max_length=255, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') brand = models.ForeignKey(TransportBrand, verbose_name='Марка', related_name='autos_in_stock', on_delete=models.CASCADE) transport_model = models.ForeignKey(TransportModel, verbose_name='Модель', related_name='autos_in_stock', on_delete=models.SET_NULL, null=True) engine = models.ForeignKey('EngineType', null=True, verbose_name='Тип двигателя', blank=True, on_delete=models.SET_NULL, related_name='autos_in_stock') import_type = models.ForeignKey('ImportType', null=True, verbose_name='Ввоз', blank=True, on_delete=models.SET_NULL, related_name='autos_in_stock') year = models.PositiveIntegerField(verbose_name='Год', null=True) mileage = models.PositiveBigIntegerField(verbose_name='Пробег (км)', null=True, blank=True, help_text='Заполняется или поле "пробег" или поле "моточасы", при заполнении обоих будет отображен только пробег') moto_hours = models.PositiveBigIntegerField(verbose_name='Маточасы', null=True, blank=True, help_text='Заполняется или поле "пробег" или поле "моточасы", при заполнении обоих будет отображен только пробег') car_body = models.CharField(verbose_name='Кузов', max_length=255, null=True, blank=True) volume = models.PositiveIntegerField(verbose_name='Объем двигателя (см3)', null=True, blank=True) power = models.PositiveIntegerField(verbose_name='Мощность (л.с.)', null=True, blank=True) drive = models.CharField(verbose_name='Привод', max_length=255, null=True, blank=True) transmission = models.CharField(verbose_name='Трансмиссия', max_length=255, null=True, blank=True) equipment = models.CharField(verbose_name='Комплектация', max_length=255, null=True, blank=True) comment = models.TextField(verbose_name='Комментарий', null=True, blank=True) price = models.PositiveBigIntegerField(verbose_name='Цена (руб.)') auction_price = models.IntegerField(default=0) is_active = models.BooleanField(verbose_name='Активный', default=True) on_main = models.BooleanField(verbose_name='На главной странице?', default=True) order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'авто в наличии' verbose_name_plural = 'авто в наличии' ordering = ('order',) def __str__(self): if self.title: return self.title else: return self.brand.title def save(self, *args, **kwargs) -> None: if self.title is None: title = f'{self.brand.title} {self.transport_model.title}' if self.volume: eng_v = round(self.volume / 1000, 1) title += f', {eng_v} л.' if self.year: title += f', {self.year}' self.title = title return super().save(*args, **kwargs) def get_thumbnail(self): image = '-' if self.cover: image = format_html('', get_thumbnailer(self.cover)['experience_thumbnail'].url) return image get_thumbnail.allow_tags = True get_thumbnail.short_description = 'Обложка' @property def short_description(self): description = '' if self.power: description += f'{self.power} л.с.' if self.transmission: description += f' / {self.transmission}' if self.mileage: description += f' / Пробег {self.mileage} км.' elif self.moto_hours: description += f' / Моточасы: {self.mileage} мч.' description = description.strip(' / ') return description @property def readable_price(self): return f"{self.price:,}".replace(',', ' ') @property def get_absolute_url(self): brand = '' model = '' try: brand = self.brand.title.lower().replace(' ', '-') except: pass try: model = self.transport_model.title.lower().replace(' ', '-') model = f'-{model}' except: pass if self.year: year = f'-{self.year}' else: year = '' url = f'{brand}{model}{year}-{self.id}' url = url.strip('-') return reverse('core:stock_auto_details', kwargs={'lot_id': url}) class City(models.Model): title = models.CharField(verbose_name='Город/регион', max_length=128, unique=True) slug = models.CharField(verbose_name='Уникальный URL', max_length=128, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'город/регион' verbose_name_plural = 'города/регионы' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if StaticPage.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class Contact(models.Model): phone = models.CharField(max_length=64) bitrix_id = models.IntegerField() def __str__(self): return self.phone class Lead(models.Model): contact = models.ForeignKey(Contact, on_delete=models.CASCADE) bitrix_id = models.IntegerField() def __str__(self): return str(self.bitrix_id) class ImportType(models.Model): title = models.CharField(verbose_name='Ввоз', max_length=128, unique=True) slug = models.CharField(verbose_name='Уникальный URL', max_length=128, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'тип ввоза' verbose_name_plural = 'типы ввоза' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if StaticPage.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class EngineType(models.Model): title = models.CharField(verbose_name='Двигатель', max_length=128, unique=True) slug = models.CharField(verbose_name='Уникальный URL', max_length=128, null=True, blank=True, help_text='Оставить пустым для автоматической генерации') order = models.PositiveIntegerField(verbose_name='Порядок', default=0, blank=False, null=False) class Meta: verbose_name = 'тип двигателя' verbose_name_plural = 'типы двигателей' ordering = ('order',) def __str__(self): return self.title def save(self, *args, **kwargs) -> None: if self.slug is None: slug = f'{slugify(unidecode(self.title))}' if StaticPage.objects.filter(slug=slug).exists(): slug = f'{slugify(unidecode(self.title))}-{self.id}' self.slug = slug return super().save(*args, **kwargs) class BitrixEmployee(models.Model): bitrix_id = models.IntegerField(verbose_name='ID пользователя в bitrix', unique=True) active = models.BooleanField(verbose_name='Активен?', default=True) updated_at = models.DateTimeField(auto_now=True) class Meta: verbose_name = 'сотрудник Bitrix' verbose_name_plural = 'сотрудники Bitrix' ordering = ('updated_at',) def __str__(self): return str(self.bitrix_id)