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": {}
}
```
## 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()
undocumented_types: dict[PydanticBaseModel, tuple[str, list[str]]] = dict()
global_config_dict: dict[str, Any] = dict()
def get_title(config: PydanticBaseModel) -> str:
if config.__doc__ is None:
@ -209,8 +211,12 @@ def generate_config_table_md(
table += ".. code-block:: json\n\n"
if has_examples_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:
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 += "\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
)
# 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
markdown = markdown.rstrip("\n")
markdown += "\n"
@ -290,7 +306,8 @@ def main():
except Exception as e:
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__":

View File

@ -14,6 +14,7 @@ Key Features:
import json
import re
from copy import deepcopy
from typing import Any, Dict, List, Optional, Type, Union
from zoneinfo import ZoneInfo
@ -45,7 +46,7 @@ def merge_models(source: BaseModel, update_dict: dict[str, Any]) -> dict[str, An
return update_dict
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