Files
press/cabinet_service/brand_doc_generator.py
T
apuc c6048aeea0 feat: AI Press Secretary - content generation and publishing platform
Features:
- AI-powered post generation from brand profiles
- Image generation with templates
- Content planning
- Telegram publishing
- Social profile analyzer
- Community organization support
- Markdown to HTML conversion for Telegram
- Inline AI chat for post editing

Deploy:
- Add deployment scripts for Linux servers
- Add PowerShell deploy script for Windows
- Add Nginx and SSL setup scripts
- Add backup and restore scripts
2026-08-27 15:24:15 +03:00

313 lines
16 KiB
Python

"""Генерация .docx документа стратегии личного бренда из данных опроса."""
import io
from datetime import datetime
from docx import Document
from docx.shared import Pt, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
def _split_lines(text):
"""Разбивает текст на строки, удаляя пустые."""
if not text:
return []
return [line.strip() for line in text.strip().split("\n") if line.strip()]
def _split_pipe(text):
"""Разбивает строки по символу '|' на колонки."""
rows = []
for line in _split_lines(text):
cols = [c.strip() for c in line.split("|")]
rows.append(cols)
return rows
def _add_table(doc, headers, rows):
"""Добавляет таблицу с заголовками и строками."""
table = doc.add_table(rows=1, cols=len(headers))
table.style = "Table Grid"
hdr = table.rows[0].cells
for i, h in enumerate(headers):
hdr[i].text = h
for row_data in rows:
cells = table.add_row().cells
for i, val in enumerate(row_data):
cells[i].text = val if i < len(row_data) else ""
doc.add_paragraph()
def generate_brand_docx(data: dict) -> bytes:
"""Генерирует .docx документ стратегии личного бренда или организации.
data — dict с полями опроса.
Возвращает байты документа."""
doc = Document()
brand_type = data.get("brand_type", "personal")
is_org = (brand_type == "organization")
is_community = (brand_type == "community")
# === ТИТУЛ ===
if is_community:
entity_name = data.get("org_name", "")
title = doc.add_heading(f"Стратегия развития бизнес-сообщества\n{entity_name}", 0)
elif is_org:
entity_name = data.get("org_name", "")
title = doc.add_heading(f"Стратегия бренд-коммуникаций\n{entity_name}", 0)
else:
entity_name = data.get("client_name", "")
title = doc.add_heading(f"Стратегия развития личного бренда\n{entity_name}", 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
version = data.get("version", "1.0")
date_str = data.get("date_str") or datetime.now().strftime("%B %Y")
doc.add_paragraph(f"Версия {version} {date_str}")
doc.add_paragraph()
# === 1. РЕЗЮМЕ И ЦЕЛИ ===
doc.add_heading("1. Резюме и цели", level=1)
if is_community:
doc.add_heading("Общественная организация:", level=3)
doc.add_paragraph(f"Название: {data.get('org_name', '—')}")
doc.add_paragraph(f"Сфера деятельности: {data.get('org_industry', '—')}")
doc.add_paragraph(f"Миссия: {data.get('org_mission', '—')}")
doc.add_paragraph(f"Направления деятельности: {data.get('org_products', '—')}")
doc.add_paragraph(f"Участников: {data.get('org_team_size', '—')}")
doc.add_paragraph(f"Год основания: {data.get('org_founded', '—')}")
doc.add_paragraph(f"Формат участия: {data.get('org_format', '—')}")
elif is_org:
doc.add_heading("Организация:", level=3)
doc.add_paragraph(f"Название: {data.get('org_name', '—')}")
doc.add_paragraph(f"Сфера деятельности: {data.get('org_industry', '—')}")
doc.add_paragraph(f"Миссия: {data.get('org_mission', '—')}")
doc.add_paragraph(f"Продукты/услуги: {data.get('org_products', '—')}")
doc.add_paragraph(f"Сотрудников: {data.get('org_team_size', '—')}")
doc.add_paragraph(f"Год основания: {data.get('org_founded', '—')}")
else:
doc.add_heading("Текущая ситуация:", level=3)
doc.add_paragraph(data.get("current_situation", "—"))
doc.add_heading("Ключевые цели на 12 месяцев:", level=3)
doc.add_paragraph(
f"Качественный рост аудитории: Набрать {data.get('goal_audience_number', '—')} "
f"подписчиков в основном {data.get('goal_audience_platform', '—')} "
f"(активная аудитория: {data.get('goal_audience_description', '—')})."
)
doc.add_paragraph(
f"Укрепление экспертного статуса: Стать признанным федеральным экспертом "
f"в сфере {data.get('goal_expert_field', '—')} "
f"(не менее {data.get('goal_expert_speaking', '—')} приглашений выступить "
f"на федеральных площадках в год)."
)
doc.add_paragraph(
f"Коммерциализация личного бренда: Обеспечить поток из не менее "
f"{data.get('goal_commercial_leads', '—')} заявок в месяц на платные услуги "
f"({data.get('goal_commercial_services', '—')}) через личный бренд."
)
doc.add_paragraph(
f"Системный контент: {data.get('goal_content_system', '—')}"
)
# === 2. ПОЗИЦИОНИРОВАНИЕ И УТП ===
doc.add_heading("2. Позиционирование и УТП", level=1)
doc.add_heading("Сущность бренда:", level=3)
doc.add_paragraph(data.get("brand_essence", "—"))
doc.add_heading("Позиционирование (Ответы на ключевые вопросы):", level=3)
if is_community:
doc.add_paragraph(f"Для кого создано: {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решает: {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Ценность для участника: {data.get('positioning_result', '—')}")
doc.add_paragraph(f"Чем отличаемся от других: {data.get('positioning_difference', '—')}")
doc.add_paragraph(f"Форматы взаимодействия: {data.get('community_formats', '—')}")
elif is_org:
doc.add_paragraph(f"Кому помогаем: {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решаем: {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Какой результат получает клиент: {data.get('positioning_result', '—')}")
doc.add_paragraph(f"Почему выбирают нас: {data.get('positioning_difference', '—')}")
else:
doc.add_paragraph(f"Кому я помогаю? {data.get('positioning_who', '—')}")
doc.add_paragraph(f"Какую проблему решаю? {data.get('positioning_problem', '—')}")
doc.add_paragraph(f"Какой результат получает клиент? {data.get('positioning_result', '—')}")
doc.add_heading("Уникальное торговое предложение (УТП):", level=3)
doc.add_paragraph(data.get("usp", "—"))
# === 3. АУДИТОРИИ И КАНАЛЫ ===
doc.add_heading("3. Аудитории и каналы коммуникации", level=1)
doc.add_heading("Идеальные подписчики/клиенты:", level=3)
for line in _split_lines(data.get("ideal_audiences", "")):
doc.add_paragraph(line, style="List Bullet")
doc.add_heading("Карта каналов (по приоритету):", level=3)
channels = data.get("channels", [])
if channels:
_add_table(doc, ["Канал", "Тип", "Частота", "Описание"], [
[ch.get("platform", ""), ch.get("type", ""), ch.get("frequency", ""), ch.get("description", "")]
for ch in channels
])
else:
doc.add_paragraph("—")
# === 4. ВИЗУАЛЬНАЯ И ВЕРБАЛЬНАЯ ИДЕНТИЧНОСТЬ ===
doc.add_heading("4. Визуальная и вербальная идентичность", level=1)
doc.add_heading("А. Визуальная идентичность", level=3)
doc.add_paragraph(f"Концепция: {data.get('visual_concept', '—')}")
doc.add_paragraph("Цветовая палитра:")
_add_table(doc, ["Роль", "Название", "HEX"], [
["Основной", data.get("color_main_name", ""), data.get("color_main_hex", "")],
["Акцент 1", data.get("color_accent1_name", ""), data.get("color_accent1_hex", "")],
["Акцент 2", data.get("color_accent2_name", ""), data.get("color_accent2_hex", "")],
["Нейтральная база", data.get("color_neutral", ""), ""],
])
doc.add_paragraph("Типографика:")
doc.add_paragraph(f"Заголовки: {data.get('typography_headings', '—')}", style="List Bullet")
doc.add_paragraph(f"Текст: {data.get('typography_body', '—')}", style="List Bullet")
doc.add_paragraph(f"Акценты: {data.get('typography_accents', '—')}", style="List Bullet")
doc.add_heading("Б. Tone of Voice (Тон коммуникации)", level=3)
doc.add_paragraph(data.get("tov_description", "—"))
doc.add_paragraph(f"Референсы: {data.get('tov_references', '—')}")
tov_axes = _split_lines(data.get("tov_axes", ""))
if tov_axes:
doc.add_paragraph("Оси ToV:")
for axis in tov_axes:
doc.add_paragraph(axis, style="List Number")
# === 5. КОНТЕНТ-СТРАТЕГИЯ ===
doc.add_heading("5. Контент-стратегия: Три столпа", level=1)
doc.add_paragraph(
"Контент строится на балансе трех направлений, работающих на разные этапы воронки доверия."
)
pillars = [
("1", data.get("pillar1_name", "Кто я"), data.get("pillar1_goal", ""),
data.get("pillar1_formats", ""), data.get("pillar1_frequency", "30%")),
("2", data.get("pillar2_name", "Решаю боль"), data.get("pillar2_goal", ""),
data.get("pillar2_formats", ""), data.get("pillar2_frequency", "50%")),
("3", data.get("pillar3_name", "Втягиваю"), data.get("pillar3_goal", ""),
data.get("pillar3_formats", ""), data.get("pillar3_frequency", "20%")),
]
_add_table(doc, ["Столп", "Цель", "Ключевые форматы", "Частота"], [
[f"{p[1]}", p[2], p[3], p[4]]
for p in pillars
])
special = data.get("special_formats", "")
if special:
doc.add_paragraph(f"Особые форматы: {special}")
# === 6. ВОРОНКА И ПРОДУКТЫ ===
doc.add_heading("6. Воронка вовлечения и продуктовый ряд", level=1)
doc.add_heading("Воронка (путь от незнакомца к клиенту):", level=3)
funnel_stages = data.get("funnel", [])
if funnel_stages:
_add_table(doc, ["Этап", "Контент", "CTA"], [
[s.get("name", ""), s.get("content", ""), s.get("cta", "")]
for s in funnel_stages
])
else:
doc.add_paragraph("—")
doc.add_heading("Продуктовый ряд (от простого к сложному):", level=3)
doc.add_paragraph(f"Входная точка (Lead Magnet): {data.get('product_lead_magnet', '—')}")
doc.add_paragraph(f"Самая простая услуга: {data.get('product_simple', '—')}")
doc.add_paragraph(f"Услуга-конвертер: {data.get('product_converter', '—')}")
doc.add_paragraph(f"Комплексные решения: {data.get('product_complex', '—')}")
doc.add_paragraph(f"Экспертные продукты: {data.get('product_expert', '—')}")
# === 7. МЕТРИКИ ===
doc.add_heading("7. Метрики", level=1)
quant_rows = _split_pipe(data.get("metrics_quantitative", ""))
if quant_rows:
doc.add_heading("Количественные:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], quant_rows)
qual_rows = _split_pipe(data.get("metrics_qualitative", ""))
if qual_rows:
doc.add_heading("Качественные:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], qual_rows)
comm_rows = _split_pipe(data.get("metrics_commercial", ""))
if comm_rows:
doc.add_heading("Коммерческие:", level=3)
_add_table(doc, ["Метрика", "Целевое значение", "Измерение"], comm_rows)
# === 8. РИСКИ И РЕКОМЕНДАЦИИ ===
doc.add_heading("8. Потенциальные риски и рекомендации", level=1)
risk_rows = _split_pipe(data.get("risks", ""))
if risk_rows:
doc.add_heading("Потенциальные риски:", level=3)
_add_table(doc, ["Риск", "Решение"], risk_rows)
recs = _split_lines(data.get("recommendations", ""))
if recs:
doc.add_heading("Рекомендации:", level=3)
for rec in recs:
doc.add_paragraph(rec, style="List Bullet")
# === 9. АРХЕТИПИЧЕСКИЙ ОБРАЗ ===
doc.add_heading("9. Архетипический образ", level=1)
doc.add_heading(f"Ключевой архетип: {data.get('archetype_key_name', '—')}", level=3)
doc.add_paragraph(f"Суть: {data.get('archetype_key_essence', '—')}")
manifestations = _split_lines(data.get("archetype_key_manifestations", ""))
if manifestations:
doc.add_paragraph("Проявления:")
for m in manifestations:
doc.add_paragraph(m, style="List Bullet")
supporting = _split_pipe(data.get("archetype_supporting", ""))
if supporting:
doc.add_heading("Вспомогательные архетипы:", level=3)
_add_table(doc, ["Архетип", "Суть", "Проявления"], supporting)
doc.add_heading("Синтез архетипов:", level=3)
doc.add_paragraph(data.get("archetype_synthesis", "—"))
table_rows = _split_pipe(data.get("archetype_table", ""))
if table_rows:
doc.add_heading("Таблица проявлений:", level=3)
_add_table(doc, ["Ситуация / Контент-формат", "Доминирующий архетип", "Как это выглядит и звучит"], table_rows)
# === 10. ПРОМПТ ДЛЯ ГЕНЕРАЦИИ КОНТЕНТА ===
doc.add_heading("10. Промпт для генерации контента", level=1)
doc.add_heading("Контекст и личность:", level=3)
doc.add_paragraph(data.get("prompt_context", "—"))
doc.add_heading("Tone of Voice:", level=3)
doc.add_paragraph(data.get("prompt_tov", "—"))
doc.add_heading("Структура мысли:", level=3)
doc.add_paragraph(data.get("prompt_structure", "—"))
doc.add_heading("Контент-направления:", level=3)
for line in _split_lines(data.get("prompt_directions", "")):
doc.add_paragraph(line, style="List Bullet")
doc.add_heading("Форматы и шаблоны:", level=3)
doc.add_paragraph(data.get("prompt_formats", "—"))
doc.add_heading("Технические требования к тексту:", level=3)
doc.add_paragraph(data.get("prompt_technical", "—"))
# === Сериализация в байты ===
buf = io.BytesIO()
doc.save(buf)
buf.seek(0)
return buf.read()