Skip to content

Export a report as Word (.docx)#1924

Open
ghostiee-11 wants to merge 18 commits into
holoviz:mainfrom
ghostiee-11:feat/report-docx-export
Open

Export a report as Word (.docx)#1924
ghostiee-11 wants to merge 18 commits into
holoviz:mainfrom
ghostiee-11:feat/report-docx-export

Conversation

@ghostiee-11

@ghostiee-11 ghostiee-11 commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Adds a Word (.docx) option to the report export menu, alongside Notebook and HTML. The .docx carries the report headings, narrative text, native tables, and charts as images, and mirrors the other exports (so it respects the section selection and includes the AI story and notes). Closes #1913.

Note: this stacks on #1911 (#1922) and #1912 (#1923); the earlier commits belong to those PRs, so please review after they merge. It adds python-docx as a core dependency, matching how nbformat backs the ipynb export.

Untitled.-.09.July.2026.at.22.45.25.1.mp4

Add a Section.include_in_export flag and route to_notebook/to_html
through _export_views/_export_view so a report can be exported with a
subset of its sections. Defaults keep every section included, so
existing exports are unchanged.
Render each report accordion card header as a checkbox bound to the
section include_in_export flag next to its title, so users can pick
which sections to keep before exporting. The checkbox appears once the
report has outputs and leaves the rendered report unchanged.
Apply the HoloViz cleanup review: replace the list-building loop in
Report._export_views with a comprehension. No behavior change.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.70529% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.79%. Comparing base (eaf1c6d) to head (d350ad5).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
lumen/ai/report.py 89.51% 30 Missing ⚠️
lumen/ai/export.py 87.50% 5 Missing ⚠️
lumen/ai/agents/story.py 90.90% 4 Missing ⚠️
lumen/tests/ai/test_docx.py 97.82% 2 Missing ⚠️
lumen/tests/ai/test_story.py 98.80% 2 Missing ⚠️
lumen/tests/ai/test_report.py 98.14% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1924      +/-   ##
==========================================
+ Coverage   71.22%   71.79%   +0.56%     
==========================================
  Files         198      201       +3     
  Lines       34163    34885     +722     
==========================================
+ Hits        24332    25044     +712     
- Misses       9831     9841      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ghostiee-11
ghostiee-11 marked this pull request as ready for review July 9, 2026 17:28
@ghostiee-11
ghostiee-11 requested a review from ahuang11 July 9, 2026 18:20
@ahuang11

ahuang11 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

One quick comment; I think it should be an optional dependency.

Maybe you can create something like

import importlib


def try_import(module: str):
    """Attempt to import a module.

    Returns
    -------
    module | None
        The imported module if successful, otherwise None.
    """
    try:
        return importlib.import_module(module)
    except ImportError:
        return None

@ghostiee-11

Copy link
Copy Markdown
Collaborator Author

Okay sure

Only Report is ever exported, so the base TaskGroup _export_views /
_export_view wrappers (which just returned self.views / self._view) were
dead code. Drop them and move to_notebook / to_html onto Report next to
its _export_views / _export_view overrides.
Backend for annotating a report with an AI story (holoviz#1912): a StoryAgent
(LLMUser + Jinja2 prompts + Story/SectionNote response models) that
writes an overall narrative and per-section notes, plus helpers that
build compact prompt context (specs + data summaries) from the selected
report views.
The Annotate toolbar button (and SpeedDial entry) runs StoryAgent over
the selected sections, inserting an overall story card at the top and a
short note under each section. Story and notes flow into both the
notebook and HTML export, re-running replaces prior annotations, and the
button is disabled when no llm is configured.
Add a _story_outline (list of heading/section blocks) that, when set,
drives the export order and lets headings be inserted, seeded from the
selected sections. Export follows the outline; default order is used
when it is empty.
Add an Arrange toolbar button (and SpeedDial entry) that opens a
JSONEditor seeded from the current sections. Editing the outline (drag to
reorder, add nested heading blocks) updates _story_outline, which drives
the export order.
The report's JSONEditor has its own JS bundle; the app only loaded the
agents' extensions plus filedropper, so a dynamically built report could
not render the editor. Add jsoneditor to the loaded extensions.
Reorder the live accordion to the outline (via _ordered_sections), add an
explicit Apply button, and make the HTML export (_export_view) follow the
outline like the notebook does. Factor the outline walk into a single
_iter_arrangement helper that resolves and de-duplicates sections.
Replace the overall-story-plus-notes annotation with a StoryAgent that returns ordered prose and view blocks, so the exported report reads like a blog post with narrative woven between the charts and tables. Clicking Annotate now opens a guidance popup (tone presets plus free text) to steer the angle, and a Report/Story toggle keeps the section export checkboxes reachable after a story is generated.
Rework the story tests for the block model and add coverage for the guidance dialog presets, the Report/Story toggle, and story reset.
Add a small try_import(module) helper that returns the imported module or None, so optional dependencies can be handled gracefully at the call site.
Add a to_docx export that walks the report views and writes headings, markdown, native tables and chart images into a Word document, wired into the export menu and the mobile SpeedDial. python-docx is an optional dependency: to_docx checks for it via try_import and raises a clear error if it is missing, and the module no longer imports docx at load time.
Add tests for the docx helpers and the report export, skipping the module when python-docx is unavailable, and add python-docx to the test environments so CI still exercises the export.
@ghostiee-11
ghostiee-11 force-pushed the feat/report-docx-export branch from 0cbd362 to c03b638 Compare July 10, 2026 13:40
@ahuang11

Copy link
Copy Markdown
Contributor

Clean up / remove try_import_xarray and try_import_* if you have the dynamic one

Drop the specific try_import_xarray helper now that a generic try_import exists, and check for xarray and xarray-sql directly at the call sites in the xarray source and the UI upload handlers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export Report as DocX

3 participants