The Jerusalem PostTrump press secretary Zacharia failed US bar exam despite claiming to practice law - reportCNN TürkAntalya 14 milyon yabancı turisti ağırladı: İşte öne çıkan pazarlarPunchZelensky to resume strikes on Russian refineries as energy truce collapsesDaily MaverickINPICTURES: Wildlife photographer of the year 2026 winnersRTP DesportoSporting de Braga visita Copenhaga para a Liga ConferênciaInquirerFormer Senate leader Koko Pimentel calls for respect for PWDsColliderRussell Crowe’s Best-Reviewed Movie in 27 Years Officially Removed From 1,500 TheatersGhaflaWinnie Odinga Faces Travel Delay After Kenya Airways Cancels Flight On Eve Of Raila MemorialSky TG24Angelina Mango, il nuovo singolo Vita da adulti uscirà il 16 ottobreGIGAZINE2026年10月15日のヘッドラインニュースSoompiB1A4’s Jin Young Dishes On His Upcoming Drama “Summer Vacation,” Chemistry With Cho Yi Hyun, And MoreZDF heuteEntdecken Sie das ZDF-Nachrichtenstudio
The Daily Newsstand · Free, Always
Thursday, October 15, 2026

Как я сделал Report Slides для еженедельных отчётов из CSV в PowerPoint

Translate

Я хочу убрать ручной перенос плановых и фактических показателей из Excel в PowerPoint. Цифры уже собраны, расчёты понятны, структура отчёта почти не меняется. Однако каждую неделю приходится открывать прошлую презентацию, заменять значения, двигать подписи и проверять, не осталась ли где-нибудь старая дата.

Работа небольшая. Повторяется бесконечно.

При этом цена опечатки вполне осязаема: на слайде обсуждают число, которое разошлось с исходной таблицей. Или сравнивают показатели по диаграмме, у которой после обновления незаметно изменилась шкала. Хотелось сделать этот переход воспроизводимым: пользователь загружает новую таблицу, проверяет значения и получает очередной отчёт с теми же правилами оформления.

Так появился Report Slides — локальное приложение на Python, которое принимает CSV с планом и фактом и собирает редактируемый PPTX. Интерфейс открывается в браузере. Модель и API-ключ не нужны, пользовательские отчёты приложение само на диск не записывает.

Ниже я расскажу об устройстве генератора: строгом импорте, арифметике через Decimal, нативных диаграммах PowerPoint, встроенных книгах Excel и состоянии интерфейса. Больше всего внимания досталось местам, где маленькая утилита способна незаметно исказить результат.

Report Slides собирает регулярный отчёт из CSV в редактируемый PowerPoint

Report Slides собирает регулярный отчёт из CSV в редактируемый PowerPoint

Один повторяющийся сценарий определяет весь продукт

Представьте руководителя небольшой команды. За неделю у него появились четыре числа: сколько заявок планировали, сколько получили, какой была выручка и сколько времени занимал ответ. Источник — обычная таблица. Получатель ждёт презентацию.

Этому человеку нужен короткий маршрут:

  1. Сохранить лист Excel как CSV UTF-8.

  2. Загрузить файл и сверить прочитанные значения.

  3. Указать период, при необходимости добавить комментарий.

  4. Скачать PowerPoint.

Через неделю повторить.

Первый релиз я ограничил одним файлом, 12 показателями и одним светлым шаблоном 16:9. Название отчёта, период и комментарий задаются в интерфейсе. Состав слайдов определяется данными: сначала титул, затем таблицы, потом диаграммы по единицам измерения.

У этого решения есть существенная граница. Если вручную переставить объекты в скачанном PowerPoint, следующий экспорт этих правок не подхватит. Новая презентация снова соберётся из CSV и полей приложения. Постоянство оформления обеспечивает встроенный шаблон.

Такую границу лучше назвать сразу. Фраза «обновление презентации» легко создаёт ожидание, что программа умеет бережно редактировать произвольный корпоративный PPTX. Первый релиз этого не поддерживает.

Архитектура из трёх модулей

В приложении три основных модуля:

app.py       Интерфейс Streamlit и состояние текущего отчёта
data.py      Проверка CSV, модель показателя и расчёты
slides.py    Разбиение на страницы и сборка PPTX

