Files
office_translator/tests/test_excel_chart_sheet_refs.py
sepehr 07c4c12e6a
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m19s
fix(translate): chart labels, sheet-refs, sheet-name offset, paid-user Memento
Multiple translation bugs in Word/Excel/PPTX that caused chart elements
to be left untranslated or charts to render as empty series.

Backend
-------
* providers (deepseek/openai/minimax): tighten system prompt so the LLM
  actually translates chart titles/axis labels/legend/category labels,
  month abbreviations, and clarifies what counts as a 'real' proper noun
  (people/place/company/product names) vs. technical labels. Old rule
  'keep proper nouns unchanged' was being read too broadly by the model
  and caused chart text to be skipped.
* excel_translator.py:
  - Sheet reference rewrite: when a sheet is renamed, the chart XML's
    c:f refs (e.g. 'Sales 2024'!$D$2:$D$61) are now rewritten to the
    new name with proper apostrophe escaping (Chiffre d'affaires ->
    'Chiffre d''affaires') and auto-quoting when the new name contains
    spaces or special chars. Without this, the chart points at a sheet
    that no longer exists and renders 0/empty series.
  - Sheet name offset bug: sheet_name_offset was computed after chart
    text was appended to text_elements, causing sheet names to receive
    chart text translations. Now captured BEFORE sheet names are added.
* New tests:
  - test_excel_chart_sheet_refs.py (10 unit tests, synthetic inputs)
  - test_chart_translation_prompt.py (3 contract tests on the prompt)

Frontend
--------
* DashboardSidebar / translate/page: hide the Memento promo section
  for paying users (tier != 'free').
* constants.ts: temporarily comment out the 'CLES API' nav item.
  Update constants.test.ts to match the new state.

All fixes are generic - no file-specific hardcoding, edge cases covered
(empty mapping, missing bang, apostrophe escaping, partial renames,
multi-series).
2026-07-15 22:04:40 +02:00

139 lines
4.7 KiB
Python

"""Regression tests for the bug where translated Excel chart <c:f> references
kept pointing at the original (now-renamed) sheet name, causing the chart
to render as zero/empty series after translation.
This is a real bug: see e.g. test_excel.xlsx where the chart references
'Sales 2024'!$D$2:$D$61 but the sheet is renamed to "Chiffre d'affaires
par produit" — the chart then shows 0 for every data point.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from lxml import etree
from translators.excel_translator import ExcelTranslator
NS_C = "http://schemas.openxmlformats.org/drawingml/2006/chart"
def _build_chart_xml(series_refs):
"""Helper: build a chart XML with the given <c:f> strings."""
sers = "\n".join(
f"<c:ser><c:tx><c:strRef><c:f>{ref}</c:f></c:strRef></c:tx>"
f"<c:cat><c:strRef><c:f>{ref}</c:f></c:strRef></c:cat>"
f"<c:val><c:numRef><c:f>{ref}</c:f></c:numRef></c:val></c:ser>"
for ref in series_refs
)
return f"""<c:chartSpace xmlns:c="{NS_C}" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<c:chart>
<c:plotArea><c:lineChart>{sers}</c:lineChart></c:plotArea>
</c:chart>
</c:chartSpace>"""
def test_quoted_sheet_name_with_apostrophe_in_new_name():
r = ExcelTranslator._rewrite_sheet_ref(
"'Sales 2024'!$D$2:$D$61",
{"Sales 2024": "Chiffre d'affaires par produit"},
)
assert r == "'Chiffre d''affaires par produit'!$D$2:$D$61", f"got: {r!r}"
def test_unquoted_sheet_name_simple():
r = ExcelTranslator._rewrite_sheet_ref(
"Sheet1!$A$1",
{"Sheet1": "Feuille1"},
)
assert r == "Feuille1!$A$1", f"got: {r!r}"
def test_new_name_with_space_gets_quoted():
r = ExcelTranslator._rewrite_sheet_ref(
"Sheet1!$A$1",
{"Sheet1": "Ma Feuille"},
)
assert r == "'Ma Feuille'!$A$1", f"got: {r!r}"
def test_name_not_in_mapping_unchanged():
r = ExcelTranslator._rewrite_sheet_ref(
"Other!$A$1",
{"Sheet1": "Feuille1"},
)
assert r == "Other!$A$1", f"got: {r!r}"
def test_empty_mapping_is_noop():
r = ExcelTranslator._rewrite_sheet_ref("Sheet1!$A$1", {})
assert r == "Sheet1!$A$1", f"got: {r!r}"
def test_no_bang_is_noop():
r = ExcelTranslator._rewrite_sheet_ref("just_a_label", {"x": "y"})
assert r == "just_a_label", f"got: {r!r}"
def test_real_chart_xml_all_refs_rewritten():
chart_xml = etree.fromstring(_build_chart_xml(["Sheet1!$B$1"]))
n = ExcelTranslator._rewrite_sheet_refs_in_chart(chart_xml, {"Sheet1": "Ma Feuille"})
assert n == 3, f"expected 3 rewrites, got {n}"
refs = [f.text for f in chart_xml.iter(f"{{{NS_C}}}f")]
assert refs == [
"'Ma Feuille'!$B$1",
"'Ma Feuille'!$B$1",
"'Ma Feuille'!$B$1",
], f"got: {refs}"
def test_real_chart_xml_apostrophe_escaped_in_new_name():
chart_xml = etree.fromstring(_build_chart_xml(["Sheet1!$B$1"]))
n = ExcelTranslator._rewrite_sheet_refs_in_chart(
chart_xml, {"Sheet1": "Chiffre d'affaires"}
)
assert n == 3
refs = [f.text for f in chart_xml.iter(f"{{{NS_C}}}f")]
assert all(r == "'Chiffre d''affaires'!$B$1" for r in refs), f"got: {refs}"
def test_partial_mapping_leaves_other_refs_intact():
"""When only one sheet is renamed, refs to other sheets must stay."""
chart_xml = etree.fromstring(
_build_chart_xml(["Sheet1!$A$1", "Sheet2!$B$1"])
)
n = ExcelTranslator._rewrite_sheet_refs_in_chart(
chart_xml, {"Sheet1": "Feuille1"}
)
# 3 refs to Sheet1 (one series) are rewritten; 3 refs to Sheet2 stay.
assert n == 3, f"expected 3 rewrites, got {n}"
refs = [f.text for f in chart_xml.iter(f"{{{NS_C}}}f")]
assert "Feuille1!$A$1" in refs
assert "Sheet2!$B$1" in refs
def test_multiple_series_renamed():
"""When several sheets are renamed, refs to any of them get updated."""
chart_xml = etree.fromstring(
_build_chart_xml(["Sheet1!$A$1", "Sheet2!$B$1", "Sheet3!$C$1"])
)
n = ExcelTranslator._rewrite_sheet_refs_in_chart(
chart_xml,
{"Sheet1": "F1", "Sheet2": "F2", "Sheet3": "F3"},
)
assert n == 9, f"expected 9 rewrites (3 series x 3 refs each), got {n}"
if __name__ == "__main__":
test_quoted_sheet_name_with_apostrophe_in_new_name()
test_unquoted_sheet_name_simple()
test_new_name_with_space_gets_quoted()
test_name_not_in_mapping_unchanged()
test_empty_mapping_is_noop()
test_no_bang_is_noop()
test_real_chart_xml_all_refs_rewritten()
test_real_chart_xml_apostrophe_escaped_in_new_name()
test_partial_mapping_leaves_other_refs_intact()
test_multiple_series_renamed()
print("All chart sheet-ref rewrite tests passed.")