mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
Add documentation that covers: - configuration - prediction Add Python scripts that support automatic documentation generation for configuration data defined with pydantic. Adapt EOS configuration to provide more methods for REST API and automatic documentation generation. Adapt REST API to allow for EOS configuration file load and save. Sort REST API on generation of openapi markdown for docs. Move logutil to core/logging to allow configuration of logging by standard config. Make Akkudoktor predictions always start extraction of prediction data at start of day. Previously extraction started at actual hour. This is to support the code that assumes prediction data to start at start of day. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
162 lines
5.6 KiB
Python
Executable File
162 lines
5.6 KiB
Python
Executable File
#!.venv/bin/python
|
|
"""Utility functions for Configuration specification generation."""
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
from akkudoktoreos.config.config import get_config
|
|
from akkudoktoreos.core.logging import get_logger
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
config_eos = get_config()
|
|
|
|
# Fixed set of prefixes to filter configuration values and their respective titles
|
|
CONFIG_PREFIXES = {
|
|
"battery": "Battery Device Simulation Configuration",
|
|
"bev": "Battery Electric Vehicle Device Simulation Configuration",
|
|
"dishwasher": "Dishwasher Device Simulation Configuration",
|
|
"inverter": "Inverter Device Simulation Configuration",
|
|
"measurement": "Measurement Configuration",
|
|
"optimization": "General Optimization Configuration",
|
|
"server": "Server Configuration",
|
|
"elecprice": "Electricity Price Prediction Configuration",
|
|
"load": "Load Prediction Configuration",
|
|
"logging": "Logging Configuration",
|
|
"prediction": "General Prediction Configuration",
|
|
"pvforecast": "PV Forecast Configuration",
|
|
"weather": "Weather Forecast Configuration",
|
|
}
|
|
|
|
# Static set of configuration names to include in a separate table
|
|
GENERAL_CONFIGS = [
|
|
"config_default_file_path",
|
|
"config_file_path",
|
|
"config_folder_path",
|
|
"config_keys",
|
|
"config_keys_read_only",
|
|
"data_cache_path",
|
|
"data_cache_subpath",
|
|
"data_folder_path",
|
|
"data_output_path",
|
|
"data_output_subpath",
|
|
"latitude",
|
|
"longitude",
|
|
"package_root_path",
|
|
"timezone",
|
|
]
|
|
|
|
|
|
def generate_config_table_md(configs, title):
|
|
"""Generate a markdown table for given configurations.
|
|
|
|
Args:
|
|
configs (dict): Configuration values with keys and their descriptions.
|
|
title (str): Title for the table.
|
|
|
|
Returns:
|
|
str: The markdown table as a string.
|
|
"""
|
|
if not configs:
|
|
return ""
|
|
|
|
table = f"## {title}\n\n"
|
|
table += ":::{table} " + f"{title}\n:widths: 10 10 5 5 30\n:align: left\n\n"
|
|
table += "| Name | Type | Read-Only | Default | Description |\n"
|
|
table += "| ---- | ---- | --------- | ------- | ----------- |\n"
|
|
for name, config in sorted(configs.items()):
|
|
type_name = config["type"]
|
|
if type_name.startswith("typing."):
|
|
type_name = type_name[len("typing.") :]
|
|
table += f"| `{config['name']}` | `{type_name}` | `{config['read-only']}` | `{config['default']}` | {config['description']} |\n"
|
|
table += ":::\n\n" # Add an empty line after the table
|
|
return table
|
|
|
|
|
|
def generate_config_md() -> str:
|
|
"""Generate configuration specification in Markdown with extra tables for prefixed values.
|
|
|
|
Returns:
|
|
str: The Markdown representation of the configuration spec.
|
|
"""
|
|
configs = {}
|
|
config_keys = config_eos.config_keys
|
|
config_keys_read_only = config_eos.config_keys_read_only
|
|
for config_key in config_keys:
|
|
config = {}
|
|
config["name"] = config_key
|
|
config["value"] = getattr(config_eos, config_key)
|
|
|
|
if config_key in config_keys_read_only:
|
|
config["read-only"] = "ro"
|
|
computed_field_info = config_eos.__pydantic_decorators__.computed_fields[
|
|
config_key
|
|
].info
|
|
config["default"] = "N/A"
|
|
config["description"] = computed_field_info.description
|
|
config["type"] = str(computed_field_info.return_type)
|
|
else:
|
|
config["read-only"] = "rw"
|
|
field_info = config_eos.model_fields[config_key]
|
|
config["default"] = field_info.default
|
|
config["description"] = field_info.description
|
|
config["type"] = str(field_info.annotation)
|
|
|
|
configs[config_key] = config
|
|
|
|
# Generate markdown for the main table
|
|
markdown = "# Configuration Table\n\n"
|
|
|
|
# Generate table for general configuration names
|
|
general_configs = {k: v for k, v in configs.items() if k in GENERAL_CONFIGS}
|
|
for k in general_configs.keys():
|
|
del configs[k] # Remove general configs from the main configs dictionary
|
|
markdown += generate_config_table_md(general_configs, "General Configuration Values")
|
|
|
|
non_prefixed_configs = {k: v for k, v in configs.items()}
|
|
|
|
# Generate tables for each prefix (sorted by value) and remove prefixed configs from the main dictionary
|
|
sorted_prefixes = sorted(CONFIG_PREFIXES.items(), key=lambda item: item[1])
|
|
for prefix, title in sorted_prefixes:
|
|
prefixed_configs = {k: v for k, v in configs.items() if k.startswith(prefix)}
|
|
for k in prefixed_configs.keys():
|
|
del non_prefixed_configs[k]
|
|
markdown += generate_config_table_md(prefixed_configs, title)
|
|
|
|
# Generate markdown for the remaining non-prefixed configs if any
|
|
if non_prefixed_configs:
|
|
markdown += generate_config_table_md(non_prefixed_configs, "Other Configuration Values")
|
|
|
|
return markdown
|
|
|
|
|
|
def main():
|
|
"""Main function to run the generation of the Configuration specification as Markdown."""
|
|
parser = argparse.ArgumentParser(description="Generate Configuration Specification as Markdown")
|
|
parser.add_argument(
|
|
"--output-file",
|
|
type=str,
|
|
default=None,
|
|
help="File to write the Configuration Specification to",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
config_md = generate_config_md()
|
|
if args.output_file:
|
|
# Write to file
|
|
with open(args.output_file, "w") as f:
|
|
f.write(config_md)
|
|
else:
|
|
# Write to std output
|
|
print(config_md)
|
|
|
|
except Exception as e:
|
|
print(f"Error during Configuration Specification generation: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|