ver Feb20th

fixed server/main.py
fixed several sheetcopilot tasks
fixed several calc metrics, including float difference of sheet_data and
conditional formatting recognition
This commit is contained in:
David Chang
2024-02-20 17:10:42 +08:00
parent e57d3ef981
commit 96e2c6ee34
7 changed files with 91 additions and 40 deletions

View File

@@ -117,7 +117,7 @@ def compare_table(result: str, expected: str = None, **options) -> float:
pdworkbooke = None
worksheete_names: List[str] = None
parse_idx: Callable[[Union[str, int], BOOK, BOOK], BOOK] = \
parse_idx: Callable[[Union[str, int], BOOK, BOOK], Tuple[BOOK, str]] = \
functools.partial(
_parse_sheet_idx,
result_sheet_names=worksheetr_names,
@@ -136,10 +136,15 @@ def compare_table(result: str, expected: str = None, **options) -> float:
# Compare Sheet Data by Internal Value {{{ #
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
# sheet_idx1: as sheet_idx0
# precision: int as number of decimal digits, default to 4
sheet1: pd.DataFrame = _load_sheet(*parse_idx(r["sheet_idx0"], pdworkbookr, pdworkbooke))
sheet2: pd.DataFrame = _load_sheet(*parse_idx(r["sheet_idx1"], pdworkbookr, pdworkbooke))
error_limit: int = r.get("precision", 4)
sheet1: pd.DataFrame = _load_sheet(*parse_idx(r["sheet_idx0"], pdworkbookr, pdworkbooke)).round(error_limit)
sheet2: pd.DataFrame = _load_sheet(*parse_idx(r["sheet_idx1"], pdworkbookr, pdworkbooke)).round(error_limit)
metric: bool = sheet1.equals(sheet2)
logger.debug("Sheet1: \n%s", str(sheet1))
logger.debug("Sheet2: \n%s", str(sheet2))
logger.debug("Sheet1 =v= Sheet2: \n%s", str(sheet1==sheet2))
logger.debug("Assertion: %s =v= %s - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Compare Sheet Data by Internal Value #
@@ -187,8 +192,13 @@ def compare_table(result: str, expected: str = None, **options) -> float:
# sheet_idx1: as sheet_idx0
# props: list of str indicating concerned styles, see utils._read_cell_style
styles1: Dict[str, List[Any]] = load_xlsx_styles(*parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke), **r)
styles2: Dict[str, List[Any]] = load_xlsx_styles(*parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke), **r)
sheet_idx1: Tuple[Book, str] = parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke)
book_name1: str = parse_idx(r["sheet_idx0"], result, expected)[0]
styles1: Dict[str, List[Any]] = load_xlsx_styles(*sheet_idx1, book_name1, **r)
sheet_idx2: Tuple[Book, str] = parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke)
book_name2: str = parse_idx(r["sheet_idx1"], result, expected)[0]
styles2: Dict[str, List[Any]] = load_xlsx_styles(*sheet_idx2, book_name2, **r)
# number_formats1: List[str] = [c.number_format.lower() for col in sheet1.iter_cols() for c in col if c.value is not None and c.data_type=="n"]
# number_formats2: List[str] = [c.number_format.lower() for col in sheet2.iter_cols() for c in col if c.value is not None and c.data_type=="n"]
metric: bool = styles1 == styles2

View File

