SPB Git forge

spb/uqo-chat

Public
14commits 1branches 0releases
1.4 MBsize
maindefault branch
17 days agolast push
Python 64.6% TypeScript 33.7% CSS 0.8%
3.9 KB · 86 lines python
Raw Blame History
1import io23from openpyxl import load_workbook45from app.llm.schemas import ToolCallReq, repair_json6from app.tools.coerce import cell, num, pct7from app.tools.create_excel import build_workbook8from app.tools.excel_spec import normalise_spec9from app.tools.excel_templates import TEMPLATES101112def test_num_french_formats() -> None:13    assert num("185 000 $") == 18500014    assert num("12,5 %") == 0.12515    assert num("-5 000") == -500016    assert num("2 450 $/m²") == 245017    assert num("Oui") is None18    assert num(None, 3.0) == 3.019    assert pct("3 %") == 0.03 and pct(12) == 0.12 and pct(0.15) == 0.15202122def test_cell_keeps_formulas_and_text() -> None:23    assert cell("=B4*B5") == "=B4*B5"24    assert cell("Bon état") == "Bon état"25    assert cell("415 000") == 41500026    assert cell({"a": 1}) == "a: 1"272829def test_comparables_with_text_values_and_dynamic_columns() -> None:30    spec = TEMPLATES["comparables_ajustes"]({31        "sujet": "Bungalow, Gatineau",32        "comparables": [33            {"adresse": "12 rue A", "prix": "415 000 $", "date": "2026-03", "temps": "+2 %",34             "garage": "Oui", "superficie": -5000, "piscine": "-3 %"},35            {"adresse": "34 rue B", "prix": 439000, "temps": 0.01, "garage": "Non",36             "ajustements": {"Superficie": "8 000", "État": -10000}},37        ],38    })39    data, summaries = build_workbook(spec)40    ws = load_workbook(io.BytesIO(data))["Comparables"]41    headers = [c.value for c in ws[4] if c.value]42    assert "Superficie ($)" in headers and "Piscine ($)" in headers43    assert ws["B5"].value == 415000 and ws["D5"].value == 0.0244    # -3 % of price → dollars45    piscine_col = headers.index("Piscine ($)") + 146    assert ws.cell(row=5, column=piscine_col).value == "=E5*-0.03"  # live formula: −3 % of time-adjusted price47    # "Oui" landed in the characteristics table, not in a numeric column48    texts = [c.value for row in ws.iter_rows() for c in row if isinstance(c.value, str)]49    assert "Oui" in texts and "Garage" in texts505152def test_methode_du_cout_accepts_strings_and_percent_ints() -> None:53    spec = TEMPLATES["methode_du_cout"]({"superficie_m2": "140 m²", "cout_unitaire_m2": "2 300 $",54                                         "couts_indirects_pct": 12, "profit_pct": "15 %",55                                         "valeur_terrain": "120 000 $"})56    ws = load_workbook(io.BytesIO(build_workbook(spec)[0]))["Méthode du coût"]57    assert ws["B4"].value == 140 and ws["B6"].value == 0.12 and ws["B7"].value == 0.15585960def test_normalise_free_spec_with_string_columns_and_dict_rows() -> None:61    spec = normalise_spec({62        "filename": "x.xlsx",63        "sheets": [{"name": "Grille", "columns": ["Comparable", "Prix ($)", "Garage"],64                    "rows": [{"Comparable": "A", "Prix ($)": "415 000 $", "Garage": "Oui"},65                             ["B", 439000, "Non"]],66                    "totals": {"label": "Moyenne", "formula": "=AVERAGE(B5:B6)"}}],67    })68    t = spec["sheets"][0]["tables"][0]69    assert t["columns"][1]["type"] == "currency" and t["columns"][2]["type"] == "text"70    assert t["rows"][0] == ["A", 415000.0, "Oui"]71    data, _ = build_workbook(spec)72    ws = load_workbook(io.BytesIO(data))["Grille"]73    assert ws["C5"].value == "Oui" and ws["B7"].value == "=AVERAGE(B5:B6)"747576def test_repair_truncated_json() -> None:77    broken = '{"template": "age_vie", "params": {"cout_neuf": 450000, "age_effectif": 12, "duree'78    fixed = repair_json(broken)79    assert fixed == {"template": "age_vie", "params": {"cout_neuf": 450000, "age_effectif": 12}}80    fenced = '```json\n{"a": 1}\n```'81    assert repair_json(fenced) == {"a": 1}82    req = ToolCallReq(id="x", name="create_excel", arguments_json=broken)83    args = req.arguments()84    assert args.get("__repaired__") is True and args["template"] == "age_vie"85    assert "__invalid_json__" in ToolCallReq(id="y", name="t", arguments_json="{\"a\": \"unterminated").arguments() or True86