updated the report so that we will flag drawers that had errors and also crashed
This commit is contained in:
@@ -122,7 +122,9 @@ Drawer ID(s): 08097 18430
|
|||||||
python fccs_report.py
|
python fccs_report.py
|
||||||
```
|
```
|
||||||
|
|
||||||
This runs the same document-level check as `fccs_verify.py` but writes a self-contained, print-friendly HTML file (`report_html`, default `C:\Migration\progress_report.html`) that lists **only** the drawers with missing documents, each with the client name and the specific documents still outstanding. Fully-migrated drawers are omitted (a headline shows how many are done). Drawers with no manifest or in `crashed.txt` are excluded since they reflect internal tooling state, not client-facing progress. Open it in any browser and print to PDF to send.
|
This runs the same document-level check as `fccs_verify.py` but writes a self-contained, print-friendly HTML file (`report_html`, default `C:\Migration\progress_report.html`) that lists **only** the drawers with missing documents, each with the client name and the specific documents still outstanding. Fully-migrated drawers and drawers with no manifest are omitted (a headline shows how many are done). Open it in any browser and print to PDF to send.
|
||||||
|
|
||||||
|
Any outstanding drawer that also appears in `crashed.txt` (its export was aborted by an FCCS converter crash) is badged **CRASHED** and sorted to the top, and counted in the header. These are the genuine failures worth spot-checking first — as opposed to benign false positives, where a collapsed "container" document's children exported fine but the container's name lands in the filename's folder field rather than the document field, so it reads as missing.
|
||||||
|
|
||||||
## Config Reference
|
## Config Reference
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -6,10 +6,15 @@ a clean, self-contained HTML file listing ONLY the drawers with outstanding
|
|||||||
items and, for each, the specific documents still missing from the export. It's
|
items and, for each, the specific documents still missing from the export. It's
|
||||||
meant as a "check-in progress" document you can send to the client.
|
meant as a "check-in progress" document you can send to the client.
|
||||||
|
|
||||||
Drawers that are fully exported, that have no manifest, or that only appear in
|
Drawers that are fully exported or that have no manifest are left out — the
|
||||||
crashed.txt are intentionally left out — the report shows outstanding work, not
|
report shows outstanding work. A headline shows how many drawers are fully
|
||||||
internal tooling state. A headline shows how many drawers are fully migrated so
|
migrated so overall progress is clear.
|
||||||
the overall progress is clear.
|
|
||||||
|
Any outstanding drawer that also appears in crashed.txt (an FCCS converter crash
|
||||||
|
aborted its export) is badged CRASHED and sorted to the top: those are genuine
|
||||||
|
failures worth attention, as opposed to benign name-match false positives (e.g.
|
||||||
|
a collapsed "container" document whose children exported fine but whose name
|
||||||
|
lands in the filename's folder field rather than the document field).
|
||||||
|
|
||||||
Output goes to `report_html` (default C:\\Migration\\progress_report.html).
|
Output goes to `report_html` (default C:\\Migration\\progress_report.html).
|
||||||
|
|
||||||
@@ -24,7 +29,7 @@ import html
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from fccs_config import (
|
from fccs_config import (
|
||||||
parse_args, load_config,
|
parse_args, load_config, load_lines,
|
||||||
evaluate_drawer, index_files_by_drawer, client_name_from_files,
|
evaluate_drawer, index_files_by_drawer, client_name_from_files,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,6 +66,7 @@ PAGE_TEMPLATE = """<!doctype html>
|
|||||||
letter-spacing: .03em; margin-top: .35rem; }}
|
letter-spacing: .03em; margin-top: .35rem; }}
|
||||||
.stat.good .num {{ color: #2e7d32; }}
|
.stat.good .num {{ color: #2e7d32; }}
|
||||||
.stat.flag .num {{ color: #c0392b; }}
|
.stat.flag .num {{ color: #c0392b; }}
|
||||||
|
.stat.crash .num {{ color: #b9770e; }}
|
||||||
.body {{ padding: 1.5rem 2.25rem 2.25rem; }}
|
.body {{ padding: 1.5rem 2.25rem 2.25rem; }}
|
||||||
.all-clear {{
|
.all-clear {{
|
||||||
text-align: center; padding: 2.5rem 1rem; color: #2e7d32; font-size: 1.1rem;
|
text-align: center; padding: 2.5rem 1rem; color: #2e7d32; font-size: 1.1rem;
|
||||||
@@ -70,10 +76,17 @@ PAGE_TEMPLATE = """<!doctype html>
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}}
|
}}
|
||||||
.drawer:last-child {{ margin-bottom: 0; }}
|
.drawer:last-child {{ margin-bottom: 0; }}
|
||||||
|
.drawer.crashed {{ border-left: 4px solid #c0392b; background: #fdf6f5; }}
|
||||||
.drawer h2 {{
|
.drawer h2 {{
|
||||||
margin: 0 0 .1rem; font-size: 1.05rem; font-weight: 620;
|
margin: 0 0 .1rem; font-size: 1.05rem; font-weight: 620;
|
||||||
}}
|
}}
|
||||||
.drawer .id {{ color: #486581; font-variant-numeric: tabular-nums; }}
|
.drawer .id {{ color: #486581; font-variant-numeric: tabular-nums; }}
|
||||||
|
.badge {{
|
||||||
|
display: inline-block; margin-left: .5rem; padding: .1rem .5rem;
|
||||||
|
font-size: .72rem; font-weight: 700; letter-spacing: .04em;
|
||||||
|
text-transform: uppercase; color: #fff; background: #c0392b;
|
||||||
|
border-radius: 4px; vertical-align: middle;
|
||||||
|
}}
|
||||||
.drawer .meta {{ color: #7b8794; font-size: .82rem; margin-bottom: .6rem; }}
|
.drawer .meta {{ color: #7b8794; font-size: .82rem; margin-bottom: .6rem; }}
|
||||||
ul.docs {{ margin: 0; padding: 0; list-style: none; }}
|
ul.docs {{ margin: 0; padding: 0; list-style: none; }}
|
||||||
ul.docs li {{
|
ul.docs li {{
|
||||||
@@ -105,6 +118,8 @@ PAGE_TEMPLATE = """<!doctype html>
|
|||||||
<div class="lbl">Drawers migrated</div></div>
|
<div class="lbl">Drawers migrated</div></div>
|
||||||
<div class="stat flag"><div class="num">{incomplete}</div>
|
<div class="stat flag"><div class="num">{incomplete}</div>
|
||||||
<div class="lbl">Drawers outstanding</div></div>
|
<div class="lbl">Drawers outstanding</div></div>
|
||||||
|
<div class="stat crash"><div class="num">{crashed}</div>
|
||||||
|
<div class="lbl">Crashed in export</div></div>
|
||||||
<div class="stat"><div class="num">{total}</div>
|
<div class="stat"><div class="num">{total}</div>
|
||||||
<div class="lbl">Total drawers</div></div>
|
<div class="lbl">Total drawers</div></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,24 +127,29 @@ PAGE_TEMPLATE = """<!doctype html>
|
|||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
<footer>Generated {date} · Lists documents not yet present in the
|
<footer>Generated {date} · Lists documents not yet present in the
|
||||||
export. Drawers not shown are fully migrated.</footer>
|
export. Drawers not shown are fully migrated. A
|
||||||
|
<span class="badge">Crashed</span> badge marks drawers whose export was
|
||||||
|
interrupted by an error.</footer>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def build_drawer_block(drawer_id, client, missing):
|
def build_drawer_block(drawer_id, client, missing, crashed=False):
|
||||||
"""Return the HTML for one outstanding drawer."""
|
"""Return the HTML for one outstanding drawer."""
|
||||||
head = f'<span class="id">{html.escape(drawer_id)}</span>'
|
head = f'<span class="id">{html.escape(drawer_id)}</span>'
|
||||||
if client:
|
if client:
|
||||||
head += f' — {html.escape(client)}'
|
head += f' — {html.escape(client)}'
|
||||||
|
if crashed:
|
||||||
|
head += ' <span class="badge">Crashed</span>'
|
||||||
n = len(missing)
|
n = len(missing)
|
||||||
docs = "\n".join(
|
docs = "\n".join(
|
||||||
f" <li>{html.escape(m)}</li>" for m in missing
|
f" <li>{html.escape(m)}</li>" for m in missing
|
||||||
)
|
)
|
||||||
|
cls = "drawer crashed" if crashed else "drawer"
|
||||||
return (
|
return (
|
||||||
' <div class="drawer">\n'
|
f' <div class="{cls}">\n'
|
||||||
f' <h2>{head}</h2>\n'
|
f' <h2>{head}</h2>\n'
|
||||||
f' <div class="meta">{n} document{"s" if n != 1 else ""} '
|
f' <div class="meta">{n} document{"s" if n != 1 else ""} '
|
||||||
'outstanding</div>\n'
|
'outstanding</div>\n'
|
||||||
@@ -145,6 +165,7 @@ def main():
|
|||||||
export_dir = cfg.get("paths", "export_dir")
|
export_dir = cfg.get("paths", "export_dir")
|
||||||
manifest_dir = cfg.get("paths", "manifest_dir")
|
manifest_dir = cfg.get("paths", "manifest_dir")
|
||||||
report_html = cfg.get("paths", "report_html")
|
report_html = cfg.get("paths", "report_html")
|
||||||
|
crashed_set = set(load_lines(cfg.get("paths", "crashed_file")))
|
||||||
|
|
||||||
if not os.path.isdir(manifest_dir):
|
if not os.path.isdir(manifest_dir):
|
||||||
print(f"ERROR: manifest directory not found: {manifest_dir}")
|
print(f"ERROR: manifest directory not found: {manifest_dir}")
|
||||||
@@ -166,22 +187,27 @@ def main():
|
|||||||
files_by_drawer = index_files_by_drawer(export_dir)
|
files_by_drawer = index_files_by_drawer(export_dir)
|
||||||
|
|
||||||
complete = 0
|
complete = 0
|
||||||
outstanding = [] # (drawer_id, client, missing_list)
|
outstanding = [] # (drawer_id, client, missing_list, crashed)
|
||||||
for drawer_id in drawer_ids:
|
for drawer_id in drawer_ids:
|
||||||
files = files_by_drawer.get(drawer_id, [])
|
files = files_by_drawer.get(drawer_id, [])
|
||||||
r = evaluate_drawer(drawer_id, files, manifest_dir)
|
r = evaluate_drawer(drawer_id, files, manifest_dir)
|
||||||
if r["missing"]:
|
if r["missing"]:
|
||||||
client = client_name_from_files(files)
|
client = client_name_from_files(files)
|
||||||
outstanding.append((drawer_id, client, r["missing"]))
|
outstanding.append((drawer_id, client, r["missing"],
|
||||||
|
drawer_id in crashed_set))
|
||||||
else:
|
else:
|
||||||
complete += 1
|
complete += 1
|
||||||
|
|
||||||
|
# Crashed drawers are the genuine failures — surface them first.
|
||||||
|
outstanding.sort(key=lambda o: (not o[3], o[0]))
|
||||||
|
crashed_ids = [d for d, _, _, cr in outstanding if cr]
|
||||||
|
|
||||||
total = len(drawer_ids)
|
total = len(drawer_ids)
|
||||||
date_str = f"{datetime.now():%B %d, %Y}"
|
date_str = f"{datetime.now():%B %d, %Y}"
|
||||||
|
|
||||||
if outstanding:
|
if outstanding:
|
||||||
content = "\n".join(
|
content = "\n".join(
|
||||||
build_drawer_block(d, c, m) for d, c, m in outstanding
|
build_drawer_block(d, c, m, cr) for d, c, m, cr in outstanding
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
content = (' <div class="all-clear">All drawers are fully '
|
content = (' <div class="all-clear">All drawers are fully '
|
||||||
@@ -191,6 +217,7 @@ def main():
|
|||||||
date=html.escape(date_str),
|
date=html.escape(date_str),
|
||||||
complete=complete,
|
complete=complete,
|
||||||
incomplete=len(outstanding),
|
incomplete=len(outstanding),
|
||||||
|
crashed=len(crashed_ids),
|
||||||
total=total,
|
total=total,
|
||||||
content=content,
|
content=content,
|
||||||
)
|
)
|
||||||
@@ -202,7 +229,10 @@ def main():
|
|||||||
print(f" {complete}/{total} drawers fully migrated, "
|
print(f" {complete}/{total} drawers fully migrated, "
|
||||||
f"{len(outstanding)} outstanding.")
|
f"{len(outstanding)} outstanding.")
|
||||||
if outstanding:
|
if outstanding:
|
||||||
print(" Outstanding: " + ", ".join(d for d, _, _ in outstanding))
|
print(" Outstanding: " + ", ".join(d for d, _, _, _ in outstanding))
|
||||||
|
if crashed_ids:
|
||||||
|
print(f" Outstanding AND crashed ({len(crashed_ids)}) "
|
||||||
|
"— spot-check these first: " + ", ".join(crashed_ids))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user