rpa-bot-handoff: one short Handover SOP (max 4 pages) instead of the 11-section guide

The first real handover (MEK Charge Reconciliation) came out at 9 pages.
Ops want it short: which URL to open and what to click, what shows where
after the click, what to do when an exception comes, and how to email us.

- Output is "<Project Name> Handover SOP" (HANDOVER-SOP.md + .docx) with
  5 sections: how to run, what you see while it runs, result, when
  something goes wrong, email us. HANDOVER-DEV.md is dropped; dev-team
  findings go in the chat.
- At most ~1,400 words; to_docx.py prints the word count and flags overflow,
  and renders *italic* for the variable parts of messages.
- Every quoted message, label and URL is checked against the code. The SOP
  must say what clicking Run again does to the data.
- One scan subagent; failure classes stay as a scan checklist.
- Tebra reference rewritten in the SOP format; test, README, glossary
  updated; ADR-0005 supersedes ADR-0004.

The handoff eval runner (tests/evals/run_handoff_eval.py) still grades an
older 13-section format and was already out of date; not touched here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
VasanthandClaude Opus 5.5 committed 2026-10-28 16:57:48 +05:30
1 parent 71281bdf91
commit ff2f7fcd4a
13 files changed
+260 -377

No files matched your search

@@ -1,19 +0,0 @@
# {{BOT_NAME}}: dev notes for the handover
Version {{VERSION}} · Written {{DATE}} · The ops guide is `HANDOVER.md`. This file is for developers only.
## Failure-class check
{{ONE LINE PER CLASS F1-F12: "F<n> covered in HANDOVER.md section <s> / E<nn>", "F<n> gap <#> below", or "F<n> not applicable: <reason>"}}
## Gaps to fix before handover
| # | Gap | Why it hurts ops | Where (file:line) |
|---|---|---|---|
| 1 | {{GAP}} | {{IMPACT}} | {{REF}} |
## Incidents
| When / version | What ops saw | Cause | Fix | Ref |
|---|---|---|---|---|
| {{DATE / VERSION}} | {{SYMPTOM}} | {{CAUSE}} | {{FIX}} | {{COMMIT}} |
@@ -0,0 +1,73 @@
# {{PROJECT_NAME}} Handover SOP
Version {{VERSION}} · {{DATE}} · Problems: email {{EMAIL_GROUP}} (section 5)
{{ONE_LINE: what the bot does, from which app or portal, and what it produces}}
## 1. How to run
1. {{e.g. Open https://... in Chrome / Open "<app>" from the Desktop.}}
2. {{e.g. Sign in with the team password.}}
3. {{e.g. Click "Run".}}
4. {{e.g. Wait until the status says "Finished" or "Stopped" (section 2).}}
**Before you click {{RUN_LABEL}}:**
- {{WHAT A NEW CLICK DOES: e.g. "Every click starts a fresh run on today's data. It deletes the previous run's reports first, so download them before you click."}}
- {{e.g. "Only one run at a time. A greyed-out button means a run is going."}}
## 2. What you see while it runs
| Where on the screen | What it shows |
|---|---|
| {{AREA, exact label}} | {{TEXTS IT SHOWS, in order}} |
**A normal run:**
1. {{e.g. Both lanes say "Signing in…".}}
2. {{e.g. Each lane fills: "3 of 10 locations".}}
3. {{e.g. Status "Finished", last line "Finished. Your reports are ready to download below."}}
| Step | The bot waits at most | Then |
|---|---|---|
| {{STEP}} | {{e.g. 3 min}} | {{e.g. skips that location / stops / retries 3 times}} |
**Step in only when:** {{e.g. the run has ended, or nothing new has appeared for 10 minutes}}.
## 3. Result
- **Good run:** {{HOW IT ENDS ON SCREEN}}
- **Needs a look:** {{HOW IT ENDS, including any misleading green or "Finished"}}
**Where:** {{e.g. Reports box → Download. Excel: Summary sheet, Reconciliation sheet, Result column.}}
| Status | Meaning | What you do |
|---|---|---|
| {{STATUS}} | {{ONE LINE}} | {{ACTION or "nothing"}} |
## 4. When something goes wrong
**How it shows:** {{e.g. a red or orange dot in the activity list under the Run button; the lane turns red}}.
| ID | Message you see | What it means | What you do |
|---|---|---|---|
| E01 | {{EXACT MESSAGE}} | {{ONE LINE}} | {{STEPS. "Email us" when ops can't fix it}} |
**Don't:**
- {{DON'T + short reason}}
**To stop it:** {{THE ONE RIGHT WAY}}
## 5. Email us
Email **{{EMAIL_GROUP}}** when section 4 says "email us", or when the same error comes back after one re-run. Answer: {{RESPONSE_TIME}}.
```
Subject: {{PROJECT_NAME}} - E<number> - <date>
Run started: <time from the screen>
Error: E<number> - <the message, copied>
Where: <location / item>
What I did: <e.g. ran it again once>
Attached: screenshot of the screen
```
**Never send:** passwords, {{SENSITIVE: e.g. the Excel reports (they hold patient names)}}.
@@ -1,92 +0,0 @@
# {{BOT_NAME}}: ops guide
Version {{VERSION}} · Written {{DATE}} · Questions or problems: {{ESCALATION_CHANNEL}} (see section 9)
## 1. What this bot does
{{AT_MOST_2_LINES: what it does, in which app, from which input}}
## 2. Where is what
| Where | What you see there | Open it when |
|---|---|---|
| {{TAB / SCREEN / FILE / FOLDER}} | {{WHAT IT SHOWS}} | {{WHEN}} |
## 3. How to run
**Before you start**
- [ ] {{PREREQUISITE: e.g. the target app is installed and you can sign in}}
- [ ] {{INPUT RULE: e.g. one clinic per row. Wrong: "North, South". Right: "North" and "South" on two rows.}}
- [ ] The app shows version {{VERSION}}.
**Steps**
1. {{e.g. Open "<app name>" from the Desktop.}}
2. {{e.g. On the Run tab, drop the input Excel on the upload box.}}
3. {{e.g. Check the preview: every sheet has rows.}}
4. {{e.g. Click "Trigger automation".}}
**What a normal run looks like:** {{e.g. the 7 checkpoint boxes turn green one by one; the live log shows each item}}
## 4. How long to wait
| Step | Normal time | The bot waits at most | After that the bot | What you do |
|---|---|---|---|---|
| {{STEP}} | {{e.g. 1 min, or "not measured"}} | {{e.g. 5 min}} | {{e.g. restarts the app, up to 3 times}} | {{e.g. nothing, wait}} |
**When do you step in?** {{e.g. only when the run ends red, or a box stays red after 3 restarts}}
## 5. After the run
| Ending | Meaning | What you do |
|---|---|---|
| {{e.g. green "Completed"}} | {{MEANING}} | {{ACTION}} |
**The report:** {{e.g. Report tab → choose the batch → File Status column. Export with the Excel button.}}
| Status | Colour | Meaning | What you do |
|---|---|---|---|
| {{STATUS}} | {{COLOUR}} | {{MEANING}} | {{ACTION or "nothing"}} |
## 6. How to see an exception
1. {{e.g. Report tab → filter File Status = Error.}}
2. {{e.g. Read the Comments column.}}
3. Find that message in section 7.
## 7. Exception list
| ID | Message you see | Where | What it means | What you do | Bot retries / call us when |
|---|---|---|---|---|---|
| E01 | {{EXACT MESSAGE}} | {{TAB → COLUMN, or FILE}} | {{ONE SENTENCE}} | {{STEPS}} | {{e.g. "Bot retries 3 times, wait. Call us if it fails again."}} |
## 8. How you will know
{{e.g. "There is no email or alert. Check the Dashboard after every run, and every morning for the night run."}}
## 9. How to tell us
- **Where:** {{ESCALATION_CHANNEL}}. Expect an answer within {{RESPONSE_TIME}}.
- **Send:** {{e.g. batch ID, time of the problem, E-number from section 7, a screenshot of the screen, the report row}}
- **Never send:** passwords, the settings file, or patient or customer data in chat.
Copy this message:
```
Bot: {{BOT_NAME}} v{{VERSION}}
Batch / run ID:
Time:
E-number and message:
What I already did:
Screenshot: attached
```
## 10. Don'ts
- {{DON'T: e.g. "Don't use the mouse or keyboard while it runs. The bot's clicks land in the wrong window."}}
- **To stop the bot:** {{THE ONE RIGHT WAY}}
## 11. Issues seen before
| When / version | What you saw | What to do now |
|---|---|---|
| {{DATE / VERSION}} | {{SYMPTOM}} | {{ACTION, or "fixed in vX, nothing to do"}} |
@@ -1,171 +1,98 @@
# Worked example: Tebra Auto Post (v2.5.3), scrubbed
What the two files look like for a real bot. It is shortened: the real guide lists every status and every message. Client names, IDs, amounts, credentials and server addresses are removed, and the channel name is an example. **Copy the depth and the plain wording, not the content.**
What the SOP looks like for a real bot: about 2.5 pages. Client names, IDs, amounts, credentials and server addresses are removed, and the email address is an example. **Copy the length and the plain wording, not the content.**
---
# File 1: `HANDOVER.md`
# Tebra Auto Post Handover SOP
# Tebra Auto Post: ops guide
Version 2.5.3 · 28 Oct 2026 · Problems: email rpa-support@example.com (section 5)
Version 2.5.3 · Questions or problems: #rpa-support Teams channel (see section 9)
Posts insurance ERA payments into the Tebra desktop app, company by company and practice by practice, from the input Excel, then turns each ERA into a PDF.
## 1. What this bot does
## 1. How to run
Posts insurance ERA payments into the Tebra desktop app, company by company and practice by practice, from the input Excel. At the end it turns each downloaded ERA into a PDF with EasyPrint.
## 2. Where is what
| Where | What you see there | Open it when |
|---|---|---|
| **Run tab** | Upload box, sheet preview, **Trigger automation** button, live log, 7 checkpoint boxes | To start a run, and to watch it |
| **Dashboard** | Every batch with its status (Pending / Completed / Cleared). Click a batch to see each company and practice | After a run, to see if it finished |
| **Report tab** | One row per ERA: File Status (colour), Comments, Check #, Insurance. Search by Batch ID. Exports to Excel | To see which ERA failed and why |
| `data/files/Need_attention/` | ERAs whose PDF could not be made | When PDF Converted = No |
| `data/files/Xcel/ERA_Summary_<time>.xlsx` | Per-file PDF result: Status, Failure Reason | To see why a PDF failed |
## 3. How to run
**Before you start**
- [ ] EasyPrint shortcut is on the Desktop, the Public Desktop or the Start Menu.
- [ ] Input Excel has the sheets Login, Company, Location, Settings. Download the sample from the Run tab if unsure.
- [ ] **One practice per row.** Wrong: `North Clinic, South Clinic` in one cell (the bot splits it and skips both). Right: each on its own row.
- [ ] Practice names spelled exactly as in Tebra, including `&`.
- [ ] A new practice has a Client ID. Ask us to add it before the first run.
- [ ] The title bar shows v2.5.3.
**Steps**
1. Open **Ecare Tebra Automation Tool** from the Desktop.
2. Run tab → drop the input Excel on the upload box.
3. Check the preview: Login, Company and Location sheets all have rows.
1. Open **Ecare Tebra Automation Tool** from the Desktop. Check the title bar shows v2.5.3.
2. **Run** tab → drop the input Excel on the upload box.
3. Check the preview: the Login, Company and Location sheets all have rows.
4. Click **Trigger automation**.
5. Leave the machine alone until the run ends.
5. Leave the mouse and keyboard alone until the run ends.
**What a normal run looks like:** the checkpoint boxes turn green one by one. The live log shows each company, practice and ERA. Tebra opens and closes by itself for every company.
**Before you click Trigger automation:**
- One practice per row. Wrong: `North Clinic, South Clinic` in one cell (both are skipped). Right: one per row, spelled exactly as in Tebra.
- Every click starts a new batch from the Excel you uploaded. ERAs already posted are not posted again.
- Close SQL Server Management Studio and any other window with "Tebra" in its title.
## 4. How long to wait
## 2. What you see while it runs
| Step | Normal time | The bot waits at most | After that the bot | What you do |
|---|---|---|---|---|
| Any checkpoint box | — | 5 min (10 min for the ERA list) with no progress | Marks the box red **stuck**, closes Tebra, restarts. Up to 3 times | Nothing. Wait for the 3 restarts |
| Open one ERA | not measured | 90 s, 3 tries, then 3 Tebra restarts | Writes the row as Error, moves to the next ERA | Nothing during the run |
| Open the payment screen | not measured | 120 s, 4 tries, then 3 Tebra restarts | Pauses the run | See E07 |
| Save one payment | about 12 s (slowest seen 27 s) | 45 s + 30 s + 30 s | Checks the ERA list. If the ERA is gone, counts it as posted. If not, Error | See E05 |
| One company | depends on ERAs | 3 attempts | Leaves the company Pending | See E08 |
| PDF step | not measured | 60 min | Puts the file in `Need_attention/` | See E09 |
| Where on the screen | What it shows |
|---|---|
| **Run** tab → checkpoint boxes | 7 boxes, grey → green as each step finishes; red **stuck** when a step makes no progress |
| **Run** tab → live log | Each company, practice and ERA as it is posted |
| **Dashboard** | The batch: Pending while running, Completed when every practice is done |
| **Report** tab | One row per ERA: File Status (colour) and Comments |
**When do you step in?** Only when the run has ended, or the log says "Stuck - automatic restarts exhausted. Click Resume.". Restarts before that are normal.
**A normal run:**
1. Tebra opens by itself and signs in to the first company.
2. The checkpoint boxes turn green one by one; the live log lists each ERA.
3. Tebra closes and reopens for the next company.
4. The Dashboard shows the batch as **Completed**.
## 5. After the run
| Ending | Meaning | What you do |
| Step | The bot waits at most | Then |
|---|---|---|
| Batch **Completed** on the Dashboard | Every practice finished | Check the Report tab for amber/grey rows that need follow-up |
| Batch **Pending** | At least one practice is not finished | Section 6, then E08 |
| "Stuck - automatic restarts exhausted. Click Resume." | The bot gave up after 3 restarts | Click **Resume** once. If it stops again, tell us |
| Any checkpoint box | 5 min with no progress (10 min for the ERA list) | Marks it red **stuck**, restarts Tebra, up to 3 times |
| Open one ERA | 90 s, 3 tries, then 3 Tebra restarts | Writes the row as Error, moves on |
| Save one payment | 45 s + 30 s + 30 s | Checks the ERA list; Error if still there (E03) |
| PDF of one ERA | 60 min | Moves the file to `Need_attention` |
**The report:** Report tab → search the Batch ID → read File Status and Comments. Export it to Excel from the same tab.
**Step in only when:** the run has ended, or the log says "Stuck - automatic restarts exhausted. Click Resume.".
| Status - Comments | Colour | Meaning | What you do |
## 3. Result
- **Good run:** the batch is **Completed** on the Dashboard and the Report tab has no red rows.
- **Needs a look:** the batch stays **Pending**, or a row is red. **Three different results show green**: read the Comments of every green row.
**Where:** Report tab → search the Batch ID → File Status and Comments. Export with the Excel button.
| Status - Comments | Meaning | What you do |
|---|---|---|
| Completed - "Posted successfully" | Posted | Nothing |
| Completed - "ERA Errors on posting" | **Not posted.** Tebra rejected it | Review it in Tebra by hand |
| Completed - "negative balance account … not posted" | **Not posted on purpose** | Follow the negative-balance process |
| Deferred (grey) | Check date after the cutoff | Nothing; a later run picks it up |
| Error (red) | Failed after all retries | Section 4 |
## 4. When something goes wrong
**How it shows:** a red row on the Report tab (read Comments), a red **stuck** box on the Run tab, or a batch still **Pending** on the Dashboard.
| ID | Message you see | What it means | What you do |
|---|---|---|---|
| Completed - "Posted successfully" | green | Posted and saved | nothing |
| Completed - "ERA Errors on posting" | green | **Not posted.** Tebra rejected it | Review it in Tebra by hand |
| Completed - "negative balance account … not posted" | green | **Not posted on purpose** | Follow the negative-balance process |
| Completed - "Payment method '…' - not posted" | green | Not ACH, skipped | nothing |
| Deferred | grey | Check date after the cutoff or in the future | nothing, a later run picks it up |
| Error | red | Failed after all retries | Section 7 |
| E01 | "[login] FAILED for company …" | Tebra's sign-in never appeared | Check the Login sheet, then click **Resume**. Email us if it fails again |
| E02 | "Wrong practice opened … leaving Pending" | The practice name doesn't match Tebra | Fix the spelling in the Excel, click **Resume** |
| E03 | "Payment not confirmed after Save" | The bot clicked Save but never saw "saved". **It may have posted** | **Check the payment in Tebra before re-running.** If posted, set the row to Completed. If not, click **Resume** |
| E04 | "Save button element not found" | The payment screen had no Save button | Email us |
| E05 | "Stuck - automatic restarts exhausted. Click Resume." | 3 restarts didn't help | Click **Resume** once. Email us if it stops again |
| E06 | Batch stays **Pending** | One red or unfinished row keeps its practice open | Find the red row (Report tab), fix or edit it. Email us if you can't find it |
**Read the Comments column on every green row.** Three different results all show green.
**Don't:**
- Use the mouse or keyboard while it runs: its clicks land in the wrong window.
- Close Tebra to stop the bot: the bot keeps going.
## 6. How to see an exception
**To stop it:** close the **Ecare Tebra Automation Tool** window.
1. Report tab → search the Batch ID → sort by File Status → look at the red **Error** rows.
2. Read the **Comments** column.
3. Find that message in section 7.
4. If no row is red but the batch is Pending, open the batch on the Dashboard and find the practice that is still Pending.
## 5. Email us
## 7. Exception list
| ID | Message you see | Where | What it means | What you do | Bot retries / call us when |
|---|---|---|---|---|---|
| E01 | "[login] FAILED for company …" | Live log; company Pending on the Dashboard | Tebra's sign-in window or fields never appeared | Check the username and password in the Login sheet, then Resume | Bot tries the company 3 times. Call us if Resume fails too |
| E02 | "Wrong practice opened … leaving Pending" | Live log; practice Pending | The practice Tebra opened has a different name from the input | Fix the spelling in the input to match Tebra exactly (`&`, commas). Resume | Call us if the spelling is already exact |
| E03 | "Clearinghouse Reports not found after retries" | Live log; practice Pending | Tebra's menu didn't show up, even after restarts | Resume once | Call us if it happens again |
| E04 | "Could not open the ERA to view it" | Report tab, Comments, red | That ERA wouldn't open after 3 tries and 3 restarts | Resume. The bot continues at that ERA | Call us if the same ERA fails again |
| E05 | "Payment not confirmed after Save" | Report tab, Comments, red | The bot clicked Save but never saw "saved". **It may have posted** | 1. **Check the payment in Tebra before anything else.** 2. If it posted, edit the row in the Report tab to Completed. 3. Only if it did not post, Resume. **Re-running without checking can post it twice** | Call us if it is not posted and fails again |
| E06 | "Save button element not found" | Report tab, Comments, red | The payment screen had no Save button | Nothing. Tell us | Always call us |
| E07 | "… still failing after 3 Tebra restarts" | Live log; run paused | The payment screen won't open | Click Resume once | Call us if it pauses again |
| E08 | Batch stays **Pending** | Dashboard | One red or unfinished row keeps its practice open | Find the row (section 6). Fix or edit it, or request a Clear (an admin approves it). Yesterday's Pending batch does **not** block today's upload | Call us if you can't find the row |
| E09 | File in `Need_attention/` | `ERA_Summary_<time>.xlsx`, Failure Reason | EasyPrint couldn't make the PDF | Nothing. The next run tries again | Call us if it is still there after 2 runs |
## 8. How you will know
There is **no email or alert**. The bot only shows results on screen. Check the Dashboard after every run: a batch that is still Pending needs you.
## 9. How to tell us
- **Where:** #rpa-support Teams channel. Expect an answer the same working day.
- **Send:** Batch ID, time of the problem, E-number, a screenshot of the Run tab and of Tebra, the Report row.
- **Never send:** the Login sheet, passwords, the settings file, or patient names in chat.
Email **rpa-support@example.com** when section 4 says "email us", or when the same error comes back after one Resume. Answer: same working day.
```
Bot: Tebra Auto Post v2.5.3
Batch ID:
Time:
E-number and message:
What I already did:
Screenshot: attached
Subject: Tebra Auto Post - E<number> - <date>
Batch ID: <from the Dashboard>
Error: E<number> - <the message, copied>
Where: <company / practice / check number>
What I did: <e.g. clicked Resume once>
Attached: screenshot of the Run tab and of Tebra
```
## 10. Don'ts
- **Don't use the mouse or keyboard while it runs.** Windows blocks the bot from taking focus, and its clicks land in the wrong window.
- **Don't keep SQL Server Management Studio, or any window with "Tebra" in its title, open.** The bot can grab it instead of Tebra.
- **Don't close Tebra to stop the bot.** The bot keeps going.
- **To stop the bot:** close the **Ecare Tebra Automation Tool** window.
## 11. Issues seen before
| When / version | What you saw | What to do now |
|---|---|---|
| v2.5.1 | Red "Payment not confirmed" rows that had really posted | Partly fixed. Always check in Tebra first (E05) |
| v2.5.0-2.5.3 | Batches Pending for weeks | Request a Clear (E08) |
| v2.5.1 | Stayed on 2.5.0 after an update | Check the title bar version after every update. Tell us if it didn't change |
| v2.5.2 | Some practices skipped on every run | One practice per row, no commas |
| v1.0.x | Bot drove the database tool, then closed Tebra | Close other "Tebra" windows (section 10) |
---
# File 2: `HANDOVER-DEV.md`
## Failure-class check (excerpt)
- F1 covered in HANDOVER.md section 3 (EasyPrint shortcut); gap 6 (ODBC driver not enforced)
- F2 covered in section 10
- F4 covered in section 3 (one practice per row); gap 5 (`&` in names)
- F6 covered in E05
- F7 covered in section 5 (three green results); gap 1
- F9 covered in section 10
- F10 covered in E08
- F11 gap 2, gap 3
- F12 covered in section 11; gap 4
## Gaps to fix before handover (excerpt)
| # | Gap | Why it hurts ops | Where |
|---|---|---|---|
| 1 | "ERA Errors" and "negative balance" rows are stored as Completed (green) | Ops thinks they are done | `src/automation/era_log.py` status map |
| 2 | The developer cause (TechComments) is in the DB only, not on the Report tab | Ops must ask dev for every red row | `src/db/report.py` |
| 3 | The app's own watchdog messages go to the screen only | Lost after the run | `src/ui/main_window.py` `_log()` |
| 4 | `ERA_Summary_*.xlsx` is named by time only | Can't be tied to a batch | `src/automation/remittance_pdf.py` |
| 5 | `&` in a practice name breaks the title match (fixed in the TVL fork only) | Practice abandoned after 13 tries | `src/automation/orchestrator.py` practice check |
| 6 | No check for ODBC Driver 17/18 at start | Cryptic "HYC00" error mid-run | `src/db/database.py` |
## Incidents (excerpt)
| When / version | What ops saw | Cause | Fix | Ref |
|---|---|---|---|---|
| v2.5.1 | "Payment not confirmed" rows that had posted | Save slower than the 20 s wait | 45/30/30 s + grid re-check | bug-206, bug-234 |
| v2.5.1 | Users on 2.5.0 for a month | Server marked the deploy done but kept the old container | Stop + Start on the server | bug-243 |
| v2.5.2 | 3 practices skipped every run | Split on commas | Split on newline/semicolon only | bug-233 |
| v1.0.x | Launcher never updated | Self-signed certificate | Retry without verification | bug-080 |
**Never send:** the Login sheet, passwords, or patient names.
+27 -14
View File
@@ -1,11 +1,13 @@
"""Turn the ops guide (HANDOVER.md) into a Word document ops can open, print and pass around.
"""Turn the Handover SOP (HANDOVER-SOP.md) into a Word document ops can open, print and pass around.
python to_docx.py HANDOVER.md # writes HANDOVER.docx next to it
python to_docx.py HANDOVER.md -o guide.docx
python to_docx.py HANDOVER-SOP.md # writes HANDOVER-SOP.docx next to it
python to_docx.py HANDOVER-SOP.md -o "<Project Name> Handover SOP.docx"
python to_docx.py --selfcheck
Prints the word count: the SOP must stay at or under MAX_WORDS (about 4 pages).
Handles what the template uses: # headings, paragraphs, - bullets, - [ ] checkboxes, 1. steps,
| tables |, ``` blocks, **bold** and `code`. Needs python-docx (pip install python-docx).
| tables |, ``` blocks, **bold**, *italic* and `code`. Needs python-docx (pip install python-docx).
"""
import argparse
import re
@@ -16,13 +18,16 @@ from pathlib import Path
from docx import Document
from docx.shared import Pt
INLINE = re.compile(r"(\*\*[^*]+\*\*|`[^`]+`)")
MAX_WORDS = 1400 # about 4 pages with tables
INLINE = re.compile(r"(\*\*[^*]+\*\*|\*[^*\s][^*]*\*|`[^`]+`)")
def add_runs(par, text):
for part in INLINE.split(text):
if part.startswith("**") and part.endswith("**"):
par.add_run(part[2:-2]).bold = True
elif len(part) > 2 and part.startswith("*") and part.endswith("*"):
par.add_run(part[1:-1]).italic = True
elif part.startswith("`") and part.endswith("`"):
par.add_run(part[1:-1]).font.name = "Consolas"
elif part:
@@ -82,18 +87,24 @@ def convert(md_text, out_path):
return doc
def words(md_text):
return len(re.findall(r"[A-Za-z0-9][\w'’.-]*", re.sub(r"^\|[\s:|-]+\|\s*$", "", md_text, flags=re.M)))
def selfcheck():
example = Path(__file__).parent / "reference" / "tebra-auto-post.md"
guide = example.read_text(encoding="utf-8").split("# File 2:")[0].split("# File 1: `HANDOVER.md`")[1]
out = Path(tempfile.mkdtemp()) / "HANDOVER.docx"
convert(guide, out)
sop = "# " + example.read_text(encoding="utf-8").split("\n---\n", 1)[1].split("\n# ", 1)[1]
out = Path(tempfile.mkdtemp()) / "HANDOVER-SOP.docx"
convert(sop, out)
doc = Document(out)
heads = [p.text for p in doc.paragraphs if p.style.name.startswith("Heading")]
assert [h.split(".")[0] for h in heads if h[:1].isdigit()] == [str(n) for n in range(1, 12)], heads
assert len(doc.tables) >= 6, len(doc.tables)
assert doc.tables[0].cell(0, 0).text == "Where", doc.tables[0].cell(0, 0).text
assert doc.paragraphs[0].text.endswith("Handover SOP"), doc.paragraphs[0].text
assert [h.split(".")[0] for h in heads if h[:1].isdigit()] == [str(n) for n in range(1, 6)], heads
assert len(doc.tables) >= 4, len(doc.tables)
assert doc.tables[0].cell(0, 0).text == "Where on the screen", doc.tables[0].cell(0, 0).text
assert not any("**" in p.text or "|---" in p.text for p in doc.paragraphs)
print(f"selfcheck ok: {len(heads)} headings, {len(doc.tables)} tables -> {out}")
assert words(sop) <= MAX_WORDS, words(sop)
print(f"selfcheck ok: {len(heads)} headings, {len(doc.tables)} tables, {words(sop)} words -> {out}")
if __name__ == "__main__":
@@ -109,5 +120,7 @@ if __name__ == "__main__":
ap.error("give the HANDOVER.md path, or --selfcheck")
src = Path(a.md)
out = Path(a.out) if a.out else src.with_suffix(".docx")
convert(src.read_text(encoding="utf-8"), out)
print(f"wrote {out}")
text = src.read_text(encoding="utf-8")
convert(text, out)
n = words(text)
print(f"wrote {out} ({n} words{'' if n <= MAX_WORDS else f', OVER the {MAX_WORDS}-word limit: cut it'})")