rostran/providers/excel/template.py (112 lines of code) (raw):
import logging
from typing import List
from openpyxl import load_workbook
from openpyxl.cell.cell import Cell
from openpyxl.worksheet.worksheet import Worksheet
from rostran.core.exceptions import TemplateFormatNotSupport, InvalidExcelTemplate
from rostran.core.format import FileFormat
from rostran.core.parameters import Parameters, Parameter
from rostran.core.resources import Resources, Resource
from rostran.core.properties import Property
from rostran.core.template import Template, RosTemplate
logger = logging.getLogger(__name__)
MAX_TEMPLATES = 5
class ExcelTemplate(Template):
SECTIONS = (PARAMETERS, RESOURCES) = (
"ROS::Parameters",
"ROS::Resources",
)
@classmethod
def initialize(cls, path: str, format: FileFormat = FileFormat.Excel):
if format != FileFormat.Excel:
raise TemplateFormatNotSupport(path=path, format=format)
source = load_workbook(path, read_only=True, data_only=True)
return cls(source=source)
def transform(self) -> List[RosTemplate]:
logger.info(f"Transform excel template to ROS template")
# check column
sheet: Worksheet = self.source.active
max_column = sheet.max_column
if max_column > MAX_TEMPLATES + 1 or max_column < 2:
reason = (
f"Column number {max_column} not meet limit(>=2,<={MAX_TEMPLATES + 1}"
)
raise InvalidExcelTemplate(reason=reason)
return self._transform()
def _transform(self) -> List[RosTemplate]:
sheet: Worksheet = self.source.active
max_column = sheet.max_column
templates = []
for _ in range(max_column - 1):
templates.append(RosTemplate())
cur_section = None
cur_resources = None
section_occurs = {}
for row in sheet.rows:
header_cell: Cell = row[0]
header_value = header_cell.value
if isinstance(header_value, str):
header_value = header_value.strip()
header_value_str = str(header_value)
# Section
if header_value_str.startswith("#"):
continue
if header_value_str.startswith("ROS::"):
if header_value_str not in self.SECTIONS:
reason = f"Section {header_value} on {header_cell} is invalid. Allowed sections: {self.SECTIONS}"
raise InvalidExcelTemplate(reason=reason)
cur_section = header_value_str
continue
if cur_section and not header_value:
cur_section = None
if not cur_section:
continue
# Parameters
if cur_section == self.PARAMETERS:
if (
section_occurs.get(header_value_str)
and header_value_str == cur_section
):
raise InvalidExcelTemplate(
reason=f"Section {header_value_str} on {header_cell} is duplicated"
)
else:
section_occurs[header_value_str] = True
for i, cell in enumerate(row[1:max_column]):
parameter = Parameter.initialize_from_excel(header_cell, cell)
templates[i].parameters.add(parameter)
# Resources
if cur_section == self.RESOURCES:
if (
section_occurs.get(header_value_str)
and header_value_str == cur_section
):
raise InvalidExcelTemplate(
reason=f"Section {header_value_str} on {header_cell} is duplicated"
)
else:
section_occurs[header_value_str] = True
# Specific resource
if "::" in header_value_str.split("\n")[0]:
cur_resources = []
for i, cell in enumerate(row[1:max_column]):
cell_value = (
"" if cell.value is None else str(cell.value).strip()
)
if cell_value:
resource = Resource.initialize_from_excel(header_cell, cell)
templates[i].resources.add(resource)
else:
resource = None
cur_resources.append(resource)
else:
for i, cell in enumerate(row[1:max_column]):
resource = cur_resources[i]
if not resource:
continue
prop = Property.initialize_from_excel(header_cell, cell)
resource.properties.add(prop)
for template in templates:
for res in template.resources.values():
res: Resource
res.properties = res.properties.resolve()
return templates
def _handle_parameters(self, parameters: Parameters, cell: Cell):
pass
def _handle_resources(self, resources: Resources, cell: Cell):
pass