Skip to main content
INTERNAL TOOLING

Bulk Office work that cannot quietly go wrong

Two Windows desktop apps, batch .docx editing with verified backups and Outlook mail merge behind a review gate.

THE PROBLEM

What was in the way

A change that takes ten seconds in one Word document takes an afternoon across four hundred. The tools that do it in bulk are usually the ones with no undo, so the safe option is to keep clicking.

The awkward cases are what force the manual route. Background shading sits in six different places inside a .docx, so clearing it means visiting each one. A mail merge where every recipient needs a table of their own rows is not something the built-in merge does.

THE APPROACH

How it was built

Both are Qt desktop apps rather than scripts, so the work is a file picker and a progress bar. Each carries a PyInstaller build config: a frozen binary needs freeze_support and module-level worker functions before multiprocessing runs at all.

Neither tool does the irreversible thing first. The editor copies a file, reopens the copy with python-docx to prove it is readable, and only then lets the batch touch the original.

The mail merge validates rows before anything is composed, and a draft mode leaves the send itself to a human in Outlook.

Input to reversible change
Input to reversible change
HIGHLIGHTS

How it works

Shading hides in six places

The remover walks body text, tables, text boxes in both drawing formats, headers and footers, and footnotes. It also strips the theme fills that silently override an explicit colour.

No character is replaced in silence

Reading Word's HTML export with a lenient decoder turned accents into replacement characters nobody noticed until the mail landed. It now tries each candidate encoding strictly and fails loudly instead.

Every run is reversible

Backups are recorded against their original path, so restoring is one lookup. Cleanup only ever expires backups that were never restored from.

RESULTS

What it does now

verified before overwritethe copy is reopened and read before the original is touched
one bad file, not the batchper-file timeouts and errors are recorded, the run continues
nothing sends unreviewedrecipients and body shown first, with a draft-only mode
STACK

What it is built on

Pythonboth desktop apps
PySide6Qt desktop interface
python-docx and lxmldirect .docx XML edits
win32comWord, Excel and Outlook
pandasspreadsheet rows and grouping
SQLitebackup and job ledger
PyInstallerfrozen Windows build
pytestunit, GUI and integration tests

Got something in your workflow that looks like this?