Трек «Метод Дегтярева»: концепция, презентация и рабочие листы
This commit is contained in:
commit
2c79f59064
28 files changed
+2999
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user