102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
"""
|
|
Base Boilerplate Model
|
|
======================
|
|
|
|
This module provides a base class for all L5X models that use boilerplate templates.
|
|
"""
|
|
|
|
import xml.etree.ElementTree as ET
|
|
from dataclasses import dataclass
|
|
from typing import Optional, Dict
|
|
import os
|
|
from datetime import datetime
|
|
from abc import ABC, abstractmethod
|
|
|
|
|
|
@dataclass
|
|
class BaseModuleConfig(ABC):
|
|
"""Base configuration for all modules."""
|
|
name: str
|
|
|
|
@abstractmethod
|
|
def get_updates(self) -> Dict[str, any]:
|
|
"""Get dictionary of updates to apply to the boilerplate."""
|
|
pass
|
|
|
|
|
|
class BaseBoilerplateGenerator(ABC):
|
|
"""Base generator for L5X files using boilerplate templates."""
|
|
|
|
def __init__(self, config: BaseModuleConfig):
|
|
"""Initialize with configuration."""
|
|
self.config = config
|
|
self.tree = None
|
|
self.root = None
|
|
|
|
def load_boilerplate(self):
|
|
"""Load the boilerplate L5X file."""
|
|
if not hasattr(self.config, 'boilerplate_path'):
|
|
raise AttributeError("Config must have a boilerplate_path attribute")
|
|
|
|
if not os.path.exists(self.config.boilerplate_path):
|
|
raise FileNotFoundError(f"Boilerplate file not found: {self.config.boilerplate_path}")
|
|
|
|
self.tree = ET.parse(self.config.boilerplate_path)
|
|
self.root = self.tree.getroot()
|
|
|
|
|
|
|
|
def update_export_date(self):
|
|
"""Update the export date to current time."""
|
|
export_date = datetime.now().strftime("%a %b %d %H:%M:%S %Y")
|
|
self.root.set("ExportDate", export_date)
|
|
|
|
@abstractmethod
|
|
def apply_updates(self):
|
|
"""Apply module-specific updates to the boilerplate."""
|
|
pass
|
|
|
|
def generate(self) -> str:
|
|
"""Generate the complete L5X file as a string."""
|
|
# Load boilerplate
|
|
self.load_boilerplate()
|
|
|
|
# Apply updates
|
|
self.apply_updates()
|
|
self.update_export_date()
|
|
|
|
# Convert to string with proper formatting
|
|
return self._to_pretty_xml()
|
|
|
|
def _to_pretty_xml(self) -> str:
|
|
"""Convert the XML tree to a pretty-printed string."""
|
|
# Create XML declaration
|
|
xml_str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
|
|
|
|
# Add the root element
|
|
self._indent(self.root)
|
|
xml_str += ET.tostring(self.root, encoding='unicode')
|
|
|
|
return xml_str
|
|
|
|
def _indent(self, elem, level=0):
|
|
"""Add proper indentation to XML elements."""
|
|
i = "\n" + level * " "
|
|
if len(elem):
|
|
if not elem.text or not elem.text.strip():
|
|
elem.text = i + " "
|
|
if not elem.tail or not elem.tail.strip():
|
|
elem.tail = i
|
|
for child in elem:
|
|
self._indent(child, level + 1)
|
|
if not child.tail or not child.tail.strip():
|
|
child.tail = i
|
|
else:
|
|
if level and (not elem.tail or not elem.tail.strip()):
|
|
elem.tail = i
|
|
|
|
def save_to_file(self, filename: str):
|
|
"""Generate and save the L5X content to a file."""
|
|
content = self.generate()
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|
f.write(content) |