Files
f9f6bc01fd
CI / test (push) Canceled after 0s
Feature/delta team/epic r04 (#7)
## Summary

epic r04 - begin refactor

## Change Type

- [x] Cowork feature
- [ ] Bug fix
- [ ] Core AI contribution
- [ ] Test / hardening
- [ ] Performance
- [ ] Documentation

## Related Work

Cowork Task:

Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets

Core AI Issue:

Core Task:

Related PR:

## Scope

What is intentionally included?

What is intentionally NOT included?

## Validation

- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual verification
- [ ] Regression check

Commands / evidence:

## Security Impact

Permission / credential / network / customer data impact:

## Compatibility

- [ ] No breaking change
- [ ] Breaking change documented

## Reviewer Notes

Anything Cowork reviewers should pay attention to.

---------

Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com>
Co-authored-by: Huong Le Thi Thien <huongltt35@fpt.com>
Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com>
Co-authored-by: Vu Dam Tuan <vudt15@fpt.com>
Co-authored-by: Hiep Ha Van <hiephv3@fpt.com>
Co-authored-by: Lam Hoang Van <lamhv7@fpt.com>
Reviewed-on: #7
Co-authored-by: Duy Le Huu <duylh19@fpt.com>
2026-08-31 05:15:13 +00:00

100 lines
3.9 KiB
Python

"""Build a REAL .xlsx from text content so an agent can CREATE Excel by calling
save_file/write_file('report.xlsx', <table text>).
A .xlsx is a binary ZIP package — writing the model's text straight to a .xlsx
corrupts it (the file won't open). This turns the content the model produces
(CSV / TSV / a Markdown table / JSON rows) into a genuine workbook via openpyxl
(already a dependency). Pure logic (no Qt) → unit-testable.
"""
from __future__ import annotations
import csv
import io
import json
from pathlib import Path
from typing import List
def _openpyxl():
"""Import openpyxl, auto-installing it on first use if it isn't present —
same self-healing path the Excel VIEWER and doc_style_extract use
(``deps.ensure_module``). openpyxl is a declared dependency, so this only
matters for a from-source run whose venv is missing it; a normal install /
frozen build already bundles it. Returns the module or None."""
try:
from .deps import ensure_module
return ensure_module("openpyxl", "openpyxl")
except Exception: # noqa: BLE001 - fall back to a plain import
try:
import openpyxl # noqa: F401
return openpyxl
except Exception: # noqa: BLE001
return None
def is_available() -> bool:
"""Máy đã cài ``openpyxl`` chưa — không có thì mọi tính năng Excel tắt."""
return _openpyxl() is not None
def _rows_from_text(content: str) -> List[list]:
"""Parse table content into a list of rows. Accepts JSON (list-of-lists or
list-of-dicts), a Markdown table, or CSV/TSV (delimiter sniffed)."""
content = content or ""
s = content.strip()
# JSON: [[...],[...]] or [{...},{...}] or {"rows"/"data": [...]}
if s[:1] in ("[", "{"):
try:
data = json.loads(s)
if isinstance(data, dict):
data = data.get("rows") or data.get("data") or [data]
rows: List[list] = []
if isinstance(data, list):
if data and isinstance(data[0], dict):
headers = list(dict.fromkeys(k for d in data if isinstance(d, dict) for k in d))
rows.append(headers)
for d in data:
rows.append([d.get(h, "") for h in headers] if isinstance(d, dict) else [d])
else:
for r in data:
rows.append(list(r) if isinstance(r, (list, tuple)) else [r])
if rows:
return rows
except (ValueError, TypeError):
pass
lines = [ln for ln in content.splitlines() if ln.strip()]
# Markdown table: rows delimited by '|', a --- separator row skipped.
if lines and lines[0].lstrip().startswith("|"):
rows = []
for ln in lines:
body = ln.strip()
if set(body) <= set("|-: "): # separator row like |---|---|
continue
rows.append([c.strip() for c in body.strip("|").split("|")])
if rows:
return rows
# CSV / TSV — sniff which delimiter dominates.
delim = "\t" if content.count("\t") > content.count(",") else ","
return list(csv.reader(io.StringIO(content), delimiter=delim))
def build_xlsx_from_text(path, content: str) -> bool:
"""Write a genuine .xlsx at ``path`` from CSV/TSV/Markdown-table/JSON
``content``. Returns True on success, False if openpyxl is unavailable or the
write fails (caller can then fall back). Never raises."""
openpyxl = _openpyxl()
if openpyxl is None:
return False
try:
rows = _rows_from_text(content)
wb = openpyxl.Workbook()
ws = wb.active
for r in rows:
ws.append(["" if v is None else v for v in r])
p = Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
wb.save(str(p))
return True
except Exception: # noqa: BLE001
return False