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
313 lines
16 KiB
Python
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()
|