1################################################################ 2# 3# Licensed to the Apache Software Foundation (ASF) under one 4# or more contributor license agreements. See the NOTICE file 5# distributed with this work for additional information 6# regarding copyright ownership. The ASF licenses this file 7# to you under the Apache License, Version 2.0 (the 8# "License"); you may not use this file except in compliance 9# with the License. You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, 14# software distributed under the License is distributed on an 15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16# KIND, either express or implied. See the License for the 17# specific language governing permissions and limitations 18# under the License. 19 20"""Evaluates formulas in a new Calc document. The data area the cases refer to is described at the top of 21data/calcval-cases.tsv. Ported from the ODF harness's calcval.py.""" 22import math 23 24from aootest.calcval import api_formula, parse_mode 25from aootest_worker.ops import core 26 27 28def _num(v): 29 return v if math.isfinite(v) else repr(v) 30 31 32def cell_result(cell): 33 try: 34 rt = cell.getPropertyValue("FormulaResultType").value 35 except Exception: 36 rt = None 37 return {"rt": rt, "err": cell.getError(), "str": cell.getString(), "val": _num(float(cell.getValue()))} 38 39 40def setup_data(doc): 41 sheets = doc.getSheets() 42 sheets.insertNewByName("Other", 1) 43 sh = sheets.getByIndex(0) 44 45 def put(addr, vals): 46 a0 = sh.getCellRangeByName(addr).getRangeAddress() 47 for r, row in enumerate(vals): 48 for c, v in enumerate(row): 49 if v is None: 50 continue 51 cell = sh.getCellByPosition(a0.StartColumn + c, a0.StartRow + r) 52 if isinstance(v, str): 53 cell.setString(v) 54 else: 55 cell.setValue(v) 56 57 put("A1", [[v] for v in "yxxyxyxxy"]) 58 put("B1", [[v] for v in "yxyyyyxyx"]) 59 put("D1", [[10 * i, 11 * i, 101 * i] for i in range(1, 6)]) 60 put("H1", [[10 * i for i in range(1, 6)], [11 * i for i in range(1, 6)], [101 * i for i in range(1, 6)]]) 61 put("N1", [[v] for v in (3, 5, 8, 9, 13, 14)]) 62 put("O1", [[v] for v in (1, 2, 3, 4, 5, 6)]) 63 put("P1", [[v] for v in (2, 4, 7, 15, 30, 58)]) 64 put("R1", [["Name", "Age", "Score"], ["A", 20, 5], ["B", 30, "n/a"], ["C", 40, 15], ["D", 50, None], ["E", 60, 25]]) 65 put("V1", [["Age"], [">=30"]]) 66 put("W1", [["Age"], [None]]) 67 put("Y1", [["Name"], ["A"]]) 68 put("AH1", [[v] for v in (6, 6, 6, 6, 3, 2, 1, 2, 6, 4, 2)]) 69 put("AI1", [[v] for v in (1, 3, 5)]) 70 put("AJ1", [[v] for v in (2, 4, 7, 9)]) 71 put("AK1", [[v] for v in (2, 4, 7, 9)]) 72 return sh 73 74 75def calc_eval(cases): 76 """Sets each case's formula in a fresh Calc document and returns {"cases": [{"id", "result", "readback"} or 77 {"id", "exception"}], "aborted_at": id or None}. The scratch area AA1:AF12 is cleared after every case.""" 78 doc = core._desktop().loadComponentFromURL("private:factory/scalc", "_blank", 0, (core.pv("Hidden", True),)) 79 if doc is None: 80 raise core.Fail("uno", "could not create a Calc document") 81 results, aborted = [], None 82 try: 83 sh = setup_data(doc) 84 scratch = sh.getCellRangeByName("AA1:AF12") 85 for case in cases: 86 kind, cols, rows, row = parse_mode(case["mode"]) 87 rec = {"id": case["id"]} 88 try: 89 formula = api_formula(case["formula"]) 90 if kind == "cell": 91 cell = sh.getCellByPosition(26, row - 1) 92 cell.setFormula(formula) 93 cells = [cell] 94 else: 95 rng = sh.getCellRangeByPosition(26, 0, 26 + cols - 1, rows - 1) 96 rng.setArrayFormula(formula) 97 cells = [sh.getCellByPosition(26 + c, r) for r in range(rows) for c in range(cols)] 98 doc.calculateAll() 99 rec["result"] = [cell_result(c) for c in cells] 100 rec["readback"] = cells[0].getFormula() 101 except Exception as e: 102 rec["exception"] = "%s: %s" % (type(e).__name__, e) 103 if "Disposed" in type(e).__name__ or "connect" in str(e).lower(): 104 aborted = case["id"] 105 results.append(rec) 106 try: 107 scratch.clearContents(1 | 2 | 4 | 8 | 16 | 32) 108 except Exception as e: 109 aborted = aborted or case["id"] 110 rec["exception"] = rec.get("exception", "") + " clear: %s" % e 111 if aborted: 112 break 113 finally: 114 core._close(doc) 115 return {"cases": results, "aborted_at": aborted} 116 117 118OPS = {"calc_eval": core.guarded(calc_eval)} 119