Поток данных получается коротким:

байты CSV    → parse_csv    → неизменяемые Metric с Decimal    → предварительная таблица и состав слайдов    → build_presentation    → байты PPTX

parse_csv возвращает кортеж показателей. Каждый показатель — dataclass(frozen=True) с названием, планом, фактом и единицей измерения. Рядом находятся вычисляемые свойства: отклонение и отношение факта к плану.

Генератор получает уже прочитанные показатели и текстовые поля, возвращает bytes. Он не знает про виджеты и не управляет скачиванием. Благодаря этому настоящий файл презентации можно собрать и проверить без веб-сервера.

Прямых зависимостей тоже три: streamlit==1.65.0, python-pptx==1.0.2, XlsxWriter==3.2.9. CSV и арифметика обходятся стандартной библиотекой. Для текущего приложения нужен Python 3.11 или новее.

Здесь нет базы, очереди задач и отдельного HTTP API. Пока весь объём работы помещается в 12 строк таблицы, эти компоненты не решают задачу пользователя. Ограниченный размер входа позволяет и генерацию выполнять синхронно по кнопке.

CSV требует точного входного контракта

Формат выглядит так:

показатель;план;факт;единица
Заявки;120;135;шт.
Выручка;250000,50;275000,75;руб.
Время ответа;2,5;2;ч
Возвраты;0;3;шт.

Четыре колонки, фиксированный порядок. Заголовок сравнивается без учёта регистра и внешних пробелов. Пользователь явно выбирает разделитель: точку с запятой или запятую.

С автоматическим определением формата можно было бы сделать интерфейс чуть короче. Я предпочёл явный выбор: десятичная запятая и запятая между колонками легко становятся источником двусмысленности. При разделителе , дробное число с запятой нужно заключать в кавычки. Excel обычно делает это при экспорте.

Дальше начинают работать ограничения:

Вход

Правило

Файл

CSV UTF-8 с BOM или без, до 262 144 байт

Показатели

От 1 до 12, названия уникальны без учёта регистра

Название показателя

До 60 символов

Единица

До 16 символов

План и факт

От −1 000 000 000 до 1 000 000 000, максимум два дробных знака

Текст показателя и единицы

Без табуляции и переводов строк, только допустимые для XML символы

Размер проверяется по реальным байтам файла. Ограничение загрузчика в браузере остаётся дополнительным барьером, но не заменяет проверку внутри parse_csv.

Файл декодируется через utf-8-sig: это позволяет убрать BOM, который встречается в выгрузках Excel. Ошибка кодировки превращается в понятную просьбу сохранить CSV UTF-8. Молча переключаться на другую кодировку программа не пытается.

Для разбора используется csv.reader с strict=True и StringIO(..., newline=''). Но перед ним есть ещё одна проверка — небольшой автомат для кавычек.

Почему понадобилась дополнительная логика? У приложения более узкий контракт, чем у универсального CSV-парсера. Кавычки внутри некавыченного поля нужно отклонять, а пробел перед открывающей кавычкой объяснять отдельной ошибкой. Автомат различает начало поля, внешние пробелы, обычный текст, содержимое в кавычках и закрывающую кавычку. При подсчёте строк он учитывает LF, CR и CRLF, причём CRLF считается одним переносом.

После этого стандартный парсер занимается своим делом. Повторять его целиком не пришлось.

Есть и менее заметная последовательность проверок: табы и запрещённые переносы в текстовых полях проверяются до strip(). Иначе повреждённое название с переводом строки на краю превратится в приличную строку, а информация об ошибке исчезнет.

Если одна запись не прошла проверку, импорт останавливается целиком. Программа не выбрасывает неудобную строку и не подставляет ноль вместо неразобранного значения. Для отчёта на несколько показателей это особенно существенно: пропавшая строка может изменить весь смысл встречи.

Хорошая ошибка помогает закончить импорт

Человек выгрузил данные из Excel и увидел отказ. Что дальше?

Сообщение «некорректное число» оставляет его один на один с файлом. Поэтому диагностика называет строку, поле и, когда причина узнаваема, способ исправления.

