Docs: Add global example documentation.

* merge_models: Use deecopy to not change input data.
This commit is contained in:
Dominique Lasserre 2025-01-20 23:39:43 +01:00
parent c1dd31528b
commit 5bd8321e95
3 changed files with 152 additions and 2 deletions

View File

@ -879,3 +879,135 @@ Attributes:
"utils": {} "utils": {}
} }
``` ```
## Full example Config
```{eval-rst}
.. code-block:: json
{
"general": {
"data_folder_path": null,
"data_output_subpath": "output",
"data_cache_subpath": "cache",
"latitude": 52.52,
"longitude": 13.405
},
"logging": {
"level": "INFO"
},
"devices": {
"batteries": [
{
"device_id": "battery1",
"hours": null,
"capacity_wh": 8000,
"charging_efficiency": 0.88,
"discharging_efficiency": 0.88,
"max_charge_power_w": 5000,
"initial_soc_percentage": 0,
"min_soc_percentage": 0,
"max_soc_percentage": 100
}
],
"inverters": [],
"home_appliances": []
},
"measurement": {
"load0_name": "Household",
"load1_name": null,
"load2_name": null,
"load3_name": null,
"load4_name": null
},
"optimization": {
"hours": 48,
"penalty": 10,
"ev_available_charge_rates_percent": [
0.0,
0.375,
0.5,
0.625,
0.75,
0.875,
1.0
]
},
"prediction": {
"hours": 48,
"historic_hours": 48
},
"elecprice": {
"provider": "ElecPriceAkkudoktor",
"charges_kwh": 0.21,
"provider_settings": null
},
"load": {
"provider": "LoadAkkudoktor",
"provider_settings": null
},
"pvforecast": {
"provider": "PVForecastAkkudoktor",
"planes": [
{
"surface_tilt": 10.0,
"surface_azimuth": 10.0,
"userhorizon": [
10.0,
20.0,
30.0
],
"peakpower": 5.0,
"pvtechchoice": "crystSi",
"mountingplace": "free",
"loss": 14.0,
"trackingtype": 0,
"optimal_surface_tilt": false,
"optimalangles": false,
"albedo": null,
"module_model": null,
"inverter_model": null,
"inverter_paco": 6000,
"modules_per_string": 20,
"strings_per_inverter": 2
},
{
"surface_tilt": 20.0,
"surface_azimuth": 20.0,
"userhorizon": [
5.0,
15.0,
25.0
],
"peakpower": 3.5,
"pvtechchoice": "crystSi",
"mountingplace": "free",
"loss": 14.0,
"trackingtype": 1,
"optimal_surface_tilt": false,
"optimalangles": false,
"albedo": null,
"module_model": null,
"inverter_model": null,
"inverter_paco": 4000,
"modules_per_string": 20,
"strings_per_inverter": 2
}
],
"provider_settings": null
},
"weather": {
"provider": "WeatherImport",
"provider_settings": null
},
"server": {
"host": "0.0.0.0",
"port": 8503,
"verbose": false,
"startup_eosdash": true,
"eosdash_host": "0.0.0.0",
"eosdash_port": 8504
},
"utils": {}
}
```

View File

@ -22,6 +22,8 @@ logger = get_logger(__name__)
documented_types: set[PydanticBaseModel] = set() documented_types: set[PydanticBaseModel] = set()
undocumented_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict() undocumented_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict()
global_config_dict: dict[str, Any] = dict()
def get_title(config: PydanticBaseModel) -> str: def get_title(config: PydanticBaseModel) -> str:
if config.__doc__ is None: if config.__doc__ is None:
@ -209,8 +211,12 @@ def generate_config_table_md(
table += ".. code-block:: json\n\n" table += ".. code-block:: json\n\n"
if has_examples_list: if has_examples_list:
input_dict = build_nested_structure(toplevel_keys[:-1], ins_dict_list) input_dict = build_nested_structure(toplevel_keys[:-1], ins_dict_list)
if not extra_config:
global_config_dict[toplevel_keys[0]] = ins_dict_list
else: else:
input_dict = build_nested_structure(toplevel_keys, ins_dict_list[0]) input_dict = build_nested_structure(toplevel_keys, ins_dict_list[0])
if not extra_config:
global_config_dict[toplevel_keys[0]] = ins_dict_list[0]
table += textwrap.indent(json.dumps(input_dict, indent=4), " ") table += textwrap.indent(json.dumps(input_dict, indent=4), " ")
table += "\n" table += "\n"
table += "```\n\n" table += "```\n\n"
@ -258,6 +264,16 @@ def generate_config_md(config_eos: ConfigEOS) -> str:
field_type, [field_name], f"EOS_{field_name.upper()}__", True field_type, [field_name], f"EOS_{field_name.upper()}__", True
) )
# Full config
markdown += "## Full example Config\n\n"
markdown += "```{eval-rst}\n"
markdown += ".. code-block:: json\n\n"
# Test for valid config first
config_eos.merge_settings_from_dict(global_config_dict)
markdown += textwrap.indent(json.dumps(global_config_dict, indent=4), " ")
markdown += "\n"
markdown += "```\n\n"
# Assure there is no double \n at end of file # Assure there is no double \n at end of file
markdown = markdown.rstrip("\n") markdown = markdown.rstrip("\n")
markdown += "\n" markdown += "\n"
@ -290,7 +306,8 @@ def main():
except Exception as e: except Exception as e:
print(f"Error during Configuration Specification generation: {e}", file=sys.stderr) print(f"Error during Configuration Specification generation: {e}", file=sys.stderr)
sys.exit(1) # keep throwing error to debug potential problems (e.g. invalid examples)
raise e
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -14,6 +14,7 @@ Key Features:
import json import json
import re import re
from copy import deepcopy
from typing import Any, Dict, List, Optional, Type, Union from typing import Any, Dict, List, Optional, Type, Union
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
@ -45,7 +46,7 @@ def merge_models(source: BaseModel, update_dict: dict[str, Any]) -> dict[str, An
return update_dict return update_dict
source_dict = source.model_dump(exclude_unset=True) source_dict = source.model_dump(exclude_unset=True)
merged_dict = deep_update(source_dict, update_dict) merged_dict = deep_update(source_dict, deepcopy(update_dict))
return merged_dict return merged_dict