add: перенесены и расширены специфичные для проекта настройки-константы в constants.py и добавлены миграции

This commit is contained in:
2026-10-11 23:39:22 +03:00
parent d826907ad5
commit 6e3b0d3eb4
4 changed files with 371 additions and 161 deletions
@@ -0,0 +1,18 @@
# Generated by Django 6.0.5 on 2026-10-11 18:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('frontend', '0005_remove_tboffer_s_offer_skip32_and_more'),
]
operations = [
migrations.AlterField(
model_name='tbarticle',
name='l_article_type',
field=models.CharField(blank=True, choices=[('artist', 'Artist: артист, группа или бренд'), ('style', 'Style: музыкальный стиль'), ('item', 'Item: Альбом, релиз или товар (кассета, hifi, аксессуар)'), ('label', 'Label: Лейбл, издатель или компания'), ('offer', 'Offer: конкретное предложение от продавца'), ('seller', 'Seller: продавец или магазин'), ('blog', 'Blog: Новость или блог'), ('action', 'Action: Спецпредложение, акция, распродажа и т.п.'), ('adv', 'Adv: Реклама или баннер'), ('HUB', 'HUB: СТАТЬЯ-ХАБ (использует DSL для отображения контента)'), ('info', 'Info: Текстовый контент (для FAQ, условий конфиденциальности, правил/соглашений и т.д.)'), ('--', '?¿?: Другое')], db_index=True, default='--', max_length=7, verbose_name='Тип статьи'),
),
]
+37 -107
View File
@@ -172,15 +172,34 @@ from filer.fields.image import FilerImageField
from filer.fields.file import FilerFileField from filer.fields.file import FilerFileField
from frontend.utils import make_slug, update_synonyms_in_metadata, create_or_get_related_article from frontend.utils import make_slug, update_synonyms_in_metadata, create_or_get_related_article
from frontend.utils_validators import validate_and_raise_for_duplicates from frontend.utils_validators import validate_and_raise_for_duplicates
from lpon_site.constants import (
KEY_IMAGE_TYPE,
VALUE_IMAGE_ABSTRACT,
KEY_IMAGE_FROM,
VALUE_IMAGE_FROM_USER,
KEY_IMAGE_URL,
KEY_IMAGE_NOTE,
KEY_OFFER_NOTE,
KEY_OFFER_ALL_MEDIA,
get_image_metadata_default,
get_offer_metadata_default,
get_item_metadata_default,
ArticleType,
SellerType,
Currency,
Condition,
Format,
SourceType,
MediaType,
Market,
TapeType,
MediaLength,
ShellColor,
WriteProtectTabs,
InlayCondition,
Packaging,
)
from lpon_site.settings import ( from lpon_site.settings import (
KEY_IMAGE_TYPE, # Ключ, о том, какой тип изображения (реальное или абстрактное)
VALUE_IMAGE_ABSTRACT, # Реальная картинка (актуально, для фото товаров в TbOffer)
KEY_IMAGE_FROM, # Ключ, о том, как получено изображение
VALUE_IMAGE_FROM_USER, # Картинка загружена пользователем через админку
KEY_IMAGE_URL, # Ключ, о том, откуда получена картинка (URL источника)
KEY_IMAGE_NOTE, # Ключ, для заметок о картинке (например, "обложка", "задник", "вкладка" и т.д.)
KEY_OFFER_NOTE, # Ключ, для заметок о коммерческом предложении (например, "замят угол конверта" и т.д.)
KEY_OFFER_ALL_MEDIA, # Ключ, для хранения информации о том, какие носители входят в оффер (например, CD+2LP)
OFFER_HASHIDS_SALT, # Соль для криптографического кодирования ID оферов OFFER_HASHIDS_SALT, # Соль для криптографического кодирования ID оферов
OFFER_HASHIDS_MIN_LENGTH, # Минимальная длина кода OFFER_HASHIDS_MIN_LENGTH, # Минимальная длина кода
) )
@@ -188,34 +207,6 @@ from lpon_site.settings import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# ============================================================================
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ДЛЯ DEFAULTS В МОДЕЛЯХ
# ============================================================================
def get_image_metadata_default():
"""
Возвращает значение по умолчанию для j_img_metadata.
Используется как callable default в Django моделях (Django не может сериализовать lambda).
"""
return {
KEY_IMAGE_TYPE: VALUE_IMAGE_ABSTRACT,
KEY_IMAGE_FROM: VALUE_IMAGE_FROM_USER,
KEY_IMAGE_URL: None,
KEY_IMAGE_NOTE: '???',
}
def get_offer_metadata_default():
"""
Возвращает значение по умолчанию для j_offer_metadata.
Используется как callable default в Django моделях (Django не может сериализовать lambda).
"""
return {
KEY_OFFER_NOTE: '',
KEY_OFFER_ALL_MEDIA: [],
}
# ============================================================================ # ============================================================================
# МЕТАДАННЫЕ ИЗОБРАЖЕНИЙ # МЕТАДАННЫЕ ИЗОБРАЖЕНИЙ
# ============================================================================ # ============================================================================
@@ -317,30 +308,8 @@ class TbArticle(models.Model):
• Соединитель для Artist, Item, Label, Seller, Style (все 1:1→article) • Соединитель для Artist, Item, Label, Seller, Style (все 1:1→article)
• Хранит текстовый контент, SEO, изображения справочников • Хранит текстовый контент, SEO, изображения справочников
""" """
class ArticleType(models.TextChoices): # Алиас на перечисление типов статей из constants.py (для обратной совместимости)
""" ArticleType = ArticleType
Типы статей и контентных разделов.
ВАЖНО ДЛЯ РОУТИНГА И URL:
Значение каждого элемента (например, 'info', 'blog', 'artist', 'item' и т.д.)
используется напрямую как первый сегмент канонического URL-пути статьи:
/<l_article_type>/<slug>/ (например: /info/privacy-policy/, /blog/new-release-2026-september/)
Исключения:
- HUB ('HUB'): статьи-хабы публикуются напрямую в корне сайта (/<slug>/).
"""
ARTIST = 'artist', 'Artist: артист, группа или бренд'
STYLE = 'style', 'Style: музыкальный стиль'
ITEM = 'item', 'Item: Альбом, релиз или товар (кассета, hifi, аксессуар)'
LABEL = 'label', 'Label: Лейбл, издатель или компания'
OFFER = 'offer', 'Offer: конкретное предложение от продавца'
SELLER = 'seller', 'Seller: продавец или магазин'
BLOG = 'blog', 'Blog: Новость или блог'
ACTION = 'action', 'Action: Спецпредложение, акция, распродажа и т.п.'
ADV = 'adv', 'Adv: Реклама или баннер'
HUB = 'HUB', 'HUB: СТАТЬЯ-ХАБ (использует DSL для отображения контента)'
INFO = 'info', 'Info: Текстовый контент (для FAQ, условий конфиденциальности, правил/соглашений и т.д.)'
OTHER = '--', '?¿?: Другое'
s_article_title = models.CharField( s_article_title = models.CharField(
max_length=255, max_length=255,
@@ -1081,25 +1050,9 @@ class TbSeller(models.Model):
• save(): создаёт связанную статью автоматически (синонимы не используются) • save(): создаёт связанную статью автоматически (синонимы не используются)
- Если статья не привязана: создаёт новую через create_or_get_related_article() - Если статья не привязана: создаёт новую через create_or_get_related_article()
""" """
class SellerType(models.TextChoices): # Алиасы на перечисления из constants.py (для обратной совместимости)
SELLER = 'seller', 'Продавец' SellerType = SellerType
LABEL = 'label', 'Лейбл (издатель)' Currency = Currency
DIY = 'diy', 'Самиздат группы'
CROWD = 'crowd', 'Краудфандинг'
OTHER = '???', 'Другое'
class Currency(models.TextChoices):
RUB = 'rub', 'RUB: российский рубль'
USD = 'usd', 'USD: американский доллар'
EUR = 'eur', 'EUR: евро'
AMD = 'amd', 'AMD: армянских драм'
TRY = 'try', 'TRY: турецкая лира'
JPY = 'jpy', 'JPY: японская иена'
GBP = 'gbp', 'GBP: британский фунт'
CNY = 'cny', 'CNY: китайский юань'
BYN = 'byn', 'BYN: белорусский рубль'
TON = 'ton', 'TON: криптовалюта TON'
OTHER = '??', 'Other'
# Используем SmallAutoField для оптимизации (макс ~32k) # Используем SmallAutoField для оптимизации (макс ~32k)
# Продавцов обычно до 1000-10000, поэтому достаточно # Продавцов обычно до 1000-10000, поэтому достаточно
@@ -1215,29 +1168,9 @@ class TbOffer(models.Model):
В админке M2M выглядит как список картинок с сортировкой и метаданными. В админке M2M выглядит как список картинок с сортировкой и метаданными.
""" """
class Condition(models.TextChoices): # Алиасы на перечисления из constants.py (для обратной совместимости)
S = 's', 'Still Sealed (новое, запечатано)' Condition = Condition
M = 'm', 'Mint (новое, распакованное)' Format = Format
NM = 'nm', 'Near Mint (почти новое)'
VG = 'vg', 'Very Good (очень хорошее)'
G = 'g', 'Good (хорошее)'
F = 'f', 'Fair (удовлетворительное)'
P = 'p', 'Poor (плохое)'
OTHER = '??', 'Other'
class Format(models.TextChoices):
LP = 'lp', 'Vinyl Long-Play (12")'
EP = 'ep', 'Vinyl Extended-Play (12", 10", 7")'
V45 = '45', 'Vinyl 7" (45 rpm)'
CD = 'cd', 'Compact Disc'
LD = 'ld', 'LaserDisc'
REC_MD = 'md', 'MiniDisc Record'
USE_MD = 'ms', 'Used MiniDisc (для записи)'
REC_CS = 'cs', 'Cassette Record'
USE_CS = 'uc', 'Used Cassette (для записи)'
REC_RR = 'tp', 'Tape Reel Record'
USE_RR = 'ur', 'Used Tape Reel (для записи)'
OTHER = '??', 'Other'
s_offer = models.CharField( s_offer = models.CharField(
max_length=128, max_length=128,
@@ -1535,11 +1468,8 @@ class TbSource(models.Model):
МЕТОДЫ: МЕТОДЫ:
• Нет переопределённых методов save(). Используется стандартное поведение Django. • Нет переопределённых методов save(). Используется стандартное поведение Django.
""" """
class SourceType(models.TextChoices): # Алиас на перечисление из constants.py (для обратной совместимости)
EXCEL = 'excel', 'Excel-файл от продавца или издателя' SourceType = SourceType
CSV = 'csv', 'CSV-файл от продавца или издателя'
URL = 'url', 'URL страницы с данными (например, HTML-страница с каталогом товаров)'
OTHER = '??', 'Другое (включая ручной ввод)'
# Используем SmallAutoField для оптимизации (макс ~32k) # Используем SmallAutoField для оптимизации (макс ~32k)
# Источников обычно до 1000, достаточно # Источников обычно до 1000, достаточно
+313
View File
@@ -0,0 +1,313 @@
"""
Константы, перечисления и ключи метаданных проекта LPON.
Данный модуль является единым источником правды для:
• Ключей JSON-метаданных (TbItem.j_item_metadata, TbOffer.j_offer_metadata и др.)
• Контролируемых словарей и перечислений (типы носителей, типы статей, продавцов, валют)
• Функций генерации дефолтных JSON-структур для моделей
• Валидационных настроек и системных констант
"""
from enum import IntEnum
from django.db import models
# ============================================================================
# СИСТЕМНЫЕ НАСТРОЙКИ И ОГРАНИЧЕНИЯ
# ============================================================================
# Максимальная длина slug: Весь URL оптимально иметь до 70 символов (методичка Google).
# SLUG_MAX_LENGTH задает ограничение для слагов, создаваемых автоматически.
SLUG_MAX_LENGTH = 60
# Минимальная длина слова для поиска совпадений схожести в синонимах
MIN_SYNONYM_WORD_LENGTH = 4
# ============================================================================
# КЛЮЧИ МЕТАДАННЫХ ДЛЯ ОБЩИХ ТАБЛИЦ (TbLabel, TbSeller, TbArtist, TbMusicStyle)
# ============================================================================
KEY_SYNONYM_EN = 'SYN_EN' # Синонимы на английском языке (для поиска и парсеров)
KEY_SYNONYM_RU = 'SYN_RU' # Синонимы на русском языке (для отображения на фронтенде)
# ============================================================================
# КЛЮЧИ И ЗНАЧЕНИЯ ДЛЯ МЕТАДАННЫХ ИЗОБРАЖЕНИЙ (TbImageMetadata)
# ============================================================================
KEY_IMAGE_TYPE = 'IMG_IS' # Ключ для хранения типа картинки
VALUE_IMAGE_ABSTRACT = 'abstract' # Абстрактная картинка (логотипы, промо, типовые обложки)
VALUE_IMAGE_REAL = 'real' # Реальная фотография физического лота
KEY_IMAGE_FROM = 'IMG_FROM' # Источник происхождения изображения
VALUE_IMAGE_FROM_USER = '_adm' # Загружено пользователем через панель управления
VALUE_IMAGE_FROM_PARSE = 'parse' # Получено автоматически через парсер (Discogs, Bandcamp и т.д.)
KEY_IMAGE_URL = 'IMG_URL' # URL оригинального источника изображения
KEY_IMAGE_NOTE = 'IMG_NOTE' # Текстовая заметка или подпись к изображению
# ============================================================================
# КЛЮЧИ МЕТАДАННЫХ СТАТЕЙ (TbArticle)
# ============================================================================
KEY_ARTICLE_HUB = 'HUB' # Флаг того, что статья является хабом/сборником
# ============================================================================
# КЛЮЧИ МЕТАДАННЫХ ДЛЯ ТОВАРОВ И МОДЕЛЕЙ НОСИТЕЛЕЙ (TbItem.j_item_metadata)
# ============================================================================
KEY_ITEM_MEDIA_TYPE = 'media_type' # Тип сущности: музыкальный альбом, модель носителя и т.д.
KEY_ITEM_MEDIA_FORMAT = 'media_format' # Формат физического носителя (кассета, MD, винил, CD)
KEY_ITEM_TAPE_TYPE = 'tape_type' # Тип магнитной ленты (для кассет: Type I, II, III, IV)
KEY_ITEM_LENGTH = 'length' # Хронометраж носителя (46, 60, 74, 90 мин)
KEY_ITEM_SHELL_COLOR = 'shell_color' # Цвет корпуса кассеты или минидиска
KEY_ITEM_RELEASE_YEAR = 'release_year' # Год или диапазон годов производства модели/релиза
KEY_ITEM_MARKET = 'market' # Регион дистрибуции (JP, US, EU, GLOBAL)
KEY_ITEM_GALLERY_MODEL = 'gallery_model' # Список ID эталонных изображений модели в django-filer
KEY_ITEM_CATALOG_NUM = 'catalog_num' # Оригинальный каталожный номер производителя
KEY_ITEM_BARCODE = 'barcode' # Штрихкод (EAN/UPC)
# ============================================================================
# КЛЮЧИ МЕТАДАННЫХ ДЛЯ КОММЕРЧЕСКИХ ПРЕДЛОЖЕНИЙ (TbOffer.j_offer_metadata)
# ============================================================================
KEY_OFFER_NOTE = 'OFR_NT' # Текстовая заметка по лоту (дефекты, нюансы)
KEY_OFFER_ALL_MEDIA = 'OFR_MF' # Состав комплекта носителей (например, {"lp": 2, "cd": 1})
KEY_OFFER_IS_RECORDED = 'is_recorded' # Флаг наличия уникальной аудиозаписи на носителе
KEY_OFFER_RECORDING_INFO = 'recording_info' # Описание записанного материала (эфир, альбом, бутлег)
KEY_OFFER_TABS = 'write_protect_tabs' # Состояние язычков защиты от записи (INTACT, BROKEN)
KEY_OFFER_INLAY = 'inlay_condition' # Состояние бумажного вкладыша J-Card (CLEAN, PENCIL, WRITTEN)
KEY_OFFER_SERIES_TAG = 'series_tag' # Слаг/метка серии или коллекции
KEY_OFFER_PACKAGING = 'packaging' # Сохранность упаковки (SEALED, OPENED)
# ============================================================================
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ГЕНЕРАЦИИ ДЕФОЛТНЫХ ЗНАЧЕНИЙ JSON (CALLABLE DEFAULTS)
# ============================================================================
def get_image_metadata_default() -> dict:
"""
Возвращает значение по умолчанию для TbImageMetadata.j_img_metadata.
Используется как callable default в Django моделях и миграциях.
"""
return {
KEY_IMAGE_TYPE: VALUE_IMAGE_ABSTRACT,
KEY_IMAGE_FROM: VALUE_IMAGE_FROM_USER,
KEY_IMAGE_URL: None,
KEY_IMAGE_NOTE: '???',
}
def get_offer_metadata_default() -> dict:
"""
Возвращает значение по умолчанию для TbOffer.j_offer_metadata.
Используется как callable default в Django моделях и миграциях.
"""
return {
KEY_OFFER_NOTE: '',
KEY_OFFER_ALL_MEDIA: [],
}
def get_item_metadata_default() -> dict:
"""
Возвращает значение по умолчанию для TbItem.j_item_metadata.
"""
return {
KEY_ITEM_MEDIA_TYPE: 'blank_media',
KEY_ITEM_MEDIA_FORMAT: 'other',
}
# ============================================================================
# КОНТРОЛИРУЕМЫЕ ПЕРЕЧИСЛЕНИЯ (ENUMS / CHOICES ДЛЯ МОДЕЛЕЙ И JSON)
# ============================================================================
class ArticleType(models.TextChoices):
"""
Типы статей и контентных разделов (TbArticle.l_article_type).
ВАЖНО ДЛЯ РОУТИНГА И URL:
Значение каждого элемента (например, 'info', 'blog', 'artist', 'item' и т.д.)
используется напрямую как первый сегмент канонического URL-пути статьи:
/<l_article_type>/<slug>/ (например: /info/privacy-policy/, /blog/new-release-2026-september/)
Исключения:
- HUB ('HUB'): статьи-хабы публикуются напрямую в корне сайта (/<slug>/).
"""
ARTIST = 'artist', 'Artist: артист, группа или бренд'
STYLE = 'style', 'Style: музыкальный стиль'
ITEM = 'item', 'Item: Альбом, релиз или товар (кассета, hifi, аксессуар)'
LABEL = 'label', 'Label: Лейбл, издатель или компания'
OFFER = 'offer', 'Offer: конкретное предложение от продавца'
SELLER = 'seller', 'Seller: продавец или магазин'
BLOG = 'blog', 'Blog: Новость или блог'
ACTION = 'action', 'Action: Спецпредложение, акция, распродажа и т.п.'
ADV = 'adv', 'Adv: Реклама или баннер'
HUB = 'HUB', 'HUB: СТАТЬЯ-ХАБ (использует DSL для отображения контента)'
INFO = 'info', 'Info: Текстовый контент (для FAQ, условий конфиденциальности, правил/соглашений и т.д.)'
OTHER = '--', '?¿?: Другое'
class SellerType(models.TextChoices):
"""Тип продавца или магазина (TbSeller.l_seller_type)."""
SELLER = 'seller', 'Продавец'
LABEL = 'label', 'Лейбл (издатель)'
DIY = 'diy', 'Самиздат группы'
CROWD = 'crowd', 'Краудфандинг'
OTHER = '???', 'Другое'
class Currency(models.TextChoices):
"""Валюта источника/продавца (TbSeller.l_seller_currency)."""
RUB = 'rub', 'RUB: российский рубль'
USD = 'usd', 'USD: американский доллар'
EUR = 'eur', 'EUR: евро'
AMD = 'amd', 'AMD: армянских драм'
TRY = 'try', 'TRY: турецкая лира'
JPY = 'jpy', 'JPY: японская иена'
GBP = 'gbp', 'GBP: британский фунт'
CNY = 'cny', 'CNY: китайский юань'
BYN = 'byn', 'BYN: белорусский рубль'
TON = 'ton', 'TON: криптовалюта TON'
OTHER = '??', 'Other'
class Condition(models.TextChoices):
"""
Международный грейдинг состояния носителя, обложки, вкладышей, плакатов и т.п. (Goldmine Standard).
Используется в TbOffer.l_offer_condition_media и TbOffer.l_offer_condition_sleeve.
"""
S = 's', 'Still Sealed (новое, запечатано)'
M = 'm', 'Mint (новое, распакованное)'
NM = 'nm', 'Near Mint (почти новое)'
VG = 'vg', 'Very Good (очень хорошее)'
G = 'g', 'Good (хорошее)'
F = 'f', 'Fair (удовлетворительное)'
P = 'p', 'Poor (плохое)'
OTHER = '??', 'Other'
class Format(models.TextChoices):
"""Физический формат носителя оффера (TbOffer.l_offer_to_format)."""
LP = 'lp', 'Vinyl Long-Play (12")'
EP = 'ep', 'Vinyl Extended-Play (12", 10", 7")'
V45 = '45', 'Vinyl 7" (45 rpm)'
CD = 'cd', 'Compact Disc'
LD = 'ld', 'LaserDisc'
REC_MD = 'md', 'MiniDisc Record'
USE_MD = 'ms', 'Used MiniDisc (для записи)'
REC_CS = 'cs', 'Cassette Record'
USE_CS = 'uc', 'Used Cassette (для записи)'
REC_RR = 'tp', 'Tape Reel Record'
USE_RR = 'ur', 'Used Tape Reel (для записи)'
OTHER = '??', 'Other'
class SourceType(models.TextChoices):
"""Тип источника данных о товарах (TbSource.l_source_type)."""
EXCEL = 'excel', 'Excel-файл от продавца или издателя'
CSV = 'csv', 'CSV-файл от продавца или издателя'
URL = 'url', 'URL страницы с данными (например, HTML-страница с каталогом товаров)'
OTHER = '??', 'Другое (включая ручной ввод)'
class MediaType(models.TextChoices):
"""Тип сущности товара в каталоге."""
M_ALBUM = 'music_album', 'Музыкальный альбом / релиз (кассета, CD, винил, MD и т.д.)'
M_MEDIA = 'blank_media', 'Носитель под запись (чистый или б/у модель)'
M_RARE = 'rare_recording', 'Раритетная аудиозапись или бутлег'
M_EQUIPMENT = 'equipment', 'Аудиотехника и аксессуары'
class Market(models.TextChoices):
"""Регион дистрибуции релиза или модели носителя."""
JAPAN = 'jp', 'Япония (Japan / NTSC-J)'
USA = 'us', 'США / Северная Америка (USA / NTSC-U)'
EUROPE = 'eu', 'Европа (Europe / PAL)'
UK = 'uk', 'Великобритания (United Kingdom)'
USSR = 'su', 'СССР / Мелодия'
RU = 'ru', 'Россия'
GLOBAL = 'global', 'Международный / Global'
class TapeType(models.TextChoices):
"""Тип магнитной ленты для аудиокассет."""
TYPE_1_NORMAL = 'type_1', 'Type I: Normal Position (Fe / Fe2O3)'
TYPE_2_HIGH = 'type_2', 'Type II: High Position (CrO2 / Chrome / Cobalt)'
TYPE_3_FECR = 'type_3', 'Type III: Ferro-Chrome (FeCr)'
TYPE_4_METAL = 'type_4', 'Type IV: Metal Position (Metal Tape)'
class MediaLength(models.IntegerChoices):
"""Стандартные хронометражи кассет и минидисков в минутах."""
L10 = 10, '10 минут'
L20 = 20, '20 минут'
L30 = 30, '30 минут'
L40 = 40, '40 минут'
L46 = 46, '46 минут'
L50 = 50, '50 минут'
L54 = 54, '54 минуты'
L60 = 60, '60 минут'
L74 = 74, '74 минуты (стандарт MiniDisc)'
L80 = 80, '80 минут'
L90 = 90, '90 минут'
L100 = 100, '100 минут'
L110 = 110, '110 минут'
L120 = 120, '120 минут'
L150 = 150, '150 минут'
class ShellColor(models.TextChoices):
"""Цвет корпуса компакт-кассеты или минидиска."""
AMBER = 'amber', 'Янтарный (Amber / Прозрачный оранжевый)'
SMOKE = 'smoke', 'Дымчатый (Smoke / Полупрозрачный серый)'
CLEAR = 'clear', 'Прозрачный (Clear)'
BLACK = 'black', 'Черный (Solid Black)'
WHITE = 'white', 'Белый (Solid White)'
GOLD = 'gold', 'Золотой / Металлик (Gold Metallic)'
BLUE = 'blue', 'Синий / Голубой (Blue / Cyan)'
RED = 'red', 'Красный / Рубиновый (Red / Ruby)'
GREEN = 'green', 'Зеленый (Green)'
class WriteProtectTabs(models.TextChoices):
"""Состояние язычков защиты от записи на компакт-кассетах."""
INTACT = 'intact', 'Целы (Intact / Запись возможна)'
BROKEN = 'broken', 'Выломаны (Broken / Защита активна)'
class InlayCondition(models.TextChoices):
"""Состояние бумажного вкладыша (J-Card)."""
CLEAN = 'clean', 'Чистый (Без надписей / как новый)'
PENCIL = 'pencil', 'Надписи простым карандашом (стираемые)'
WRITTEN = 'written', 'Исписан ручкой / штампы / наклейки'
MISSING = 'missing', 'Вкладыш отсутствует'
class Packaging(models.TextChoices):
"""Тип сохранности упаковки физического экземпляра."""
SEALED = 'sealed', 'Заводская запечатка (Still Sealed)'
OPENED = 'opened', 'Вскрыт / б/у (Opened)'
# ============================================================================
# КОНСТАНТЫ ДЛЯ ПОИСКА ДУБЛИКАТОВ И ВАЛИДАЦИИ
# ============================================================================
VALIDATE_KEY__MATCH_TYPE = 'MATCH_TYPE'
VALIDATE_KEY__MODEL = 'MODEL'
VALIDATE_KEY__VALUE = 'MATCH_VALUE'
class ValidateMatchType(IntEnum):
"""Типы совпадений при поиске дубликатов."""
IS_DUPLICATE = 1 # Точное совпадение основного поля (s_label, s_artist и т.д.)
FIND_IN_SYNONYM = 2 # Точное совпадение в синонимах
EXACT_SYNONYM_MATCH = 3 # Точное совпадение синонимов с синонимами остальных записей
PARTIAL_MATCH = 4 # Схожесть: частичное совпадение
PARTIAL_MATCH__RISK_SHORT_WORDS = 5 # Схожесть: совпадение по коротким словам (риск ложных срабатываний)
+3 -54
View File
@@ -316,58 +316,7 @@ LOGGING = {
# =================================================== # ===================================================
# СПЕЦИФИЧНЫЕ ДЛЯ ПРОЕКТА КОНСТАНТЫ И НАСТРОЙКИ # СПЕЦИФИЧНЫЕ ДЛЯ ПРОЕКТА КОНСТАНТЫ И НАСТРОЙКИ
# Вынесены в constants.py для изоляции бизнес-логики и чистоты настроек
# Максимальная длина slug: Весь URL оптимально иметь до 70 символов (методичка Google, так как такой URL виден целиком # ===================================================
# на страничке поисковой выдачи, вызывает больше доверия и кликов). С учетом того, что from .constants import *
# в URL входит имя домена и slug-и разделов, оптимально устанавливать slug даже короче
# (20-30 символов максимум).
# SLUG_MAX_LENGTH задает ограничение для slug-ов созданных автоматически. Вручную в admin можно задать до 255 символов.
SLUG_MAX_LENGTH = 60
# Ключи для типовых параметров в мета-полях (для TbLabel, TbSeller, TbArtist, TbMusicStyle и т.д.)
KEY_SYNONYM_EN = 'SYN_EN' # Ключ для хранения синонимов на английском языке (для поиска по синонимам) и ДЛЯ ПАРСЕРОВ
KEY_SYNONYM_RU = 'SYN_RU' # Ключ для хранения синонимов на русском языке (для представления на фронтенд)
# Ключи и значения для метаданных картинок
KEY_IMAGE_TYPE = 'IMG_IS' # Ключ для хранения типа картинки
VALUE_IMAGE_ABSTRACT = 'abstract' # Абстрактная картинка (лого лейбла, фото исполнителя, типовая обложка альбома...)
VALUE_IMAGE_REAL = 'real' # Реальная картинка (актуально, для фото товаров в TbOffer)
KEY_IMAGE_FROM = 'IMG_FROM' # Ключ, о том, как получено изображение
VALUE_IMAGE_FROM_USER = '_adm' # Картинка загружена пользователем через админку
VALUE_IMAGE_FROM_PARSE = 'parse' # Картинка получена через парсер (например, с сайта Discogs, Bandcamp и т.д.)
KEY_IMAGE_URL = 'IMG_URL' # Ключ для хранения URL оригинальной картинки (обычно это должен прописать парсер)
KEY_IMAGE_NOTE = 'IMG_NOTE' # Ключ для хранения заметки о картинке
# Ключи и значения для коммерческих предложений (офферов, см. TbOffer и OfferAdmin)
KEY_OFFER_NOTE = 'OFR_NT' # Ключ для заметок по офферу (например, "замят угол конверта" и т.д.)
KEY_OFFER_ALL_MEDIA = 'OFR_MF' # Ключ для хранения информации о том, какие носители входят в оффер (например, CD+2LP)
# Ключи метанных статей (TbArticle).
KEY_ARTICLE_HUB = 'HUB' # Ключ для хранения информации о том, что статья является хабом (содержит ссылки на другие статьи)
# ДЛЯ ВАЛИДАЦИИ (поиска похожих исполнителей, стилей и т.д. (используется в TbLabel.matching_type, TbSeller.matching_type и т.д.)
VALIDATE_KEY__MATCH_TYPE = 'MATCH_TYPE'
VALIDATE_KEY__MODEL = 'MODEL'
VALIDATE_KEY__VALUE = 'MATCH_VALUE'
# Типы совпадений как Enum (более информативно чем просто числа, работает в match-case)
class ValidateMatchType(IntEnum):
"""Типы совпадений при поиске дубликатов."""
IS_DUPLICATE = 1 # Точное совпадение основного поля (s_label, s_artist и т.д.)
FIND_IN_SYNONYM = 2 # Точное совпадение в синонимах
EXACT_SYNONYM_MATCH = 3 # Точное совпадение синонимов проверяемой записи с синонимами остальных записей
PARTIAL_MATCH = 4 # Схожесть: частичное совпадение (например, часть названия артиста или лейбла в других записях).
# Основного поля и синонимами проверяемой записи и всех остальных записей.
PARTIAL_MATCH__RISK_SHORT_WORDS = 5 # Схожесть: Частичное совпадение, но с короткими словами (например,
# Японская группа "B'Z" или лейбл "XL"), что может вызвать ложные срабатывания
# схожести. Нужны быть аккуратнее, проверять вручную (особенно если короткие
# слова среди синонимов внутри метаданных других записей). Довольно частое
# явление для распростарннеых "суффиксов" MC, DJ, артиклей, инициалов
# исполнителей и т.п.
# Минимальная длина слова для поиска совпадений схожести в синонимах (например, в "The Beatles": "The" не учитывается,
# а "Beatles" учитывается)
MIN_SYNONYM_WORD_LENGTH = 4