Например, число 250 000,50 содержит разделитель разрядов. В него могут попасть обычный, неразрывный или узкий неразрывный пробел. Приложение предлагает отключить группировку разрядов в числовом формате Excel и заново сохранить CSV.

Для 25% подсказка другая: записать 25 вместо 25%. Единицу % можно указать в соответствующей колонке CSV. Лишние колонки сопровождаются фактическим числом полей и советом удалить лишние колонки справа на листе Excel.

Ничего загадочного. Зато следующую попытку можно сделать, не читая исходники.

При этом я не стал автоматически удалять пробелы внутри чисел или отрезать %. Такое исправление выглядит заботливым до первого случая, когда оно меняет смысл входа. На границе импорта лучше получить явное решение человека.

Арифметика остаётся десятичной до границы диаграммы

Отчёт использует две формулы:

отклонение = факт − план
отношение факта к плану = факт / план × 100

Вторая применяется только при положительном плане. Если план равен нулю или отрицателен, в таблице стоит тире.

Это выбранное правило представления. Отрицательный план математически допускает деление, но смысл получившегося процента без договорённости с предметной областью сомнителен. Приложение показывает исходные значения и отклонение, а отношение не вычисляет.

Входные числа сразу становятся Decimal. Сначала строка проходит проверки пробелов, знака процента и допустимого числового формата. Затем десятичная запятая заменяется точкой, создаётся Decimal и проверяются границы величины. Промежуточного float на этом пути нет.

Отношение округляется до одного дробного знака по ROUND_HALF_UP: 1 / 16 × 100 даёт 6,3. Расчёт идёт внутри localcontext() с точностью 28. Так результат не зависит от того, какой decimal-контекст до этого выставил другой код в процессе.

Основную логику можно выразить следующим самостоятельным фрагментом. В проекте она находится в свойстве Metric.ratio:

from decimal import Decimal, ROUND_HALF_UP, localcontext
from typing import Optional
PERCENT_SCALE = Decimal('100')
RATIO_QUANTUM = Decimal('0.1')
DECIMAL_PRECISION = 28
def calculate_ratio(plan: Decimal, actual: Decimal) -> Optional[Decimal]:    """Calculate the displayed actual to plan ratio    Args:        plan: Validated planned value        actual: Validated actual value    Returns:        Rounded percentage, or None when the plan is not positive    """    if plan <= 0:        return None    with localcontext() as context:        context.prec = DECIMAL_PRECISION        ratio: Decimal = actual / plan * PERCENT_SCALE        rounded: Decimal = ratio.quantize(RATIO_QUANTUM, rounding=ROUND_HALF_UP)        return rounded.copy_abs() if rounded == 0 else rounded

Последняя строка убирает отрицательный ноль после округления. Он вполне может получиться у маленького отрицательного отношения. На слайде −0,0 скорее озадачит, чем поможет.

Единица % не включает специальную арифметику. Входное число 25 остаётся числом 25, включая данные диаграммы. Формат Excel с процентами здесь применять нельзя: он мог бы показать 2500%.

Я также отказался от общего процента выполнения по всему отчёту. У заявок, выручки и времени ответа разные размерности. Их сумма не имеет смысла, а среднее из частных отношений потребовало бы отдельно обосновать веса.

Цвета поэтому обозначают только серии «План» и «Факт». Рост возвратов и снижение времени ответа невозможно оценивать одной и той же автоматической раскраской.

Пагинация задаёт предсказуемый размер отчёта

На одном табличном слайде помещается до четырёх показателей. Диаграммы тоже содержат максимум четыре, но перед разбиением показатели группируются по единице измерения.

В образце четыре строки и три единицы. Получается пять слайдов: титул, одна таблица, график заявок и возвратов, отдельный график выручки, отдельный график времени ответа.

Группы сохраняют порядок первого появления единиц в CSV. Внутри каждой группы порядок показателей тоже сохраняется. Сравнение единиц нечувствительно к регистру, но программа не склеивает синонимы: руб., ₽ и рублей останутся разными группами.

Количество слайдов можно посчитать заранее:

1 + ceil(число показателей / 4)  + сумма ceil(размер группы единицы / 4)

