projects/org/pirotehnika/app/pim/standards/NAMING.md

ШАБЛОНЫ НАИМЕНОВАНИЙ ТОВАРОВ

Версия: 2.0.0
Дата: 2025-12-25
Статус: Стандарт


НАЗНАЧЕНИЕ

Правила генерации стандартизированных названий для всех типов пиротехнических изделий.


СОДЕРЖАНИЕ

  1. Общая структура
  2. Шаблоны по типам
  3. Функция генерации
  4. Склонение слов

1. ОБЩАЯ СТРУКТУРА

Все наименования следуют единому формату:

[ТИП] "[НАЗВАНИЕ]" [ХАРАКТЕРИСТИКИ]

Компоненты:
- ТИП - тип изделия с заглавной буквы (Батарея салютов, Фонтан, и т.д.)
- НАЗВАНИЕ - коммерческое название в двойных кавычках
- ХАРАКТЕРИСТИКИ - калибр, залпы, высота, время (зависит от типа)


2. ШАБЛОНЫ ПО ТИПАМ

2.1 БАТАРЕИ САЛЮТОВ (salute_type: батарея)

Шаблон:

Батарея салютов "[НАЗВАНИЕ]" [КАЛИБР]мм [ЗАЛПЫ] залпов

Regex:

^Батарея салютов "([^"]+)" (\d+)мм (\d+) залпов?$

Примеры:
- Батарея салютов "Праздничный" 30мм 49 залпов
- Батарея салютов "Юбилей" 30мм 100 залпов
- Батарея салютов "Золотая Москва" 25мм 25 залпов

Варианты:
- Короткая форма: Батарея "[НАЗВАНИЕ]" [ЗАЛПЫ] залпов
- Альтернатива: Салют "[НАЗВАНИЕ]" [ЗАЛПЫ] залпов


2.2 ФОНТАНЫ (salute_type: фонтан)

Шаблон:

Фонтан "[НАЗВАНИЕ]" [ВЫСОТА]м

Regex:

^Фонтан "([^"]+)" (\d+)м$

Примеры:
- Фонтан "Золотой водопад" 3м
- Фонтан "Серебряный каскад" 5м
- Фонтан "Огненный дождь" 3м 60сек (с временем)


2.3 РИМСКИЕ СВЕЧИ (salute_type: римская свеча)

Шаблон:

Римская свеча "[НАЗВАНИЕ]" [ВЫСТРЕЛЫ] выстрелов

Regex:

^Римская свеча "([^"]+)" (\d+) выстрелов?$

Примеры:
- Римская свеча "Северное сияние" 10 выстрелов
- Римская свеча "Комета" 8 выстрелов
- Римская свеча "Звезда" 20мм 10 выстрелов (с калибром)


2.4 ПЕТАРДЫ (salute_type: петарда)

Шаблон:

Петарды "[НАЗВАНИЕ]" упаковка [N] шт

Regex:

^Петарды "([^"]+)" упаковка (\d+) шт$

Примеры:
- Петарды "Корсар-1" упаковка 20 шт
- Петарды "Корсар-4" упаковка 6 шт
- Петарды "Черная дробь" упаковка 10 шт


2.5 РАКЕТЫ (salute_type: ракета)

Шаблон:

Ракета "[НАЗВАНИЕ]" [N] залпов

Regex:

^Ракета "([^"]+)" (\d+) залпов?$

Примеры:
- Ракета "Комета" 5 залпов
- Ракета "Метеор" 3 залпа


2.6 БЕНГАЛЬСКИЕ ОГНИ (salute_type: бенгальские)

Шаблон:

Бенгальские огни [N]см, [N] штук

ВАЖНО: Коммерческого названия в кавычках НЕТ!

Regex:

^Бенгальские огни (\d+)см, (\d+) штук?$

Примеры:
- Бенгальские огни 25см, 10 штук
- Бенгальские огни 40см, 5 штук
- Бенгальские огни 50см, 3 штуки


2.7 ДЫМОВЫЕ ИЗДЕЛИЯ (salute_type: дым)

Шаблон:

Дымовое изделие "[НАЗВАНИЕ]" [ЦВЕТ]

ВАЖНО: Не "Дым", а "Дымовое изделие"!

Regex:

^Дымовое изделие "([^"]+)" ([а-я]+)$

Примеры:
- Дымовое изделие "Облако" синий
- Дымовое изделие "Туман" красный
- Дымовое изделие "MA0509" зеленый

Цвета: белый, красный, синий, зеленый, желтый, оранжевый, фиолетовый


2.8 ХЛОПУШКИ (salute_type: хлопушка)

Шаблон:

Хлопушка "[НАЗВАНИЕ]" [ДЛИНА]см

Regex:

^Хлопушка "([^"]+)" (\d+)см$

Примеры:
- Хлопушка "Праздничная" 30см
- Хлопушка "Новогодняя" 60см


2.9 ЛЕТАЮЩИЕ ИЗДЕЛИЯ (salute_type: летающий)

Шаблон:

[ТИП] "[НАЗВАНИЕ]"

Типы: НЛО, Вертолёт, Пчёлка

Примеры:
- НЛО "Летающая тарелка"
- Вертолёт "Апачи"
- Пчёлка "Майя"


2.10 НАБОРЫ (salute_type: набор)

Шаблон:

Набор "[НАЗВАНИЕ]"

Regex:

^Набор "([^"]+)"$

Примеры:
- Набор "Новогодний"
- Набор "Семейный праздник"


3. ФУНКЦИЯ ГЕНЕРАЦИИ

3.1 Основной алгоритм

