Initial commit - Selenium automation framework
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
from pathlib import Path
|
||||
import re
|
||||
import zipfile
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
|
||||
MAIN_NS = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"}
|
||||
REL_NS = {"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"}
|
||||
PACKAGE_REL_NS = {"pr": "http://schemas.openxmlformats.org/package/2006/relationships"}
|
||||
|
||||
|
||||
def load_single_bid_market_matrix(excel_path, trading_market, sheet_name="Sheet1", excel_layout=None):
|
||||
|
||||
excel_file = Path(excel_path)
|
||||
if not excel_file.exists():
|
||||
raise FileNotFoundError(f"Excel input file not found: {excel_file}")
|
||||
|
||||
layout = excel_layout or {}
|
||||
|
||||
with zipfile.ZipFile(excel_file) as workbook_zip:
|
||||
shared_strings = _load_shared_strings(workbook_zip)
|
||||
sheet_xml_path = _resolve_sheet_xml_path(workbook_zip, sheet_name)
|
||||
sheet_root = ET.fromstring(workbook_zip.read(sheet_xml_path))
|
||||
cell_map = _load_cell_map(sheet_root, shared_strings)
|
||||
|
||||
start_column = layout.get("column_start", "B")
|
||||
market_price_rows = layout.get("market_price_rows", {})
|
||||
common_data_start_row = int(layout.get("common_data_start_row", 6))
|
||||
price_row = int(market_price_rows.get(trading_market, 2))
|
||||
_, start_col = _split_cell_reference(f"{start_column}1")
|
||||
max_row, max_col = _max_used_extent(cell_map, price_row, start_col)
|
||||
|
||||
matrix = []
|
||||
for row_index in [price_row, *range(common_data_start_row, max_row + 1)]:
|
||||
row_values = []
|
||||
for col_index in range(start_col, max_col + 1):
|
||||
row_values.append(cell_map.get((row_index, col_index), ""))
|
||||
matrix.append(row_values)
|
||||
|
||||
matrix = _trim_trailing_empty_rows_and_columns(matrix)
|
||||
if not matrix or not matrix[0]:
|
||||
raise ValueError(f"No bidding data found in {excel_file} for market {trading_market}.")
|
||||
|
||||
tsv = "\n".join("\t".join(value for value in row) for row in matrix)
|
||||
return {
|
||||
"matrix": matrix,
|
||||
"tsv": tsv,
|
||||
"row_count": len(matrix),
|
||||
"column_count": len(matrix[0]),
|
||||
"first_value": matrix[0][0],
|
||||
}
|
||||
|
||||
|
||||
def _load_shared_strings(workbook_zip):
|
||||
|
||||
try:
|
||||
root = ET.fromstring(workbook_zip.read("xl/sharedStrings.xml"))
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
values = []
|
||||
for item in root.findall("a:si", MAIN_NS):
|
||||
text_parts = [node.text or "" for node in item.findall(".//a:t", MAIN_NS)]
|
||||
values.append("".join(text_parts))
|
||||
return values
|
||||
|
||||
|
||||
def _resolve_sheet_xml_path(workbook_zip, sheet_name):
|
||||
|
||||
workbook_root = ET.fromstring(workbook_zip.read("xl/workbook.xml"))
|
||||
relationships_root = ET.fromstring(workbook_zip.read("xl/_rels/workbook.xml.rels"))
|
||||
|
||||
relationship_map = {
|
||||
rel.attrib["Id"]: rel.attrib["Target"]
|
||||
for rel in relationships_root.findall("pr:Relationship", PACKAGE_REL_NS)
|
||||
}
|
||||
|
||||
for sheet in workbook_root.findall("a:sheets/a:sheet", {**MAIN_NS, **REL_NS}):
|
||||
if sheet.attrib.get("name") != sheet_name:
|
||||
continue
|
||||
|
||||
relationship_id = sheet.attrib.get(f"{{{REL_NS['r']}}}id")
|
||||
target = relationship_map[relationship_id]
|
||||
target = target.lstrip("/")
|
||||
if not target.startswith("xl/"):
|
||||
target = f"xl/{target}"
|
||||
return target
|
||||
|
||||
raise KeyError(f"Sheet '{sheet_name}' not found in workbook.")
|
||||
|
||||
|
||||
def _load_cell_map(sheet_root, shared_strings):
|
||||
|
||||
cell_map = {}
|
||||
|
||||
for row in sheet_root.findall(".//a:sheetData/a:row", MAIN_NS):
|
||||
for cell in row.findall("a:c", MAIN_NS):
|
||||
reference = cell.attrib.get("r", "")
|
||||
row_index, col_index = _split_cell_reference(reference)
|
||||
cell_map[(row_index, col_index)] = _cell_value(cell, shared_strings)
|
||||
|
||||
return cell_map
|
||||
|
||||
|
||||
def _cell_value(cell, shared_strings):
|
||||
|
||||
cell_type = cell.attrib.get("t")
|
||||
|
||||
if cell_type == "inlineStr":
|
||||
text_nodes = cell.findall(".//a:t", MAIN_NS)
|
||||
return "".join(node.text or "" for node in text_nodes)
|
||||
|
||||
value_node = cell.find("a:v", MAIN_NS)
|
||||
if value_node is None or value_node.text is None:
|
||||
return ""
|
||||
|
||||
raw_value = value_node.text
|
||||
if cell_type == "s":
|
||||
return shared_strings[int(raw_value)]
|
||||
|
||||
return raw_value
|
||||
|
||||
|
||||
def _split_cell_reference(reference):
|
||||
|
||||
match = re.match(r"([A-Z]+)(\d+)", reference)
|
||||
if not match:
|
||||
raise ValueError(f"Unsupported cell reference: {reference}")
|
||||
|
||||
column_letters, row_number = match.groups()
|
||||
return int(row_number), _column_letters_to_number(column_letters)
|
||||
|
||||
|
||||
def _column_letters_to_number(column_letters):
|
||||
|
||||
result = 0
|
||||
for character in column_letters:
|
||||
result = result * 26 + (ord(character) - ord("A") + 1)
|
||||
return result
|
||||
|
||||
|
||||
def _max_used_extent(cell_map, start_row, start_col):
|
||||
|
||||
used_rows = [row for row, col in cell_map if row >= start_row and col >= start_col and cell_map[(row, col)] != ""]
|
||||
used_cols = [col for row, col in cell_map if row >= start_row and col >= start_col and cell_map[(row, col)] != ""]
|
||||
|
||||
if not used_rows or not used_cols:
|
||||
raise ValueError("No populated cells were found in the requested data area.")
|
||||
|
||||
return max(used_rows), max(used_cols)
|
||||
|
||||
|
||||
def _trim_trailing_empty_rows_and_columns(matrix):
|
||||
|
||||
while matrix and all(value == "" for value in matrix[-1]):
|
||||
matrix.pop()
|
||||
|
||||
if not matrix:
|
||||
return matrix
|
||||
|
||||
while matrix and matrix[0]:
|
||||
if any(row[-1] != "" for row in matrix):
|
||||
break
|
||||
for row in matrix:
|
||||
row.pop()
|
||||
|
||||
return matrix
|
||||
Reference in New Issue
Block a user