Для 12 показателей с одной единицей это семь слайдов. Если все 12 единиц разные, будет 16. Приложение показывает состав до экспорта, поэтому человек сразу видит, что длинный хвост единиц породил отдельные графики.

Это ещё один довод против тихой нормализации. Можно автоматически признать руб. и ₽ одной сущностью. Тогда потребуется словарь, правила неоднозначностей и объяснение, почему именно эти названия объединились. Первый релиз оставляет решение автору данных.

Редактируемость требует нативных объектов PowerPoint

Диаграмма на картинке может прекрасно выглядеть. Однако число внутри неё уже не поменять через «Изменить данные».

Для редактируемых слайдов я использую обычные текстовые блоки, таблицы PowerPoint и горизонтальные диаграммы BAR_CLUSTERED через python-pptx. У диаграмм две серии: план и факт. Механизм работы с диаграммами описан в документации python-pptx.

За внешним видом у PPTX есть внутреннее устройство. Это ZIP-пакет с XML-частями и связями между ними. У диаграммы имеются числовые кэши в XML и связанная встроенная книга XLSX с данными. В Report Slides и кэш, и книга должны содержать одни и те же исходные значения.

Именно здесь заканчивается арифметика исключительно на Decimal: интерфейс формирования диаграммы получает float. Деление и округление отношения к этому моменту уже выполнены, табличные значения подготовлены отдельно.

Поэтому обещать, что вся презентация хранит произвольную десятичную точность, было бы неверно. Входной контракт ограничивает величину чисел и допускает не больше двух дробных знаков. Тесты читают обе копии данных и сверяют их с источником до сотой, в том числе на значениях 0.01, 0.29 и 999999999.99.

Без такой проверки можно получить неприятное раздвоение: график показывает одно, а при открытии его книги обнаруживается другое. Одного успешного save() недостаточно.

Подпись показателя не должна становиться формулой Excel

Вспомогательная XLSX-книга принесла отдельную задачу безопасности.

Допустим, показатель называется =1+1. Для отчёта это строка. В книгу она тоже должна попасть строкой. Однако универсальная запись ячейки в XlsxWriter может распознать начинающийся с = текст как формулу. Аналогично URL может превратиться в гиперссылку.

У XlsxWriter есть настройки strings_to_formulas и strings_to_urls; по умолчанию обе включены. Это поведение описано в параметрах Workbook.

В Report Slides класс SafeChartData переопределяет свойство xlsxblob у CategoryChartData и сам собирает встроенную книгу. Настройки передаются явно:

from typing import Dict
workbook_options: Dict[str, bool] = {    'in_memory': True,    'strings_to_formulas': False,    'strings_to_urls': False,
}

Название категории и имена серий записываются через write_string, значения — через write_number. Это сохраняет тип на самой границе записи. Обе настройки остаются явно выключенными и для дальнейших изменений этого участка.

in_memory=True решает другую задачу: сборка встроенного XLSX обходится без временных файлов. Одного BytesIO для этого недостаточно — у XlsxWriter есть собственная промежуточная сборка.

При проверке экспорта тесты распаковывают PPTX, затем находят и распаковывают встроенные XLSX. Они ищут узлы формул и гиперссылок в XML листов, внешние связи и макросы в пакетах, а также сверяют буквальный текст категорий.

Снаружи всё ещё выглядит как маленький график с двумя полосками. Внутри я проверяю, что пользовательский текст остался данными.

Шкалу диаграммы приходится рассчитывать отдельно

Для заданного диапазона чисел автоматическая шкала оставляла слишком много решений редактору. В тестах встречаются десятые и сотые доли, большие положительные величины, отрицательные значения и наборы из одних нулей. Хотелось сохранить ноль на оси и получить различимые деления во всех этих случаях.

Алгоритм в calculateaxis_scale начинается с диапазона, включающего ноль. Затем к ненулевым краям добавляется запас 30%: справа или слева нужно место для подписи значения у конца полосы.

Дальше оценивается шаг для четырёх интервалов. Он округляется вверх до ближайшего значения семейства:

1 × 10ⁿ
2 × 10ⁿ
5 × 10ⁿ

