Трек «Метод Дегтярева»: концепция, презентация и рабочие листы
This commit is contained in:
commit
2c79f59064
28 files changed
+2999
No files matched your search
+15
@@ -0,0 +1,15 @@
|
||||
# Временные файлы
|
||||
~$*
|
||||
*.tmp
|
||||
_*.png
|
||||
_*.pdf
|
||||
_*.docx
|
||||
_*.pptx
|
||||
_names.txt
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# ОС
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
desktop.ini
|
||||
Binary file not shown.
@@ -0,0 +1,135 @@
|
||||
# Трек «Метод Дегтярева»
|
||||
|
||||
Материалы трека Форума молодёжи Донбасса «ФМД: у нас так принято».
|
||||
Смена 1 «ПСБ Патриот», 10–13 ноября 2026.
|
||||
|
||||
**Эксперт и наставник трека:** Бойко К.С.
|
||||
|
||||
---
|
||||
|
||||
## О треке
|
||||
|
||||
Трек о том, как превращать реальные проблемы региона в цифровые социальные проекты. В основе — метод Владимира Дегтярева, «хозяина Донбасса», который в 1963–1976 годах превратил шахтёрский край в самый зелёный промышленный город планеты.
|
||||
|
||||
**Слоган:** «От забоя до миллиона роз».
|
||||
|
||||
**Главная мысль:** цифровизация начинается не с сервера, а с человека. Технология становится ценной только тогда, когда у неё есть хозяин — тот, кто понимает, кому и зачем она служит.
|
||||
|
||||
### Пять шагов
|
||||
|
||||
| Шаг | Вопрос | Артефакт |
|
||||
|---|---|---|
|
||||
| **Забой** | Что болит на самом деле? | Карта проблемы, интервью, данные |
|
||||
| **Смета** | Кому и насколько станет лучше? | Портрет пользователя, 3 метрики |
|
||||
| **Завод** | Что нужно, чтобы решалось системно? | Архитектура, ресурсы, партнёры |
|
||||
| **Роза** | Как люди это почувствуют? | Ценность, сценарий, MVP |
|
||||
| **Хозяин** | Кто останется после нас? | Устойчивость, дорожная карта 30/90 |
|
||||
|
||||
### Прикладной результат
|
||||
|
||||
**Участник:** паспорт проекта на 1 странице, питч на 3 минуты, дорожная карта 30/90, методичка «5 шагов».
|
||||
|
||||
**Регион:** банк проблем, 5–7 концепций цифровых социальных сервисов, 2–3 проекта к пилоту или гранту, тиражируемая методика.
|
||||
|
||||
---
|
||||
|
||||
## Материалы
|
||||
|
||||
| Файл | Назначение |
|
||||
|---|---|
|
||||
| `Метод_Дегтярева_концепция_трека.docx` | Полная концепция: задумка, цели, задачи, результат, метод, встройка в программу, роли, критерии, сценарии на 7 и 5 минут, Q&A |
|
||||
| `Метод_Дегтярева_презентация.pptx` | 8 слайдов под установочную сессию 10.11, 19:30. В заметках — текст выступления |
|
||||
| `Метод_Дегтярева_карта_проблемы.docx` | Рабочий лист «Забой»: кто, где, что мешает, что теряет, доказательства, 5 почему |
|
||||
| `Метод_Дегтярева_гайд_интервью.docx` | 8 карточек с вопросами и правилами интервью |
|
||||
| `Метод_Дегтярева_смета.docx` | Рабочий лист «Смета»: пользователь, было → стало, три метрики, масштаб, цена эффекта |
|
||||
| `Метод_Дегтярева_роза.docx` | Рабочий лист «Роза»: ценность, сценарий, эмоции, MVP, отличие, питч |
|
||||
| `Метод_Дегтярева_хозяин.docx` | Рабочий лист «Хозяин»: команда, модель устойчивости, партнёры, дорожная карта 30/90, риски |
|
||||
| `Метод_Дегтярева_канвас.docx` | Универсальный канвас на 8 блоков для команд |
|
||||
| `Метод_Дегтярева_концепция_трека.md` | Исходный текст концепции в Markdown |
|
||||
| `1 смена Проект программы ФМД_07.11.docx` | Исходная программа форума |
|
||||
|
||||
PDF-версии всех материалов лежат рядом с исходниками и предназначены для печати и быстрой отправки.
|
||||
|
||||
---
|
||||
|
||||
## Как использовать
|
||||
|
||||
### Печать
|
||||
|
||||
- Формат A4, масштаб 100%, без «по размеру страницы».
|
||||
- «Карта проблемы», «Смета», «Роза», «Хозяин» — по одной на команду.
|
||||
- «Гайд интервью» — по одной карточке на пару интервьюеров. В файле 8 карточек.
|
||||
- «Канвас» — по одному на команду как сводный рабочий лист.
|
||||
- Презентация — 8 слайдов, рассчитана на 7 минут. В заметках — текст выступления.
|
||||
|
||||
### Установочная сессия
|
||||
|
||||
10 ноября, 19:30–20:00. Презентация трека: зачем, как, что получится. Регламент — 5–7 минут. В концепции есть полный сценарий на 7 минут и сжатая версия на 5 минут.
|
||||
|
||||
### Лаборатории
|
||||
|
||||
| Дата | Время | Шаг | Материал |
|
||||
|---|---|---|---|
|
||||
| 11.11 | 10:45–12:00 | Забой + Смета | «Карта проблемы», «Гайд интервью» |
|
||||
| 11.11 | 18:00–19:00 | Завод + Роза | «Роза», канвас |
|
||||
| 12.11 | 10:30–11:30 | Хозяин | «Хозяин» |
|
||||
| 12.11 | 11:30–12:30 | Консультации | Индивидуально |
|
||||
| 12.11 | 13:00–14:30 | Сборка | Паспорт проекта, питч |
|
||||
| 12.11 | 14:45–16:30 | Защита | 3 минуты питч + 2 минуты вопросы |
|
||||
|
||||
---
|
||||
|
||||
## Структура репозитория
|
||||
|
||||
```
|
||||
.
|
||||
├── README.md
|
||||
├── Метод_Дегтярева_концепция_трека.md
|
||||
├── Метод_Дегтярева_концепция_трека.docx / .pdf
|
||||
├── Метод_Дегтярева_презентация.pptx / .pdf
|
||||
├── Метод_Дегтярева_карта_проблемы.docx / .pdf
|
||||
├── Метод_Дегтярева_гайд_интервью.docx / .pdf
|
||||
├── Метод_Дегтярева_смета.docx / .pdf
|
||||
├── Метод_Дегтярева_роза.docx / .pdf
|
||||
├── Метод_Дегтярева_хозяин.docx / .pdf
|
||||
├── Метод_Дегтярева_канвас.docx / .pdf
|
||||
├── build_*.py
|
||||
└── 1 смена Проект программы ФМД_07.11.docx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Сборка материалов
|
||||
|
||||
Требования: Python 3.12, пакеты `python-docx` и `python-pptx`. Для PDF — LibreOffice.
|
||||
|
||||
```bash
|
||||
py -3 -m pip install python-docx python-pptx
|
||||
|
||||
py -3 build_docx.py
|
||||
py -3 build_pptx.py
|
||||
py -3 build_canvas.py
|
||||
py -3 build_problem_card.py
|
||||
py -3 build_interview_cards.py
|
||||
py -3 build_estimate_card.py
|
||||
py -3 build_rose_card.py
|
||||
py -3 build_owner_card.py
|
||||
```
|
||||
|
||||
Конвертация в PDF:
|
||||
|
||||
```bash
|
||||
soffice --headless --convert-to pdf --outdir . <файл>.docx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Автор
|
||||
|
||||
Бойко К.С., эксперт и наставник трека «Метод Дегтярева».
|
||||
|
||||
---
|
||||
|
||||
## Примечание
|
||||
|
||||
В репозитории хранятся только материалы трека. Пароли, токены и другие секреты в него не добавляются.
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.section import WD_ORIENT
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_канвас.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=60, bottom=60, left=140, right=140):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='C9D6CF', size=6):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def dotted_line(paragraph, color='C9D6CF'):
|
||||
pPr = paragraph._p.get_or_add_pPr()
|
||||
pBdr = OxmlElement('w:pBdr')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '1')
|
||||
bottom.set(qn('w:color'), color)
|
||||
pBdr.append(bottom)
|
||||
pPr.append(pBdr)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=3, width_cm=12.4):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(0.55)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def add_block(cell, num, title, prompt, color, fill, lines=3):
|
||||
cell.text = ''
|
||||
shade(cell, fill)
|
||||
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run(num + ' ')
|
||||
set_run(r, size=8, bold=True, color=color)
|
||||
r = p.add_run(title)
|
||||
set_run(r, name='Georgia', size=12, bold=True, color=color)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(6)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(prompt)
|
||||
set_run(r, size=8, color=GRAY)
|
||||
|
||||
add_line_table(cell, lines=lines)
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
|
||||
section = doc.sections[0]
|
||||
section.orientation = WD_ORIENT.LANDSCAPE
|
||||
section.page_width = Cm(29.7)
|
||||
section.page_height = Cm(21.0)
|
||||
section.top_margin = Cm(1.1)
|
||||
section.bottom_margin = Cm(1.0)
|
||||
section.left_margin = Cm(1.2)
|
||||
section.right_margin = Cm(1.2)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
# Header
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('КАНВАС «МЕТОД ДЕГТЯРЕВА»')
|
||||
set_run(r, name='Georgia', size=18, bold=True, color=GREEN)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(2)
|
||||
p.paragraph_format.space_after = Pt(4)
|
||||
r = p.add_run('Команда: ______________________________ Проблема: ______________________________________ Трек: «Метод Дегтярева»')
|
||||
set_run(r, size=8.5, color=GRAY)
|
||||
|
||||
# Table
|
||||
table = doc.add_table(rows=4, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
|
||||
for row in table.rows:
|
||||
row.height = Cm(3.45)
|
||||
for cell in row.cells:
|
||||
cell.width = Cm(13.5)
|
||||
|
||||
blocks = [
|
||||
('01', 'ЗАБОЙ', 'Что болит на самом деле? У кого? Доказательства: факты, интервью, данные.', GREEN, 'F3F9F6'),
|
||||
('02', 'СМЕТА', 'Кому и насколько станет лучше? Сколько людей? Как измерим? 3 метрики.', GREEN, 'F3F9F6'),
|
||||
('03', 'ЗАВОД', 'Что нужно, чтобы решалось системно? Люди, данные, партнёры, деньги.', GREEN, 'F3F9F6'),
|
||||
('04', 'РОЗА', 'Как люди это почувствуют? Сценарий использования. Что скажут?', ROSE, 'FDF4F6'),
|
||||
('05', 'ХОЗЯИН', 'Кто останется после форума? Модель, команда, первый шаг.', ROSE, 'FDF4F6'),
|
||||
('06', 'РИСКИ', 'Что может пойти не так? Как снижаем?', ROSE, 'FDF4F6'),
|
||||
('07', 'МЕТРИКИ', '3 цифры, по которым поймём, что получилось.', GREEN, 'F3F9F6'),
|
||||
('08', 'ПИТЧ', 'Одна фраза, с которой начнём защиту.', GREEN, 'F3F9F6'),
|
||||
]
|
||||
|
||||
for idx, (num, title, prompt, color, fill) in enumerate(blocks):
|
||||
row = idx // 2
|
||||
col = idx % 2
|
||||
add_block(table.cell(row, col), num, title, prompt, color, fill, lines=3)
|
||||
|
||||
# Footer
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(4)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Заполняйте по шагам. Не переходите к следующему блоку, пока не закрыт предыдущий. • Метод Дегтярева • Форум молодёжи Донбасса, 2026')
|
||||
set_run(r, size=7.5, color=LIGHT)
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.section import WD_ORIENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
SRC = 'Метод_Дегтярева_концепция_трека.md'
|
||||
OUT = 'Метод_Дегтярева_концепция_трека.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT_GRAY = RGBColor(0x8A, 0x90, 0x95)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=10.5, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
rFonts.set(qn('w:ascii'), name)
|
||||
rFonts.set(qn('w:hAnsi'), name)
|
||||
rFonts.set(qn('w:eastAsia'), name)
|
||||
rFonts.set(qn('w:cs'), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=45, bottom=45, left=80, right=80):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='D9E2DD', size=4):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def para_bottom_border(paragraph, color='2E7D5B', size=8):
|
||||
pPr = paragraph._p.get_or_add_pPr()
|
||||
pBdr = OxmlElement('w:pBdr')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'single')
|
||||
bottom.set(qn('w:sz'), str(size))
|
||||
bottom.set(qn('w:space'), '4')
|
||||
bottom.set(qn('w:color'), color)
|
||||
pBdr.append(bottom)
|
||||
pPr.append(pBdr)
|
||||
|
||||
|
||||
def para_left_border(paragraph, color='B03A55', size=18):
|
||||
pPr = paragraph._p.get_or_add_pPr()
|
||||
pBdr = OxmlElement('w:pBdr')
|
||||
left = OxmlElement('w:left')
|
||||
left.set(qn('w:val'), 'single')
|
||||
left.set(qn('w:sz'), str(size))
|
||||
left.set(qn('w:space'), '8')
|
||||
left.set(qn('w:color'), color)
|
||||
pBdr.append(left)
|
||||
pPr.append(pBdr)
|
||||
|
||||
|
||||
def add_page_number(paragraph):
|
||||
run = paragraph.add_run()
|
||||
fld1 = OxmlElement('w:fldChar')
|
||||
fld1.set(qn('w:fldCharType'), 'begin')
|
||||
instr = OxmlElement('w:instrText')
|
||||
instr.set(qn('xml:space'), 'preserve')
|
||||
instr.text = 'PAGE'
|
||||
fld2 = OxmlElement('w:fldChar')
|
||||
fld2.set(qn('w:fldCharType'), 'end')
|
||||
run._r.append(fld1)
|
||||
run._r.append(instr)
|
||||
run._r.append(fld2)
|
||||
set_run(run, size=8, color=LIGHT_GRAY)
|
||||
|
||||
|
||||
def parse_inline(text):
|
||||
parts = re.split(r'(\*\*.*?\*\*|_.*?_)', text)
|
||||
out = []
|
||||
for p in parts:
|
||||
if not p:
|
||||
continue
|
||||
if p.startswith('**') and p.endswith('**') and len(p) > 4:
|
||||
out.append((p[2:-2], True, False))
|
||||
elif p.startswith('_') and p.endswith('_') and len(p) > 2:
|
||||
out.append((p[1:-1], False, True))
|
||||
else:
|
||||
out.append((p, False, False))
|
||||
return out
|
||||
|
||||
|
||||
def add_rich_paragraph(doc, text, style=None, size=10.5, color=DARK, bold=False,
|
||||
italic=False, align=None, space_before=0, space_after=4,
|
||||
left_indent=None, first_line_indent=None, line_spacing=1.15):
|
||||
p = doc.add_paragraph(style=style) if style else doc.add_paragraph()
|
||||
pf = p.paragraph_format
|
||||
pf.space_before = Pt(space_before)
|
||||
pf.space_after = Pt(space_after)
|
||||
pf.line_spacing = line_spacing
|
||||
if align is not None:
|
||||
p.alignment = align
|
||||
if left_indent is not None:
|
||||
pf.left_indent = Cm(left_indent)
|
||||
if first_line_indent is not None:
|
||||
pf.first_line_indent = Cm(first_line_indent)
|
||||
for chunk, b, i in parse_inline(text):
|
||||
r = p.add_run(chunk)
|
||||
set_run(r, size=size, bold=(bold or b), italic=(italic or i), color=color)
|
||||
return p
|
||||
|
||||
|
||||
def add_heading(doc, text, level):
|
||||
p = doc.add_paragraph()
|
||||
pf = p.paragraph_format
|
||||
pf.keep_with_next = True
|
||||
if level == 1:
|
||||
pf.space_before = Pt(14)
|
||||
pf.space_after = Pt(5)
|
||||
r = p.add_run(text)
|
||||
set_run(r, name='Georgia', size=15, bold=True, color=GREEN)
|
||||
para_bottom_border(p, color='CFE3D9', size=6)
|
||||
else:
|
||||
pf.space_before = Pt(10)
|
||||
pf.space_after = Pt(3)
|
||||
r = p.add_run(text)
|
||||
set_run(r, name='Arial', size=11.5, bold=True, color=DARK)
|
||||
return p
|
||||
|
||||
|
||||
def add_table(doc, rows, keep_together=True):
|
||||
ncols = len(rows[0])
|
||||
table = doc.add_table(rows=len(rows), cols=ncols)
|
||||
table.alignment = 1
|
||||
table.autofit = True
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
for ri, row in enumerate(rows):
|
||||
tr = table.rows[ri]._tr
|
||||
trPr = tr.get_or_add_trPr()
|
||||
cantSplit = OxmlElement('w:cantSplit')
|
||||
trPr.append(cantSplit)
|
||||
if ri == 0:
|
||||
tblHeader = OxmlElement('w:tblHeader')
|
||||
trPr.append(tblHeader)
|
||||
for ci, cell_text in enumerate(row):
|
||||
cell = table.cell(ri, ci)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
if keep_together and ri < len(rows) - 1:
|
||||
p.paragraph_format.keep_with_next = True
|
||||
for chunk, b, i in parse_inline(cell_text):
|
||||
r = p.add_run(chunk)
|
||||
if ri == 0:
|
||||
set_run(r, size=8.8, bold=True, color=GREEN)
|
||||
else:
|
||||
set_run(r, size=8.8, bold=b, italic=i, color=DARK)
|
||||
if ri == 0:
|
||||
shade(cell, 'EAF4EF')
|
||||
elif ri % 2 == 0:
|
||||
shade(cell, 'F7FAF8')
|
||||
doc.add_paragraph().paragraph_format.space_after = Pt(2)
|
||||
return table
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
|
||||
# Page setup
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(2.0)
|
||||
section.bottom_margin = Cm(2.0)
|
||||
section.left_margin = Cm(2.2)
|
||||
section.right_margin = Cm(2.2)
|
||||
|
||||
# Base styles
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(10.5)
|
||||
normal.font.color.rgb = DARK
|
||||
normal.paragraph_format.line_spacing = 1.15
|
||||
normal.paragraph_format.space_after = Pt(4)
|
||||
|
||||
# Footer
|
||||
footer_p = section.footer.paragraphs[0]
|
||||
footer_p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
|
||||
r = footer_p.add_run('Метод Дегтярева • Форум молодёжи Донбасса • ')
|
||||
set_run(r, size=8, color=LIGHT_GRAY)
|
||||
add_page_number(footer_p)
|
||||
|
||||
# Cover block
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('ФОРУМ МОЛОДЁЖИ ДОНБАССА «ФМД: У НАС ТАК ПРИНЯТО»')
|
||||
set_run(r, size=8.5, bold=True, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(10)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('ТРЕК «МЕТОД ДЕГТЯРЕВА»')
|
||||
set_run(r, name='Georgia', size=26, bold=True, color=DARK)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_after = Pt(8)
|
||||
r = p.add_run('Цифра, у которой есть хозяин')
|
||||
set_run(r, name='Georgia', size=15, italic=True, color=GREEN)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Смена 1 «ПСБ Патриот» • 10–13 ноября 2026')
|
||||
set_run(r, size=9.5, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_after = Pt(10)
|
||||
r = p.add_run('Эксперт и наставник трека: Бойко К.С.')
|
||||
set_run(r, size=9.5, color=GRAY)
|
||||
para_bottom_border(p, color='2E7D5B', size=10)
|
||||
|
||||
# Parse markdown from the first content heading
|
||||
with open(SRC, 'r', encoding='utf-8') as f:
|
||||
lines = f.read().splitlines()
|
||||
|
||||
start = 0
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip().startswith('## Короткая версия'):
|
||||
start = i
|
||||
break
|
||||
|
||||
i = start
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
stripped = line.strip()
|
||||
|
||||
if not stripped:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if stripped == '---':
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(2)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
para_bottom_border(p, color='E3E8E5', size=4)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if stripped.startswith('### '):
|
||||
add_heading(doc, stripped[4:].strip(), 2)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if stripped.startswith('## '):
|
||||
add_heading(doc, stripped[3:].strip(), 1)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if stripped.startswith('# '):
|
||||
add_heading(doc, stripped[2:].strip(), 1)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Table
|
||||
if stripped.startswith('|') and stripped.endswith('|'):
|
||||
rows = []
|
||||
while i < len(lines):
|
||||
s = lines[i].strip()
|
||||
if not (s.startswith('|') and s.endswith('|')):
|
||||
break
|
||||
cells = [c.strip() for c in s.strip('|').split('|')]
|
||||
if all(re.fullmatch(r':?-{2,}:?', c) for c in cells):
|
||||
i += 1
|
||||
continue
|
||||
rows.append(cells)
|
||||
i += 1
|
||||
if rows:
|
||||
add_table(doc, rows, keep_together=(len(rows) <= 7))
|
||||
continue
|
||||
|
||||
# Blockquote
|
||||
if stripped.startswith('> '):
|
||||
text = stripped[2:].strip()
|
||||
p = add_rich_paragraph(doc, text, size=10.5, italic=True, color=GRAY,
|
||||
left_indent=0.8, space_before=2, space_after=6)
|
||||
para_left_border(p, color='B03A55', size=14)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Bullet
|
||||
if stripped.startswith('- '):
|
||||
text = stripped[2:].strip()
|
||||
p = add_rich_paragraph(doc, '• ' + text, size=10.5,
|
||||
left_indent=0.55, first_line_indent=-0.55,
|
||||
space_after=2)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Numbered list
|
||||
m = re.match(r'^(\d+)\.\s+(.*)$', stripped)
|
||||
if m:
|
||||
num, text = m.group(1), m.group(2)
|
||||
p = add_rich_paragraph(doc, f'{num}. ' + text, size=10.5,
|
||||
left_indent=0.6, first_line_indent=-0.6,
|
||||
space_after=2)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Regular paragraph
|
||||
add_rich_paragraph(doc, stripped, size=10.5, space_after=4)
|
||||
i += 1
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
@@ -0,0 +1,363 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_смета.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=60, bottom=60, left=100, right=100):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='C9D6CF', size=6):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=2, width_cm=8.0, height_cm=0.5):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(height_cm)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def add_block(cell, num, title, prompt, color=GREEN, fill='F3F9F6', lines=2, width_cm=8.0):
|
||||
cell.text = ''
|
||||
shade(cell, fill)
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run(num + ' ')
|
||||
set_run(r, size=7.5, bold=True, color=color)
|
||||
r = p.add_run(title)
|
||||
set_run(r, size=9.5, bold=True, color=color)
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(prompt)
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
add_line_table(cell, lines=lines, width_cm=width_cm, height_cm=0.5)
|
||||
|
||||
|
||||
def nested_grid(cell, rows, cols, widths, header_fill='EAF4EF'):
|
||||
tbl = cell.add_table(rows=rows, cols=cols)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), '4')
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), 'D9E2DD')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
for i, c in enumerate(row.cells):
|
||||
c.width = Cm(widths[i])
|
||||
return tbl
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(1.3)
|
||||
section.bottom_margin = Cm(1.1)
|
||||
section.left_margin = Cm(1.4)
|
||||
section.right_margin = Cm(1.4)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
# Header
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('СМЕТА')
|
||||
set_run(r, name='Georgia', size=20, bold=True, color=GREEN)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(1)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Шаг 2. «Смета» • Метод Дегтярева')
|
||||
set_run(r, size=9, bold=True, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Смета — не бухгалтерия. Считаем эффект в людях и ресурсах.')
|
||||
set_run(r, size=8, italic=True, color=ROSE)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(5)
|
||||
r = p.add_run('Команда: ______________________________ Проблема: ______________________________________')
|
||||
set_run(r, size=8.5, color=GRAY)
|
||||
|
||||
# Main table
|
||||
table = doc.add_table(rows=7, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
|
||||
heights = [3.2, 3.2, 4.8, 2.6, 2.6, 2.4, 2.8]
|
||||
for i, row in enumerate(table.rows):
|
||||
row.height = Cm(heights[i])
|
||||
for cell in row.cells:
|
||||
cell.width = Cm(9.0)
|
||||
|
||||
# 01 User (merged)
|
||||
c = table.cell(0, 0).merge(table.cell(0, 1))
|
||||
add_block(c, '01', 'ПОЛЬЗОВАТЕЛЬ',
|
||||
'Кто конкретно? Где и в каком контексте? Что для него важно? Что он делает сейчас?',
|
||||
lines=3, width_cm=17.2)
|
||||
|
||||
# 02 Before / 03 After
|
||||
add_block(table.cell(1, 0), '02', 'БЫЛО',
|
||||
'Что человек делает сейчас? Сколько тратит времени, денег, сил?',
|
||||
color=GRAY, fill='F7F8F8', lines=2, width_cm=8.2)
|
||||
add_block(table.cell(1, 1), '03', 'СТАЛО',
|
||||
'Что изменится? Быстрее, дешевле, проще, доступнее?',
|
||||
color=GREEN, fill='F3F9F6', lines=2, width_cm=8.2)
|
||||
|
||||
# 04 Metrics (merged)
|
||||
c = table.cell(2, 0).merge(table.cell(2, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('04 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ТРИ МЕТРИКИ ЭФФЕКТА')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Охват • Скорость / доступность • Качество / удовлетворённость')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
metrics = nested_grid(c, 4, 3, [4.6, 6.4, 6.2])
|
||||
metrics.rows[0].height = Cm(0.5)
|
||||
for ri in range(1, 4):
|
||||
metrics.rows[ri].height = Cm(0.95)
|
||||
headers = ['МЕТРИКА', 'КАК СЧИТАЕМ', 'СЕЙЧАС → ЦЕЛЬ']
|
||||
for i, h in enumerate(headers):
|
||||
cell = metrics.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=GREEN)
|
||||
labels = ['1. Охват', '2. Скорость', '3. Качество']
|
||||
for ri in range(1, 4):
|
||||
cell = metrics.cell(ri, 0)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(labels[ri - 1])
|
||||
set_run(r, size=7.5, bold=True, color=DARK)
|
||||
for ci in (1, 2):
|
||||
cell = metrics.cell(ri, ci)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 05 Scale (merged)
|
||||
c = table.cell(3, 0).merge(table.cell(3, 1))
|
||||
c.text = ''
|
||||
shade(c, 'FDF4F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('05 ')
|
||||
set_run(r, size=7.5, bold=True, color=ROSE)
|
||||
r = p.add_run('МАСШТАБ')
|
||||
set_run(r, size=9.5, bold=True, color=ROSE)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Сколько людей сталкивается, до кого дотянемся, кого охватим за первый год?')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
scale = nested_grid(c, 2, 3, [5.7, 5.7, 5.7])
|
||||
scale.rows[0].height = Cm(0.45)
|
||||
scale.rows[1].height = Cm(0.85)
|
||||
scale_headers = ['TAM — все, кто сталкивается', 'SAM — до кого дотянемся', 'SOM — охватим за год']
|
||||
for i, h in enumerate(scale_headers):
|
||||
cell = scale.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'FDF4F6')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=ROSE)
|
||||
for i in range(3):
|
||||
cell = scale.cell(1, i)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 06 Price (merged)
|
||||
c = table.cell(4, 0).merge(table.cell(4, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('06 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ЦЕНА ЭФФЕКТА')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Что нужно, чтобы эффект случился? Не отчёт, а ресурсы.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
price = nested_grid(c, 2, 5, [3.4, 3.4, 3.4, 3.4, 3.4])
|
||||
price.rows[0].height = Cm(0.45)
|
||||
price.rows[1].height = Cm(0.85)
|
||||
price_headers = ['ЛЮДИ', 'ВРЕМЯ', 'ДЕНЬГИ', 'ДАННЫЕ', 'ПАРТНЁРЫ']
|
||||
for i, h in enumerate(price_headers):
|
||||
cell = price.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=GREEN)
|
||||
for i in range(5):
|
||||
cell = price.cell(1, i)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 07 Check (merged)
|
||||
c = table.cell(5, 0).merge(table.cell(5, 1))
|
||||
add_block(c, '07', 'ПРОВЕРКА',
|
||||
'Самый дешёвый способ проверить эффект до разработки: интервью, лендинг, ручной прототип, пилот на 10 пользователях.',
|
||||
color=ROSE, fill='FDF4F6', lines=2, width_cm=17.2)
|
||||
|
||||
# 08 Formulation (merged)
|
||||
c = table.cell(6, 0).merge(table.cell(6, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('08 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ФОРМУЛИРОВКА ЭФФЕКТА')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('«___________ получит ___________, потому что ___________. Мы измерим это по ___________.»')
|
||||
set_run(r, size=8, italic=True, color=GRAY)
|
||||
add_line_table(c, lines=2, width_cm=17.2, height_cm=0.5)
|
||||
|
||||
# Footer
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(5)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Считайте в людях, а не в лайках. • Метод Дегтярева • Форум молодёжи Донбасса, 2026')
|
||||
set_run(r, size=7.5, color=LIGHT)
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
@@ -0,0 +1,200 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_гайд_интервью.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def cell_margins(table, top=80, bottom=80, left=180, right=180):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=4, width_cm=17.5, height_cm=0.6):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(height_cm)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def card_border(cell):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), '14' if edge == 'top' else '4')
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), '2E7D5B' if edge == 'top' else 'C9D6CF')
|
||||
tcBorders.append(el)
|
||||
tcPr.append(tcBorders)
|
||||
|
||||
|
||||
def add_card(cell):
|
||||
cell.text = ''
|
||||
card_border(cell)
|
||||
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('ГАЙД ИНТЕРВЬЮ')
|
||||
set_run(r, name='Georgia', size=15, bold=True, color=GREEN)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(1)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Шаг 1. «Забой» • Метод Дегтярева')
|
||||
set_run(r, size=8, bold=True, color=GRAY)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(4)
|
||||
r = p.add_run('Команда: ______________________________')
|
||||
set_run(r, size=8, color=GRAY)
|
||||
|
||||
questions = [
|
||||
'Расскажите, как вы в последний раз решали эту задачу?',
|
||||
'Что в этом процессе было самым неприятным? Почему?',
|
||||
'Что вы сделали, чтобы это обойти?',
|
||||
'Сколько времени, денег или сил это у вас забрало?',
|
||||
'Кто ещё с этим сталкивается?',
|
||||
'Что происходит, если проблему не решить?',
|
||||
'Если бы это исчезло, что бы изменилось?',
|
||||
]
|
||||
for i, q in enumerate(questions, 1):
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
p.paragraph_format.left_indent = Cm(0.5)
|
||||
p.paragraph_format.first_line_indent = Cm(-0.5)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(f'{i}. ')
|
||||
set_run(r, size=9, bold=True, color=GREEN)
|
||||
r = p.add_run(q)
|
||||
set_run(r, size=9, color=DARK)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(4)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('ПРАВИЛА')
|
||||
set_run(r, size=8, bold=True, color=ROSE)
|
||||
|
||||
rules = [
|
||||
'Спрашивайте о прошлом, а не о будущем.',
|
||||
'Не презентуйте свою идею — слушайте.',
|
||||
'Записывайте точные цитаты.',
|
||||
'Ищите повторяемость, а не яркие истории.',
|
||||
'Минимум 5 интервью, лучше 10.',
|
||||
]
|
||||
for rule in rules:
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.left_indent = Cm(0.45)
|
||||
p.paragraph_format.first_line_indent = Cm(-0.45)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run('• ')
|
||||
set_run(r, size=8, color=ROSE)
|
||||
r = p.add_run(rule)
|
||||
set_run(r, size=8, color=GRAY)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(4)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('ЗАМЕТКИ')
|
||||
set_run(r, size=8, bold=True, color=GREEN)
|
||||
add_line_table(cell, lines=4, width_cm=17.5, height_cm=0.6)
|
||||
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(2)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('5 интервью — минимум. Спрашивайте о прошлом, не о будущем.')
|
||||
set_run(r, size=7, italic=True, color=LIGHT)
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(1.0)
|
||||
section.bottom_margin = Cm(1.0)
|
||||
section.left_margin = Cm(1.0)
|
||||
section.right_margin = Cm(1.0)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
for page in range(4):
|
||||
table = doc.add_table(rows=2, cols=1)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
cell_margins(table)
|
||||
for row in table.rows:
|
||||
row.height = Cm(13.2)
|
||||
row.cells[0].width = Cm(19.0)
|
||||
add_card(row.cells[0])
|
||||
if page < 3:
|
||||
doc.add_page_break()
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
@@ -0,0 +1,375 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_хозяин.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=60, bottom=60, left=100, right=100):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='C9D6CF', size=6):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=2, width_cm=8.0, height_cm=0.5):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(height_cm)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def add_block(cell, num, title, prompt, color=GREEN, fill='F3F9F6', lines=2, width_cm=8.0):
|
||||
cell.text = ''
|
||||
shade(cell, fill)
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run(num + ' ')
|
||||
set_run(r, size=7.5, bold=True, color=color)
|
||||
r = p.add_run(title)
|
||||
set_run(r, size=9.5, bold=True, color=color)
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(prompt)
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
add_line_table(cell, lines=lines, width_cm=width_cm, height_cm=0.5)
|
||||
|
||||
|
||||
def nested_grid(cell, rows, cols, widths, header_fill='EAF4EF'):
|
||||
tbl = cell.add_table(rows=rows, cols=cols)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), '4')
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), 'D9E2DD')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
for i, c in enumerate(row.cells):
|
||||
c.width = Cm(widths[i])
|
||||
return tbl
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(1.3)
|
||||
section.bottom_margin = Cm(1.1)
|
||||
section.left_margin = Cm(1.4)
|
||||
section.right_margin = Cm(1.4)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
# Header
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('ХОЗЯИН')
|
||||
set_run(r, name='Georgia', size=20, bold=True, color=GREEN)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(1)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Шаг 5. «Хозяин» • Метод Дегтярева')
|
||||
set_run(r, size=9, bold=True, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Проект не заканчивается защитой. Кто останется после форума?')
|
||||
set_run(r, size=8, italic=True, color=ROSE)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(5)
|
||||
r = p.add_run('Команда: ______________________________ Проект: ______________________________________')
|
||||
set_run(r, size=8.5, color=GRAY)
|
||||
|
||||
# Main table
|
||||
table = doc.add_table(rows=6, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
|
||||
heights = [3.2, 2.6, 3.0, 3.6, 3.2, 2.6]
|
||||
for i, row in enumerate(table.rows):
|
||||
row.height = Cm(heights[i])
|
||||
for cell in row.cells:
|
||||
cell.width = Cm(9.0)
|
||||
|
||||
# 01 Team (merged)
|
||||
c = table.cell(0, 0).merge(table.cell(0, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('01 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('КОМАНДА ПОСЛЕ ФОРУМА')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Кто остаётся? За что отвечает? Почему не уйдёт?')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
team = nested_grid(c, 2, 4, [4.3, 4.3, 4.3, 4.3])
|
||||
team.rows[0].height = Cm(0.45)
|
||||
team.rows[1].height = Cm(1.55)
|
||||
team_headers = ['ПРОДУКТ', 'ТЕХНОЛОГИЯ', 'ПАРТНЁРЫ', 'ДЕНЬГИ']
|
||||
for i, h in enumerate(team_headers):
|
||||
cell = team.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=GREEN)
|
||||
cell2 = team.cell(1, i)
|
||||
cell2.text = ''
|
||||
p = cell2.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Кто?')
|
||||
set_run(r, size=6.5, color=GRAY)
|
||||
|
||||
# 02 Sustainability model (merged)
|
||||
c = table.cell(1, 0).merge(table.cell(1, 1))
|
||||
c.text = ''
|
||||
shade(c, 'FDF4F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('02 ')
|
||||
set_run(r, size=7.5, bold=True, color=ROSE)
|
||||
r = p.add_run('МОДЕЛЬ УСТОЙЧИВОСТИ')
|
||||
set_run(r, size=9.5, bold=True, color=ROSE)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('Как проект живёт без форума? Выберите 1–2 модели.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('□ Грант □ Партнёр □ Бюджет региона □ Подписка □ Вуз □ Волонтёры □ Другое: ____________')
|
||||
set_run(r, size=8, color=DARK)
|
||||
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Почему это сработает: ____________________________________________________________')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
# 03 Partners (two columns)
|
||||
add_block(table.cell(2, 0), '03', 'ПАРТНЁРЫ: КТО НУЖЕН',
|
||||
'Вуз, IT-компания, НКО, министерство, бизнес, школа.',
|
||||
lines=3, width_cm=8.2)
|
||||
add_block(table.cell(2, 1), '04', 'ЧТО МЫ ДАЁМ',
|
||||
'Данные, пилот, охват, репутация, кадры. Партнёр должен получить выгоду.',
|
||||
color=ROSE, fill='FDF4F6', lines=3, width_cm=8.2)
|
||||
|
||||
# 05 Roadmap (merged)
|
||||
c = table.cell(3, 0).merge(table.cell(3, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('05 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ДОРОЖНАЯ КАРТА 30/90')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Что делаем в первые 30 и 90 дней после форума?')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
road = nested_grid(c, 2, 2, [8.6, 8.6])
|
||||
road.rows[0].height = Cm(0.45)
|
||||
road.rows[1].height = Cm(1.75)
|
||||
road_headers = ['30 ДНЕЙ: пилот, 10 пользователей, первая метрика', '90 ДНЕЙ: масштаб, партнёр, заявка на грант']
|
||||
for i, h in enumerate(road_headers):
|
||||
cell = road.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=GREEN)
|
||||
cell2 = road.cell(1, i)
|
||||
cell2.text = ''
|
||||
p = cell2.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 06 Risks (merged)
|
||||
c = table.cell(4, 0).merge(table.cell(4, 1))
|
||||
c.text = ''
|
||||
shade(c, 'FDF4F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('06 ')
|
||||
set_run(r, size=7.5, bold=True, color=ROSE)
|
||||
r = p.add_run('РИСКИ')
|
||||
set_run(r, size=9.5, bold=True, color=ROSE)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Что может убить проект? Как снижаем? Кто отвечает?')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
risks = nested_grid(c, 3, 3, [6.0, 6.6, 4.6])
|
||||
risks.rows[0].height = Cm(0.45)
|
||||
risks.rows[1].height = Cm(0.75)
|
||||
risks.rows[2].height = Cm(0.75)
|
||||
risk_headers = ['РИСК', 'КАК СНИЖАЕМ', 'КТО ОТВЕЧАЕТ']
|
||||
for i, h in enumerate(risk_headers):
|
||||
cell = risks.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'FDF4F6')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=ROSE)
|
||||
for ri in (1, 2):
|
||||
for ci in range(3):
|
||||
cell = risks.cell(ri, ci)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 07 Survival metric + first step (merged)
|
||||
c = table.cell(5, 0).merge(table.cell(5, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('07 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('МЕТРИКА ВЫЖИВАНИЯ И ПЕРВЫЙ ШАГ')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('«Если через 90 дней ___________ не достигнет ___________, мы меняем модель».')
|
||||
set_run(r, size=8, italic=True, color=GRAY)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('Первый шаг на следующей неделе: ______________________. Ответственный: ____________. Дата: ________.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
add_line_table(c, lines=1, width_cm=17.2, height_cm=0.5)
|
||||
|
||||
# Footer
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(5)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Останьтесь хозяином после запуска. • Метод Дегтярева • Форум молодёжи Донбасса, 2026')
|
||||
set_run(r, size=7.5, color=LIGHT)
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
+484
@@ -0,0 +1,484 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pptx import Presentation
|
||||
from pptx.util import Inches, Pt
|
||||
from pptx.dml.color import RGBColor
|
||||
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
||||
from pptx.enum.shapes import MSO_SHAPE, MSO_CONNECTOR
|
||||
|
||||
OUT = 'Метод_Дегтярева_презентация.pptx'
|
||||
|
||||
BG = '121417'
|
||||
CARD = '1C2024'
|
||||
CARD2 = '23282D'
|
||||
GREEN = '3FA37A'
|
||||
GREEN_LIGHT = '7FD1AE'
|
||||
ROSE = 'D94A6A'
|
||||
WHITE = 'F5F5F5'
|
||||
GRAY = '9AA0A6'
|
||||
DARK_GRAY = '6B7075'
|
||||
GOLD = 'C9A227'
|
||||
|
||||
|
||||
def rgb(hexstr):
|
||||
return RGBColor.from_string(hexstr)
|
||||
|
||||
|
||||
def add_bg(slide, color=BG):
|
||||
bg = slide.background
|
||||
fill = bg.fill
|
||||
fill.solid()
|
||||
fill.fore_color.rgb = rgb(color)
|
||||
|
||||
|
||||
def add_text(slide, x, y, w, h, text, font='Arial', size=12, color=WHITE,
|
||||
bold=False, italic=False, align=PP_ALIGN.LEFT,
|
||||
anchor=MSO_ANCHOR.TOP, line_spacing=1.0, space_after=0,
|
||||
margins=(0, 0, 0, 0)):
|
||||
tb = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
|
||||
tf = tb.text_frame
|
||||
tf.word_wrap = True
|
||||
tf.margin_left = Inches(margins[0])
|
||||
tf.margin_right = Inches(margins[1])
|
||||
tf.margin_top = Inches(margins[2])
|
||||
tf.margin_bottom = Inches(margins[3])
|
||||
tf.vertical_anchor = anchor
|
||||
lines = text.split('\n')
|
||||
for i, line in enumerate(lines):
|
||||
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
|
||||
p.alignment = align
|
||||
p.line_spacing = line_spacing
|
||||
p.space_after = Pt(space_after)
|
||||
r = p.add_run()
|
||||
r.text = line
|
||||
r.font.name = font
|
||||
r.font.size = Pt(size)
|
||||
r.font.bold = bold
|
||||
r.font.italic = italic
|
||||
r.font.color.rgb = rgb(color)
|
||||
return tb
|
||||
|
||||
|
||||
def add_rect(slide, x, y, w, h, fill=None, line=None, line_w=1.0,
|
||||
shape=MSO_SHAPE.ROUNDED_RECTANGLE, radius=0.06):
|
||||
shp = slide.shapes.add_shape(shape, Inches(x), Inches(y), Inches(w), Inches(h))
|
||||
if fill:
|
||||
shp.fill.solid()
|
||||
shp.fill.fore_color.rgb = rgb(fill)
|
||||
else:
|
||||
shp.fill.background()
|
||||
if line:
|
||||
shp.line.color.rgb = rgb(line)
|
||||
shp.line.width = Pt(line_w)
|
||||
else:
|
||||
shp.line.fill.background()
|
||||
try:
|
||||
shp.shadow.inherit = False
|
||||
except Exception:
|
||||
pass
|
||||
if shape == MSO_SHAPE.ROUNDED_RECTANGLE:
|
||||
try:
|
||||
shp.adjustments[0] = radius
|
||||
except Exception:
|
||||
pass
|
||||
return shp
|
||||
|
||||
|
||||
def add_line(slide, x1, y1, x2, y2, color=DARK_GRAY, width=1.0):
|
||||
conn = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT,
|
||||
Inches(x1), Inches(y1), Inches(x2), Inches(y2))
|
||||
conn.line.color.rgb = rgb(color)
|
||||
conn.line.width = Pt(width)
|
||||
return conn
|
||||
|
||||
|
||||
def add_notes(slide, text):
|
||||
slide.notes_slide.notes_text_frame.text = text
|
||||
|
||||
|
||||
def slide_title(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_rect(s, 0.75, 1.15, 0.055, 4.95, fill=GREEN, shape=MSO_SHAPE.RECTANGLE)
|
||||
|
||||
add_text(s, 1.05, 0.72, 8.2, 0.3,
|
||||
'ФОРУМ МОЛОДЁЖИ ДОНБАССА • СМЕНА 1 «ПСБ ПАТРИОТ»',
|
||||
size=10, color=GRAY, bold=True)
|
||||
|
||||
add_text(s, 1.0, 1.32, 8.3, 2.35, 'МЕТОД\nДЕГТЯРЕВА',
|
||||
font='Georgia', size=54, color=WHITE, bold=True, line_spacing=0.92)
|
||||
|
||||
add_text(s, 1.05, 3.78, 8.2, 0.55, 'Цифра, у которой есть хозяин',
|
||||
font='Georgia', size=24, color=GREEN, italic=True)
|
||||
|
||||
add_text(s, 1.05, 4.48, 8.2, 0.4, 'От забоя до миллиона роз',
|
||||
size=14, color=GRAY)
|
||||
|
||||
add_text(s, 1.05, 5.05, 8.2, 0.35, 'Бойко К.С. • эксперт и наставник трека',
|
||||
size=11, color=GREEN_LIGHT, bold=True)
|
||||
|
||||
add_line(s, 1.05, 6.18, 8.4, 6.18, color='2A2F34', width=1.0)
|
||||
|
||||
add_text(s, 1.05, 6.38, 8.2, 0.35, '3 дня • 5 шагов • 1 защита',
|
||||
size=11, color=DARK_GRAY)
|
||||
|
||||
# Big 5 circle
|
||||
add_rect(s, 9.55, 1.55, 3.1, 3.1, fill=None, line=GREEN, line_w=1.5,
|
||||
shape=MSO_SHAPE.OVAL)
|
||||
add_text(s, 9.55, 1.72, 3.1, 1.9, '5', font='Georgia', size=96,
|
||||
color=GREEN, bold=True, align=PP_ALIGN.CENTER,
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
add_text(s, 9.55, 3.62, 3.1, 0.35, 'ШАГОВ', size=12, color=GRAY,
|
||||
bold=True, align=PP_ALIGN.CENTER)
|
||||
add_rect(s, 12.28, 4.28, 0.34, 0.34, fill=ROSE, shape=MSO_SHAPE.OVAL)
|
||||
|
||||
add_notes(s, 'Хук: В 1970-х ЮНЕСКО назвала Донецк самым зелёным промышленным городом планеты. '
|
||||
'Город, который мы привыкли называть всесоюзной кочегаркой. Это сделал человек, '
|
||||
'у которого не было ни интернета, ни смартфона, ни искусственного интеллекта. '
|
||||
'Его звали Владимир Дегтярев. И у него был метод. За три дня мы применим этот метод к цифре.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_paradox(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Парадокс, который стоит изучать',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'Донецк: из «всесоюзной кочегарки» — в самый зелёный промышленный город планеты',
|
||||
size=13, color=GRAY)
|
||||
|
||||
# Left card
|
||||
add_rect(s, 0.75, 2.0, 4.55, 2.15, fill=CARD)
|
||||
add_text(s, 1.0, 2.2, 4.05, 0.3, 'ДО 1963', size=10, color=DARK_GRAY, bold=True)
|
||||
add_text(s, 1.0, 2.55, 4.05, 0.95, 'ВСЕСОЮЗНАЯ\nКОЧЕГАРКА',
|
||||
font='Georgia', size=22, color=GRAY, bold=True, line_spacing=0.95)
|
||||
add_text(s, 1.0, 3.55, 4.05, 0.4, 'уголь, бараки, дефицит воды', size=11, color=DARK_GRAY)
|
||||
|
||||
# Arrow
|
||||
add_rect(s, 5.55, 2.82, 0.65, 0.5, fill=GREEN, shape=MSO_SHAPE.RIGHT_ARROW)
|
||||
|
||||
# Right card
|
||||
add_rect(s, 6.45, 2.0, 6.1, 2.15, fill=CARD)
|
||||
add_text(s, 6.7, 2.2, 5.6, 0.3, '1970-Е', size=10, color=GREEN, bold=True)
|
||||
add_text(s, 6.7, 2.55, 5.6, 1.0, 'САМЫЙ ЗЕЛЁНЫЙ\nПРОМЫШЛЕННЫЙ ГОРОД\nПЛАНЕТЫ',
|
||||
font='Georgia', size=19, color=GREEN, bold=True, line_spacing=0.95)
|
||||
add_text(s, 6.7, 3.62, 5.6, 0.35, 'ЮНЕСКО. Миллион роз. Ботанический сад.',
|
||||
size=11, color=GRAY)
|
||||
|
||||
# Stats
|
||||
stats = [
|
||||
('3,1×', 'рост жилищного фонда', GREEN),
|
||||
('5', 'академических институтов', GREEN),
|
||||
('1 000 000', 'кустов роз', ROSE),
|
||||
]
|
||||
x = 0.75
|
||||
for num, label, color in stats:
|
||||
add_rect(s, x, 4.65, 3.8, 1.45, fill=CARD2)
|
||||
add_text(s, x + 0.25, 4.82, 3.3, 0.65, num, font='Georgia', size=32,
|
||||
color=color, bold=True)
|
||||
add_text(s, x + 0.25, 5.5, 3.3, 0.35, label, size=10.5, color=GRAY)
|
||||
x += 4.0
|
||||
|
||||
add_text(s, 0.75, 6.55, 11.8, 0.3,
|
||||
'По материалам Донецкого агентства новостей, 20.02.2026',
|
||||
size=9, color=DARK_GRAY)
|
||||
|
||||
add_notes(s, 'Парадокс: город угля и бараков стал самым зелёным промышленным городом планеты. '
|
||||
'Жильё выросло в 3,1 раза, появился научный центр из пяти институтов, миллион роз. '
|
||||
'Вопрос: как? Ответ — метод.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_man(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Человек, который не был айтишником',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'Владимир Иванович Дегтярев (1920–1993) — «хозяин Донбасса»',
|
||||
size=13, color=GRAY)
|
||||
|
||||
facts = [
|
||||
('1920', 'Родился под Ставрополем в семье железнодорожного рабочего'),
|
||||
('1948', 'Назначен на шахту «Центральная» в Чистяково (Торез)'),
|
||||
('1953', 'Возглавил трест «Чистяковантрацит». Ему неполных 33 года'),
|
||||
('1963–1976', 'Первый секретарь Донецкого обкома. 13 лет преобразований'),
|
||||
]
|
||||
x = 0.75
|
||||
for year, text in facts:
|
||||
add_rect(s, x, 2.0, 2.85, 2.55, fill=CARD)
|
||||
add_text(s, x + 0.22, 2.2, 2.4, 0.55, year, font='Georgia', size=24,
|
||||
color=GREEN, bold=True)
|
||||
add_text(s, x + 0.22, 2.85, 2.4, 1.5, text, size=10.5, color=WHITE,
|
||||
line_spacing=1.1)
|
||||
x += 3.05
|
||||
|
||||
# Quote
|
||||
add_rect(s, 0.75, 4.95, 11.8, 1.25, fill=CARD2)
|
||||
add_rect(s, 0.75, 4.95, 0.06, 1.25, fill=ROSE, shape=MSO_SHAPE.RECTANGLE)
|
||||
add_text(s, 1.05, 5.12, 11.2, 0.6,
|
||||
'«Опыта в работе мало, да и годы невелики: было всего неполных 33 года».',
|
||||
font='Georgia', size=16, color=WHITE, italic=True)
|
||||
add_text(s, 1.05, 5.78, 11.2, 0.3, '— В. И. Дегтярев', size=10, color=GRAY)
|
||||
|
||||
add_text(s, 0.75, 6.5, 11.8, 0.35,
|
||||
'Его называли «хозяином» не за должность, а за метод.',
|
||||
size=12, color=GREEN_LIGHT, bold=True)
|
||||
|
||||
add_notes(s, 'Биография: горный инженер, начальник участка шахты, в 32 года — управляющий трестом, '
|
||||
'в 43 — первый секретарь обкома. За 13 лет: жильё, наука, культура, зелёный город. '
|
||||
'Он не был айтишником. Он был хозяином.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_method(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Пять шагов хозяина',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'Каждый шаг — вопрос и артефакт на канвасе',
|
||||
size=13, color=GRAY)
|
||||
|
||||
steps = [
|
||||
('01', 'ЗАБОЙ', 'Что болит\nна самом деле?', 'Карта проблемы', GREEN),
|
||||
('02', 'СМЕТА', 'Кому и насколько\nстанет лучше?', 'Портрет + метрики', GREEN),
|
||||
('03', 'ЗАВОД', 'Что нужно, чтобы\nрешалось системно?', 'Архитектура + ресурсы', GREEN),
|
||||
('04', 'РОЗА', 'Как люди это\nпочувствуют?', 'Ценность + MVP', ROSE),
|
||||
('05', 'ХОЗЯИН', 'Кто останется\nпосле нас?', 'Устойчивость + план', ROSE),
|
||||
]
|
||||
x = 0.75
|
||||
for num, name, question, artifact, color in steps:
|
||||
add_rect(s, x, 2.0, 2.3, 3.85, fill=CARD)
|
||||
add_text(s, x + 0.2, 2.18, 1.9, 0.3, num, size=12, color=color, bold=True)
|
||||
add_text(s, x + 0.2, 2.55, 1.9, 0.45, name, font='Georgia', size=19,
|
||||
color=WHITE, bold=True)
|
||||
add_text(s, x + 0.2, 3.15, 1.9, 0.95, question, size=11, color=GRAY,
|
||||
line_spacing=1.1)
|
||||
add_line(s, x + 0.2, 4.35, x + 2.1, 4.35, color='2E3439', width=1.0)
|
||||
add_text(s, x + 0.2, 4.5, 1.9, 0.7, artifact, size=9.5, color=color,
|
||||
bold=True, line_spacing=1.05)
|
||||
x += 2.45
|
||||
|
||||
add_text(s, 0.75, 6.25, 11.8, 0.4,
|
||||
'Метод работает и для проекта, и для карьеры, и для региона.',
|
||||
size=12, color=GREEN_LIGHT, bold=True)
|
||||
|
||||
add_notes(s, 'Метод: Забой — спустись туда, где реально болит. Смета — посчитай в людях, а не в отчётах. '
|
||||
'Завод — построй систему, а не разовую акцию. Роза — сделай результат видимым и желанным. '
|
||||
'Хозяин — останься после запуска.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_map(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Три дня — пять шагов',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'Лекции дают контекст. Лаборатории дают артефакты. Защита даёт обратную связь.',
|
||||
size=13, color=GRAY)
|
||||
|
||||
add_line(s, 0.9, 3.05, 12.45, 3.05, color='2E3439', width=2.0)
|
||||
|
||||
nodes = [
|
||||
('10.11 • 19:30', 'Установка', 'Зачем и как', 1.9),
|
||||
('11.11 • 10:45', 'Забой + Смета', 'Проблема и пользователь', 4.9),
|
||||
('11.11 • 18:00', 'Завод + Роза', 'Решение и ценность', 7.9),
|
||||
('12.11 • 10:30', 'Хозяин', 'Устойчивость и метрики', 10.9),
|
||||
]
|
||||
for date, title, desc, cx in nodes:
|
||||
add_rect(s, cx - 0.11, 2.94, 0.22, 0.22, fill=GREEN, shape=MSO_SHAPE.OVAL)
|
||||
add_text(s, cx - 1.25, 2.35, 2.5, 0.3, date, size=10.5, color=GREEN,
|
||||
bold=True, align=PP_ALIGN.CENTER)
|
||||
add_text(s, cx - 1.4, 3.35, 2.8, 0.4, title, font='Georgia', size=15,
|
||||
color=WHITE, bold=True, align=PP_ALIGN.CENTER)
|
||||
add_text(s, cx - 1.4, 3.78, 2.8, 0.6, desc, size=10, color=GRAY,
|
||||
align=PP_ALIGN.CENTER, line_spacing=1.05)
|
||||
|
||||
chips = [
|
||||
'Цифровая трансформация',
|
||||
'Ограничения как топливо',
|
||||
'Технология человечная',
|
||||
'Регион как лаборатория',
|
||||
'ИИ в действии',
|
||||
]
|
||||
x = 0.75
|
||||
for chip in chips:
|
||||
add_rect(s, x, 5.0, 2.25, 0.55, fill=CARD, line='2E3439', line_w=0.75)
|
||||
add_text(s, x + 0.1, 5.08, 2.05, 0.4, chip, size=9, color=GRAY,
|
||||
align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
|
||||
x += 2.4
|
||||
|
||||
add_text(s, 0.75, 6.15, 11.8, 0.4,
|
||||
'14:45 — защита проектов. 3 минуты питч + 2 минуты вопросы.',
|
||||
size=12, color=GREEN_LIGHT, bold=True)
|
||||
|
||||
add_notes(s, 'Карта трека: 10 ноября — установка. 11 ноября — проблема, пользователь, решение, ценность. '
|
||||
'12 ноября — устойчивость, консультации, сборка и защита. Лекции работают на метод.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_team(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Команда = маленький обком',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'5–7 человек. Разные специальности. Один канвас.',
|
||||
size=13, color=GRAY)
|
||||
|
||||
roles = [
|
||||
('ЗАБОЙЩИК', 'исследователь: поле, интервью, данные', GREEN),
|
||||
('ИНЖЕНЕР', 'технарь: решение, архитектура, MVP', GREEN),
|
||||
('ХОЗЯИН', 'экономист: ресурсы, метрики, устойчивость', ROSE),
|
||||
('ГОЛОС', 'коммуникатор: питч, визуал, история', ROSE),
|
||||
]
|
||||
positions = [(0.75, 2.0), (3.9, 2.0), (0.75, 3.95), (3.9, 3.95)]
|
||||
for (name, desc, color), (x, y) in zip(roles, positions):
|
||||
add_rect(s, x, y, 2.95, 1.7, fill=CARD)
|
||||
add_text(s, x + 0.22, y + 0.18, 2.5, 0.35, name, font='Georgia', size=15,
|
||||
color=color, bold=True)
|
||||
add_text(s, x + 0.22, y + 0.65, 2.5, 0.85, desc, size=10, color=GRAY,
|
||||
line_spacing=1.1)
|
||||
|
||||
# Canvas preview
|
||||
add_rect(s, 7.15, 2.0, 5.45, 4.25, fill=CARD2)
|
||||
add_text(s, 7.4, 2.18, 4.9, 0.3, 'КАНВАС «МЕТОД ДЕГТЯРЕВА»', size=10,
|
||||
color=GREEN, bold=True)
|
||||
rows = [
|
||||
('01', 'Забой', 'проблема и доказательства'),
|
||||
('02', 'Смета', 'пользователь и метрики'),
|
||||
('03', 'Завод', 'решение и ресурсы'),
|
||||
('04', 'Роза', 'ценность и MVP'),
|
||||
('05', 'Хозяин', 'устойчивость и план'),
|
||||
]
|
||||
y = 2.65
|
||||
for num, name, desc in rows:
|
||||
add_text(s, 7.4, y, 0.45, 0.3, num, size=9, color=GREEN, bold=True)
|
||||
add_text(s, 7.85, y - 0.02, 1.3, 0.3, name, size=11, color=WHITE, bold=True)
|
||||
add_text(s, 9.15, y, 3.2, 0.3, desc, size=9.5, color=GRAY)
|
||||
add_line(s, 7.4, y + 0.42, 12.3, y + 0.42, color='2E3439', width=0.75)
|
||||
y += 0.68
|
||||
|
||||
add_text(s, 0.75, 6.5, 11.8, 0.35,
|
||||
'Канвас печатается на А4. Один на команду.',
|
||||
size=11, color=DARK_GRAY)
|
||||
|
||||
add_notes(s, 'Роли: Забойщик, Инженер, Хозяин, Голос. Команда смешанная. Канвас — один на команду, '
|
||||
'заполняется по шагам. Это не отчёт, а рабочий инструмент.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_result(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_text(s, 0.75, 0.55, 11.8, 0.7, 'Что вы унесёте',
|
||||
font='Georgia', size=30, color=WHITE, bold=True)
|
||||
add_text(s, 0.75, 1.22, 11.8, 0.4,
|
||||
'Проект не заканчивается защитой.',
|
||||
size=13, color=GRAY)
|
||||
|
||||
# Left column
|
||||
add_text(s, 0.75, 1.95, 5.5, 0.35, 'УЧАСТНИК', size=11, color=GREEN, bold=True)
|
||||
left = [
|
||||
('1', 'паспорт проекта'),
|
||||
('3', 'минуты питча'),
|
||||
('30/90', 'дней дорожная карта'),
|
||||
('5', 'шагов методичка'),
|
||||
]
|
||||
y = 2.45
|
||||
for num, label in left:
|
||||
add_text(s, 0.75, y, 1.35, 0.55, num, font='Georgia', size=24,
|
||||
color=GREEN, bold=True)
|
||||
add_text(s, 2.15, y + 0.08, 4.0, 0.4, label, size=12, color=WHITE)
|
||||
add_line(s, 0.75, y + 0.72, 6.1, y + 0.72, color='2E3439', width=0.75)
|
||||
y += 0.9
|
||||
|
||||
# Right column
|
||||
add_text(s, 6.95, 1.95, 5.5, 0.35, 'РЕГИОН', size=11, color=ROSE, bold=True)
|
||||
right = [
|
||||
('5–7', 'концепций цифровых сервисов'),
|
||||
('1', 'банк проблем'),
|
||||
('2–3', 'проекта к пилоту или гранту'),
|
||||
('∞', 'тиражируемая методика'),
|
||||
]
|
||||
y = 2.45
|
||||
for num, label in right:
|
||||
add_text(s, 6.95, y, 1.35, 0.55, num, font='Georgia', size=24,
|
||||
color=ROSE, bold=True)
|
||||
add_text(s, 8.35, y + 0.08, 4.2, 0.4, label, size=12, color=WHITE)
|
||||
add_line(s, 6.95, y + 0.72, 12.3, y + 0.72, color='2E3439', width=0.75)
|
||||
y += 0.9
|
||||
|
||||
add_text(s, 0.75, 6.35, 11.8, 0.4,
|
||||
'Лучшие проекты получают дорожную карту и поддержку партнёров.',
|
||||
size=12, color=GREEN_LIGHT, bold=True)
|
||||
|
||||
add_notes(s, 'Результат: у участника — паспорт, питч, дорожная карта, методичка. '
|
||||
'У региона — банк проблем, 5–7 концепций, 2–3 проекта к пилоту или гранту, '
|
||||
'тиражируемая методика.')
|
||||
return s
|
||||
|
||||
|
||||
def slide_cta(prs):
|
||||
s = prs.slides.add_slide(prs.slide_layouts[6])
|
||||
add_bg(s)
|
||||
|
||||
add_rect(s, 0.75, 1.15, 0.055, 4.95, fill=ROSE, shape=MSO_SHAPE.RECTANGLE)
|
||||
|
||||
add_text(s, 1.05, 1.35, 8.2, 0.4, 'ЗАВТРА В 10:45', size=16, color=GREEN, bold=True)
|
||||
|
||||
add_text(s, 1.0, 1.9, 8.6, 1.2, 'Спуск в забой.',
|
||||
font='Georgia', size=48, color=WHITE, bold=True)
|
||||
|
||||
add_text(s, 1.05, 3.35, 8.4, 1.2, 'Приходите с проблемой,\nа не с идеей.',
|
||||
font='Georgia', size=26, color=ROSE, italic=True, line_spacing=1.05)
|
||||
|
||||
add_text(s, 1.05, 4.95, 8.2, 0.4, 'Зал «Жуков 2»', size=14, color=GRAY)
|
||||
|
||||
add_line(s, 1.05, 6.15, 8.4, 6.15, color='2A2F34', width=1.0)
|
||||
|
||||
add_text(s, 1.05, 6.35, 8.2, 0.35,
|
||||
'Метод Дегтярева • Цифра, у которой есть хозяин',
|
||||
size=11, color=DARK_GRAY)
|
||||
|
||||
# Decorative circle
|
||||
add_rect(s, 9.55, 1.55, 3.1, 3.1, fill=None, line=ROSE, line_w=1.5,
|
||||
shape=MSO_SHAPE.OVAL)
|
||||
add_text(s, 9.55, 2.55, 3.1, 1.1, '→', font='Georgia', size=64,
|
||||
color=ROSE, bold=True, align=PP_ALIGN.CENTER,
|
||||
anchor=MSO_ANCHOR.MIDDLE)
|
||||
|
||||
add_notes(s, 'Вызов: завтра в 10:45 — спуск в забой. Зал «Жуков 2». '
|
||||
'Приходите с проблемой, а не с идеей. Дальше — метод Дегтярева.')
|
||||
return s
|
||||
|
||||
|
||||
def build():
|
||||
prs = Presentation()
|
||||
prs.slide_width = Inches(13.333)
|
||||
prs.slide_height = Inches(7.5)
|
||||
|
||||
slide_title(prs)
|
||||
slide_paradox(prs)
|
||||
slide_man(prs)
|
||||
slide_method(prs)
|
||||
slide_map(prs)
|
||||
slide_team(prs)
|
||||
slide_result(prs)
|
||||
slide_cta(prs)
|
||||
|
||||
prs.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
@@ -0,0 +1,254 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_карта_проблемы.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=70, bottom=70, left=110, right=110):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='C9D6CF', size=6):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=2, width_cm=8.0, height_cm=0.5):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(height_cm)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def add_block(cell, num, title, prompt, color=GREEN, fill='F3F9F6', lines=2, width_cm=8.0):
|
||||
cell.text = ''
|
||||
shade(cell, fill)
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run(num + ' ')
|
||||
set_run(r, size=7.5, bold=True, color=color)
|
||||
r = p.add_run(title)
|
||||
set_run(r, size=9.5, bold=True, color=color)
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(prompt)
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
add_line_table(cell, lines=lines, width_cm=width_cm, height_cm=0.5)
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(1.3)
|
||||
section.bottom_margin = Cm(1.1)
|
||||
section.left_margin = Cm(1.4)
|
||||
section.right_margin = Cm(1.4)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
# Header
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('КАРТА ПРОБЛЕМЫ')
|
||||
set_run(r, name='Georgia', size=20, bold=True, color=GREEN)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(1)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Шаг 1. «Забой» • Метод Дегтярева')
|
||||
set_run(r, size=9, bold=True, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(5)
|
||||
r = p.add_run('Команда: ______________________________ Проблемное поле: ______________________________________')
|
||||
set_run(r, size=8.5, color=GRAY)
|
||||
|
||||
# Main table
|
||||
table = doc.add_table(rows=7, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
|
||||
heights = [3.0, 3.0, 3.0, 1.8, 3.8, 3.2, 2.8]
|
||||
for i, row in enumerate(table.rows):
|
||||
row.height = Cm(heights[i])
|
||||
for cell in row.cells:
|
||||
cell.width = Cm(9.0)
|
||||
|
||||
# Rows 0-2: two columns
|
||||
blocks = [
|
||||
('01', 'КТО?', 'Конкретная группа. Не «люди», а кто именно.', 0, 0),
|
||||
('02', 'КОГДА И ГДЕ?', 'Контекст: в какой момент и в каком месте возникает проблема.', 0, 1),
|
||||
('03', 'ЧТО ПЫТАЕТСЯ СДЕЛАТЬ?', 'Действие человека: какую задачу он решает.', 1, 0),
|
||||
('04', 'ЧТО МЕШАЕТ?', 'Барьер: что конкретно не даёт это сделать.', 1, 1),
|
||||
('05', 'ЧТО ТЕРЯЕТ?', 'Последствие: время, деньги, здоровье, нервы.', 2, 0),
|
||||
('06', 'КАК РЕШАЕТ СЕЙЧАС?', 'Обходной путь — главный признак реальной боли.', 2, 1),
|
||||
]
|
||||
for num, title, prompt, row, col in blocks:
|
||||
add_block(table.cell(row, col), num, title, prompt, lines=2, width_cm=8.2)
|
||||
|
||||
# Row 3: scale (merged)
|
||||
c = table.cell(3, 0).merge(table.cell(3, 1))
|
||||
add_block(c, '07', 'МАСШТАБ', 'Сколько таких людей? Как часто возникает проблема?', lines=1, width_cm=17.2)
|
||||
|
||||
# Row 4: evidence (merged)
|
||||
c = table.cell(4, 0).merge(table.cell(4, 1))
|
||||
add_block(c, '08', 'ДОКАЗАТЕЛЬСТВА', '5 цитат • 2–3 цифры • 1 наблюдение. Записывайте точные слова людей.', lines=4, width_cm=17.2)
|
||||
|
||||
# Row 5: 5 why (merged)
|
||||
c = table.cell(5, 0).merge(table.cell(5, 1))
|
||||
c.text = ''
|
||||
shade(c, 'FDF4F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('09 ')
|
||||
set_run(r, size=7.5, bold=True, color=ROSE)
|
||||
r = p.add_run('5 ПОЧЕМУ')
|
||||
set_run(r, size=9.5, bold=True, color=ROSE)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Цепочка до причины, а не симптома.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
why = c.add_table(rows=1, cols=5)
|
||||
why.autofit = False
|
||||
tblPr = why._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), '4')
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), 'D9E2DD')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for i, cell in enumerate(why.rows[0].cells):
|
||||
cell.width = Cm(3.4)
|
||||
cell.text = ''
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(str(i + 1))
|
||||
set_run(r, size=8, bold=True, color=ROSE)
|
||||
p2 = cell.add_paragraph()
|
||||
p2.paragraph_format.space_before = Pt(0)
|
||||
p2.paragraph_format.space_after = Pt(0)
|
||||
r = p2.add_run('Почему?')
|
||||
set_run(r, size=7, color=GRAY)
|
||||
|
||||
# Row 6: formulation (merged)
|
||||
c = table.cell(6, 0).merge(table.cell(6, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('10 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ФОРМУЛИРОВКА ПРОБЛЕМЫ')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('«___________ не может ___________, потому что ___________. Это стоит им ___________.»')
|
||||
set_run(r, size=8, italic=True, color=GRAY)
|
||||
add_line_table(c, lines=1, width_cm=17.2, height_cm=0.5)
|
||||
|
||||
# Footer
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(5)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Не называйте решение. Сначала проблема. • Метод Дегтярева • Форум молодёжи Донбасса, 2026')
|
||||
set_run(r, size=7.5, color=LIGHT)
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
@@ -0,0 +1,345 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from docx import Document
|
||||
from docx.shared import Pt, Cm, RGBColor
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.oxml.ns import qn
|
||||
from docx.oxml import OxmlElement
|
||||
|
||||
OUT = 'Метод_Дегтярева_роза.docx'
|
||||
|
||||
GREEN = RGBColor(0x2E, 0x7D, 0x5B)
|
||||
ROSE = RGBColor(0xB0, 0x3A, 0x55)
|
||||
DARK = RGBColor(0x1A, 0x1A, 0x1A)
|
||||
GRAY = RGBColor(0x5F, 0x66, 0x6B)
|
||||
LIGHT = RGBColor(0x8A, 0x90, 0x95)
|
||||
|
||||
|
||||
def set_run(run, name='Arial', size=9, bold=False, italic=False, color=DARK):
|
||||
run.font.name = name
|
||||
run.font.size = Pt(size)
|
||||
run.font.bold = bold
|
||||
run.font.italic = italic
|
||||
run.font.color.rgb = color
|
||||
rPr = run._element.get_or_add_rPr()
|
||||
rFonts = rPr.find(qn('w:rFonts'))
|
||||
if rFonts is None:
|
||||
rFonts = OxmlElement('w:rFonts')
|
||||
rPr.append(rFonts)
|
||||
for attr in ('w:ascii', 'w:hAnsi', 'w:eastAsia', 'w:cs'):
|
||||
rFonts.set(qn(attr), name)
|
||||
|
||||
|
||||
def shade(cell, hex_color):
|
||||
tcPr = cell._tc.get_or_add_tcPr()
|
||||
shd = OxmlElement('w:shd')
|
||||
shd.set(qn('w:val'), 'clear')
|
||||
shd.set(qn('w:color'), 'auto')
|
||||
shd.set(qn('w:fill'), hex_color)
|
||||
tcPr.append(shd)
|
||||
|
||||
|
||||
def cell_margins(table, top=60, bottom=60, left=100, right=100):
|
||||
tblPr = table._tbl.tblPr
|
||||
mar = OxmlElement('w:tblCellMar')
|
||||
for tag, val in (('top', top), ('left', left), ('bottom', bottom), ('right', right)):
|
||||
el = OxmlElement('w:' + tag)
|
||||
el.set(qn('w:w'), str(val))
|
||||
el.set(qn('w:type'), 'dxa')
|
||||
mar.append(el)
|
||||
tblPr.append(mar)
|
||||
|
||||
|
||||
def table_borders(table, color='C9D6CF', size=6):
|
||||
tblPr = table._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), str(size))
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), color)
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
|
||||
|
||||
def add_line_table(cell, lines=2, width_cm=8.0, height_cm=0.5):
|
||||
tbl = cell.add_table(rows=lines, cols=1)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'none')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
row.height = Cm(height_cm)
|
||||
c = row.cells[0]
|
||||
c.width = Cm(width_cm)
|
||||
tcPr = c._tc.get_or_add_tcPr()
|
||||
tcBorders = OxmlElement('w:tcBorders')
|
||||
bottom = OxmlElement('w:bottom')
|
||||
bottom.set(qn('w:val'), 'dotted')
|
||||
bottom.set(qn('w:sz'), '4')
|
||||
bottom.set(qn('w:space'), '0')
|
||||
bottom.set(qn('w:color'), 'AFC4B9')
|
||||
tcBorders.append(bottom)
|
||||
tcPr.append(tcBorders)
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
|
||||
|
||||
def add_block(cell, num, title, prompt, color=GREEN, fill='F3F9F6', lines=2, width_cm=8.0):
|
||||
cell.text = ''
|
||||
shade(cell, fill)
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run(num + ' ')
|
||||
set_run(r, size=7.5, bold=True, color=color)
|
||||
r = p.add_run(title)
|
||||
set_run(r, size=9.5, bold=True, color=color)
|
||||
p = cell.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
p.paragraph_format.line_spacing = 1.0
|
||||
r = p.add_run(prompt)
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
add_line_table(cell, lines=lines, width_cm=width_cm, height_cm=0.5)
|
||||
|
||||
|
||||
def nested_grid(cell, rows, cols, widths, header_fill='EAF4EF'):
|
||||
tbl = cell.add_table(rows=rows, cols=cols)
|
||||
tbl.autofit = False
|
||||
tblPr = tbl._tbl.tblPr
|
||||
borders = OxmlElement('w:tblBorders')
|
||||
for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
el = OxmlElement('w:' + edge)
|
||||
el.set(qn('w:val'), 'single')
|
||||
el.set(qn('w:sz'), '4')
|
||||
el.set(qn('w:space'), '0')
|
||||
el.set(qn('w:color'), 'D9E2DD')
|
||||
borders.append(el)
|
||||
tblPr.append(borders)
|
||||
for row in tbl.rows:
|
||||
for i, c in enumerate(row.cells):
|
||||
c.width = Cm(widths[i])
|
||||
return tbl
|
||||
|
||||
|
||||
def build():
|
||||
doc = Document()
|
||||
section = doc.sections[0]
|
||||
section.page_width = Cm(21.0)
|
||||
section.page_height = Cm(29.7)
|
||||
section.top_margin = Cm(1.3)
|
||||
section.bottom_margin = Cm(1.1)
|
||||
section.left_margin = Cm(1.4)
|
||||
section.right_margin = Cm(1.4)
|
||||
|
||||
normal = doc.styles['Normal']
|
||||
normal.font.name = 'Arial'
|
||||
normal.font.size = Pt(9)
|
||||
normal.font.color.rgb = DARK
|
||||
|
||||
# Header
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('РОЗА')
|
||||
set_run(r, name='Georgia', size=20, bold=True, color=ROSE)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(1)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Шаг 4. «Роза» • Метод Дегтярева')
|
||||
set_run(r, size=9, bold=True, color=GRAY)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('Сделайте результат видимым и желанным. Не функция, а то, что человек почувствует.')
|
||||
set_run(r, size=8, italic=True, color=ROSE)
|
||||
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(5)
|
||||
r = p.add_run('Команда: ______________________________ Продукт: ______________________________________')
|
||||
set_run(r, size=8.5, color=GRAY)
|
||||
|
||||
# Main table
|
||||
table = doc.add_table(rows=6, cols=2)
|
||||
table.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
table.autofit = False
|
||||
table_borders(table)
|
||||
cell_margins(table)
|
||||
|
||||
heights = [3.0, 3.6, 2.6, 3.2, 3.0, 2.6]
|
||||
for i, row in enumerate(table.rows):
|
||||
row.height = Cm(heights[i])
|
||||
for cell in row.cells:
|
||||
cell.width = Cm(9.0)
|
||||
|
||||
# 01 Value (merged)
|
||||
c = table.cell(0, 0).merge(table.cell(0, 1))
|
||||
add_block(c, '01', 'ЦЕННОСТЬ В ОДНОМ ПРЕДЛОЖЕНИИ',
|
||||
'«[Продукт] помогает [группа] [решить проблему] за счёт [механизм]. В отличие от [альтернатива], мы [отличие]».',
|
||||
lines=3, width_cm=17.2)
|
||||
|
||||
# 02 Scenario (merged)
|
||||
c = table.cell(1, 0).merge(table.cell(1, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('02 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('СЦЕНАРИЙ ИСПОЛЬЗОВАНИЯ')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Путь человека от боли до результата. Пять шагов.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
scenario = nested_grid(c, 2, 5, [3.4, 3.4, 3.4, 3.4, 3.4])
|
||||
scenario.rows[0].height = Cm(0.45)
|
||||
scenario.rows[1].height = Cm(1.55)
|
||||
headers = ['1. ТРИГГЕР', '2. ВХОД', '3. ДЕЙСТВИЕ', '4. РЕЗУЛЬТАТ', '5. ВОЗВРАТ']
|
||||
prompts = ['Что происходит в момент боли?', 'Как человек попадает в продукт?', 'Что он делает?', 'Что получает?', 'Почему возвращается?']
|
||||
for i, h in enumerate(headers):
|
||||
cell = scenario.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=6.8, bold=True, color=GREEN)
|
||||
cell2 = scenario.cell(1, i)
|
||||
cell2.text = ''
|
||||
p = cell2.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(prompts[i])
|
||||
set_run(r, size=6.5, color=GRAY)
|
||||
|
||||
# 03 Before / 04 After
|
||||
add_block(table.cell(2, 0), '03', 'ДО',
|
||||
'Что человек чувствует сейчас: тревога, злость, усталость, стыд?',
|
||||
color=GRAY, fill='F7F8F8', lines=2, width_cm=8.2)
|
||||
add_block(table.cell(2, 1), '04', 'ПОСЛЕ',
|
||||
'Что он почувствует с продуктом: спокойствие, контроль, гордость?',
|
||||
color=ROSE, fill='FDF4F6', lines=2, width_cm=8.2)
|
||||
|
||||
# 05 MVP (merged)
|
||||
c = table.cell(3, 0).merge(table.cell(3, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('05 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('MVP — МИНИМАЛЬНАЯ ВЕРСИЯ, КОТОРАЯ ПРОВЕРЯЕТ ЦЕННОСТЬ')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Что обязательно входит, что делаем вручную, что не входит в первую версию.')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
mvp = nested_grid(c, 2, 3, [5.7, 5.7, 5.7])
|
||||
mvp.rows[0].height = Cm(0.45)
|
||||
mvp.rows[1].height = Cm(1.35)
|
||||
mvp_headers = ['ВХОДИТ В MVP', 'ДЕЛАЕМ ВРУЧНУЮ', 'НЕ ВХОДИТ']
|
||||
for i, h in enumerate(mvp_headers):
|
||||
cell = mvp.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'EAF4EF')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=GREEN)
|
||||
cell2 = mvp.cell(1, i)
|
||||
cell2.text = ''
|
||||
p = cell2.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 06 Difference (merged)
|
||||
c = table.cell(4, 0).merge(table.cell(4, 1))
|
||||
c.text = ''
|
||||
shade(c, 'FDF4F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('06 ')
|
||||
set_run(r, size=7.5, bold=True, color=ROSE)
|
||||
r = p.add_run('ОТЛИЧИЕ ОТ АЛЬТЕРНАТИВ')
|
||||
set_run(r, size=9.5, bold=True, color=ROSE)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(3)
|
||||
r = p.add_run('Чем вы лучше по трём параметрам: скорость, цена, доверие?')
|
||||
set_run(r, size=7.5, color=GRAY)
|
||||
|
||||
diff = nested_grid(c, 2, 3, [5.7, 5.7, 5.7])
|
||||
diff.rows[0].height = Cm(0.45)
|
||||
diff.rows[1].height = Cm(1.25)
|
||||
diff_headers = ['МЫ', 'АЛЬТЕРНАТИВА 1', 'АЛЬТЕРНАТИВА 2']
|
||||
for i, h in enumerate(diff_headers):
|
||||
cell = diff.cell(0, i)
|
||||
cell.text = ''
|
||||
shade(cell, 'FDF4F6')
|
||||
p = cell.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(h)
|
||||
set_run(r, size=7, bold=True, color=ROSE)
|
||||
cell2 = diff.cell(1, i)
|
||||
cell2.text = ''
|
||||
p = cell2.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run(' ')
|
||||
set_run(r, size=7, color=RGBColor(0xFF, 0xFF, 0xFF))
|
||||
|
||||
# 07 Pitch (merged)
|
||||
c = table.cell(5, 0).merge(table.cell(5, 1))
|
||||
c.text = ''
|
||||
shade(c, 'F3F9F6')
|
||||
p = c.paragraphs[0]
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(1)
|
||||
r = p.add_run('07 ')
|
||||
set_run(r, size=7.5, bold=True, color=GREEN)
|
||||
r = p.add_run('ПИТЧ-ФОРМУЛА')
|
||||
set_run(r, size=9.5, bold=True, color=GREEN)
|
||||
p = c.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(0)
|
||||
p.paragraph_format.space_after = Pt(2)
|
||||
r = p.add_run('Проблема → Решение → Ценность → Доказательство → Призыв.')
|
||||
set_run(r, size=8, italic=True, color=GRAY)
|
||||
add_line_table(c, lines=2, width_cm=17.2, height_cm=0.5)
|
||||
|
||||
# Footer
|
||||
p = doc.add_paragraph()
|
||||
p.paragraph_format.space_before = Pt(5)
|
||||
p.paragraph_format.space_after = Pt(0)
|
||||
r = p.add_run('Сделайте результат видимым. • Метод Дегтярева • Форум молодёжи Донбасса, 2026')
|
||||
set_run(r, size=7.5, color=LIGHT)
|
||||
|
||||
doc.save(OUT)
|
||||
print('saved', OUT)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
build()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,277 @@
|
||||
# ТРЕК «МЕТОД ДЕГТЯРЕВА»
|
||||
## Цифра, у которой есть хозяин
|
||||
|
||||
**Форум молодёжи Донбасса «ФМД: у нас так принято»**
|
||||
Смена 1 «ПСБ Патриот» • 10–13 ноября 2026
|
||||
Эксперт и наставник трека: **Бойко К.С.**
|
||||
|
||||
---
|
||||
|
||||
## Короткая версия для заявки
|
||||
|
||||
Трек «Метод Дегтярева» — о том, как превращать реальные проблемы региона в цифровые социальные проекты. В основе — метод Владимира Дегтярева, «хозяина Донбасса», который в 1963–1976 годах превратил шахтёрский край в самый зелёный промышленный город планеты. Пять шагов: **Забой** (найди реальную проблему), **Смета** (посчитай в людях и ресурсах), **Завод** (построй системное решение), **Роза** (сделай результат видимым), **Хозяин** (останься после запуска). За три дня смешанные команды студентов технических, социальных и экономических специальностей проходят путь от проблемы до паспорта проекта и питча на 3 минуты. Прикладной результат: 5–7 концепций цифровых социальных сервисов, банк проблем региона, 2–3 проекта, готовых к пилоту или гранту, и тиражируемая методичка.
|
||||
|
||||
---
|
||||
|
||||
## 1. Задумка трека
|
||||
|
||||
В 1970-х годах ЮНЕСКО признала Донецк самым зелёным промышленным городом планеты. Город, который десятилетиями называли «всесоюзной кочегаркой», получил миллион роз, Ботанический сад, новые микрорайоны, научный центр, университеты, цирк «Космос» и Дворец молодёжи «Юность».
|
||||
|
||||
За этим стоит **Владимир Иванович Дегтярев** — горный инженер, прошедший путь от начальника участка шахты до первого секретаря Донецкого обкома (1963–1976). Его называли «хозяином Донбасса» не за должность, а за способ думать и отвечать за результат.
|
||||
|
||||
У Дегтярева не было ни интернета, ни смартфона, ни искусственного интеллекта. Но у него был метод:
|
||||
|
||||
1. сначала спуститься туда, где реально болит;
|
||||
2. потом посчитать в людях и ресурсах;
|
||||
3. построить систему, а не разовую акцию;
|
||||
4. сделать результат видимым и желанным;
|
||||
5. остаться за него ответственным.
|
||||
|
||||
**Задумка трека** — перенести этот метод в цифровую эпоху. Мы не учим «делать сайты» и не соревнуемся в технологиях. Мы учим превращать реальную проблему региона в концепцию цифрового социального продукта или сервиса — и доводить её до состояния, в котором её можно защитить, показать партнёру и подать на грант.
|
||||
|
||||
**Слоган:** «От забоя до миллиона роз».
|
||||
|
||||
**Главная мысль:** цифровизация начинается не с сервера, а с человека. Технология становится ценной только тогда, когда у неё есть хозяин — тот, кто понимает, кому и зачем она служит.
|
||||
|
||||
---
|
||||
|
||||
## 2. Цель трека
|
||||
|
||||
Сформировать у участников практическую компетенцию превращать социальную проблему региона в концепцию цифрового продукта или сервиса с измеримым эффектом и устойчивой моделью — по «методу Дегтярева».
|
||||
|
||||
---
|
||||
|
||||
## 3. Задачи трека
|
||||
|
||||
1. Показать метод как рабочий инструмент: пять шагов — пять артефактов.
|
||||
2. Провести диагностику региона: собрать и верифицировать проблемы, отделить симптом от причины.
|
||||
3. Собрать смешанные команды «технарь + социальщик + экономист» и распределить роли.
|
||||
4. Разработать концепцию цифрового социального проекта: проблема, пользователь, решение, ценность, ресурсы, метрики, риски, устойчивость.
|
||||
5. Оформить паспорт проекта и питч на 3 минуты.
|
||||
6. Защитить проект перед экспертами и получить обратную связь и дорожную карту.
|
||||
7. Отобрать проекты, готовые к пилоту или подаче на грантовый конкурс.
|
||||
|
||||
---
|
||||
|
||||
## 4. Прикладной результат
|
||||
|
||||
**Для участника:**
|
||||
- паспорт проекта на 1 странице и питч на 3 минуты;
|
||||
- обратная связь от экспертов и наставника;
|
||||
- дорожная карта на 30 и 90 дней;
|
||||
- методичка «5 шагов» и канвас, которые можно применять к любому проекту.
|
||||
|
||||
**Паспорт проекта** = заполненный канвас + один слайд + питч на 3 минуты.
|
||||
|
||||
**Для форума и региона:**
|
||||
- банк проблем и решений — карта проблем региона;
|
||||
- 5–7 проработанных концепций цифровых социальных проектов;
|
||||
- 2–3 проекта, готовых к пилоту с партнёром или подаче на грант;
|
||||
- тиражируемая методика трека для следующих смен и регионов.
|
||||
|
||||
---
|
||||
|
||||
## 5. Метод: пять шагов
|
||||
|
||||
| Шаг | Название | Вопрос | Артефакт |
|
||||
|---|---|---|---|
|
||||
| 1 | **Забой** | Что болит на самом деле? | Карта проблемы, интервью, данные |
|
||||
| 2 | **Смета** | Кому и насколько станет лучше? | Портрет пользователя, 3 метрики эффекта |
|
||||
| 3 | **Завод** | Что нужно, чтобы решалось системно? | Архитектура решения, ресурсы, партнёры |
|
||||
| 4 | **Роза** | Как люди это почувствуют? | Ценностное предложение, сценарий, MVP |
|
||||
| 5 | **Хозяин** | Кто останется после нас? | Модель устойчивости, дорожная карта 30/90 |
|
||||
|
||||
**Почему это его метод, а не красивая метафора.** Забой — Дегтярев сам прошёл школу шахты и знал реальную цену работе. Смета — он считал ресурсы и строил кирпичные заводы, чтобы жильё перестало быть дефицитом. Завод — он создал научный центр, университеты и инфраструктуру, а не разовые акции. Роза — он подарил городу миллион роз и Ботанический сад, сделав результат видимым и любимым. Хозяин — он остался «хозяином Донбасса» в памяти людей, потому что отвечал за результат, а не за отчёт.
|
||||
|
||||
### Шаг 1. Забой — «Что болит на самом деле?»
|
||||
|
||||
Выйти из аудитории в реальность: интервью, наблюдение, данные, эксперты. Отделить симптом от причины. Проверить, что проблема существует не только в голове команды.
|
||||
|
||||
**Артефакт:** карта проблемы, цитаты, цифры, доказательства.
|
||||
|
||||
### Шаг 2. Смета — «Кому и насколько станет лучше?»
|
||||
|
||||
Определить пользователя и социальный эффект. Не «людям», а конкретным группам. Не «станет лучше», а на сколько и как измерим.
|
||||
|
||||
**Артефакт:** портрет пользователя, 3 метрики эффекта.
|
||||
|
||||
### Шаг 3. Завод — «Что нужно, чтобы решалось системно?»
|
||||
|
||||
Найти решение, которое меняет систему, а не закрашивает симптом. Понять, какие ресурсы, данные, партнёры и компетенции нужны.
|
||||
|
||||
**Артефакт:** архитектура решения, карта ресурсов и партнёров.
|
||||
|
||||
### Шаг 4. Роза — «Как люди это почувствуют?»
|
||||
|
||||
Сформулировать ценность человеческим языком. Придумать сценарий использования и минимальную версию продукта. Сделать результат видимым и желанным.
|
||||
|
||||
**Артефакт:** ценностное предложение, сценарий, MVP.
|
||||
|
||||
### Шаг 5. Хозяин — «Кто останется после нас?»
|
||||
|
||||
Продумать устойчивость: команда, модель, финансирование, первый шаг после форума. Проект не заканчивается защитой.
|
||||
|
||||
**Артефакт:** модель устойчивости, дорожная карта 30/90.
|
||||
|
||||
---
|
||||
|
||||
## 6. Как трек встроен в программу
|
||||
|
||||
| Дата | Время | Формат | Задача | Артефакт |
|
||||
|---|---|---|---|---|
|
||||
| 10.11 | 19:30–20:00 | Установочная сессия | Презентация трека: зачем, как, что получится | 5–7 минут |
|
||||
| 11.11 | 09:00–09:15 | Цифровой квиз | Разогрев и диагностика | — |
|
||||
| 11.11 | 09:15–10:00 | Лекция А. Фахрутдинова | Контекст: цифровая трансформация социальной сферы | — |
|
||||
| 11.11 | 10:45–12:00 | Лаборатория | Шаги 1–2: проблема и пользователь | Карта проблемы + портрет |
|
||||
| 11.11 | 16:30–17:15 | Лекция RUTUBE | Ограничения как топливо | — |
|
||||
| 11.11 | 17:15–18:00 | Лекция RUTUBE | Как сделать технологию человечной | — |
|
||||
| 11.11 | 18:00–19:00 | Лаборатория | Шаги 3–4: решение и ценность | Решение + MVP |
|
||||
| 12.11 | 09:00–09:45 | Лекция К. Бойко | Регион как лаборатория + ИИ-продукт | — |
|
||||
| 12.11 | 09:45–10:30 | Лекция Л. Макольской | ИИ в действии: от запроса до решения | — |
|
||||
| 12.11 | 10:30–11:30 | Лаборатория | Шаг 5: устойчивость и метрики | Модель устойчивости |
|
||||
| 12.11 | 11:30–12:30 | Консультация | Индивидуальная доработка | — |
|
||||
| 12.11 | 13:00–14:30 | Лаборатория | Сборка паспорта и питча | Паспорт + питч |
|
||||
| 12.11 | 14:45–16:30 | Защита | Презентация проектов | Оценка экспертов; 3 мин питч + 2 мин вопросы |
|
||||
|
||||
---
|
||||
|
||||
## 7. Формат лаборатории
|
||||
|
||||
- Команды 5–7 человек, смешанные по специальностям.
|
||||
- Каждая команда выбирает проблему из «карты проблем» или приносит свою.
|
||||
- Каждый блок — один шаг метода и один артефакт на канвасе.
|
||||
- На «Забое» команды работают с печатной «Картой проблемы» и карточкой «Гайд интервью».
|
||||
- На «Смете» — с печатным листом «Смета»: пользователь, было → стало, три метрики, масштаб, цена эффекта.
|
||||
- На «Розе» — с листом «Роза»: ценность, сценарий, MVP, отличие от альтернатив.
|
||||
- На «Хозяине» — с листом «Хозяин»: команда, модель устойчивости, партнёры, дорожная карта 30/90, риски.
|
||||
- «Доска хозяина» — визуальная карта прогресса команд.
|
||||
- Консультации экспертов по запросу.
|
||||
- Защита: 3 минуты питч + 2 минуты вопросы.
|
||||
|
||||
---
|
||||
|
||||
## 8. Роли в команде
|
||||
|
||||
| Роль | Кто это | За что отвечает |
|
||||
|---|---|---|
|
||||
| **Забойщик** | Исследователь | Поле, интервью, данные, доказательства |
|
||||
| **Инженер** | Технарь / продукт | Решение, архитектура, MVP |
|
||||
| **Хозяин** | Экономист / менеджер | Ресурсы, метрики, устойчивость |
|
||||
| **Голос** | Коммуникатор / дизайнер | Питч, визуал, история |
|
||||
|
||||
---
|
||||
|
||||
## 9. Критерии оценки
|
||||
|
||||
| Критерий | Что проверяем | Баллы |
|
||||
|---|---|---|
|
||||
| Доказанность проблемы | Есть поле, данные, цитаты; причина, а не симптом | 0–2 |
|
||||
| Социальный эффект | Кому, сколько, как измеряем | 0–2 |
|
||||
| Цифровая суть | Сервис / платформа / ИИ, а не сайт-визитка | 0–2 |
|
||||
| Реализуемость | Ресурсы, сроки, партнёры | 0–2 |
|
||||
| Устойчивость | Кто «хозяин» после форума | 0–2 |
|
||||
| Питч | Ясность, эмоция, ответы на вопросы | 0–2 |
|
||||
|
||||
**Итого:** 12 баллов.
|
||||
|
||||
**Номинации:**
|
||||
- «Хозяин Донбасса» — лучший проект по сумме баллов.
|
||||
- «Миллион роз» — самый яркий и масштабируемый.
|
||||
- «Забой» — лучшая работа с проблемой и данными.
|
||||
- «Кирпичный завод» — лучшая системная модель.
|
||||
- Приз зрительских симпатий.
|
||||
|
||||
---
|
||||
|
||||
## 10. Сценарий установочной сессии (7 минут)
|
||||
|
||||
### 0:00–0:40 — Хук
|
||||
|
||||
«В 1970-х ЮНЕСКО назвала Донецк самым зелёным промышленным городом планеты. Город, который мы привыкли называть всесоюзной кочегаркой. Это сделал человек, у которого не было ни интернета, ни смартфона, ни искусственного интеллекта. Его звали Владимир Дегтярев. И у него был метод. За три дня мы применим этот метод к цифре».
|
||||
|
||||
### 0:40–2:00 — Кто он
|
||||
|
||||
«Горный инженер. Начальник участка шахты. В 32 года — управляющий трестом. В 43 — первый секретарь Донецкого обкома. За 13 лет: жильё выросло в 3,1 раза, появился научный центр Академии наук, университеты, цирк «Космос», Дворец молодёжи «Юность», Ботанический сад и миллион роз. Его называли «хозяином Донбасса» — не за должность, а за то, что он отвечал за результат».
|
||||
|
||||
### 2:00–3:00 — Метод
|
||||
|
||||
«В чём метод? Пять шагов. **Забой**: спустись туда, где реально болит. **Смета**: посчитай в людях, а не в отчётах. **Завод**: построй систему, а не разовую акцию. **Роза**: сделай результат видимым и желанным. **Хозяин**: останься после запуска. Всё».
|
||||
|
||||
### 3:00–4:30 — Что будет
|
||||
|
||||
«За три дня вы пройдёте эти пять шагов. В первый день — проблема и пользователь. Во второй — решение и ценность. В третий — устойчивость, метрики и защита. Работаем в смешанных командах: технарь, социальщик, экономист, коммуникатор. У каждой команды — канвас и наставник».
|
||||
|
||||
### 4:30–5:30 — Результат
|
||||
|
||||
«На выходе у каждого — паспорт проекта на одну страницу и питч на три минуты. У региона — банк проблем и 5–7 концепций цифровых социальных проектов. Лучшие получат дорожную карту до пилота или гранта. Это не учебная игра: это заготовки реальных проектов».
|
||||
|
||||
### 5:30–6:30 — Правила
|
||||
|
||||
«Три правила. Первое: приходите с проблемой, а не с идеей. Второе: считайте в людях, а не в лайках. Третье: думайте, кто останется хозяином после форума. Всё остальное — на лабораториях».
|
||||
|
||||
### 6:30–7:00 — Вызов
|
||||
|
||||
«Завтра в 10:45 — спуск в забой. Зал «Жуков 2». Приходите с проблемой, а не с идеей. Дальше — метод Дегтярева».
|
||||
|
||||
### Если регламент 5 минут
|
||||
|
||||
- **0:00–0:30 — Хук.** «В 1970-х ЮНЕСКО назвала Донецк самым зелёным промышленным городом планеты. Это сделал человек без интернета и смартфона. Его звали Владимир Дегтярев. У него был метод. За три дня мы применим его к цифре».
|
||||
- **0:30–1:20 — Кто он.** «Горный инженер. В 32 года — управляющий трестом. В 43 — первый секретарь обкома. За 13 лет: жильё, наука, культура, миллион роз. Его называли «хозяином» не за должность, а за метод».
|
||||
- **1:20–2:10 — Метод.** «Пять шагов: Забой — найди реальную проблему. Смета — посчитай в людях. Завод — построй систему. Роза — сделай результат видимым. Хозяин — останься после запуска».
|
||||
- **2:10–3:20 — Что будет.** «Три дня, смешанные команды, канвас, наставник. В первый день — проблема и пользователь. Во второй — решение и ценность. В третий — устойчивость и защита».
|
||||
- **3:20–4:20 — Результат.** «Паспорт проекта, питч на три минуты, дорожная карта. У региона — банк проблем и 5–7 концепций. Лучшие — к пилоту или гранту».
|
||||
- **4:20–5:00 — Вызов.** «Завтра в 10:45 — спуск в забой. Приходите с проблемой, а не с идеей».
|
||||
|
||||
---
|
||||
|
||||
## 11. Структура презентации (8 слайдов)
|
||||
|
||||
1. **Титул.** «Метод Дегтярева. Цифра, у которой есть хозяин».
|
||||
2. **Парадокс.** «Всесоюзная кочегарка» → «самый зелёный промышленный город планеты».
|
||||
3. **Человек.** 4 факта: 1920, шахта, трест в 32, обком 1963–1976.
|
||||
4. **Метод.** 5 шагов: Забой → Смета → Завод → Роза → Хозяин.
|
||||
5. **Карта трека.** 3 дня, 5 шагов, 1 защита.
|
||||
6. **Команда и канвас.** 4 роли, 5–7 человек, один канвас.
|
||||
7. **Прикладной результат.** Паспорт, питч, дорожная карта, методичка; банк проблем, концепции, грант/пилот.
|
||||
8. **Вызов.** «Завтра в 10:45 — спуск в забой. Приходите с проблемой, а не с идеей».
|
||||
|
||||
---
|
||||
|
||||
## 12. Вопросы и ответы
|
||||
|
||||
**Почему трек назван в честь Дегтярева, если он не про IT?**
|
||||
Именно поэтому. Он про метод, а не про инструмент. Дегтярев решал проблемы региона системно, а цифра — сегодняшний инструмент для того же.
|
||||
|
||||
**Это исторический трек?**
|
||||
История — вход. Результат — цифровой проект. Мы берём не биографию, а метод.
|
||||
|
||||
**Участники не айтишники. Что они успеют?**
|
||||
Мы делаем концепцию и прототип мышления, а не production. За три дня реально пройти путь от проблемы до питча, если работать по канвасу.
|
||||
|
||||
**Не получится ли очередной агрегатор?**
|
||||
Канвас и критерии не дадут: проблема должна быть доказана, эффект измерим, модель устойчива. «Ещё один маркетплейс» не пройдёт защиту.
|
||||
|
||||
**Как отберёте лучших?**
|
||||
Критерии, эксперты, защита. Шесть критериев по два балла.
|
||||
|
||||
**Что останется после форума?**
|
||||
Паспорта проектов, банк проблем, методичка, дорожные карты, сообщество команд и наставников.
|
||||
|
||||
---
|
||||
|
||||
## 13. Что нужно от организаторов
|
||||
|
||||
- 5–7 столов, флипчарты или ватманы, маркеры, стикеры.
|
||||
- Проектор, экран, 4 микрофона.
|
||||
- Печатные канвасы, «Карты проблемы», «Сметы», «Розы», «Хозяева» и «Гайды интервью» по числу команд.
|
||||
- Список экспертов на консультации.
|
||||
- Партнёры: Росмолодёжь.Гранты, IT-компании, вузы.
|
||||
- Тайминг защиты и регламент.
|
||||
|
||||
---
|
||||
|
||||
## 14. Открытые вопросы
|
||||
|
||||
- Формат защиты 14:45–16:30: все треки в одном зале? Сколько команд от трека? Регламент — 3 минуты питч + 2 минуты вопросы.
|
||||
- Есть ли доступ к данным региона (статистика, открытые данные) для шага «Забой»?
|
||||
- Нужны ли командам ноутбуки на лабораториях или достаточно печатных канвасов?
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user