Source code for spreadsheet_intelligence.core.excel_autoshape_loader
import logging
from typing import Optional
import matplotlib.pyplot as plt
from .base_loader import BaseLoader
from spreadsheet_intelligence.read_data.excel_to_xml import (
convert_xlsx_to_xml_in_memory,
)
from spreadsheet_intelligence.parsers.drawing.drawing_xml_parser import DrawingXMLParser
from spreadsheet_intelligence.parsers.theme.theme_xml_parser import ThemeXMLParser
from spreadsheet_intelligence.models.raw.drawing.drawing_models import (
ConnectorAnchorRaw,
ShapeAnchorRaw,
)
from spreadsheet_intelligence.models.raw.theme.theme_models import Theme
from spreadsheet_intelligence.models.common.enums import ConnectorType
from spreadsheet_intelligence.converters.drawing.drawing_raw_converters.connector_raw_converter import (
StraightConnector1Converter,
BentConnector3Converter,
)
from spreadsheet_intelligence.converters.drawing.drawing_raw_converters.shape_raw_converter import (
ShapeConverter,
)
from spreadsheet_intelligence.models.converted.drawing_models import (
StraightConnector1,
BentConnector3,
Shape,
)
from spreadsheet_intelligence.formatters.drawing.format import AllDrawingsFormatter
logger = logging.getLogger(__name__)
[docs]
class ExcelAutoshapeLoader(BaseLoader):
"""
A loader class for extracting and converting autoshapes from an Excel file.
Attributes:
file_path (str): Path to the Excel file.
drawing_xml_path (str): Path to the drawing XML within the Excel file.
theme_xml_path (str): Path to the theme XML within the Excel file.
xml_contents (dict): In-memory XML contents of the specified files.
drawing_root (Element): Root element of the drawing XML.
theme_root (Element): Root element of the theme XML.
connector_list (list[ConnectorAnchorRaw]): List of raw connector data.
shape_list (list[ShapeAnchorRaw]): List of raw shape data.
theme (Optional[Theme]): Parsed theme data.
converted_connector_list (list[BentConnector3]): List of converted connectors.
converted_shape_list (list[Shape]): List of converted shapes.
id_counter (int): Counter for assigning unique IDs to converted elements.
"""
def __init__(self, file_path: str):
"""
Initializes the ExcelAutoshapeLoader with the given file path.
Args:
file_path (str): The path to the Excel file to be processed.
"""
super().__init__(file_path)
logger.debug(f"Initializing ExcelAutoshapeLoader with file_path: {file_path}")
self.drawing_xml_path = "xl/drawings/drawing1.xml"
self.theme_xml_path = "xl/theme/theme1.xml"
self.xml_contents = convert_xlsx_to_xml_in_memory(
file_path,
target_files=[self.drawing_xml_path, self.theme_xml_path],
out_dir="data/xml/flow_not_recurrent_group",
)
logger.info("XML contents successfully converted to in-memory structures.")
self.drawing_root = self.xml_contents[self.drawing_xml_path]
self.theme_root = self.xml_contents[self.theme_xml_path]
# Lists to store parsed raw data
self.connector_list: list[ConnectorAnchorRaw] = []
self.shape_list: list[ShapeAnchorRaw] = []
# Parsed theme data
self.theme: Optional[Theme] = None
# Lists to store converted data
self.converted_connector_list: list[BentConnector3] = []
self.converted_shape_list: list[Shape] = []
self.id_counter = 1
[docs]
def _parse(self):
"""
Parses the drawing and theme XML files to extract raw shape and connector data.
"""
logger.debug("Starting parsing of XML contents.")
try:
# Parse drawing XML for shapes and connectors
drawing_parser = DrawingXMLParser(self.drawing_root)
self.connector_list, self.shape_list = drawing_parser.parse()
logger.info(
f"Parsed {len(self.connector_list)} connectors and {len(self.shape_list)} shapes from drawing XML."
)
# Parse theme XML for theme data
theme_parser = ThemeXMLParser(self.theme_root)
self.theme = theme_parser.parse()
logger.info("Parsed theme information from theme XML.")
except Exception as e:
logger.error(f"Error during parsing: {e}", exc_info=True)
raise
[docs]
def _convert(self):
"""
Converts raw shape and connector data into structured objects.
"""
logger.debug("Starting conversion of raw connectors and shapes.")
try:
for connector in self.connector_list:
if (
connector.drawing.connector_type
== ConnectorType.STRAIGHT_CONNECTOR_1
):
# Convert straight connectors
converter = StraightConnector1Converter(
connector, self.theme, self.id_counter
)
converted = converter.convert()
self.converted_connector_list.append(converted)
self.id_counter += 1
logger.debug(f"Converted StraightConnector1: {converted}")
elif connector.drawing.connector_type == ConnectorType.BENT_CONNECTOR_3:
# Convert bent connectors
converter = BentConnector3Converter(
connector, self.theme, self.id_counter
)
converted = converter.convert()
self.converted_connector_list.append(converted)
self.id_counter += 1
logger.debug(f"Converted BentConnector3: {converted}")
else:
logger.warning(
f"Unsupported connector type: {connector.drawing.connector_type}"
)
raise ValueError(
f"Unsupported connector type: {connector.drawing.connector_type}"
)
for shape in self.shape_list:
# Convert shapes
converter = ShapeConverter(shape, self.theme, self.id_counter)
converted = converter.convert()
self.converted_shape_list.append(converted)
self.id_counter += 1
logger.debug(f"Converted Shape: {converted}")
logger.info(
f"Converted {len(self.converted_connector_list)} connectors and {len(self.converted_shape_list)} shapes."
)
except Exception as e:
logger.error(f"Error during conversion: {e}", exc_info=True)
raise
[docs]
def load(self):
"""
Loads and processes the Excel autoshapes by parsing, converting, and generating prompts.
"""
logger.info("Loading Excel autoshapes.")
self._parse()
self._convert()
self._format()
logger.info("Excel autoshapes loaded successfully.")
[docs]
def export(self) -> str:
"""
Exports the generated prompt as a JSON string.
Returns:
str: The JSON string of the prompt.
"""
logger.debug("Exporting prompt.")
try:
logger.info("Exporting prompt successfully.")
return self.prompt
except Exception as e:
logger.error(f"Error during export: {e}", exc_info=True)
raise
[docs]
def plot_for_debug(self):
"""
Plots the converted shapes and connectors for debugging purposes.
"""
fig, ax = plt.subplots()
for shape in self.converted_shape_list:
shape.plot(ax)
for connector in self.converted_connector_list:
connector.plot(ax)
ax.invert_yaxis()
save_path = "debug.png"
plt.savefig(save_path)
plt.close()
logger.info(f"Plot saved to {save_path}")