# -*- 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()