@@ -1,7 +1,7 @@
import logging
import zipfile
from typing import Any, TypeVar, Union, Iterable, Optional, Callable
from typing import Dict, List, Set, Match, Tuple
from typing import Dict, List, Set, Match, Tuple, Pattern
from urllib.parse import urlparse, urlunparse
import re
import functools
@@ -18,10 +18,10 @@ from openpyxl import Workbook
from openpyxl.chart._chart import ChartBase
from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.worksheet.filters import AutoFilter, SortState
from openpyxl.worksheet.cell_range import MultiCellRange
from openpyxl.worksheet.cell_range import MultiCellRange, CellRange
from openpyxl.worksheet.dimensions import DimensionHolder
from openpyxl.formatting.formatting import ConditionalFormattingList
from openpyxl.utils import coordinate_to_tuple
from openpyxl.utils import coordinate_to_tuple, get_column_letter
from openpyxl.cell.cell import Cell
from openpyxl.styles.differential import DifferentialStyle
from openpyxl.pivot.table import TableDefinition as PivotTableDefinition
@@ -255,7 +255,7 @@ def load_pivot_tables(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[s
if "filter" in pivot_props:
info["filter_fields"] = set(f.fld for f in pvt.pageFields)
if "col_fields" in pivot_props:
info["col_fields"] = [f.x for f in pvt.colFields]
info["col_fields"] = [f.x-left_bias for f in pvt.colFields]
if "row_fields" in pivot_props:
info["row_fields"] = [f.x-left_bias for f in pvt.rowFields]
if "data_fields" in pivot_props:
@@ -347,22 +347,39 @@ def _read_cell_style(style_name: str, cell: Cell, diff_style: Optional[Different
elif style_name=="font_size":
return (diff_style or cell).font.size if cell.value is not None else None
elif style_name=="fill_type":
return (diff_style or cell).fill.tagname
try:
return (diff_style or cell).fill.tagname
except:
return None
elif style_name=="bgcolor":
return (diff_style or cell).fill.bgColor.rgb
try:
return (diff_style or cell).fill.bgColor.rgb
except:
return None
elif style_name=="fgcolor":
return (diff_style or cell).fill.fgColor.rgb
try:
return (diff_style or cell).fill.fgColor.rgb
except:
return None
elif style_name=="hyperlink":
return cell.hyperlink or "" if cell.value is not None else None
else:
raise NotImplementedError("Unsupported Style: {:}".format(style_name))
def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[str, List[Any]]:
_absolute_range_pattern: Pattern[str] = re.compile( r"""\$(?P<col1>[A-Z]{1,3})\$(?P<row1>\d+) # coord1
(?::
\$(?P<col2>[A-Z]{1,3})\$(?P<row2>\d+) # coord2
)?
"""
, re.X
)
def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, book_name: str, **options) -> Dict[str, List[Any]]:
# function load_xlsx_styles {{{ #
"""
Args:
xlsx_file (Workbook): concerned excel book
sheet_name (str): sheet name
book_name (str): book name
options (Dict[str, List[str]): dick like {"props": list of str} giving
the concerned styles
@@ -398,10 +415,35 @@ def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[st
active_cells: List[Cell] = []
if r.type == "expression":
condition: Callable[[str], bool] = formula_parser.ast("=" + r.formula[0])[1].compile()
logger.debug("Expression condition: %s", r.formula[0])
arguments: List[Any] = []
absolute_range_match: List[Tuple[str, str, str, str]] = _absolute_range_pattern.findall(r.formula[0])
for m in absolute_range_match:
logger.debug("Absolute ranges: %s", repr(m))
if m[2] is None and m[3] is None:
arguments.append(read_cell_value(book_name, sheet_name, coordinate="{:}{:}".format(m[0], m[1])))
else:
arguments.append( [ read_cell_value( book_name, sheet_name
, coordinate="{:}{:}".format( get_column_letter(c[1])
, c[0]
)
)\
for c in CellRange("{:}{:}:{:}{:}".format(m[0], m[1], m[2], m[3])).cells\
]
)
logger.debug("Absolute range arguments: %s", repr(arguments))
for rge in fmt.cells:
for c in rge.cells:
cell: Cell = worksheet.cell(row=c[0], column=c[1])
if condition(str(cell.value)):
cell_value = read_cell_value( book_name, sheet_name
, coordinate="{:}{:d}".format( get_column_letter(c[1])
, c[0]
)
)
if condition(cell_value, *arguments):
logger.debug("Active Cell %s(%s) for %s", repr(cell), str(cell_value), r.formula[0])
active_cells.append(cell)
else:
raise NotImplementedError("Not Implemented Condition Type: {:}".format(r.type))
@@ -409,6 +451,7 @@ def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[st
for c in active_cells:
style_dict[c.coordinate] = [_read_cell_style(st, c, r.dxf) for st in concerned_styles]
logger.debug(".[%s].styles: %s", sheet_name, repr(style_dict))
return style_dict
# }}} function load_xlsx_styles #

View File

@@ -387,12 +387,15 @@ def _create_atspi_node(node: Accessible, depth: int = 0, flag: Optional[str] = N
index_base += MAXIMUN_COLUMN
return xml_node
else:
for i, ch in enumerate(node):
# HYPERPARAMETER
if i>=1025:
logger.warning("Max width reached")
break
xml_node.append(_create_atspi_node(ch, depth+1, flag))
try:
for i, ch in enumerate(node):
# HYPERPARAMETER
if i>=1025:
logger.warning("Max width reached")
break
xml_node.append(_create_atspi_node(ch, depth+1, flag))
except:
logger.warning("Error occurred during children traversing. Has Ignored. Node: %s", lxml.etree.tostring(xml_node, encoding="unicode"))
return xml_node
# }}} function _create_atspi_node #

View File

@@ -1,7 +1,7 @@
{
"id": "1954cced-e748-45c4-9c26-9855b97fbc5e",
"snapshot": "libreoffice_calc",
"instruction": "Create a Pivot Table in a new sheet to count how many times each \"Invoice No.\" appears.",
"instruction": "Create a Pivot Table in a new sheet (Sheet2) to count how many times each \"Invoice No.\" appears.",
"source": "SheetCopilot@104",
"config": [
{
@@ -73,8 +73,8 @@
"rules": [
{
"type": "pivot_table",
"sheet_idx0": 0,
"sheet_idx1": "EI0",
"sheet_idx0": "RNSheet2",
"sheet_idx1": "ENSheet2",
"pivot_props": [
"col_fields",
"filter",
@@ -85,4 +85,4 @@
]
}
}
}
}

View File

@@ -73,10 +73,10 @@
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
"sheet_idx0": "RNSheet2",
"sheet_idx1": "ENSheet2"
}
]
}
}
}
}

View File

@@ -1,7 +1,7 @@
{
"id": "1de60575-bb6e-4c3d-9e6a-2fa699f9f197",
"snapshot": "libreoffice_calc",
"instruction": "Summarize the total revenue for each promotion type in a new sheet with the promotion names as the column headers.",
"instruction": "Summarize the total revenue for each promotion type in a new sheet (Sheet2) with the promotion names as the column headers.",
"source": "SheetCopilot@55",
"config": [
{
@@ -54,7 +54,7 @@
{
"type": "sleep",
"parameters": {
"seconds": 0.5
"seconds": 3.0
}
}
],
@@ -73,8 +73,8 @@
"rules": [
{
"type": "pivot_table",
"sheet_idx0": 0,
"sheet_idx1": "EI0",
"sheet_idx0": "RNSheet2",
"sheet_idx1": "ENSheet2",
"pivot_props": [
"col_fields",
"filter",
@@ -85,4 +85,4 @@
]
}
}
}
}

View File

@@ -1,7 +1,7 @@
{
"id": "21ab7b40-77c2-4ae6-8321-e00d3a086c73",
"snapshot": "libreoffice_calc",
"instruction": "Please calculate the period rate for my data in a new column with header \"Period Rate (%)\", convert the results as number type, and highlight the highest result as green.",
"instruction": "Please calculate the period rate for my data in a new column with header \"Period Rate (%)\", convert the results as number type, and highlight the highest result with green (#00ff00) font.",
"source": "SheetCopilot@124",
"config": [
{
@@ -81,15 +81,10 @@
"sheet_idx0": 0,
"sheet_idx1": "EI0",
"props": [
"font_bold",
"font_color",
"bgcolor",
"font_name",
"font_italic",
"font_underline"
"font_color"
]
}
]
}
}
}
}