В коде среди кандидатов есть и 10 × 10ⁿ: это переход к следующему порядку. Нижняя граница округляется вниз до кратного шагу, верхняя — вверх. Если интервалов получилось меньше трёх, диапазон расширяется.

У шага есть нижний предел 0.01. Подписи обычной ненулевой диаграммы показывают два дробных знака; более мелкие деления могли бы после форматирования превратиться в одинаковые числа.

Для примера достаточно единственного ненулевого значения 0.01. После запаса верхний край равен 0.013, предварительный шаг — 0.00325. Алгоритм подбирает удобный шаг, затем применяет нижний предел 0.01. Диапазон расширяется до трёх интервалов: от 0 до 0.03.

У полностью нулевого набора отдельный случай: шкала от 0 до 1 и целочисленные подписи значений. Полоски имеют нулевую длину, зато сами нули остаются видимыми.

Если все значения отрицательны, верхняя граница заканчивается нулём. Для смешанного набора ось охватывает обе стороны. В таких графиках серии сохраняют свои цвета: автоматическую инверсию цвета отрицательных значений я отключил.

Компромисс тоже виден. Единая большая шкала может сделать маленькую полосу почти незаметной. Группировка по единицам уменьшает риск бессмысленных сравнений, но не устраняет большой разброс внутри группы. Точные числа остаются в подписях и таблице.

Длинные названия влияют на устройство макета

Название показателя может содержать 60 символов. Единица — 16. На титуле разрешены 100 символов названия, 60 символов периода и комментарий до 400 символов, максимум с шестью непустыми абзацами.

На коротком примере почти любой шаблон выглядит прилично. Стоит заполнить лимиты широкими буквами — и подпись уже налезла на график.

Я выделил названиям категорий отдельные текстовые блоки слева от диаграммы. Автоматические подписи оси категорий скрыты. Для самих полос задана область построения через c:manualLayout, чтобы отдельный текст можно было согласовать с центрами категорий.

Так переносами управляет обычный текстовый блок. Размер шрифта не уменьшается по мере удлинения названия: в таблицах и основном тексте это 16 пунктов, в элементах диаграммы — 14. Макет должен выдерживать разрешённый вход при этих размерах.

Цена решения проявляется при редактировании. Встроенная книга хранит полное имя категории, но видимая подпись слева — самостоятельный объект. Если переименовать категорию в Excel, подпись на слайде потребуется поправить отдельно. Числовые значения диаграммы остаются редактируемыми обычным способом.

У комментария на титуле нашёлся другой край: пустые строки. Ограничение в шесть непустых абзацев разрешает длинную последовательность пустых строк между ними. Перед размещением код убирает пустые абзацы, чтобы они не вытеснили содержательный текст за край.

Табуляция в комментарии допускается. Для неё в XML абзаца задаётся ширина шага, связанная с размером шрифта: штатная полудюймовая табуляция слишком быстро расходовала доступную ширину.

Эти детали трудно заметить по одному красивому скриншоту. Они проявляются именно на границах входного контракта.

Широкий слайд ещё не делает широкими его макеты

У презентации есть видимые страницы, мастера, макеты и тема. Если изменить только ширину и высоту слайда, новые страницы могут выглядеть правильно, а элементы унаследованного макета останутся рассчитанными на прежнюю ширину.

В configuretemplate я устанавливаю размер 16:9 и отдельно пересчитываю горизонтальные координаты и ширины элементов мастеров и макетов. Для латинских шрифтов темы задаётся Arial. Шрифты видимых объектов выставляются явно.

Это влияет на продолжение работы в PowerPoint: если человек добавит слайд из встроенного макета, геометрия должна соответствовать широкому формату. Отдельный тест добавляет страницу Title and Content и проверяет положение и ширину её заполнителей.

Метаданные тоже обновляются. В файл попадают текущее время UTC, название отчёта, имя приложения, число слайдов и формат экрана. В экспорт не должны утекать описание и даты библиотечного шаблона.

Здесь я обращаюсь к внутренним XML-объектам python-pptx. Это осознанная зависимость от устройства библиотеки. Версия закреплена, а проверки читают соответствующие части пакета. При обновлении библиотеки эти участки требуют внимания даже тогда, когда публичный вызов сохранения не изменился.

