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:
@@ -117,7 +117,7 @@ def compare_table(result: str, expected: str = None, **options) -> float:
|
|||||||
pdworkbooke = None
|
pdworkbooke = None
|
||||||
worksheete_names: List[str] = 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(
|
functools.partial(
|
||||||
_parse_sheet_idx,
|
_parse_sheet_idx,
|
||||||
result_sheet_names=worksheetr_names,
|
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 {{{ #
|
# Compare Sheet Data by Internal Value {{{ #
|
||||||
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
|
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
|
||||||
# sheet_idx1: as sheet_idx0
|
# 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))
|
error_limit: int = r.get("precision", 4)
|
||||||
sheet2: pd.DataFrame = _load_sheet(*parse_idx(r["sheet_idx1"], pdworkbookr, pdworkbooke))
|
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)
|
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)
|
logger.debug("Assertion: %s =v= %s - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
|
||||||
# }}} Compare Sheet Data by Internal Value #
|
# }}} 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
|
# sheet_idx1: as sheet_idx0
|
||||||
# props: list of str indicating concerned styles, see utils._read_cell_style
|
# 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)
|
sheet_idx1: Tuple[Book, str] = parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke)
|
||||||
styles2: Dict[str, List[Any]] = load_xlsx_styles(*parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke), **r)
|
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_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"]
|
# 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
|
metric: bool = styles1 == styles2
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import zipfile
|
import zipfile
|
||||||
from typing import Any, TypeVar, Union, Iterable, Optional, Callable
|
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
|
from urllib.parse import urlparse, urlunparse
|
||||||
import re
|
import re
|
||||||
import functools
|
import functools
|
||||||
@@ -18,10 +18,10 @@ from openpyxl import Workbook
|
|||||||
from openpyxl.chart._chart import ChartBase
|
from openpyxl.chart._chart import ChartBase
|
||||||
from openpyxl.worksheet.worksheet import Worksheet
|
from openpyxl.worksheet.worksheet import Worksheet
|
||||||
from openpyxl.worksheet.filters import AutoFilter, SortState
|
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.worksheet.dimensions import DimensionHolder
|
||||||
from openpyxl.formatting.formatting import ConditionalFormattingList
|
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.cell.cell import Cell
|
||||||
from openpyxl.styles.differential import DifferentialStyle
|
from openpyxl.styles.differential import DifferentialStyle
|
||||||
from openpyxl.pivot.table import TableDefinition as PivotTableDefinition
|
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:
|
if "filter" in pivot_props:
|
||||||
info["filter_fields"] = set(f.fld for f in pvt.pageFields)
|
info["filter_fields"] = set(f.fld for f in pvt.pageFields)
|
||||||
if "col_fields" in pivot_props:
|
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:
|
if "row_fields" in pivot_props:
|
||||||
info["row_fields"] = [f.x-left_bias for f in pvt.rowFields]
|
info["row_fields"] = [f.x-left_bias for f in pvt.rowFields]
|
||||||
if "data_fields" in pivot_props:
|
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":
|
elif style_name=="font_size":
|
||||||
return (diff_style or cell).font.size if cell.value is not None else None
|
return (diff_style or cell).font.size if cell.value is not None else None
|
||||||
elif style_name=="fill_type":
|
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":
|
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":
|
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":
|
elif style_name=="hyperlink":
|
||||||
return cell.hyperlink or "" if cell.value is not None else None
|
return cell.hyperlink or "" if cell.value is not None else None
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Unsupported Style: {:}".format(style_name))
|
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 {{{ #
|
# function load_xlsx_styles {{{ #
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
xlsx_file (Workbook): concerned excel book
|
xlsx_file (Workbook): concerned excel book
|
||||||
sheet_name (str): sheet name
|
sheet_name (str): sheet name
|
||||||
|
book_name (str): book name
|
||||||
options (Dict[str, List[str]): dick like {"props": list of str} giving
|
options (Dict[str, List[str]): dick like {"props": list of str} giving
|
||||||
the concerned styles
|
the concerned styles
|
||||||
|
|
||||||
@@ -398,10 +415,35 @@ def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[st
|
|||||||
active_cells: List[Cell] = []
|
active_cells: List[Cell] = []
|
||||||
if r.type == "expression":
|
if r.type == "expression":
|
||||||
condition: Callable[[str], bool] = formula_parser.ast("=" + r.formula[0])[1].compile()
|
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 rge in fmt.cells:
|
||||||
for c in rge.cells:
|
for c in rge.cells:
|
||||||
cell: Cell = worksheet.cell(row=c[0], column=c[1])
|
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)
|
active_cells.append(cell)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Not Implemented Condition Type: {:}".format(r.type))
|
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:
|
for c in active_cells:
|
||||||
style_dict[c.coordinate] = [_read_cell_style(st, c, r.dxf) for st in concerned_styles]
|
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
|
return style_dict
|
||||||
# }}} function load_xlsx_styles #
|
# }}} function load_xlsx_styles #
|
||||||
|
|
||||||
|
|||||||
@@ -387,12 +387,15 @@ def _create_atspi_node(node: Accessible, depth: int = 0, flag: Optional[str] = N
|
|||||||
index_base += MAXIMUN_COLUMN
|
index_base += MAXIMUN_COLUMN
|
||||||
return xml_node
|
return xml_node
|
||||||
else:
|
else:
|
||||||
for i, ch in enumerate(node):
|
try:
|
||||||
# HYPERPARAMETER
|
for i, ch in enumerate(node):
|
||||||
if i>=1025:
|
# HYPERPARAMETER
|
||||||
logger.warning("Max width reached")
|
if i>=1025:
|
||||||
break
|
logger.warning("Max width reached")
|
||||||
xml_node.append(_create_atspi_node(ch, depth+1, flag))
|
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
|
return xml_node
|
||||||
# }}} function _create_atspi_node #
|
# }}} function _create_atspi_node #
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "1954cced-e748-45c4-9c26-9855b97fbc5e",
|
"id": "1954cced-e748-45c4-9c26-9855b97fbc5e",
|
||||||
"snapshot": "libreoffice_calc",
|
"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",
|
"source": "SheetCopilot@104",
|
||||||
"config": [
|
"config": [
|
||||||
{
|
{
|
||||||
@@ -73,8 +73,8 @@
|
|||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"type": "pivot_table",
|
"type": "pivot_table",
|
||||||
"sheet_idx0": 0,
|
"sheet_idx0": "RNSheet2",
|
||||||
"sheet_idx1": "EI0",
|
"sheet_idx1": "ENSheet2",
|
||||||
"pivot_props": [
|
"pivot_props": [
|
||||||
"col_fields",
|
"col_fields",
|
||||||
"filter",
|
"filter",
|
||||||
|
|||||||
@@ -73,8 +73,8 @@
|
|||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"type": "sheet_data",
|
"type": "sheet_data",
|
||||||
"sheet_idx0": 0,
|
"sheet_idx0": "RNSheet2",
|
||||||
"sheet_idx1": "EI0"
|
"sheet_idx1": "ENSheet2"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "1de60575-bb6e-4c3d-9e6a-2fa699f9f197",
|
"id": "1de60575-bb6e-4c3d-9e6a-2fa699f9f197",
|
||||||
"snapshot": "libreoffice_calc",
|
"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",
|
"source": "SheetCopilot@55",
|
||||||
"config": [
|
"config": [
|
||||||
{
|
{
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
{
|
{
|
||||||
"type": "sleep",
|
"type": "sleep",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"seconds": 0.5
|
"seconds": 3.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -73,8 +73,8 @@
|
|||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"type": "pivot_table",
|
"type": "pivot_table",
|
||||||
"sheet_idx0": 0,
|
"sheet_idx0": "RNSheet2",
|
||||||
"sheet_idx1": "EI0",
|
"sheet_idx1": "ENSheet2",
|
||||||
"pivot_props": [
|
"pivot_props": [
|
||||||
"col_fields",
|
"col_fields",
|
||||||
"filter",
|
"filter",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "21ab7b40-77c2-4ae6-8321-e00d3a086c73",
|
"id": "21ab7b40-77c2-4ae6-8321-e00d3a086c73",
|
||||||
"snapshot": "libreoffice_calc",
|
"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",
|
"source": "SheetCopilot@124",
|
||||||
"config": [
|
"config": [
|
||||||
{
|
{
|
||||||
@@ -81,12 +81,7 @@
|
|||||||
"sheet_idx0": 0,
|
"sheet_idx0": 0,
|
||||||
"sheet_idx1": "EI0",
|
"sheet_idx1": "EI0",
|
||||||
"props": [
|
"props": [
|
||||||
"font_bold",
|
"font_color"
|
||||||
"font_color",
|
|
||||||
"bgcolor",
|
|
||||||
"font_name",
|
|
||||||
"font_italic",
|
|
||||||
"font_underline"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user