def generate_product_name(product: Dict[str, Any]) -> str:
    """
    Генерирует стандартизированное название товара

    Args:
        product: Нормализованные данные товара с полями:
            - salute_type: тип изделия (lowercase!)
            - commercial_name: коммерческое название
            - caliber_mm: калибр в мм
            - shots_count: количество залпов
            - height_mm: высота в мм
            - duration_sec: время работы
            - pack_qty: количество в упаковке
            - length_mm: длина (для бенгальских, хлопушек)

    Returns:
        Стандартизированное название
    """

    salute_type = product.get('salute_type', '').lower()
    name = product.get('commercial_name', 'Без названия')

    # БАТАРЕИ
    if salute_type == 'батарея':
        caliber = product.get('caliber_mm')
        shots = product.get('shots_count')

        parts = ['Батарея салютов', f'"{name}"']

        if caliber:
            parts.append(f'{int(caliber)}мм')

        if shots:
            shots_word = get_shots_word(shots)  # залп/залпа/залпов
            parts.append(f'{shots} {shots_word}')

        return ' '.join(parts)

    # ФОНТАНЫ
    elif salute_type == 'фонтан':
        height_mm = product.get('height_mm')
        duration = product.get('duration_sec')

        parts = ['Фонтан', f'"{name}"']

        if height_mm:
            height_m = height_mm // 1000
            parts.append(f'{height_m}м')

        if duration:
            parts.append(f'{duration}сек')

        return ' '.join(parts)

    # РИМСКИЕ СВЕЧИ
    elif salute_type == 'римская свеча':
        shots = product.get('shots_count')
        caliber = product.get('caliber_mm')

        parts = ['Римская свеча', f'"{name}"']

        if caliber:
            parts.append(f'{int(caliber)}мм')

        if shots:
            shots_word = get_shots_word(shots, word='выстрел')
            parts.append(f'{shots} {shots_word}')

        return ' '.join(parts)

    # ПЕТАРДЫ
    elif salute_type == 'петарда':
        pack_qty = product.get('pack_qty')

        parts = ['Петарды', f'"{name}"']

        if pack_qty:
            parts.append(f'упаковка {pack_qty} шт')

        return ' '.join(parts)

    # РАКЕТЫ
    elif salute_type == 'ракета':
        shots = product.get('shots_count')

        parts = ['Ракета', f'"{name}"']

        if shots:
            shots_word = get_shots_word(shots)
            parts.append(f'{shots} {shots_word}')

        return ' '.join(parts)

    # БЕНГАЛЬСКИЕ ОГНИ (БЕЗ НАЗВАНИЯ!)
    elif salute_type == 'бенгальские':
        length_mm = product.get('length_mm')
        pack_qty = product.get('pack_qty')

        if length_mm and length_mm > 100:
            length_cm = length_mm // 10
        else:
            length_cm = length_mm if length_mm else None

        parts = ['Бенгальские огни']

        if length_cm:
            parts.append(f'{length_cm}см')

        if pack_qty:
            qty_word = get_qty_word(pack_qty)  # штука/штуки/штук
            if length_cm:
                return f'{parts[0]} {length_cm}см, {pack_qty} {qty_word}'
            else:
                return f'{parts[0]} {pack_qty} {qty_word}'

        return ' '.join(parts)

    # ДЫМОВЫЕ ИЗДЕЛИЯ
    elif salute_type == 'дым':
        # TODO: извлечь цвет из названия или отдельного поля
        return f'Дымовое изделие "{name}"'

    # ХЛОПУШКИ
    elif salute_type == 'хлопушка':
        length_mm = product.get('length_mm')

        parts = ['Хлопушка', f'"{name}"']

        if length_mm:
            if length_mm > 100:
                length_cm = length_mm // 10
            else:
                length_cm = length_mm
            parts.append(f'{length_cm}см')

        return ' '.join(parts)

    # ЛЕТАЮЩИЕ
    elif salute_type == 'летающий':
        # TODO: определить конкретный тип (НЛО, Вертолёт, Пчёлка)
        return f'Летающее изделие "{name}"'

    # НАБОРЫ
    elif salute_type == 'набор':
        return f'Набор "{name}"'

    # FALLBACK
    else:
        return f'{salute_type.capitalize()} "{name}"'

4. СКЛОНЕНИЕ СЛОВ

4.1 Функция склонения залпов/выстрелов

def get_shots_word(count: int, word: str = 'залп') -> str:
    """Правильное склонение для залпов/выстрелов"""
    # залп, залпа, залпов
    # выстрел, выстрела, выстрелов

    if count % 10 == 1 and count % 100 != 11:
        return word
    elif count % 10 in (2, 3, 4) and count % 100 not in (12, 13, 14):
        if word == 'залп':
            return 'залпа'
        elif word == 'выстрел':
            return 'выстрела'
    else:
        if word == 'залп':
            return 'залпов'
        elif word == 'выстрел':
            return 'выстрелов'

Примеры:
- 1 залп, 21 залп, 31 залп
- 2 залпа, 3 залпа, 4 залпа, 22 залпа
- 5 залпов, 10 залпов, 25 залпов, 100 залпов

4.2 Функция склонения штук

def get_qty_word(count: int) -> str:
    """Правильное склонение для штук"""
    # штука, штуки, штук

    if count % 10 == 1 and count % 100 != 11:
        return 'штука'
    elif count % 10 in (2, 3, 4) and count % 100 not in (12, 13, 14):
        return 'штуки'
    else:
        return 'штук'

Примеры:
- 1 штука, 21 штука
- 2 штуки, 3 штуки, 4 штуки, 22 штуки
- 5 штук, 10 штук, 20 штук


Версия: 2.0.0
Дата: 2025-12-25