Самая опасная кнопка предлагает скачать старый отчёт

Допустим, пользователь создал PPTX, загрузил новую таблицу, получил ошибку импорта — а кнопка скачивания прошлого результата осталась на месте.

Ничего не упало. Но интерфейс уже вводит в заблуждение.

Для генератора регулярных отчётов соответствие входа и результата важнее самого наличия файла. Поэтому экспорт инвалидируется при изменении данных и оформления. В app.py используются два набора признаков:

Что сравнивается

Состав

Источник данных

Имя файла, SHA-256 его байтов, выбранный разделитель

Оформление отчёта

Название, период, комментарий

Хеш здесь нужен для сравнения содержимого. Одинаковое имя файла не означает, что внутри остались прежние цифры.

При смене источника старые показатели и PPTX убираются до нового импорта. При смене только разделителя период и комментарий сохраняются, но данные разбираются заново, а прежний экспорт исчезает. Изменение названия, периода или комментария тоже убирает старый PPTX.

Если повторная генерация с неизменившимися входами завершилась ошибкой, прежний успешный файл остаётся доступным. Он всё ещё соответствует этим данным. При изменившемся входе такой возможности уже нет.

Состояние находится в st.session_state. У Streamlit изменение виджета запускает повторное выполнение сценария, а callback выполняется перед ним; этот порядок описан в документации Session State. Мне пришлось рассматривать загрузку, редактирование и экспорт как переходы состояния, которые должны переживать такие повторы.

Для нового CSV есть отдельная кнопка сброса. Она сразу удаляет прежний результат, очищает период и комментарий, увеличивает ревизию ключа загрузчика и сохраняет название отчёта.

Зачем явный сброс, если существует событие загрузки? Слишком большой файл браузер может отклонить раньше, чем приложение увидит новые байты. После нажатия «Загрузить новый CSV» старый отчёт уже снят с экрана — независимо от того, закончится ли следующая загрузка успехом.

Наконец, ошибки текстовых полей показываются после редактирования конкретного поля. При первом открытии человек не получает красную форму за то, что ещё ничего не успел ввести. Проверка доступности кнопки генерации при этом работает сразу.

Локальная работа требует явных настроек

По умолчанию сервер слушает 127.0.0.1:8504, сбор статистики использования Streamlit отключён. Интернет нужен для установки зависимостей. Загрузка CSV, просмотр данных и сборка PPTX выполняются локально; в проверенном браузерном сценарии внешних HTTP-запросов не было.

CSV и готовый результат живут в текущей сессии. Приложение не ведёт историю и не записывает пользовательские отчёты на диск; итог сохраняет браузер по действию человека. После обновления страницы или перезапуска сохранность сессии не обещается.

Такое устройство рассчитано на одного пользователя и одну активную вкладку. Размещение в интернете потребует отдельной работы с доступом, сроком жизни данных и ограничением ресурсов. Достаточно поменять адрес прослушивания, чтобы изменить условия эксплуатации; считать это готовым многопользовательским сервисом было бы преждевременно.

При ошибке генерации интерфейс сохраняет введённые данные и показывает понятное сообщение. Сервер пишет traceback и хеш CSV для сопоставления событий. Исходное сообщение исключения заменяется нейтральным перед логированием: внутри него могут оказаться пользовательские значения. Отмену через asyncio.CancelledError обработчик явно пропускает дальше.

Хеш помогает связать сбой с конкретным входом, но сам по себе не делает данные анонимными. Это технический идентификатор, а не обещание обезличивания.

Проверка байтов файла и поведения интерфейса

Для такого приложения недостаточно проверить, что функция вернула непустой bytes. Даже успешное чтение через ту же библиотеку, которая записала файл, отвечает лишь на часть вопросов.

В текущем наборе 58 тестов на unittest. Проверки разделены по границам приложения:

Слой

Что проверяется

Импорт и расчёты

BOM, разделители, кавычки, переводы строк, размер файла, 12 и 13 строк, границы чисел, Unicode, HALF_UP и отрицательный ноль

PPTX и вложенные XLSX

Настоящие таблицы и диаграммы, числовые кэши, содержимое книг, буквальный текст, отсутствие формул и внешних связей, тема, макеты и метаданные

Интерфейс

Начальное состояние, замена и сброс CSV, ошибки, редактирование полей, доступность экспорта, изоляция сессий и отказ генерации

Для интерфейса используется Streamlit AppTest. Он позволяет воспроизводить переходы без ручного обхода формы. Отдельный сценарий Playwright работает с уже запущенным приложением в браузере, скачивает настоящий PPTX и повторяет экспорт с изменённым числом. Затем сценарий сбрасывает отчёт, пробует загрузить CSV с NaN и проверяет, что после отказа импорта прежний PPTX остаётся недоступен.

В журнале приёмки от 14 октября зафиксированы успешные прогоны 58 тестов на Python 3.11 и 3.14 под macOS. Там же описаны браузерные проверки, включая отсутствие внешних запросов в выполненном сценарии.

Визуальная проверка устроена отдельно. Пять наборов данных дают 37 слайдов: обычный пример, предельные длины строк, много разных единиц, отрицательные и нулевые числа, комментарий с пустыми абзацами. Презентации конвертировались в PDF через LibreOffice, затем все страницы просматривались в рендерах. В этих рендерах обрезок и наложений не обнаружено.

До полной приёмки остаётся Microsoft 365 на Windows: открыть файл без восстановления, изменить текст, таблицу и данные диаграммы, сохранить и открыть повторно. Рендеринг LibreOffice не подтверждает этот сценарий. Полный ручной проход с экранным диктором также ещё предстоит.

Поэтому нынешняя оценка конкретна: внутреннее устройство файлов и перечисленные сценарии проверены, редактирование в целевой настольной версии Office ещё ждёт отдельного прогона. Подробности находятся в журнале приёмки.

Первый отчёт можно собрать на готовом образце

Для запуска на macOS или Linux из каталога проекта:

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m streamlit run app.py --server.address 127.0.0.1 --server.port 8504 --browser.gatherUsageStats false

Нужен Python 3.11+. После запуска откройте http://127.0.0.1:8504. Команды для Windows PowerShell есть в README; их проверка на Windows пока не завершена.

В интерфейсе можно скачать образец CSV. Загрузите его обратно, оставьте разделитель ;, укажите период и создайте презентацию. Образец даст пять слайдов. Прежде чем переносить собственный отчёт, удобно проверить на нём весь маршрут, включая открытие файла в своём PowerPoint.

Автоматические проверки запускаются отдельно:

.venv/bin/python -m unittest discover -s tests -v

Если собственный CSV содержит другие колонки или больше 12 показателей, его потребуется подготовить под описанный формат. Импорта XLSX, PDF и произвольного PPTX, редактора макетов и ИИ-комментариев в первом релизе нет.

Что даёт такой небольшой генератор

Самая полезная часть Report Slides — повторяемая связь между исходной таблицей и содержимым презентации. Одинаковые правила импорта, явная арифметика, заранее известный состав слайдов и отказ от скачивания устаревшего результата убирают несколько мест, которые при ручном обновлении приходится проверять по памяти.

При этом после экспорта у человека остаётся обычный PowerPoint. Можно поправить формулировку, изменить ячейку или значение диаграммы, подготовить материал к конкретной встрече.

Я пока не измерял экономию времени на реальных пользователях и не обещаю превосходства над другими инструментами. В репозитории есть план небольшого пилота с повторными обновлениями отчёта. Для решения о развитии продукта такие измерения полезнее впечатления от первого красивого файла.

Если вы регулярно собираете план и факт и узнаёте в этом сценарии свою работу, попробуйте Report Slides на небольшой обезличенной таблице. Начать можно с готового CSV, а затем проверить собственный набор. Проект открыт под MIT, исходники невелики — правила расчёта и экспорта можно изучить и адаптировать под свою задачу.

Report Slides на GitHub — приложение, инструкция запуска и исходный код.

Рядом лежат пример CSV, техническое задание и сценарий пилота. Если проект пригодится, буду рад звезде и обратной связи в Issues. Особенно полезны обезличенные примеры, на которых неудобен формат или ломается макет: именно они покажут, что стоит менять следующим.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.