From 31dd43e522659308c503706c297bc1a876b9cf3e Mon Sep 17 00:00:00 2001
From: stewit <>
Date: Tue, 6 Oct 2026 13:44:31 +0200
Subject: [PATCH 01/15] fall back to inherited metric metadata
Metric metadata accessors now use the properties a metric inherits from
objects higher up in an asset hierarchy (dict under "inherited" in the
metric object of the current metadata convention) if the metric itself
does not provide the requested information. Can be deactivated via the
keyword argument include_inherited=False.
Co-Authored-By: Claude Opus 5.5 (1M context) Gets display names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of metrics containing the display names.
@@ -119,11 +122,14 @@ Gets measurement (type) of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of metrics containing the measurement (type) of the MTSF metrics.
@@ -149,7 +155,7 @@ Gets a dictionary of metadata associated to metrics. In contrast to metadata associated to concrete value dimensions, this
function abstracts access to metadata associated to the underlying metric. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Gets names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of metrics containing the names. If the name is not present for a metric the corresponding value is None. Gets display name of the series from metadata series (pd.Series) – Series with metadata following the convention. series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Returns the display name of the value.
@@ -323,7 +336,7 @@ Gets an arbitrary series info Since a series has only one value dimension named “value”, this information is
equivalent to information on the metric. series (pd.Series) – Series with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Gets measurement (type) of the Series from metadata series (pd.Series) – Series with metadata following the convention. series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Returns the measurement (type) of the value.
@@ -382,11 +399,14 @@ Gets name of the series from metadata series (pd.Series) – Series with metadata following the convention. series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Returns the name of the value.
@@ -426,11 +446,14 @@ Gets short display name of the Series from metadata series (pd.Series) – Series with metadata following the convention. series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Returns the short display name of the value.
@@ -455,11 +478,14 @@ Gets name of the series from metadata series (pd.Series) – Series with metadata following the convention. series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Returns the unit of series.
@@ -496,11 +522,14 @@ Gets short display names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of metrics containing the short display names.
@@ -544,12 +573,15 @@ Gets display names of the value dimensions of a SingleTSFrame from metadata Falls back to the name of the value dimension if no display name is given. singletsframe (pd.DataFrame) – STSF with metadata following the convention. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of value dimensions containing the display names. If neither display name nor name is present for a value dimension the corresponding value is None. Gets an arbitrary info of the value dimensions of a SingleTSFrame A SingleTSFrame (STSF) holds exactly one metric - like a Series - but arbitrarily many
value dimensions - like a MultiTSFrame. Therefore, in contrast to the MTSF functions,
@@ -598,6 +630,7 @@ singletsframe (pd.DataFrame) – STSF with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Gets measurements (types) of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of value dimensions containing the measurements. If “measurement” is not given for a value dimension the corresponding value is None. Gets metadata associated to the single metric of a SingleTSFrame Counterpart of singletsframe (pd.DataFrame) – STSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Gets names of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of value dimensions containing the names. If the name is not present for a value dimension the corresponding value is None. Gets short display names of the value dimensions of a SingleTSFrame from metadata Falls back to the display name and then to the name of the value dimension if no short
display name is given. singletsframe (pd.DataFrame) – STSF with metadata following the convention. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of value dimensions containing the short display names. If none of short display name, display name and name is present for a value dimension the corresponding value is None. Gets units of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of value dimensions containing the units. If the unit is not present for a value dimension the corresponding value is None. Gets unit of value dimensions in MTSF metrics from Metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. Dictionary of metrics containing the names of the value dimensions.
diff --git a/docs/searchindex.js b/docs/searchindex.js
index 48248e2..cdd0c31 100644
--- a/docs/searchindex.js
+++ b/docs/searchindex.js
@@ -1 +1 @@
-Search.setIndex({"alltitles":{"First steps":[[0,null]],"Functions":[[1,"functions"]],"Further Information":[[1,"further-information"]],"How to get metadata with hdhelpers?":[[0,"how-to-get-metadata-with-hdhelpers"]],"How to use hdhelpers for plotting? (tbd)":[[0,"how-to-use-hdhelpers-for-plotting-tbd"]],"Introduction":[[1,"introduction"]],"exceptions":[[1,"module-hdhelpers.exceptions"]],"hdhelpers":[[1,null]],"helpers":[[1,"module-hdhelpers.helpers"]],"metadata":[[1,"module-hdhelpers.metadata"]],"plotting":[[1,"module-hdhelpers.plotting"]]},"docnames":["first_steps","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["first_steps.rst","index.rst"],"indexentries":{"get_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_display_names",false]],"get_locale() (in module hdhelpers.helpers)":[[1,"hdhelpers.helpers.get_locale",false]],"get_measurements() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_measurements",false]],"get_metric_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_metric_info",false]],"get_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_names",false]],"get_queried_interval() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_queried_interval",false]],"get_series_display_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_display_name",false]],"get_series_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_info",false]],"get_series_measurement() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_measurement",false]],"get_series_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_name",false]],"get_series_short_display_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_short_display_name",false]],"get_series_unit() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_unit",false]],"get_short_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_short_display_names",false]],"get_singlets_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_display_names",false]],"get_singlets_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_info",false]],"get_singlets_measurements() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_measurements",false]],"get_singlets_metric_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_metric_info",false]],"get_singlets_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_names",false]],"get_singlets_short_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_short_display_names",false]],"get_singlets_units() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_units",false]],"get_units() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_units",false]],"hdhelpers.exceptions":[[1,"module-hdhelpers.exceptions",false]],"hdhelpers.helpers":[[1,"module-hdhelpers.helpers",false]],"hdhelpers.metadata":[[1,"module-hdhelpers.metadata",false]],"hdhelpers.plotting":[[1,"module-hdhelpers.plotting",false]],"helperexception":[[1,"hdhelpers.exceptions.HelperException",false]],"insufficientplottingdata":[[1,"hdhelpers.exceptions.InsufficientPlottingData",false]],"modify_timezone() (in module hdhelpers.helpers)":[[1,"hdhelpers.helpers.modify_timezone",false]],"module":[[1,"module-hdhelpers.exceptions",false],[1,"module-hdhelpers.helpers",false],[1,"module-hdhelpers.metadata",false],[1,"module-hdhelpers.plotting",false]],"resolve_color() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.resolve_color",false]],"set_agnostic_theme() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.set_agnostic_theme",false]],"set_dt_ticks() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.set_dt_ticks",false]]},"objects":{"hdhelpers":[[1,0,0,"-","exceptions"],[1,0,0,"-","helpers"],[1,0,0,"-","metadata"],[1,0,0,"-","plotting"]],"hdhelpers.exceptions":[[1,1,1,"","HelperException"],[1,1,1,"","InsufficientPlottingData"]],"hdhelpers.helpers":[[1,2,1,"","get_locale"],[1,2,1,"","modify_timezone"]],"hdhelpers.metadata":[[1,2,1,"","get_display_names"],[1,2,1,"","get_measurements"],[1,2,1,"","get_metric_info"],[1,2,1,"","get_names"],[1,2,1,"","get_queried_interval"],[1,2,1,"","get_series_display_name"],[1,2,1,"","get_series_info"],[1,2,1,"","get_series_measurement"],[1,2,1,"","get_series_name"],[1,2,1,"","get_series_short_display_name"],[1,2,1,"","get_series_unit"],[1,2,1,"","get_short_display_names"],[1,2,1,"","get_singlets_display_names"],[1,2,1,"","get_singlets_info"],[1,2,1,"","get_singlets_measurements"],[1,2,1,"","get_singlets_metric_info"],[1,2,1,"","get_singlets_names"],[1,2,1,"","get_singlets_short_display_names"],[1,2,1,"","get_singlets_units"],[1,2,1,"","get_units"]],"hdhelpers.plotting":[[1,2,1,"","resolve_color"],[1,2,1,"","set_agnostic_theme"],[1,2,1,"","set_dt_ticks"]]},"objnames":{"0":["py","module","Python module"],"1":["py","exception","Python exception"],"2":["py","function","Python function"]},"objtypes":{"0":"py:module","1":"py:exception","2":"py:function"},"terms":{"000z":0,"00z":1,"01t01":1,"01t08":0,"05t13":1,"06t13":1,"A":1,"DO":0,"For":[0,1],"How":1,"If":1,"In":[0,1],"It":1,"NOT":0,"Our":0,"The":1,"This":1,"To":1,"We":0,"__data__":0,"__hd_wrapped_data_object__":0,"__metadata__":0,"abc":1,"abov":0,"absent":1,"abstract":1,"access":1,"accord":0,"adequ":1,"adit":1,"adjust":1,"agnost":1,"all_timezon":1,"allow":1,"ambigu":1,"ani":1,"anoth":1,"applic":1,"arbitrari":1,"arbitrarili":1,"arg":1,"associ":1,"assum":1,"attr":1,"avail":1,"axe":1,"axi":0,"back":1,"background":1,"base":1,"behaviour":1,"berlin":1,"bool":1,"boolean":1,"by_metr":1,"c":1,"can":[0,1],"carri":1,"case":1,"certain":1,"class":1,"cm":0,"code":[0,1],"collect":1,"color":[0,1],"color_str":1,"column":1,"column_nam":1,"compli":1,"compon":[0,1],"concret":1,"contain":1,"context":1,"contrast":1,"control":1,"convent":1,"convert":1,"convert_index":1,"correspond":[0,1],"counterpart":1,"custom":1,"dark":1,"data":1,"datafram":1,"dataset_metadata":1,"date":1,"datetim":1,"de":1,"de_d":1,"def":0,"default":1,"default_titl":0,"defaultdict":1,"defin":[0,1],"design":[0,1],"determin":1,"dict":1,"dictionari":1,"dimens":1,"direct":[0,1],"display":1,"display_nam":1,"display_name_of_metric1":1,"display_name_of_seri":1,"e":1,"ea1255":1,"eas":1,"eb6962":1,"edit":0,"empti":1,"en":1,"enabl":1,"end":[0,1],"energi":1,"entri":1,"entrypoint":0,"equival":1,"error":1,"error_cod":1,"especi":1,"europ":1,"exact":1,"exampl":[0,1],"exclus":1,"execut":1,"expect":1,"explicit":1,"explicit_local":1,"external_first":1,"external_id":1,"external_second":1,"extra_inform":1,"extract":1,"fall":1,"fig":[0,1],"figur":[0,1],"first":1,"follow":[0,1],"format":1,"frontend":1,"full_titl":0,"function":0,"fuseki":1,"g":1,"generat":1,"german":1,"get":1,"get_display_nam":1,"get_local":[0,1],"get_measur":1,"get_metric_info":1,"get_nam":1,"get_queried_interv":[0,1],"get_series_display_nam":1,"get_series_info":1,"get_series_measur":1,"get_series_nam":[0,1],"get_series_short_display_nam":1,"get_series_unit":[0,1],"get_short_display_nam":1,"get_singlets_display_nam":1,"get_singlets_info":1,"get_singlets_measur":1,"get_singlets_metric_info":1,"get_singlets_nam":1,"get_singlets_short_display_nam":1,"get_singlets_unit":1,"get_unit":1,"get_value_dimension_info":1,"given":1,"global":1,"glom":1,"go":[0,1],"good":1,"graph_object":[0,1],"handl":1,"happen":1,"helper":0,"helperexcept":1,"hetida":[0,1],"hold":1,"id":1,"identifi":1,"import":[0,1],"includ":1,"index":[0,1],"infer":1,"info":1,"inspir":1,"instal":1,"instead":1,"insuffici":1,"insufficientplottingdata":1,"int":1,"integr":1,"interpret":1,"interv":1,"iso":1,"isoformat":1,"json":[0,1],"just":1,"key":1,"ki":[0,1],"kwarg":1,"label":[0,1],"languag":1,"let":[0,1],"level":[0,1],"light":1,"like":[0,1],"limit":0,"line":0,"list":1,"local":1,"look":0,"m":1,"main":0,"mani":1,"marker":0,"meaning":1,"measur":1,"messag":1,"method":1,"metric":[0,1],"metric1":1,"metric2":1,"metric3":1,"metric_info":1,"metric_key":1,"might":0,"miss":1,"modifi":1,"modified_timezon":1,"modify_timezon":[0,1],"mtsf":1,"multipl":1,"multitsfram":1,"name":[0,1],"name_of_metric1":1,"name_of_metric2":1,"name_of_series_1":1,"name_of_series_2":1,"neither":1,"non":1,"none":1,"note":1,"object":1,"object_to_convert":1,"one":1,"onli":1,"oper":1,"option":1,"orang":1,"output":1,"packag":1,"panda":1,"paramet":1,"pd":1,"place":1,"platform":1,"pleas":1,"plot_target_set":1,"possibl":1,"present":1,"print":1,"provid":1,"provis":0,"pytz":1,"queri":1,"rais":1,"rang":0,"re":1,"red":1,"ref_interval_end_timestamp":[0,1],"ref_interval_start_timestamp":[0,1],"repres":0,"request":1,"resolv":1,"resolve_color":[0,1],"respect":1,"result":1,"retriev":[0,1],"return":[0,1],"ruhr":1,"s":[0,1],"say":0,"scatter":[0,1],"scheme":1,"second":1,"see":1,"sensibl":1,"seri":[0,1],"serial":1,"set":[0,1],"set_agnostic_them":[0,1],"set_dt_tick":[0,1],"shade":1,"short":1,"short_display_nam":[0,1],"short_display_name_of_metric1":1,"short_display_name_of_seri":1,"show":1,"similar":1,"sinc":1,"singl":1,"single_metr":1,"single_metric_dataset_metadata":0,"single_metric_metadata":[0,1],"singletsfram":1,"spec":1,"specifi":1,"standard":[0,1],"start":[0,1],"state":1,"step":1,"str":1,"streamlin":1,"string":1,"structured_metadata":[0,1],"stsf":1,"style":1,"surpress":1,"t":1,"target":1,"tbd":1,"temp":1,"temperatur":1,"templat":1,"test":1,"theme":1,"therefor":1,"tick":1,"time":1,"timeseri":[0,1],"timestamp":1,"timezon":[0,1],"to_datetim":1,"to_timezon":1,"toggleabl":1,"total_second":1,"transform":1,"tri":1,"true":1,"tupl":1,"type":1,"typeerror":1,"tz_convert":1,"underlying":1,"unit":[0,1],"unknown":1,"update_layout":0,"update_xax":0,"us":1,"use":1,"utc":1,"utcoffset":1,"valu":[0,1],"value_dim_1":1,"value_dim_info":1,"value_dimens":1,"value_dimensions_shar":1,"value_name_of_seri":1,"valueerror":1,"vari":1,"variabl":1,"via":1,"vision":0,"want":0,"water":0,"way":1,"well":1,"whether":1,"whose":1,"will":1,"without":1,"write":1,"x":[0,1],"y":[0,1],"yaxis_titl":0,"yield":1,"zone":1,"zoom":1},"titles":["First steps","hdhelpers"],"titleterms":{"Further":1,"How":0,"except":1,"first":0,"function":1,"get":0,"hdhelper":[0,1],"helper":1,"inform":1,"introduct":1,"metadata":[0,1],"plot":[0,1],"step":0,"tbd":0,"use":0}})
\ No newline at end of file
+Search.setIndex({"alltitles":{"First steps":[[0,null]],"Functions":[[1,"functions"]],"Further Information":[[1,"further-information"]],"How to get metadata with hdhelpers?":[[0,"how-to-get-metadata-with-hdhelpers"]],"How to use hdhelpers for plotting? (tbd)":[[0,"how-to-use-hdhelpers-for-plotting-tbd"]],"Introduction":[[1,"introduction"]],"exceptions":[[1,"module-hdhelpers.exceptions"]],"hdhelpers":[[1,null]],"helpers":[[1,"module-hdhelpers.helpers"]],"metadata":[[1,"module-hdhelpers.metadata"]],"plotting":[[1,"module-hdhelpers.plotting"]]},"docnames":["first_steps","index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["first_steps.rst","index.rst"],"indexentries":{"get_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_display_names",false]],"get_locale() (in module hdhelpers.helpers)":[[1,"hdhelpers.helpers.get_locale",false]],"get_measurements() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_measurements",false]],"get_metric_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_metric_info",false]],"get_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_names",false]],"get_queried_interval() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_queried_interval",false]],"get_series_display_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_display_name",false]],"get_series_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_info",false]],"get_series_measurement() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_measurement",false]],"get_series_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_name",false]],"get_series_short_display_name() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_short_display_name",false]],"get_series_unit() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_series_unit",false]],"get_short_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_short_display_names",false]],"get_singlets_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_display_names",false]],"get_singlets_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_info",false]],"get_singlets_measurements() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_measurements",false]],"get_singlets_metric_info() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_metric_info",false]],"get_singlets_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_names",false]],"get_singlets_short_display_names() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_short_display_names",false]],"get_singlets_units() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_singlets_units",false]],"get_units() (in module hdhelpers.metadata)":[[1,"hdhelpers.metadata.get_units",false]],"hdhelpers.exceptions":[[1,"module-hdhelpers.exceptions",false]],"hdhelpers.helpers":[[1,"module-hdhelpers.helpers",false]],"hdhelpers.metadata":[[1,"module-hdhelpers.metadata",false]],"hdhelpers.plotting":[[1,"module-hdhelpers.plotting",false]],"helperexception":[[1,"hdhelpers.exceptions.HelperException",false]],"insufficientplottingdata":[[1,"hdhelpers.exceptions.InsufficientPlottingData",false]],"modify_timezone() (in module hdhelpers.helpers)":[[1,"hdhelpers.helpers.modify_timezone",false]],"module":[[1,"module-hdhelpers.exceptions",false],[1,"module-hdhelpers.helpers",false],[1,"module-hdhelpers.metadata",false],[1,"module-hdhelpers.plotting",false]],"resolve_color() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.resolve_color",false]],"set_agnostic_theme() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.set_agnostic_theme",false]],"set_dt_ticks() (in module hdhelpers.plotting)":[[1,"hdhelpers.plotting.set_dt_ticks",false]]},"objects":{"hdhelpers":[[1,0,0,"-","exceptions"],[1,0,0,"-","helpers"],[1,0,0,"-","metadata"],[1,0,0,"-","plotting"]],"hdhelpers.exceptions":[[1,1,1,"","HelperException"],[1,1,1,"","InsufficientPlottingData"]],"hdhelpers.helpers":[[1,2,1,"","get_locale"],[1,2,1,"","modify_timezone"]],"hdhelpers.metadata":[[1,2,1,"","get_display_names"],[1,2,1,"","get_measurements"],[1,2,1,"","get_metric_info"],[1,2,1,"","get_names"],[1,2,1,"","get_queried_interval"],[1,2,1,"","get_series_display_name"],[1,2,1,"","get_series_info"],[1,2,1,"","get_series_measurement"],[1,2,1,"","get_series_name"],[1,2,1,"","get_series_short_display_name"],[1,2,1,"","get_series_unit"],[1,2,1,"","get_short_display_names"],[1,2,1,"","get_singlets_display_names"],[1,2,1,"","get_singlets_info"],[1,2,1,"","get_singlets_measurements"],[1,2,1,"","get_singlets_metric_info"],[1,2,1,"","get_singlets_names"],[1,2,1,"","get_singlets_short_display_names"],[1,2,1,"","get_singlets_units"],[1,2,1,"","get_units"]],"hdhelpers.plotting":[[1,2,1,"","resolve_color"],[1,2,1,"","set_agnostic_theme"],[1,2,1,"","set_dt_ticks"]]},"objnames":{"0":["py","module","Python module"],"1":["py","exception","Python exception"],"2":["py","function","Python function"]},"objtypes":{"0":"py:module","1":"py:exception","2":"py:function"},"terms":{"000z":0,"00z":1,"01t01":1,"01t08":0,"01t09":[],"01t10":[],"05t13":1,"06t13":1,"5s":[],"80b0ec":[],"A":1,"All":[],"DO":0,"For":[0,1],"How":1,"If":1,"In":[0,1],"It":1,"NOT":0,"Our":0,"That":[],"The":1,"This":1,"To":1,"We":0,"With":[],"__data__":0,"__hd_wrapped_data_object__":0,"__metadata__":0,"abc":1,"abov":0,"absent":1,"abstract":1,"access":1,"accord":0,"activ":[],"adequ":1,"adit":1,"adjust":1,"agnost":1,"all_timezon":1,"allow":1,"also":[],"ambigu":1,"ani":1,"anoth":1,"appli":[],"applic":1,"arbitrari":1,"arbitrarili":1,"arg":1,"asset":1,"associ":1,"assum":1,"attr":1,"avail":1,"awar":[],"axe":1,"axi":0,"back":1,"background":1,"background_color":[],"base":1,"becaus":[],"behaviour":1,"berlin":1,"bool":1,"boolean":1,"brand":[],"by_metr":1,"c":1,"can":[0,1],"carri":1,"case":1,"certain":1,"chosen":[],"class":1,"cm":0,"code":[0,1],"collect":1,"color":[0,1],"color_str":1,"column":1,"column_nam":1,"communic":[],"compli":1,"compon":[0,1],"concret":1,"configur":[],"contain":1,"context":1,"contrast":1,"control":1,"convent":1,"convert":1,"convert_index":1,"convey":[],"correspond":[0,1],"counterpart":1,"current":[],"custom":1,"cut":[],"danger":[],"dark":1,"data":1,"datafram":1,"dataset_metadata":1,"date":1,"datetim":1,"de":1,"de_d":1,"def":0,"default":1,"default_titl":0,"default_unit":[],"defaultdict":1,"defin":[0,1],"design":[0,1],"detail":[],"determin":1,"dict":1,"dictionari":1,"dimens":1,"direct":[0,1],"display":1,"display_nam":1,"display_name_of_metric1":1,"display_name_of_seri":1,"doe":1,"draw":[],"e":1,"ea1255":1,"eas":1,"eb6962":1,"edg":[],"edit":0,"els":[],"empti":1,"en":1,"enabl":1,"end":[0,1],"energi":1,"entri":1,"entrypoint":0,"equival":1,"error":1,"error_cod":1,"error_color":[],"especi":1,"europ":1,"exact":1,"exampl":[0,1],"exclus":1,"execcontext":[],"execut":1,"expect":1,"explicit":1,"explicit_local":1,"external_first":1,"external_id":1,"external_second":1,"extra_inform":1,"extract":1,"fail":[],"fall":1,"featur":[],"fig":[0,1],"figur":[0,1],"first":1,"flag":[],"follow":[0,1],"format":1,"four":[],"frontend":1,"full_titl":0,"function":0,"fuseki":1,"g":1,"generat":1,"german":1,"get":1,"get_and_pad_start_and_end_timestamp":[],"get_colors_from_plot_target_set":[],"get_display_nam":1,"get_local":[0,1],"get_measur":1,"get_metric_info":1,"get_nam":1,"get_queried_interv":[0,1],"get_series_display_nam":1,"get_series_info":1,"get_series_measur":1,"get_series_nam":[0,1],"get_series_short_display_nam":1,"get_series_unit":[0,1],"get_short_display_nam":1,"get_singlets_display_nam":1,"get_singlets_info":1,"get_singlets_measur":1,"get_singlets_metric_info":1,"get_singlets_nam":1,"get_singlets_short_display_nam":1,"get_singlets_unit":1,"get_unit":1,"get_value_dimension_info":1,"get_y_axis_label":[],"given":1,"global":1,"glom":1,"go":[0,1],"goal":[],"good":1,"graph_object":[0,1],"half":[],"handl":1,"happen":1,"hardwir":[],"helper":0,"helperexcept":1,"hetida":[0,1],"hide":[],"hierarchi":1,"higher":1,"hold":1,"id":1,"identifi":1,"import":[0,1],"includ":1,"include_inherit":1,"index":[0,1],"infer":1,"info":1,"info_color":[],"inform":[],"inherit":1,"inpir":[],"input":[],"insoformat":[],"inspir":1,"instal":1,"instead":1,"insuffici":1,"insufficientplottingdata":1,"int":1,"integr":1,"intend":[],"interpret":1,"interv":1,"iso":1,"isoformat":1,"json":[0,1],"just":1,"keep":[],"key":1,"ki":[0,1],"kwarg":1,"label":[0,1],"languag":1,"let":[0,1],"level":[0,1],"light":1,"like":[0,1],"limit":0,"line":0,"list":1,"load":[],"local":1,"look":0,"m":1,"magnitud":[],"main":0,"make":[],"mani":1,"marker":0,"meaning":1,"measur":1,"mention":[],"messag":1,"method":1,"metric":[0,1],"metric1":1,"metric2":1,"metric3":1,"metric_info":1,"metric_key":1,"might":0,"miss":1,"mode":[],"modifi":1,"modified_timezon":1,"modify_timezon":[0,1],"mtsf":1,"multipl":1,"multitsfram":1,"naiv":[],"name":[0,1],"name_of_metric1":1,"name_of_metric2":1,"name_of_series_1":1,"name_of_series_2":1,"neither":1,"non":1,"none":1,"note":1,"object":1,"object_to_convert":1,"one":1,"onli":1,"oper":1,"option":1,"orang":1,"order":[],"otherwis":[],"output":1,"packag":1,"panda":1,"paramet":1,"pars":[],"pass":[],"pd":1,"place":1,"platform":1,"pleas":1,"plot":[],"plot_target_set":1,"plot_target_styl":[],"plot_target_timezon":[],"plotly_fig_to_json_dict":[],"point":[],"possibl":1,"potenti":[],"precis":[],"present":1,"print":1,"properti":1,"provid":1,"provis":0,"purpos":[],"put":[],"pytz":1,"queri":1,"rais":1,"rang":0,"re":1,"reason":[],"red":1,"ref_interval_end_timestamp":[0,1],"ref_interval_start_timestamp":[0,1],"reflect":[],"repres":0,"request":1,"resolv":1,"resolve_color":[0,1],"respect":1,"result":1,"retriev":[0,1],"return":[0,1],"ruhr":1,"runtim":[],"s":[0,1],"say":0,"scatter":[0,1],"scheme":1,"second":1,"see":1,"sensibl":1,"seri":[0,1],"serial":1,"set":[0,1],"set_agnostic_them":[0,1],"set_dt_tick":[0,1],"set_language_agnostic_datetime_tick":[],"sever":[],"shade":1,"short":1,"short_display_nam":[0,1],"short_display_name_of_metric1":1,"short_display_name_of_seri":1,"show":1,"similar":1,"sinc":1,"singl":1,"single_metr":1,"single_metric_dataset_metadata":0,"single_metric_metadata":[0,1],"singletsfram":1,"spec":1,"specif":[],"specifi":1,"standard":[0,1],"start":[0,1],"start_pad":[],"state":1,"status":[],"status_color":[],"step":1,"str":1,"streamlin":1,"string":1,"structured_metadata":[0,1],"stsf":1,"style":1,"success_color":[],"sure":[],"surpress":1,"switch":[],"t":1,"take":[],"target":1,"tbd":1,"tech":[],"temp":1,"temperatur":1,"templat":1,"test":1,"theme":1,"theme_agnostic_templ":[],"therefor":1,"tick":1,"time":1,"timeseri":[0,1],"timestamp":1,"timezon":[0,1],"titl":[],"to_datetim":1,"to_timezon":1,"togeth":[],"toggleabl":1,"total_second":1,"transform":1,"tri":1,"true":1,"tupl":1,"turn":[],"type":1,"typeerror":1,"tz_convert":1,"underlying":1,"unit":[0,1],"unknown":1,"update_layout":0,"update_xax":0,"us":1,"usag":[],"use":1,"utc":1,"utcoffset":1,"valu":[0,1],"value_dim_1":1,"value_dim_info":1,"value_dimens":1,"value_dimensions_shar":1,"value_name_of_seri":1,"valueerror":1,"vari":1,"variabl":1,"via":1,"vision":0,"want":0,"warn_color":[],"water":0,"way":1,"well":1,"whether":1,"whose":1,"will":1,"without":1,"write":1,"x":[0,1],"y":[0,1],"yaxis_titl":0,"yield":1,"zone":1,"zoom":1},"titles":["First steps","hdhelpers"],"titleterms":{"Further":1,"How":0,"exampl":[],"except":1,"explan":[],"first":0,"full":[],"function":1,"get":0,"hdhelper":[0,1],"helper":1,"inform":1,"introduct":1,"metadata":[0,1],"plot":[0,1],"step":0,"tbd":0,"use":0}})
\ No newline at end of file
diff --git a/src/hdhelpers/metadata/_helpers.py b/src/hdhelpers/metadata/_helpers.py
index 0120981..e03a13b 100644
--- a/src/hdhelpers/metadata/_helpers.py
+++ b/src/hdhelpers/metadata/_helpers.py
@@ -33,7 +33,9 @@ def extract_from_metadata(metadata: Any, key: str, default: str | None = None) -
def get_value_dimension_info(
- multitsframe: pd.DataFrame | pd.Series, value_dim_info: str | Spec
+ multitsframe: pd.DataFrame | pd.Series,
+ value_dim_info: str | Spec,
+ include_inherited: bool = True,
) -> defaultdict[str, defaultdict[str, Any]]:
"""Obtain metadata info associated to the value dimensions of the metrics
@@ -51,12 +53,15 @@ def get_value_dimension_info(
dimension in the value_dimensions list under the metric, the global "value_dimensions_shared"
field of the attrs object is searched for corresponding information.
+ If include_inherited, the properties a metric inherits are used as part of its metric
+ metadata, i.e. after the metric's own information but before "value_dimensions_shared".
+
If no information is found, None is set as value and is the default value of the
inner default dict.
For examples we refer to the corresponding unit tests (/tests/helpers/test_metadata.py).
"""
- spec = _specs.by_metric_key_by_val_dimension(value_dim_info)
+ spec = _specs.by_metric_key_by_val_dimension(value_dim_info, include_inherited)
value_dimension_info_by_metric_by_value_dimension = glom(multitsframe.attrs, spec)
return defaultdict(
lambda: defaultdict(lambda: None), value_dimension_info_by_metric_by_value_dimension
@@ -84,7 +89,7 @@ def _select_single_metric(info_by_metric: dict, attrs: Any, empty: Any) -> Any:
def singlets_value_dimension_info(
- singletsframe: pd.DataFrame, value_dim_info: str | Spec
+ singletsframe: pd.DataFrame, value_dim_info: str | Spec, include_inherited: bool = True
) -> defaultdict[str, Any]:
"""Metadata info of the value dimensions of the single metric of a SingleTSFrame
@@ -100,13 +105,15 @@ def singlets_value_dimension_info(
only the metric level is collapsed away.
"""
return _select_single_metric(
- get_value_dimension_info(singletsframe, value_dim_info),
+ get_value_dimension_info(singletsframe, value_dim_info, include_inherited),
singletsframe.attrs,
empty=defaultdict(lambda: None),
)
-def singlets_metric_info(singletsframe: pd.DataFrame, metric_info: str | Spec) -> Any:
+def singlets_metric_info(
+ singletsframe: pd.DataFrame, metric_info: str | Spec, include_inherited: bool = True
+) -> Any:
"""Metadata of the single metric of a SingleTSFrame
Counterpart of get_metric_info for SingleTSFrames: instead of a mapping keyed by metric
@@ -115,7 +122,9 @@ def singlets_metric_info(singletsframe: pd.DataFrame, metric_info: str | Spec) -
not require metadata (see the metadata conventions documentation).
"""
try:
- info_by_metric = glom(singletsframe.attrs, _specs.spec_by_metric_key(metric_info))
+ info_by_metric = glom(
+ singletsframe.attrs, _specs.spec_by_metric_key(metric_info, include_inherited)
+ )
except GlomError: # no metric metadata present at all
return None
diff --git a/src/hdhelpers/metadata/_specs.py b/src/hdhelpers/metadata/_specs.py
index e5749bf..71738f9 100644
--- a/src/hdhelpers/metadata/_specs.py
+++ b/src/hdhelpers/metadata/_specs.py
@@ -12,8 +12,29 @@
DISPLAY_NAME: Spec = Coalesce("display_name", "name", default=None)
SHORT_DISPLAY_NAME: Spec = Coalesce("short_display_name", "display_name", "name", default=None)
+# Key of the dict in a metric object holding the properties the metric inherits from objects
+# higher up in an asset hierarchy, e.g. the latitude of the plant a sensor belongs to.
+INHERITED = "inherited"
-def by_metric_key_by_val_dimension(metadatum_key: str | Spec) -> Spec:
+
+def of_metric(metadatum_key: str | Spec, include_inherited: bool = True) -> Spec:
+ """Provides glom spec that extracts a metadatum from a metric object
+
+ If include_inherited, the metric's inherited properties are used as fallback: the whole
+ metadatum_key spec is first applied to the metric itself and only if that yields None
+ or fails, it is applied to the INHERITED dict of the metric. So in a fallback chain
+ like DISPLAY_NAME an own "name" beats an inherited "display_name".
+
+ metadatum_key can also be any glom spec.
+ """
+ if not include_inherited:
+ return metadatum_key
+ return Coalesce(metadatum_key, (INHERITED, metadatum_key), skip=lambda x: x is None)
+
+
+def by_metric_key_by_val_dimension(
+ metadatum_key: str | Spec, include_inherited: bool = True
+) -> Spec:
"""Providesglom spec that extracts a metadatum by metric by value dimension
The generated glom spec returns a defaultdict of defaultdicts:
@@ -27,10 +48,13 @@ def by_metric_key_by_val_dimension(metadatum_key: str | Spec) -> Spec:
Properly falls back to "value_dimensions_shared" metadata if a value_dimension
is not given for a metric if its available there.
+ For the current metadata convention, properties the metric inherits are used as part of
+ the metric metadatum if include_inherited, see of_metric.
+
metadatum_key can also be any glom spec.
"""
return Coalesce(
- _spec_new_convention(metadatum_key),
+ _spec_new_convention(metadatum_key, include_inherited),
_spec_platform_convention_series_metric(metadatum_key),
_spec_platform_convention_series_value_dim(metadatum_key),
_spec_older_convention1_metric(metadatum_key),
@@ -65,14 +89,14 @@ def _spec_defaults_by_value_dimension(metadatum_key: str | Spec) -> Spec:
)
-def _spec_defaults_by_metric(metadatum_key: str | Spec) -> Spec:
+def _spec_defaults_by_metric(metadatum_key: str | Spec, include_inherited: bool) -> Spec:
return Coalesce(
(
"metrics",
Check(instance_of=list),
_build_dict_from_iterable_from_key_and_subspec_and_then_proceed_on_result(
S.globals.metric_key,
- Coalesce(metadatum_key, default=None),
+ Coalesce(of_metric(metadatum_key, include_inherited), default=None),
key_as_value=True,
),
),
@@ -105,12 +129,12 @@ def _spec_actual_per_metric_per_value_dimensions(metadatum_key: str | Spec) -> S
)
-def _spec_new_convention(metadatum_key: str | Spec) -> Spec:
+def _spec_new_convention(metadatum_key: str | Spec, include_inherited: bool) -> Spec:
return (
{ # first gather information at different locations in metadata in a dict
"metric_key": _spec_metric_key(),
"defaults_by_value_dimension": _spec_defaults_by_value_dimension(metadatum_key),
- "defaults_by_metric": _spec_defaults_by_metric(metadatum_key),
+ "defaults_by_metric": _spec_defaults_by_metric(metadatum_key, include_inherited),
"actual_per_metric_per_value_dimensions": _spec_actual_per_metric_per_value_dimensions(
metadatum_key
),
@@ -214,7 +238,7 @@ def _spec_older_convention4(metadatum_key: str | Spec) -> Spec:
)
-def spec_by_metric_key(metadatum_key: str | Spec) -> Spec:
+def spec_by_metric_key(metadatum_key: str | Spec, include_inherited: bool = True) -> Spec:
return Coalesce(
( # current metdadata convention
{
@@ -224,7 +248,7 @@ def spec_by_metric_key(metadatum_key: str | Spec) -> Spec:
Check(instance_of=list),
_build_dict_from_iterable_from_key_and_subspec_and_then_proceed_on_result(
S.globals.metric_key,
- Coalesce(metadatum_key, default=None),
+ Coalesce(of_metric(metadatum_key, include_inherited), default=None),
key_as_value=True,
),
),
diff --git a/src/hdhelpers/metadata/helpers.py b/src/hdhelpers/metadata/helpers.py
index d0ff0c4..dd19299 100644
--- a/src/hdhelpers/metadata/helpers.py
+++ b/src/hdhelpers/metadata/helpers.py
@@ -31,12 +31,13 @@
def get_units(
- multitsframe: pd.DataFrame,
+ multitsframe: pd.DataFrame, *, include_inherited: bool = True
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets unit of value dimensions in MTSF metrics from Metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
dict[str, dict[str | None] | None]: Dictionary of metrics containing the names of the value dimensions.
@@ -78,14 +79,17 @@ def get_units(
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.UNIT)
+ return get_value_dimension_info(multitsframe, _specs.UNIT, include_inherited)
-def get_names(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdict[str, str | None]]:
+def get_names(
+ multitsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
dict[str, str | None]: Dictionary of metrics containing the names. If the name is not present for a metric the corresponding value is None.
@@ -123,14 +127,17 @@ def get_names(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdict[str, s
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.NAME)
+ return get_value_dimension_info(multitsframe, _specs.NAME, include_inherited)
-def get_display_names(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdict[str, str | None]]:
+def get_display_names(
+ multitsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets display names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the display names.
@@ -169,16 +176,17 @@ def get_display_names(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdic
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.DISPLAY_NAME)
+ return get_value_dimension_info(multitsframe, _specs.DISPLAY_NAME, include_inherited)
def get_short_display_names(
- multitsframe: pd.DataFrame,
+ multitsframe: pd.DataFrame, *, include_inherited: bool = True
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets short display names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the short display names.
@@ -220,14 +228,17 @@ def get_short_display_names(
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.SHORT_DISPLAY_NAME)
+ return get_value_dimension_info(multitsframe, _specs.SHORT_DISPLAY_NAME, include_inherited)
-def get_measurements(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdict[str, str | None]]:
+def get_measurements(
+ multitsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets measurement (type) of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the measurement (type) of the MTSF metrics.
@@ -250,10 +261,12 @@ def get_measurements(multitsframe: pd.DataFrame) -> defaultdict[str, defaultdict
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.MEASUREMENT)
+ return get_value_dimension_info(multitsframe, _specs.MEASUREMENT, include_inherited)
-def get_metric_info(multitsframe: pd.DataFrame, metric_info: str | Spec) -> defaultdict[str, Any]:
+def get_metric_info(
+ multitsframe: pd.DataFrame, metric_info: str | Spec, *, include_inherited: bool = True
+) -> defaultdict[str, Any]:
"""Gets a dictionary of metadata associated to metrics.
In contrast to metadata associated to concrete value dimensions, this
@@ -262,6 +275,7 @@ def get_metric_info(multitsframe: pd.DataFrame, metric_info: str | Spec) -> defa
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
metric_info (str | Spec): Name of information to retrieve. Note that `metric_info` is interpreted as a glom Spec.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, Any]: Dictionary, where keys are the entries of the metrics metadata specified via "metric_key" in "dataset_metadata" and values are the entries specified via "metric_info" in the metrics metadata
@@ -310,12 +324,14 @@ def get_metric_info(multitsframe: pd.DataFrame, metric_info: str | Spec) -> defa
True
"""
check_dataframe(multitsframe)
- spec = spec_by_metric_key(metric_info)
+ spec = spec_by_metric_key(metric_info, include_inherited)
metric_info = glom(multitsframe.attrs, spec)
return defaultdict(lambda: None, metric_info)
-def get_series_info(series: pd.Series, value_dim_info: str | Spec) -> Any:
+def get_series_info(
+ series: pd.Series, value_dim_info: str | Spec, *, include_inherited: bool = True
+) -> Any:
"""Gets an arbitrary series info
Since a series has only one value dimension named "value", this information is
@@ -324,6 +340,7 @@ def get_series_info(series: pd.Series, value_dim_info: str | Spec) -> Any:
Args:
series (pd.Series): Series with metadata following the convention.
value_dim_info (str | Spec): Name of information to retrieve. Note that `value_dim_info` is interpreted as a glom Spec.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
Any: Retrieved information defined by `value_dim_info`
@@ -345,9 +362,9 @@ def get_series_info(series: pd.Series, value_dim_info: str | Spec) -> Any:
# this value dimension.
check_series(series)
series_metric_key = extract_from_metadata(series.attrs, key="single_metric", default="series")
- from_new_convention = get_value_dimension_info(series, value_dim_info)[series_metric_key].get(
- "value"
- )
+ from_new_convention = get_value_dimension_info(series, value_dim_info, include_inherited)[
+ series_metric_key
+ ].get("value")
if from_new_convention is not None:
return from_new_convention
@@ -373,11 +390,12 @@ def get_series_info(series: pd.Series, value_dim_info: str | Spec) -> Any:
)
-def get_series_unit(series: pd.Series) -> str | None:
+def get_series_unit(series: pd.Series, *, include_inherited: bool = True) -> str | None:
"""Gets name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
str | None:
@@ -415,14 +433,18 @@ def get_series_unit(series: pd.Series) -> str | None:
"""
check_series(series)
- return cast(str | None, get_series_info(series, spec_not_none("unit")))
+ return cast(
+ str | None,
+ get_series_info(series, spec_not_none("unit"), include_inherited=include_inherited),
+ )
-def get_series_name(series: pd.Series) -> str | None:
+def get_series_name(series: pd.Series, *, include_inherited: bool = True) -> str | None:
"""Gets name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
str | None:
@@ -462,14 +484,18 @@ def get_series_name(series: pd.Series) -> str | None:
'name_of_series_2'
"""
check_series(series)
- return cast(str | None, get_series_info(series, spec_not_none("name")))
+ return cast(
+ str | None,
+ get_series_info(series, spec_not_none("name"), include_inherited=include_inherited),
+ )
-def get_series_display_name(series: pd.Series) -> str | None:
+def get_series_display_name(series: pd.Series, *, include_inherited: bool = True) -> str | None:
"""Gets display name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
str | None:
@@ -499,15 +525,19 @@ def get_series_display_name(series: pd.Series) -> str | None:
spec_not_none("name"),
spec_not_none("short_display_name"),
),
+ include_inherited=include_inherited,
),
)
-def get_series_short_display_name(series: pd.Series) -> str | None:
+def get_series_short_display_name(
+ series: pd.Series, *, include_inherited: bool = True
+) -> str | None:
"""Gets short display name of the Series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
str | None:
@@ -537,15 +567,17 @@ def get_series_short_display_name(series: pd.Series) -> str | None:
spec_not_none("display_name"),
spec_not_none("name"),
),
+ include_inherited=include_inherited,
),
)
-def get_series_measurement(series: pd.Series) -> str | None:
+def get_series_measurement(series: pd.Series, *, include_inherited: bool = True) -> str | None:
"""Gets measurement (type) of the Series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
str | None:
@@ -565,10 +597,14 @@ def get_series_measurement(series: pd.Series) -> str | None:
'temperature'
"""
check_series(series)
- return cast(str | None, get_series_info(series, "measurement"))
+ return cast(
+ str | None, get_series_info(series, "measurement", include_inherited=include_inherited)
+ )
-def get_singlets_info(singletsframe: pd.DataFrame, value_dim_info: str | Spec) -> Any:
+def get_singlets_info(
+ singletsframe: pd.DataFrame, value_dim_info: str | Spec, *, include_inherited: bool = True
+) -> Any:
"""Gets an arbitrary info of the value dimensions of a SingleTSFrame
A SingleTSFrame (STSF) holds exactly one metric - like a Series - but arbitrarily many
@@ -582,6 +618,7 @@ def get_singlets_info(singletsframe: pd.DataFrame, value_dim_info: str | Spec) -
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
value_dim_info (str | Spec): Name of information to retrieve. Note that `value_dim_info` is interpreted as a glom Spec.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, Any]: Dictionary of value dimensions containing the retrieved information. If the information is not present for a value dimension the corresponding value is None.
@@ -613,14 +650,17 @@ def get_singlets_info(singletsframe: pd.DataFrame, value_dim_info: str | Spec) -
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, value_dim_info)
+ return singlets_value_dimension_info(singletsframe, value_dim_info, include_inherited)
-def get_singlets_units(singletsframe: pd.DataFrame) -> defaultdict[str, str | None]:
+def get_singlets_units(
+ singletsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, str | None]:
"""Gets units of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the units. If the unit is not present for a value dimension the corresponding value is None.
@@ -669,14 +709,17 @@ def get_singlets_units(singletsframe: pd.DataFrame) -> defaultdict[str, str | No
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.UNIT)
+ return singlets_value_dimension_info(singletsframe, _specs.UNIT, include_inherited)
-def get_singlets_names(singletsframe: pd.DataFrame) -> defaultdict[str, str | None]:
+def get_singlets_names(
+ singletsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, str | None]:
"""Gets names of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the names. If the name is not present for a value dimension the corresponding value is None.
@@ -708,16 +751,19 @@ def get_singlets_names(singletsframe: pd.DataFrame) -> defaultdict[str, str | No
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.NAME)
+ return singlets_value_dimension_info(singletsframe, _specs.NAME, include_inherited)
-def get_singlets_display_names(singletsframe: pd.DataFrame) -> defaultdict[str, str | None]:
+def get_singlets_display_names(
+ singletsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, str | None]:
"""Gets display names of the value dimensions of a SingleTSFrame from metadata
Falls back to the name of the value dimension if no display name is given.
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the display names. If neither display name nor name is present for a value dimension the corresponding value is None.
@@ -749,10 +795,12 @@ def get_singlets_display_names(singletsframe: pd.DataFrame) -> defaultdict[str,
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.DISPLAY_NAME)
+ return singlets_value_dimension_info(singletsframe, _specs.DISPLAY_NAME, include_inherited)
-def get_singlets_short_display_names(singletsframe: pd.DataFrame) -> defaultdict[str, str | None]:
+def get_singlets_short_display_names(
+ singletsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, str | None]:
"""Gets short display names of the value dimensions of a SingleTSFrame from metadata
Falls back to the display name and then to the name of the value dimension if no short
@@ -760,6 +808,7 @@ def get_singlets_short_display_names(singletsframe: pd.DataFrame) -> defaultdict
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the short display names. If none of short display name, display name and name is present for a value dimension the corresponding value is None.
@@ -791,14 +840,19 @@ def get_singlets_short_display_names(singletsframe: pd.DataFrame) -> defaultdict
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.SHORT_DISPLAY_NAME)
+ return singlets_value_dimension_info(
+ singletsframe, _specs.SHORT_DISPLAY_NAME, include_inherited
+ )
-def get_singlets_measurements(singletsframe: pd.DataFrame) -> defaultdict[str, str | None]:
+def get_singlets_measurements(
+ singletsframe: pd.DataFrame, *, include_inherited: bool = True
+) -> defaultdict[str, str | None]:
"""Gets measurements (types) of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the measurements. If "measurement" is not given for a value dimension the corresponding value is None.
@@ -826,10 +880,12 @@ def get_singlets_measurements(singletsframe: pd.DataFrame) -> defaultdict[str, s
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.MEASUREMENT)
+ return singlets_value_dimension_info(singletsframe, _specs.MEASUREMENT, include_inherited)
-def get_singlets_metric_info(singletsframe: pd.DataFrame, metric_info: str | Spec) -> Any:
+def get_singlets_metric_info(
+ singletsframe: pd.DataFrame, metric_info: str | Spec, *, include_inherited: bool = True
+) -> Any:
"""Gets metadata associated to the single metric of a SingleTSFrame
Counterpart of :func:`get_metric_info` for SingleTSFrames: instead of a dictionary keyed
@@ -838,6 +894,7 @@ def get_singlets_metric_info(singletsframe: pd.DataFrame, metric_info: str | Spe
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
metric_info (str | Spec): Name of information to retrieve. Note that `metric_info` is interpreted as a glom Spec.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
Any: Retrieved information defined by `metric_info`, or None if it cannot be determined - which includes the case of absent metric metadata.
@@ -868,7 +925,7 @@ def get_singlets_metric_info(singletsframe: pd.DataFrame, metric_info: str | Spe
"""
check_dataframe(singletsframe)
- return singlets_metric_info(singletsframe, metric_info)
+ return singlets_metric_info(singletsframe, metric_info, include_inherited)
def get_queried_interval(
diff --git a/tests/metadata/test_metadata.py b/tests/metadata/test_metadata.py
index d09a127..e073ae8 100644
--- a/tests/metadata/test_metadata.py
+++ b/tests/metadata/test_metadata.py
@@ -501,3 +501,89 @@ def test_get_singlets_without_metadata():
def test_get_singlets_requires_a_dataframe(func):
with pytest.raises(TypeError):
func(pd.Series())
+
+
+@pytest.fixture
+def attrs_with_inherited() -> dict:
+ return {
+ "dataset_metadata": {"metric_key": "id", "single_metric": "sensor"},
+ "value_dimensions_shared": [{"column": "value", "unit": "shared unit"}],
+ "metrics": [
+ {
+ "id": "sensor",
+ "name": "sensor name",
+ "Latitude": None,
+ "Longitude": 7.58,
+ "inherited": {
+ "Latitude": 51.43,
+ "Longitude": 8.28,
+ "display_name": "plant display name",
+ "unit": "inherited unit",
+ },
+ "value_dimensions": [{"column": "value"}, {"column": "state"}],
+ },
+ {"id": "other", "value_dimensions": [{"column": "value"}]},
+ ],
+ }
+
+
+def test_get_metric_info_falls_back_to_inherited(attrs_with_inherited):
+ from hdhelpers.metadata import get_metric_info
+
+ dataframe = pd.DataFrame()
+ dataframe.attrs = attrs_with_inherited
+
+ assert get_metric_info(dataframe, "Latitude")["sensor"] == 51.43 # own None -> inherited
+ assert get_metric_info(dataframe, "Longitude")["sensor"] == 7.58 # own beats inherited
+ assert get_metric_info(dataframe, "Latitude")["other"] is None
+ assert get_metric_info(dataframe, "Latitude", include_inherited=False)["sensor"] is None
+ assert get_singlets_metric_info(dataframe, "Latitude") == 51.43
+ assert get_singlets_metric_info(dataframe, "Latitude", include_inherited=False) is None
+
+
+def test_get_value_dimension_info_falls_back_to_inherited_for_value_only(
+ attrs_with_inherited,
+):
+ from hdhelpers.metadata import get_units
+
+ dataframe = pd.DataFrame()
+ dataframe.attrs = attrs_with_inherited
+
+ # like the metric metadata itself, inherited metadata only applies to the "value" column
+ assert get_singlets_info(dataframe, "Latitude")["value"] == 51.43
+ assert get_singlets_info(dataframe, "Latitude")["state"] is None
+ assert get_singlets_info(dataframe, "Latitude", include_inherited=False)["value"] is None
+
+ # inherited metadata of the metric beats the dataset wide value_dimensions_shared
+ assert get_units(dataframe)["sensor"]["value"] == "inherited unit"
+ assert get_units(dataframe)["other"]["value"] == "shared unit"
+ assert get_units(dataframe, include_inherited=False)["sensor"]["value"] == "shared unit"
+ assert get_singlets_units(dataframe)["value"] == "inherited unit"
+ assert get_singlets_units(dataframe, include_inherited=False)["value"] == "shared unit"
+
+
+def test_get_series_info_falls_back_to_inherited(attrs_with_inherited):
+ from hdhelpers.metadata import get_series_info
+
+ series = pd.Series()
+ series.attrs = attrs_with_inherited
+
+ assert get_series_info(series, "Latitude") == 51.43
+ assert get_series_info(series, "Longitude") == 7.58
+ assert get_series_info(series, "Latitude", include_inherited=False) is None
+ assert get_series_unit(series) == "inherited unit"
+ assert get_series_unit(series, include_inherited=False) == "shared unit"
+
+
+def test_own_metadata_beats_inherited_metadata_in_fallback_chains(attrs_with_inherited):
+ """The whole spec is applied to the metric itself before trying its inherited metadata"""
+ series = pd.Series()
+ series.attrs = attrs_with_inherited
+
+ # own "name" beats inherited "display_name"
+ assert get_series_display_name(series) == "sensor name"
+ assert get_series_short_display_name(series) == "sensor name"
+
+ del series.attrs["metrics"][0]["name"]
+ assert get_series_display_name(series) == "plant display name"
+ assert get_series_display_name(series, include_inherited=False) is None
From ccb2cb74c3bbd85db664aefaa39217d16e0504c6 Mon Sep 17 00:00:00 2001
From: stewit <>
Date: Tue, 20 Oct 2026 22:36:45 +0200
Subject: [PATCH 02/15] fixes around fallback behaviour (null, propagation to
weaker fields)
---
src/hdhelpers/metadata/_helpers.py | 62 +++++-
src/hdhelpers/metadata/_specs.py | 6 +
src/hdhelpers/metadata/helpers.py | 111 ++++++++---
tests/metadata/test_metadata_fallbacks.py | 227 ++++++++++++++++++++++
4 files changed, 371 insertions(+), 35 deletions(-)
create mode 100644 tests/metadata/test_metadata_fallbacks.py
diff --git a/src/hdhelpers/metadata/_helpers.py b/src/hdhelpers/metadata/_helpers.py
index e03a13b..3d3b386 100644
--- a/src/hdhelpers/metadata/_helpers.py
+++ b/src/hdhelpers/metadata/_helpers.py
@@ -68,15 +68,54 @@ def get_value_dimension_info(
)
+def follows_current_convention(attrs: Any) -> bool:
+ """Whether metadata follows the current metadata convention, i.e. has a list of metrics"""
+ return isinstance(attrs, dict) and isinstance(attrs.get("metrics"), list)
+
+
+def value_dimension_info_with_fallbacks(
+ data: pd.DataFrame | pd.Series,
+ fields: tuple[str, ...],
+ legacy_spec: Spec,
+ include_inherited: bool = True,
+) -> defaultdict[str, defaultdict[str, Any]]:
+ """Obtain a field of the value dimensions, falling back to further fields
+
+ For metadata following the current metadata convention, each field is looked up on all
+ levels described in get_value_dimension_info (the value dimension, the metric for the
+ value dimension "value" and "value_dimensions_shared") before the next field is tried.
+ So e.g. a display name of the metric wins over the name of its value dimension. Fields
+ provided as null are treated as missing. Inherited properties are only used if none of the
+ fields is provided by the metadata itself.
+
+ Older metadata structures are handled via legacy_spec, keeping their established behaviour.
+ """
+ if not follows_current_convention(data.attrs):
+ return get_value_dimension_info(data, legacy_spec, include_inherited)
+
+ result: defaultdict[str, defaultdict[str, Any]] = defaultdict(lambda: defaultdict(lambda: None))
+ # inherited properties only after all fields of the metadata itself
+ for inherited in [False, True] if include_inherited else [False]:
+ for field in fields:
+ info_by_metric = get_value_dimension_info(
+ data, Coalesce(field, default=None), inherited
+ )
+ for metric, info_by_value_dimension in info_by_metric.items():
+ for value_dimension, info in info_by_value_dimension.items():
+ if result[metric][value_dimension] is None:
+ result[metric][value_dimension] = info
+ return result
+
+
_SINGLE_METRIC_KEY = "single_metric"
def _select_single_metric(info_by_metric: dict, attrs: Any, empty: Any) -> Any:
- """Pick the entry of the one metric of a SingleTSFrame out of a by-metric mapping
+ """Pick the entry of the one metric of a SingleTSFrame or Series out of a by-metric mapping
The single metric is identified via _SINGLE_METRIC_KEY in "dataset_metadata". If that is
- missing but exactly one metric is present, that one is used, since a SingleTSFrame cannot
- be ambiguous in this respect. Otherwise `empty` is returned.
+ missing but exactly one metric is present, that one is used, since a SingleTSFrame or
+ Series cannot be ambiguous in this respect. Otherwise `empty` is returned.
"""
metric = extract_from_metadata(attrs, key=_SINGLE_METRIC_KEY)
if metric is not None and metric in info_by_metric:
@@ -111,6 +150,23 @@ def singlets_value_dimension_info(
)
+def singlets_value_dimension_info_with_fallbacks(
+ singletsframe: pd.DataFrame,
+ fields: tuple[str, ...],
+ legacy_spec: Spec,
+ include_inherited: bool = True,
+) -> defaultdict[str, Any]:
+ """Counterpart of value_dimension_info_with_fallbacks for SingleTSFrames
+
+ Like singlets_value_dimension_info, the result is keyed by value dimension only.
+ """
+ return _select_single_metric(
+ value_dimension_info_with_fallbacks(singletsframe, fields, legacy_spec, include_inherited),
+ singletsframe.attrs,
+ empty=defaultdict(lambda: None),
+ )
+
+
def singlets_metric_info(
singletsframe: pd.DataFrame, metric_info: str | Spec, include_inherited: bool = True
) -> Any:
diff --git a/src/hdhelpers/metadata/_specs.py b/src/hdhelpers/metadata/_specs.py
index 71738f9..664c60f 100644
--- a/src/hdhelpers/metadata/_specs.py
+++ b/src/hdhelpers/metadata/_specs.py
@@ -16,6 +16,12 @@
# higher up in an asset hierarchy, e.g. the latitude of the plant a sensor belongs to.
INHERITED = "inherited"
+# The fallback chains of the display names for metadata following the current metadata
+# convention: Each field is looked up on all levels (value dimension, metric, shared value
+# dimensions) before the next field is tried, see value_dimension_info_with_fallbacks.
+DISPLAY_NAME_FIELDS = ("display_name", "name")
+SHORT_DISPLAY_NAME_FIELDS = ("short_display_name", "display_name", "name")
+
def of_metric(metadatum_key: str | Spec, include_inherited: bool = True) -> Spec:
"""Provides glom spec that extracts a metadatum from a metric object
diff --git a/src/hdhelpers/metadata/helpers.py b/src/hdhelpers/metadata/helpers.py
index dd19299..32ee99c 100644
--- a/src/hdhelpers/metadata/helpers.py
+++ b/src/hdhelpers/metadata/helpers.py
@@ -14,19 +14,22 @@
from typing import Any, cast
import pandas as pd
-from glom import Coalesce, Spec, glom
+from glom import Coalesce, GlomError, Spec, glom
+import hdhelpers.metadata._specs as _specs
from hdhelpers.metadata._helpers import (
check_dataframe,
check_series,
check_series_or_dataframe,
extract_from_metadata,
+ follows_current_convention,
get_value_dimension_info,
singlets_metric_info,
singlets_value_dimension_info,
+ singlets_value_dimension_info_with_fallbacks,
spec_not_none,
+ value_dimension_info_with_fallbacks,
)
-import hdhelpers.metadata._specs as _specs
from hdhelpers.metadata._specs import spec_by_metric_key
@@ -142,6 +145,9 @@ def get_display_names(
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the display names.
If the display name of the metrics is not present it returns the result of :func:`hdhelpers.metadata.get_names`.
+ For metadata following the current convention, the display name is looked up on the value dimension, on
+ the metric (for the value dimension "value") and in "value_dimensions_shared" before falling back to the
+ name. Fields provided as null count as missing.
Raises:
TypeError: If `multitsframe` is not a DataFrame.
@@ -176,7 +182,9 @@ def get_display_names(
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.DISPLAY_NAME, include_inherited)
+ return value_dimension_info_with_fallbacks(
+ multitsframe, _specs.DISPLAY_NAME_FIELDS, _specs.DISPLAY_NAME, include_inherited
+ )
def get_short_display_names(
@@ -191,6 +199,9 @@ def get_short_display_names(
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the short display names.
If the short display name of the metrics is not present it returns the result of :func:`hdhelpers.metadata.get_display_names`.
+ For metadata following the current convention, each of short display name, display name and name is
+ looked up on the value dimension, on the metric (for the value dimension "value") and in
+ "value_dimensions_shared" before falling back to the next one. Fields provided as null count as missing.
Raises:
TypeError: If `multitsframe` is not a DataFrame.
@@ -228,7 +239,9 @@ def get_short_display_names(
"""
check_dataframe(multitsframe)
- return get_value_dimension_info(multitsframe, _specs.SHORT_DISPLAY_NAME, include_inherited)
+ return value_dimension_info_with_fallbacks(
+ multitsframe, _specs.SHORT_DISPLAY_NAME_FIELDS, _specs.SHORT_DISPLAY_NAME, include_inherited
+ )
def get_measurements(
@@ -278,7 +291,7 @@ def get_metric_info(
include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
Returns:
- defaultdict[str, Any]: Dictionary, where keys are the entries of the metrics metadata specified via "metric_key" in "dataset_metadata" and values are the entries specified via "metric_info" in the metrics metadata
+ defaultdict[str, Any]: Dictionary, where keys are the entries of the metrics metadata specified via "metric_key" in "dataset_metadata" and values are the entries specified via "metric_info" in the metrics metadata. Defaults to None, also if there is no metric metadata at all.
Code example:
@@ -325,8 +338,11 @@ def get_metric_info(
"""
check_dataframe(multitsframe)
spec = spec_by_metric_key(metric_info, include_inherited)
- metric_info = glom(multitsframe.attrs, spec)
- return defaultdict(lambda: None, metric_info)
+ try:
+ info_by_metric = glom(multitsframe.attrs, spec)
+ except GlomError: # no metric metadata present at all
+ info_by_metric = {}
+ return defaultdict(lambda: None, info_by_metric)
def get_series_info(
@@ -361,10 +377,9 @@ def get_series_info(
# metadata, we can reuse the code that extracts value_dimension metadata for
# this value dimension.
check_series(series)
- series_metric_key = extract_from_metadata(series.attrs, key="single_metric", default="series")
- from_new_convention = get_value_dimension_info(series, value_dim_info, include_inherited)[
- series_metric_key
- ].get("value")
+ from_new_convention = _series_value(
+ get_value_dimension_info(series, value_dim_info, include_inherited), series
+ )
if from_new_convention is not None:
return from_new_convention
@@ -390,6 +405,40 @@ def get_series_info(
)
+def _series_value(info_by_metric: dict, series: pd.Series) -> Any:
+ """Info of the value dimension "value" of the metric of a Series
+
+ The metric is identified via "single_metric" in "dataset_metadata", defaulting to "series"
+ as used by older metadata structures. If it is not found but exactly one metric is present,
+ that one is used, since a Series cannot be ambiguous in this respect.
+ """
+ series_metric_key = extract_from_metadata(series.attrs, key="single_metric", default="series")
+ if series_metric_key in info_by_metric:
+ return info_by_metric[series_metric_key].get("value")
+ if len(info_by_metric) == 1:
+ return next(iter(info_by_metric.values())).get("value")
+ return None
+
+
+def _series_info_with_fallbacks(
+ series: pd.Series, fields: tuple[str, ...], include_inherited: bool
+) -> Any:
+ """Info of a Series, falling back to further fields
+
+ For metadata following the current metadata convention, each field is looked up on the
+ value dimension and the metric before the next field is tried, see
+ value_dimension_info_with_fallbacks. Older metadata structures keep their established
+ behaviour of using the first of the fields present on the first level providing any.
+ """
+ legacy_spec = Coalesce(*(spec_not_none(field) for field in fields))
+ if follows_current_convention(series.attrs):
+ return _series_value(
+ value_dimension_info_with_fallbacks(series, fields, legacy_spec, include_inherited),
+ series,
+ )
+ return get_series_info(series, legacy_spec, include_inherited=include_inherited)
+
+
def get_series_unit(series: pd.Series, *, include_inherited: bool = True) -> str | None:
"""Gets name of the series from metadata
@@ -518,14 +567,8 @@ def get_series_display_name(series: pd.Series, *, include_inherited: bool = True
check_series(series)
return cast(
str | None,
- get_series_info(
- series,
- Coalesce(
- spec_not_none("display_name"),
- spec_not_none("name"),
- spec_not_none("short_display_name"),
- ),
- include_inherited=include_inherited,
+ _series_info_with_fallbacks(
+ series, ("display_name", "name", "short_display_name"), include_inherited
),
)
@@ -560,14 +603,8 @@ def get_series_short_display_name(
check_series(series)
return cast(
str | None,
- get_series_info(
- series,
- Coalesce(
- spec_not_none("short_display_name"),
- spec_not_none("display_name"),
- spec_not_none("name"),
- ),
- include_inherited=include_inherited,
+ _series_info_with_fallbacks(
+ series, ("short_display_name", "display_name", "name"), include_inherited
),
)
@@ -759,7 +796,10 @@ def get_singlets_display_names(
) -> defaultdict[str, str | None]:
"""Gets display names of the value dimensions of a SingleTSFrame from metadata
- Falls back to the name of the value dimension if no display name is given.
+ Falls back to the name of the value dimension if no display name is given. The display name
+ is looked up on the value dimension, on the metric (for the value dimension "value") and in
+ "value_dimensions_shared" before falling back to the name. Fields provided as null count as
+ missing.
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
@@ -795,7 +835,9 @@ def get_singlets_display_names(
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(singletsframe, _specs.DISPLAY_NAME, include_inherited)
+ return singlets_value_dimension_info_with_fallbacks(
+ singletsframe, _specs.DISPLAY_NAME_FIELDS, _specs.DISPLAY_NAME, include_inherited
+ )
def get_singlets_short_display_names(
@@ -804,7 +846,9 @@ def get_singlets_short_display_names(
"""Gets short display names of the value dimensions of a SingleTSFrame from metadata
Falls back to the display name and then to the name of the value dimension if no short
- display name is given.
+ display name is given. Each of them is looked up on the value dimension, on the metric (for
+ the value dimension "value") and in "value_dimensions_shared" before falling back to the next
+ one. Fields provided as null count as missing.
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
@@ -840,8 +884,11 @@ def get_singlets_short_display_names(
"""
check_dataframe(singletsframe)
- return singlets_value_dimension_info(
- singletsframe, _specs.SHORT_DISPLAY_NAME, include_inherited
+ return singlets_value_dimension_info_with_fallbacks(
+ singletsframe,
+ _specs.SHORT_DISPLAY_NAME_FIELDS,
+ _specs.SHORT_DISPLAY_NAME,
+ include_inherited,
)
diff --git a/tests/metadata/test_metadata_fallbacks.py b/tests/metadata/test_metadata_fallbacks.py
new file mode 100644
index 0000000..05e7023
--- /dev/null
+++ b/tests/metadata/test_metadata_fallbacks.py
@@ -0,0 +1,227 @@
+"""Fallbacks for metadata following the current metadata convention
+
+Metadata providers like the hetida platform send fields without value explicitly as null.
+These count as missing: The accessors fall back to the next level or field as if the field
+was not provided at all.
+"""
+
+from copy import deepcopy
+
+import pandas as pd
+import pytest
+
+from hdhelpers.metadata import (
+ get_display_names,
+ get_measurements,
+ get_metric_info,
+ get_names,
+ get_series_display_name,
+ get_series_measurement,
+ get_series_name,
+ get_series_short_display_name,
+ get_series_unit,
+ get_short_display_names,
+ get_singlets_display_names,
+ get_singlets_short_display_names,
+ get_singlets_units,
+ get_units,
+)
+
+SIGNAL_ID = "7ab8c419-8745-4714-b588-57c0c772cc95"
+
+
+def value_dimension(**fields):
+ """Value dimension "value" as the hetida platform sends it, with nulls for missing fields"""
+ return {
+ "column": "value",
+ "name": None,
+ "display_name": None,
+ "short_display_name": None,
+ "description": None,
+ "unit": None,
+ "measurement": None,
+ "value_data_type": "float",
+ } | fields
+
+
+def metric(**fields):
+ """Metric as the hetida platform sends it, with nulls for missing fields"""
+ return {
+ "id": SIGNAL_ID,
+ "external_id": None,
+ "name": None,
+ "display_name": None,
+ "description": None,
+ "inherited": {},
+ "comments": [],
+ "value_dimensions": [value_dimension()],
+ "default_aggregation_method": None,
+ } | fields
+
+
+# what the hetida platform sends for a timeseries of a SERIES read
+platform_series_attrs = {
+ "dataset_metadata": {
+ "metric_key": "id",
+ "single_metric": SIGNAL_ID,
+ "queried_metrics": [SIGNAL_ID],
+ "ref_interval_start_timestamp": "2026-10-18T00:00:00Z",
+ "ref_interval_end_timestamp": "2026-10-19T00:00:00Z",
+ "ref_interval_type": "closed",
+ },
+ "metrics": [
+ metric(
+ external_id="water.north.level",
+ name="Water-lvl-north",
+ value_dimensions=[
+ value_dimension(name="Water-lvl-north", unit="m", measurement="level")
+ ],
+ default_aggregation_method="AVG",
+ )
+ ],
+}
+
+# what the hetida platform sends for a MULTITSFRAME read of a signal group: display_name of
+# a metric is the display name configured for the member of the group
+platform_multitsframe_attrs = {
+ "dataset_metadata": {"metric_key": "id", "queried_metrics": ["level", "flow"]},
+ "metrics": [
+ metric(
+ id="level",
+ name="Water-lvl-north",
+ value_dimensions=[value_dimension(name="Water-lvl-north", unit="m")],
+ ),
+ metric(
+ id="flow",
+ name="Pump-flow",
+ display_name="Flow pump 1",
+ value_dimensions=[value_dimension(name="Pump-flow")],
+ ),
+ ],
+}
+
+
+def series(attrs):
+ ser = pd.Series([1.0], index=pd.to_datetime(["2026-10-18T06:00:00Z"]))
+ ser.attrs = deepcopy(attrs)
+ return ser
+
+
+def frame(attrs):
+ df = pd.DataFrame()
+ df.attrs = deepcopy(attrs)
+ return df
+
+
+def test_series_from_platform():
+ platform_series = series(platform_series_attrs)
+
+ assert get_series_unit(platform_series) == "m"
+ assert get_series_measurement(platform_series) == "level"
+ assert get_series_name(platform_series) == "Water-lvl-north"
+ assert get_series_display_name(platform_series) == "Water-lvl-north"
+ assert get_series_short_display_name(platform_series) == "Water-lvl-north"
+
+
+def test_multitsframe_from_platform():
+ multitsframe = frame(platform_multitsframe_attrs)
+
+ assert get_units(multitsframe)["level"]["value"] == "m"
+ assert get_units(multitsframe)["flow"]["value"] is None
+ assert get_names(multitsframe)["flow"]["value"] == "Pump-flow"
+ assert get_measurements(multitsframe)["level"]["value"] is None
+ # a null display name falls back to the name
+ assert get_display_names(multitsframe)["level"]["value"] == "Water-lvl-north"
+ assert get_short_display_names(multitsframe)["level"]["value"] == "Water-lvl-north"
+ # the display name of the metric wins over the name of its value dimension
+ assert get_display_names(multitsframe)["flow"]["value"] == "Flow pump 1"
+ assert get_short_display_names(multitsframe)["flow"]["value"] == "Flow pump 1"
+
+
+def test_each_field_is_looked_up_on_all_levels_before_the_next_field():
+ attrs = {
+ "metrics": [
+ metric(
+ id="a",
+ name="metric name",
+ value_dimensions=[
+ value_dimension(name="value name"),
+ value_dimension(column="state", name="state name"),
+ ],
+ ),
+ metric(id="b", name="metric name", display_name="metric display name"),
+ metric(
+ id="c",
+ display_name="metric display name",
+ value_dimensions=[value_dimension(display_name="value display name")],
+ ),
+ ],
+ "value_dimensions_shared": [{"column": "state", "display_name": "shared display name"}],
+ }
+
+ display_names = get_display_names(frame(attrs))
+
+ assert display_names["a"]["value"] == "value name"
+ assert display_names["a"]["state"] == "shared display name"
+ assert display_names["b"]["value"] == "metric display name"
+ assert display_names["c"]["value"] == "value display name"
+
+
+def test_singletsframe_display_names():
+ attrs = {
+ "dataset_metadata": {"single_metric": "abc.temp", "metric_key": "external_id"},
+ "metrics": [
+ metric(
+ external_id="abc.temp",
+ name="ABC temperature",
+ display_name="ABC temp.",
+ value_dimensions=[
+ value_dimension(name="temperature", unit="°C"),
+ value_dimension(column="state", name="measurement state"),
+ ],
+ )
+ ],
+ }
+ singletsframe = frame(attrs)
+
+ assert get_singlets_units(singletsframe)["value"] == "°C"
+ # only the value dimension "value" falls back to the metric
+ assert get_singlets_display_names(singletsframe)["value"] == "ABC temp."
+ assert get_singlets_display_names(singletsframe)["state"] == "measurement state"
+ assert get_singlets_short_display_names(singletsframe)["state"] == "measurement state"
+
+
+def test_series_display_name_of_metric_wins_over_value_dimension_name():
+ attrs = deepcopy(platform_series_attrs)
+ attrs["metrics"][0]["display_name"] = "Level north"
+ attrs["metrics"][0]["short_display_name"] = "north"
+
+ assert get_series_display_name(series(attrs)) == "Level north"
+ assert get_series_short_display_name(series(attrs)) == "north"
+
+
+def test_series_without_single_metric_uses_its_only_metric():
+ attrs = deepcopy(platform_series_attrs)
+ del attrs["dataset_metadata"]["single_metric"]
+
+ assert get_series_unit(series(attrs)) == "m"
+ assert get_series_display_name(series(attrs)) == "Water-lvl-north"
+
+
+@pytest.mark.parametrize(
+ "attrs",
+ [{}, {"dataset_metadata": {"metric_key": "id"}}, {"metrics": None}],
+)
+def test_metric_info_without_metric_metadata(attrs):
+ """Components should not require metadata, so missing metric metadata must not raise"""
+ assert get_metric_info(frame(attrs), "name")["any metric"] is None
+
+
+def test_metric_info_from_platform():
+ multitsframe = frame(platform_multitsframe_attrs)
+ multitsframe.attrs["metrics"][0]["default_aggregation_method"] = "SUM"
+
+ aggregation_methods = get_metric_info(multitsframe, "default_aggregation_method")
+
+ assert aggregation_methods["level"] == "SUM"
+ assert aggregation_methods["flow"] is None
From 87047ab1cba2783d9e028a31560423871d7330f5 Mon Sep 17 00:00:00 2001
From: stewit <>
Date: Wed, 21 Oct 2026 09:37:53 +0200
Subject: [PATCH 03/15] switch to more sensible default and fallback behaviours
---
src/hdhelpers/metadata/_helpers.py | 54 +++++++++----
src/hdhelpers/metadata/_specs.py | 126 +++++++++++++++++------------
src/hdhelpers/metadata/helpers.py | 93 +++++++++++----------
tests/metadata/test_metadata.py | 60 +++++++++++---
4 files changed, 212 insertions(+), 121 deletions(-)
diff --git a/src/hdhelpers/metadata/_helpers.py b/src/hdhelpers/metadata/_helpers.py
index 3d3b386..f9c528b 100644
--- a/src/hdhelpers/metadata/_helpers.py
+++ b/src/hdhelpers/metadata/_helpers.py
@@ -35,7 +35,7 @@ def extract_from_metadata(metadata: Any, key: str, default: str | None = None) -
def get_value_dimension_info(
multitsframe: pd.DataFrame | pd.Series,
value_dim_info: str | Spec,
- include_inherited: bool = True,
+ include_inherited: bool = False,
) -> defaultdict[str, defaultdict[str, Any]]:
"""Obtain metadata info associated to the value dimensions of the metrics
@@ -53,8 +53,8 @@ def get_value_dimension_info(
dimension in the value_dimensions list under the metric, the global "value_dimensions_shared"
field of the attrs object is searched for corresponding information.
- If include_inherited, the properties a metric inherits are used as part of its metric
- metadata, i.e. after the metric's own information but before "value_dimensions_shared".
+ If include_inherited, the properties a metric inherits are used as last resort for the
+ "value" value dimension, i.e. after "value_dimensions_shared".
If no information is found, None is set as value and is the default value of the
inner default dict.
@@ -77,7 +77,7 @@ def value_dimension_info_with_fallbacks(
data: pd.DataFrame | pd.Series,
fields: tuple[str, ...],
legacy_spec: Spec,
- include_inherited: bool = True,
+ include_inherited: bool = False,
) -> defaultdict[str, defaultdict[str, Any]]:
"""Obtain a field of the value dimensions, falling back to further fields
@@ -128,7 +128,7 @@ def _select_single_metric(info_by_metric: dict, attrs: Any, empty: Any) -> Any:
def singlets_value_dimension_info(
- singletsframe: pd.DataFrame, value_dim_info: str | Spec, include_inherited: bool = True
+ singletsframe: pd.DataFrame, value_dim_info: str | Spec, include_inherited: bool = False
) -> defaultdict[str, Any]:
"""Metadata info of the value dimensions of the single metric of a SingleTSFrame
@@ -154,7 +154,7 @@ def singlets_value_dimension_info_with_fallbacks(
singletsframe: pd.DataFrame,
fields: tuple[str, ...],
legacy_spec: Spec,
- include_inherited: bool = True,
+ include_inherited: bool = False,
) -> defaultdict[str, Any]:
"""Counterpart of value_dimension_info_with_fallbacks for SingleTSFrames
@@ -167,6 +167,35 @@ def singlets_value_dimension_info_with_fallbacks(
)
+def metric_info_by_metric(
+ data: pd.DataFrame | pd.Series, metric_info: str | Spec, include_inherited: bool = True
+) -> dict[str, Any]:
+ """Obtain metadata info associated to the metrics, keyed by metric
+
+ For metadata following the current metadata convention, information the metric itself does
+ not provide is taken from its value dimension "value", then from "value_dimensions_shared"
+ and, if include_inherited, finally from the properties the metric inherits. Information
+ provided as null counts as missing.
+
+ Returns an empty dict if no metric metadata is present at all.
+ """
+ try:
+ info_by_metric = glom(data.attrs, _specs.spec_by_metric_key(metric_info))
+ except GlomError: # no metric metadata present at all
+ return {}
+
+ if not follows_current_convention(data.attrs):
+ return info_by_metric
+
+ value_dimension_info = get_value_dimension_info(
+ data, Coalesce(metric_info, default=None), include_inherited
+ )
+ return {
+ metric: info if info is not None else value_dimension_info[metric].get("value")
+ for metric, info in info_by_metric.items()
+ }
+
+
def singlets_metric_info(
singletsframe: pd.DataFrame, metric_info: str | Spec, include_inherited: bool = True
) -> Any:
@@ -177,11 +206,8 @@ def singlets_metric_info(
be determined - which includes the case of absent metric metadata, since components should
not require metadata (see the metadata conventions documentation).
"""
- try:
- info_by_metric = glom(
- singletsframe.attrs, _specs.spec_by_metric_key(metric_info, include_inherited)
- )
- except GlomError: # no metric metadata present at all
- return None
-
- return _select_single_metric(info_by_metric, singletsframe.attrs, empty=None)
+ return _select_single_metric(
+ metric_info_by_metric(singletsframe, metric_info, include_inherited),
+ singletsframe.attrs,
+ empty=None,
+ )
diff --git a/src/hdhelpers/metadata/_specs.py b/src/hdhelpers/metadata/_specs.py
index 664c60f..c684709 100644
--- a/src/hdhelpers/metadata/_specs.py
+++ b/src/hdhelpers/metadata/_specs.py
@@ -1,7 +1,8 @@
from collections import defaultdict
from collections.abc import Callable
+from typing import Any
-from glom import A, Check, Coalesce, Iter, Merge, S, Spec, T
+from glom import A, Check, Coalesce, Iter, Merge, S, Spec, T, Val
# The metadatum specs of the individual accessors, including their fallback chains.
# Defined once here and shared by the MultiTSFrame and the SingleTSFrame accessors, which
@@ -23,23 +24,8 @@
SHORT_DISPLAY_NAME_FIELDS = ("short_display_name", "display_name", "name")
-def of_metric(metadatum_key: str | Spec, include_inherited: bool = True) -> Spec:
- """Provides glom spec that extracts a metadatum from a metric object
-
- If include_inherited, the metric's inherited properties are used as fallback: the whole
- metadatum_key spec is first applied to the metric itself and only if that yields None
- or fails, it is applied to the INHERITED dict of the metric. So in a fallback chain
- like DISPLAY_NAME an own "name" beats an inherited "display_name".
-
- metadatum_key can also be any glom spec.
- """
- if not include_inherited:
- return metadatum_key
- return Coalesce(metadatum_key, (INHERITED, metadatum_key), skip=lambda x: x is None)
-
-
def by_metric_key_by_val_dimension(
- metadatum_key: str | Spec, include_inherited: bool = True
+ metadatum_key: str | Spec, include_inherited: bool = False
) -> Spec:
"""Providesglom spec that extracts a metadatum by metric by value dimension
@@ -54,8 +40,9 @@ def by_metric_key_by_val_dimension(
Properly falls back to "value_dimensions_shared" metadata if a value_dimension
is not given for a metric if its available there.
- For the current metadata convention, properties the metric inherits are used as part of
- the metric metadatum if include_inherited, see of_metric.
+ For the current metadata convention and include_inherited, the properties the metric
+ inherits are used as last resort for the "value" value_dimension, i.e. after
+ "value_dimensions_shared".
metadatum_key can also be any glom spec.
"""
@@ -95,14 +82,30 @@ def _spec_defaults_by_value_dimension(metadatum_key: str | Spec) -> Spec:
)
-def _spec_defaults_by_metric(metadatum_key: str | Spec, include_inherited: bool) -> Spec:
+def _spec_defaults_by_metric(metadatum_key: str | Spec) -> Spec:
return Coalesce(
(
"metrics",
Check(instance_of=list),
_build_dict_from_iterable_from_key_and_subspec_and_then_proceed_on_result(
S.globals.metric_key,
- Coalesce(of_metric(metadatum_key, include_inherited), default=None),
+ Coalesce(metadatum_key, default=None),
+ key_as_value=True,
+ ),
+ ),
+ default={},
+ )
+
+
+def _spec_inherited_by_metric(metadatum_key: str | Spec) -> Spec:
+ """The metadatum from the properties each metric inherits, keyed by metric"""
+ return Coalesce(
+ (
+ "metrics",
+ Check(instance_of=list),
+ _build_dict_from_iterable_from_key_and_subspec_and_then_proceed_on_result(
+ S.globals.metric_key,
+ Coalesce((INHERITED, metadatum_key), default=None),
key_as_value=True,
),
),
@@ -140,39 +143,57 @@ def _spec_new_convention(metadatum_key: str | Spec, include_inherited: bool) ->
{ # first gather information at different locations in metadata in a dict
"metric_key": _spec_metric_key(),
"defaults_by_value_dimension": _spec_defaults_by_value_dimension(metadatum_key),
- "defaults_by_metric": _spec_defaults_by_metric(metadatum_key, include_inherited),
+ "defaults_by_metric": _spec_defaults_by_metric(metadatum_key),
+ "inherited_by_metric": (
+ _spec_inherited_by_metric(metadatum_key) if include_inherited else Val({})
+ ),
"actual_per_metric_per_value_dimensions": _spec_actual_per_metric_per_value_dimensions(
metadatum_key
),
},
- lambda x: defaultdict(
- lambda: defaultdict(lambda: None, {}),
- { # combine dicts with gathered information / falling back to defaults
- metric: defaultdict(
- lambda: None,
- {
- value_dim: info
- if info is not None # prio 1: use, if explicitely provided
- else (
- x["defaults_by_metric"][metric]
- if (
- value_dim == "value"
- and x["defaults_by_metric"].get(metric) is not None
- ) # prio 2: only for "value" value dim: possibly use from metric
- else (
- x["defaults_by_value_dimension"].get(
- value_dim, None
- ) # prio 3: from global "value_dimensions_shared"
- )
- )
- for value_dim, info in _update_dict_and_return_it(
- x["defaults_by_value_dimension"].copy(), info_by_val_dim
- ).items()
- },
- )
- for metric, info_by_val_dim in x["actual_per_metric_per_value_dimensions"].items()
- },
- ),
+ _combine_gathered_information,
+ )
+
+
+def _first_not_none(*values: Any) -> Any:
+ return next((value for value in values if value is not None), None)
+
+
+def _combine_gathered_information(gathered: dict) -> defaultdict:
+ """Combine the information gathered by _spec_new_convention
+
+ For each value dimension of each metric, the first information which is not None is used,
+ in the following order:
+ 1. the value dimension itself
+ 2. only for the value dimension "value": the metric
+ 3. "value_dimensions_shared"
+ 4. only for the value dimension "value": the properties the metric inherits
+ """
+ return defaultdict(
+ lambda: defaultdict(lambda: None, {}),
+ {
+ metric: defaultdict(
+ lambda: None,
+ {
+ value_dim: _first_not_none(
+ info,
+ gathered["defaults_by_metric"].get(metric)
+ if value_dim == "value"
+ else None,
+ gathered["defaults_by_value_dimension"].get(value_dim),
+ gathered["inherited_by_metric"].get(metric)
+ if value_dim == "value"
+ else None,
+ )
+ for value_dim, info in _update_dict_and_return_it(
+ gathered["defaults_by_value_dimension"].copy(), info_by_val_dim
+ ).items()
+ },
+ )
+ for metric, info_by_val_dim in gathered[
+ "actual_per_metric_per_value_dimensions"
+ ].items()
+ },
)
@@ -244,7 +265,8 @@ def _spec_older_convention4(metadatum_key: str | Spec) -> Spec:
)
-def spec_by_metric_key(metadatum_key: str | Spec, include_inherited: bool = True) -> Spec:
+def spec_by_metric_key(metadatum_key: str | Spec) -> Spec:
+ """Provides glom spec that extracts a metadatum from the metric objects, keyed by metric"""
return Coalesce(
( # current metdadata convention
{
@@ -254,7 +276,7 @@ def spec_by_metric_key(metadatum_key: str | Spec, include_inherited: bool = True
Check(instance_of=list),
_build_dict_from_iterable_from_key_and_subspec_and_then_proceed_on_result(
S.globals.metric_key,
- Coalesce(of_metric(metadatum_key, include_inherited), default=None),
+ Coalesce(metadatum_key, default=None),
key_as_value=True,
),
),
diff --git a/src/hdhelpers/metadata/helpers.py b/src/hdhelpers/metadata/helpers.py
index 32ee99c..0174f8b 100644
--- a/src/hdhelpers/metadata/helpers.py
+++ b/src/hdhelpers/metadata/helpers.py
@@ -14,7 +14,7 @@
from typing import Any, cast
import pandas as pd
-from glom import Coalesce, GlomError, Spec, glom
+from glom import Coalesce, Spec, glom
import hdhelpers.metadata._specs as _specs
from hdhelpers.metadata._helpers import (
@@ -24,23 +24,23 @@
extract_from_metadata,
follows_current_convention,
get_value_dimension_info,
+ metric_info_by_metric,
singlets_metric_info,
singlets_value_dimension_info,
singlets_value_dimension_info_with_fallbacks,
spec_not_none,
value_dimension_info_with_fallbacks,
)
-from hdhelpers.metadata._specs import spec_by_metric_key
def get_units(
- multitsframe: pd.DataFrame, *, include_inherited: bool = True
+ multitsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets unit of value dimensions in MTSF metrics from Metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
dict[str, dict[str | None] | None]: Dictionary of metrics containing the names of the value dimensions.
@@ -86,13 +86,13 @@ def get_units(
def get_names(
- multitsframe: pd.DataFrame, *, include_inherited: bool = True
+ multitsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
dict[str, str | None]: Dictionary of metrics containing the names. If the name is not present for a metric the corresponding value is None.
@@ -134,13 +134,13 @@ def get_names(
def get_display_names(
- multitsframe: pd.DataFrame, *, include_inherited: bool = True
+ multitsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets display names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the display names.
@@ -188,13 +188,13 @@ def get_display_names(
def get_short_display_names(
- multitsframe: pd.DataFrame, *, include_inherited: bool = True
+ multitsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets short display names of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the short display names.
@@ -245,13 +245,13 @@ def get_short_display_names(
def get_measurements(
- multitsframe: pd.DataFrame, *, include_inherited: bool = True
+ multitsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, defaultdict[str, str | None]]:
"""Gets measurement (type) of the MTSF metrics from the metadata
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, defaultdict[str, str | None]]: Dictionary of metrics containing the measurement (type) of the MTSF metrics.
@@ -285,10 +285,18 @@ def get_metric_info(
In contrast to metadata associated to concrete value dimensions, this
function abstracts access to metadata associated to the underlying metric.
+ The information is looked up in the metric itself, then in its value dimension "value",
+ then in "value_dimensions_shared" and finally in the properties the metric inherits from
+ objects higher up in an asset hierarchy (given under "inherited" in the metric metadata),
+ e.g. the latitude of the plant a sensor belongs to. The latter can be switched off via
+ `include_inherited`. Information provided as null counts as missing. Note that in contrast
+ to this function, the functions for dedicated information like :func:`get_units` or
+ :func:`get_display_names` do not fall back to inherited properties by default.
+
Args:
multitsframe (pd.DataFrame): MTSF with metadata following the convention.
metric_info (str | Spec): Name of information to retrieve. Note that `metric_info` is interpreted as a glom Spec.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the metric, its value dimension "value" nor "value_dimensions_shared" provide the information. Defaults to True.
Returns:
defaultdict[str, Any]: Dictionary, where keys are the entries of the metrics metadata specified via "metric_key" in "dataset_metadata" and values are the entries specified via "metric_info" in the metrics metadata. Defaults to None, also if there is no metric metadata at all.
@@ -337,12 +345,9 @@ def get_metric_info(
True
"""
check_dataframe(multitsframe)
- spec = spec_by_metric_key(metric_info, include_inherited)
- try:
- info_by_metric = glom(multitsframe.attrs, spec)
- except GlomError: # no metric metadata present at all
- info_by_metric = {}
- return defaultdict(lambda: None, info_by_metric)
+ return defaultdict(
+ lambda: None, metric_info_by_metric(multitsframe, metric_info, include_inherited)
+ )
def get_series_info(
@@ -356,7 +361,7 @@ def get_series_info(
Args:
series (pd.Series): Series with metadata following the convention.
value_dim_info (str | Spec): Name of information to retrieve. Note that `value_dim_info` is interpreted as a glom Spec.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to True.
Returns:
Any: Retrieved information defined by `value_dim_info`
@@ -439,12 +444,12 @@ def _series_info_with_fallbacks(
return get_series_info(series, legacy_spec, include_inherited=include_inherited)
-def get_series_unit(series: pd.Series, *, include_inherited: bool = True) -> str | None:
+def get_series_unit(series: pd.Series, *, include_inherited: bool = False) -> str | None:
"""Gets name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
str | None:
@@ -488,12 +493,12 @@ def get_series_unit(series: pd.Series, *, include_inherited: bool = True) -> str
)
-def get_series_name(series: pd.Series, *, include_inherited: bool = True) -> str | None:
+def get_series_name(series: pd.Series, *, include_inherited: bool = False) -> str | None:
"""Gets name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
str | None:
@@ -539,12 +544,12 @@ def get_series_name(series: pd.Series, *, include_inherited: bool = True) -> str
)
-def get_series_display_name(series: pd.Series, *, include_inherited: bool = True) -> str | None:
+def get_series_display_name(series: pd.Series, *, include_inherited: bool = False) -> str | None:
"""Gets display name of the series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
str | None:
@@ -574,13 +579,13 @@ def get_series_display_name(series: pd.Series, *, include_inherited: bool = True
def get_series_short_display_name(
- series: pd.Series, *, include_inherited: bool = True
+ series: pd.Series, *, include_inherited: bool = False
) -> str | None:
"""Gets short display name of the Series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
str | None:
@@ -609,12 +614,12 @@ def get_series_short_display_name(
)
-def get_series_measurement(series: pd.Series, *, include_inherited: bool = True) -> str | None:
+def get_series_measurement(series: pd.Series, *, include_inherited: bool = False) -> str | None:
"""Gets measurement (type) of the Series from metadata
Args:
series (pd.Series): Series with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
str | None:
@@ -655,7 +660,7 @@ def get_singlets_info(
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
value_dim_info (str | Spec): Name of information to retrieve. Note that `value_dim_info` is interpreted as a glom Spec.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to True.
Returns:
defaultdict[str, Any]: Dictionary of value dimensions containing the retrieved information. If the information is not present for a value dimension the corresponding value is None.
@@ -691,13 +696,13 @@ def get_singlets_info(
def get_singlets_units(
- singletsframe: pd.DataFrame, *, include_inherited: bool = True
+ singletsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, str | None]:
"""Gets units of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the units. If the unit is not present for a value dimension the corresponding value is None.
@@ -750,13 +755,13 @@ def get_singlets_units(
def get_singlets_names(
- singletsframe: pd.DataFrame, *, include_inherited: bool = True
+ singletsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, str | None]:
"""Gets names of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the names. If the name is not present for a value dimension the corresponding value is None.
@@ -792,7 +797,7 @@ def get_singlets_names(
def get_singlets_display_names(
- singletsframe: pd.DataFrame, *, include_inherited: bool = True
+ singletsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, str | None]:
"""Gets display names of the value dimensions of a SingleTSFrame from metadata
@@ -803,7 +808,7 @@ def get_singlets_display_names(
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the display names. If neither display name nor name is present for a value dimension the corresponding value is None.
@@ -841,7 +846,7 @@ def get_singlets_display_names(
def get_singlets_short_display_names(
- singletsframe: pd.DataFrame, *, include_inherited: bool = True
+ singletsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, str | None]:
"""Gets short display names of the value dimensions of a SingleTSFrame from metadata
@@ -852,7 +857,7 @@ def get_singlets_short_display_names(
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the short display names. If none of short display name, display name and name is present for a value dimension the corresponding value is None.
@@ -893,13 +898,13 @@ def get_singlets_short_display_names(
def get_singlets_measurements(
- singletsframe: pd.DataFrame, *, include_inherited: bool = True
+ singletsframe: pd.DataFrame, *, include_inherited: bool = False
) -> defaultdict[str, str | None]:
"""Gets measurements (types) of the value dimensions of a SingleTSFrame from metadata
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor "value_dimensions_shared" provide the information. Defaults to False, since this information should be provided for the metric itself.
Returns:
defaultdict[str, str | None]: Dictionary of value dimensions containing the measurements. If "measurement" is not given for a value dimension the corresponding value is None.
@@ -936,12 +941,14 @@ def get_singlets_metric_info(
"""Gets metadata associated to the single metric of a SingleTSFrame
Counterpart of :func:`get_metric_info` for SingleTSFrames: instead of a dictionary keyed
- by metric this directly returns the requested information for the one metric.
+ by metric this directly returns the requested information for the one metric. Like
+ :func:`get_metric_info`, it falls back to the value dimension "value", to
+ "value_dimensions_shared" and finally to the properties the metric inherits.
Args:
singletsframe (pd.DataFrame): STSF with metadata following the convention.
metric_info (str | Spec): Name of information to retrieve. Note that `metric_info` is interpreted as a glom Spec.
- include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata), if the metric itself does not provide the information. Defaults to True.
+ include_inherited (bool): Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under "inherited" in the metric metadata) as last resort, i.e. if neither the metric, its value dimension "value" nor "value_dimensions_shared" provide the information. Defaults to True.
Returns:
Any: Retrieved information defined by `metric_info`, or None if it cannot be determined - which includes the case of absent metric metadata.
diff --git a/tests/metadata/test_metadata.py b/tests/metadata/test_metadata.py
index e073ae8..4e9db3d 100644
--- a/tests/metadata/test_metadata.py
+++ b/tests/metadata/test_metadata.py
@@ -554,12 +554,18 @@ def test_get_value_dimension_info_falls_back_to_inherited_for_value_only(
assert get_singlets_info(dataframe, "Latitude")["state"] is None
assert get_singlets_info(dataframe, "Latitude", include_inherited=False)["value"] is None
- # inherited metadata of the metric beats the dataset wide value_dimensions_shared
- assert get_units(dataframe)["sensor"]["value"] == "inherited unit"
- assert get_units(dataframe)["other"]["value"] == "shared unit"
- assert get_units(dataframe, include_inherited=False)["sensor"]["value"] == "shared unit"
- assert get_singlets_units(dataframe)["value"] == "inherited unit"
- assert get_singlets_units(dataframe, include_inherited=False)["value"] == "shared unit"
+ # inherited metadata is the last resort, after the dataset wide value_dimensions_shared
+ assert get_units(dataframe, include_inherited=True)["sensor"]["value"] == "shared unit"
+ assert get_units(dataframe, include_inherited=True)["other"]["value"] == "shared unit"
+ assert get_singlets_units(dataframe, include_inherited=True)["value"] == "shared unit"
+
+ del dataframe.attrs["value_dimensions_shared"]
+ assert get_units(dataframe, include_inherited=True)["sensor"]["value"] == "inherited unit"
+ assert get_singlets_units(dataframe, include_inherited=True)["value"] == "inherited unit"
+
+ # the functions for dedicated information do not fall back to inherited by default
+ assert get_units(dataframe)["sensor"]["value"] is None
+ assert get_singlets_units(dataframe)["value"] is None
def test_get_series_info_falls_back_to_inherited(attrs_with_inherited):
@@ -571,8 +577,12 @@ def test_get_series_info_falls_back_to_inherited(attrs_with_inherited):
assert get_series_info(series, "Latitude") == 51.43
assert get_series_info(series, "Longitude") == 7.58
assert get_series_info(series, "Latitude", include_inherited=False) is None
- assert get_series_unit(series) == "inherited unit"
- assert get_series_unit(series, include_inherited=False) == "shared unit"
+ assert get_series_unit(series) == "shared unit"
+ assert get_series_unit(series, include_inherited=True) == "shared unit"
+
+ del series.attrs["value_dimensions_shared"]
+ assert get_series_unit(series) is None
+ assert get_series_unit(series, include_inherited=True) == "inherited unit"
def test_own_metadata_beats_inherited_metadata_in_fallback_chains(attrs_with_inherited):
@@ -581,9 +591,35 @@ def test_own_metadata_beats_inherited_metadata_in_fallback_chains(attrs_with_inh
series.attrs = attrs_with_inherited
# own "name" beats inherited "display_name"
- assert get_series_display_name(series) == "sensor name"
- assert get_series_short_display_name(series) == "sensor name"
+ assert get_series_display_name(series, include_inherited=True) == "sensor name"
+ assert get_series_short_display_name(series, include_inherited=True) == "sensor name"
del series.attrs["metrics"][0]["name"]
- assert get_series_display_name(series) == "plant display name"
- assert get_series_display_name(series, include_inherited=False) is None
+ assert get_series_display_name(series, include_inherited=True) == "plant display name"
+ assert get_series_display_name(series) is None
+
+
+def test_get_metric_info_prefers_value_dimension_and_shared_over_inherited(
+ attrs_with_inherited,
+):
+ """Order of get_metric_info: metric, value dimension "value", shared, inherited"""
+ from hdhelpers.metadata import get_metric_info, get_units
+
+ dataframe = pd.DataFrame()
+ dataframe.attrs = attrs_with_inherited
+
+ assert get_metric_info(dataframe, "unit")["sensor"] == "shared unit"
+
+ dataframe.attrs["metrics"][0]["value_dimensions"][0]["unit"] = "m"
+ assert get_metric_info(dataframe, "unit")["sensor"] == "m"
+ assert get_units(dataframe)["sensor"]["value"] == "m"
+
+ dataframe.attrs["metrics"][0]["unit"] = "kg"
+ assert get_metric_info(dataframe, "unit")["sensor"] == "kg"
+
+ del dataframe.attrs["value_dimensions_shared"]
+ del dataframe.attrs["metrics"][0]["unit"]
+ dataframe.attrs["metrics"][0]["value_dimensions"][0]["unit"] = None
+ assert get_metric_info(dataframe, "unit")["sensor"] == "inherited unit"
+ assert get_metric_info(dataframe, "unit", include_inherited=False)["sensor"] is None
+ assert get_singlets_metric_info(dataframe, "unit") == "inherited unit"
From 28e729b42535ed7dd871ef1cc4ae457184401d69 Mon Sep 17 00:00:00 2001
From: stewit <>
Date: Wed, 21 Oct 2026 11:11:34 +0200
Subject: [PATCH 04/15] changelog and documentation changes
---
CHANGELOG.md | 6 +-
docs/_sources/first_steps.rst.txt | 79 +++++++++++++++++------
docs/_sources/index.rst.txt | 3 +-
docs/first_steps.html | 75 ++++++++++++++++------
docs/index.html | 103 ++++++++++++++++++------------
docs/searchindex.js | 2 +-
sphinx/source/first_steps.rst | 79 +++++++++++++++++------
sphinx/source/index.rst | 3 +-
8 files changed, 248 insertions(+), 102 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3e93ee..2a5876e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
## [Unreleased]
-- metric metadata accessors fall back to the properties a metric inherits (under "inherited" in the metric metadata of the current convention), can be deactivated via keyword argument include_inherited=False
+- metadata accessors can fall back to the properties a metric inherits from objects higher up in an asset hierarchy (under "inherited" in the metric metadata of the current convention), only as last resort after "value_dimensions_shared". This is the default for get_metric_info, get_singlets_metric_info, get_series_info and get_singlets_info. The functions for dedicated information (units, names, display names, short display names and measurements) only do so with keyword argument include_inherited=True, since an inherited property with the same label usually does not describe the metric itself
+- get_metric_info and get_singlets_metric_info fall back to the value dimension "value" and to "value_dimensions_shared" if the metric itself does not provide the information
+- fix: (short) display names of metadata following the current convention treat null as missing and look up each field on the value dimension, the metric and "value_dimensions_shared" before falling back to the next field. So e.g. a display name of the metric wins over the name of its value dimension
+- fix: get_metric_info returns None entries instead of raising if the metadata contains no metric information
+- the series accessors use the only metric if "single_metric" is missing in the dataset metadata
## [0.0.9] - 2026-09-10
- singletsframe support
diff --git a/docs/_sources/first_steps.rst.txt b/docs/_sources/first_steps.rst.txt
index c2a396f..1f90038 100644
--- a/docs/_sources/first_steps.rst.txt
+++ b/docs/_sources/first_steps.rst.txt
@@ -5,50 +5,91 @@ First steps
How to get metadata with hdhelpers?
===================================
-Let's say we want to retrieve the metadata of a timeseries.
+Let's say we want to retrieve the metadata of a timeseries, which follows the
+`hetida designer metadata conventions`_.
In hetida designer this series can be represented as json for *direct provisioning*
.. code-block:: json
{
- "__hd_wrapped_data_object__":"SERIES",
+ "__hd_wrapped_data_object__": "SERIES",
"__metadata__": {
- "single_metric_dataset_metadata": {
- "ref_interval_end_timestamp":"2020-01-01T08:20:00.000Z",
- "ref_interval_start_timestamp": "2020-01-01T08:10:00.000Z"
+ "dataset_metadata": {
+ "metric_key": "external_id",
+ "single_metric": "water.north.level",
+ "ref_interval_start_timestamp": "2020-01-01T08:10:00+00:00",
+ "ref_interval_end_timestamp": "2020-01-01T08:20:00+00:00",
+ "ref_interval_type": "closed"
},
- "single_metric_metadata": {
- "structured_metadata": {
- "metric": {
- "short_display_name": "Water Level",
- "unit": "cm"
- }
+ "metrics": [
+ {
+ "external_id": "water.north.level",
+ "name": "Water level north",
+ "short_display_name": "Water Level",
+ "value_dimensions": [{"column": "value", "unit": "cm"}],
+ "inherited": {"latitude": 51.43, "longitude": 7.58}
}
- }
+ ]
},
"__data__": {
"2020-01-01T08:10:00+00:00": 1,
"2020-01-01T08:15:00+00:00": 2,
"2020-01-01T08:16:00+00:00": 3,
- "2020-01-01T08:17:00+00:00": 4,
+ "2020-01-01T08:17:00+00:00": 4
}
}
-We can retrieve the name and unit of the series with the following code
+We can retrieve the name, unit and latitude of the series with the following code
.. code-block:: python
- from hdhelpers.metadata import get_series_name, get_series_unit
+ from hdhelpers.metadata import get_series_info, get_series_name, get_series_unit
def main(*, series):
# entrypoint function for this component
# ***** DO NOT EDIT LINES ABOVE *****
- name = get_series_name(series)
- unit = get_series_unit(series)
+ name = get_series_name(series) # "Water level north"
+ unit = get_series_unit(series) # "cm"
+ latitude = get_series_info(series, "latitude") # 51.43, inherited from the hierarchy
...
+.. _hetida designer metadata conventions: https://hetida.github.io/hetida-designer/user_guide/attached_metadata/
+
+Where does the information come from?
+=====================================
+
+The metadata conventions allow to provide information on different levels. The functions
+look for it in the following order and use the first information which is not null. Information
+provided as null counts as missing.
+
+**Dedicated information** like the unit, name, display name, short display name and measurement
+(e.g. ``get_units``, ``get_series_unit``, ``get_singlets_units``) is looked up per value dimension:
+
+1. in the value dimension itself,
+2. only for the value dimension ``"value"``: in the metric,
+3. in ``"value_dimensions_shared"``.
+
+For (short) display names, each field is looked up on all of these levels before falling back to
+the next field: short display name, then display name, then name. So e.g. a display name provided
+for the metric wins over the name of its value dimension.
+
+**Other information** is looked up by ``get_metric_info`` and ``get_singlets_metric_info``
+
+1. in the metric itself,
+2. in its value dimension ``"value"``,
+3. in ``"value_dimensions_shared"``,
+4. in the properties the metric inherits from objects higher up in an asset hierarchy (given under
+ ``"inherited"`` in the metric metadata), e.g. the latitude of the plant a sensor belongs to.
+
+``get_series_info`` and ``get_singlets_info`` use the order of the dedicated information, followed
+by the inherited properties as last resort.
+
+The inherited properties can be switched off via the keyword argument ``include_inherited=False``.
+In contrast, the functions for dedicated information do not use them by default, since an
+inherited property with the same label usually does not describe the metric itself, e.g. a
+property "unit" of a plant. Pass ``include_inherited=True`` to use them as last resort anyway.
How to use hdhelpers for plotting? (tbd)
@@ -78,10 +119,10 @@ Our component code might look like this to plot the timeseries accordingly:
fig = go.Figure([go.Scatter(x=series.index, y=series.values, marker={"color": resolve_color("ki.vision")})])
- start, end = get_queried_interval(series=series)
+ start, end = get_queried_interval(series)
fig.update_xaxes(range=(start, end))
- full_title = get_series_name(series=series, default_title="Level")
+ full_title = get_series_name(series) or "Level"
fig.update_layout(yaxis_title=full_title)
set_dt_ticks(fig)
diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt
index b6ca2c0..cf00ef9 100644
--- a/docs/_sources/index.rst.txt
+++ b/docs/_sources/index.rst.txt
@@ -14,12 +14,13 @@ hdhelpers is a package designed for and included in the standard installation of
It contains functions that streamline plotting components, especially those that are used in the `hetida platform`_, by
-* accessing series metadata that complies with the hetida platform metadata scheme,
+* accessing metadata of series, singletsframes and multitsframes that complies with the `hetida designer metadata conventions`_,
* aditional helper functions like adjusting the timezone of timestamps, series, and dataframes,
* accessing metadata that the hetida platform writes into the hetida designer's *plot_target_settings* context variable (tbd),
* providing toggleable standardized styling options and json serialization for plotly plots (tbd).
.. _hetida designer: https://github.com/hetida/hetida-designer
+.. _hetida designer metadata conventions: https://hetida.github.io/hetida-designer/user_guide/attached_metadata/
.. _hetida platform: https://hetida.io/
Further Information
diff --git a/docs/first_steps.html b/docs/first_steps.html
index 5796262..4c7c9d2 100644
--- a/docs/first_steps.html
+++ b/docs/first_steps.html
@@ -44,47 +44,83 @@ Let’s say we want to retrieve the metadata of a timeseries.
+ Let’s say we want to retrieve the metadata of a timeseries, which follows the
+hetida designer metadata conventions.
In hetida designer this series can be represented as json for direct provisioning We can retrieve the name and unit of the series with the following code We can retrieve the name, unit and latitude of the series with the following code The metadata conventions allow to provide information on different levels. The functions
+look for it in the following order and use the first information which is not null. Information
+provided as null counts as missing. Dedicated information like the unit, name, display name, short display name and measurement
+(e.g. in the value dimension itself, only for the value dimension in For (short) display names, each field is looked up on all of these levels before falling back to
+the next field: short display name, then display name, then name. So e.g. a display name provided
+for the metric wins over the name of its value dimension. Other information is looked up by in the metric itself, in its value dimension in in the properties the metric inherits from objects higher up in an asset hierarchy (given under
+ The inherited properties can be switched off via the keyword argument Let’s say we want to plot the same timeseries above using hdhelpers functionalities.
@@ -107,10 +143,10 @@ It contains functions that streamline plotting components, especially those that are used in the hetida platform, by accessing series metadata that complies with the hetida platform metadata scheme, accessing metadata of series, singletsframes and multitsframes that complies with the hetida designer metadata conventions, aditional helper functions like adjusting the timezone of timestamps, series, and dataframes, accessing metadata that the hetida platform writes into the hetida designer’s plot_target_settings context variable (tbd), providing toggleable standardized styling options and json serialization for plotly plots (tbd). Gets display names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Dictionary of metrics containing the display names.
-If the display name of the metrics is not present it returns the result of defaultdict[str, defaultdict[str, str | None]] Gets measurement (type) of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. multitsframe (pd.DataFrame) – MTSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the metric, its value dimension “value” nor “value_dimensions_shared” provide the information. Defaults to True. Dictionary, where keys are the entries of the metrics metadata specified via “metric_key” in “dataset_metadata” and values are the entries specified via “metric_info” in the metrics metadata Dictionary, where keys are the entries of the metrics metadata specified via “metric_key” in “dataset_metadata” and values are the entries specified via “metric_info” in the metrics metadata. Defaults to None, also if there is no metric metadata at all. defaultdict[str, Any] Gets names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets display name of the series from metadata series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. series (pd.Series) – Series with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to True. Gets measurement (type) of the Series from metadata series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets name of the series from metadata series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets short display name of the Series from metadata series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets name of the series from metadata series (pd.Series) – Series with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets short display names of the MTSF metrics from the metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Dictionary of metrics containing the short display names.
-If the short display name of the metrics is not present it returns the result of defaultdict[str, defaultdict[str, str | None]] Gets display names of the value dimensions of a SingleTSFrame from metadata Falls back to the name of the value dimension if no display name is given. Falls back to the name of the value dimension if no display name is given. The display name
+is looked up on the value dimension, on the metric (for the value dimension “value”) and in
+“value_dimensions_shared” before falling back to the name. Fields provided as null count as
+missing. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. singletsframe (pd.DataFrame) – STSF with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to True. Gets measurements (types) of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets metadata associated to the single metric of a SingleTSFrame Counterpart of singletsframe (pd.DataFrame) – STSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the metric, its value dimension “value” nor “value_dimensions_shared” provide the information. Defaults to True. Gets names of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets short display names of the value dimensions of a SingleTSFrame from metadata Falls back to the display name and then to the name of the value dimension if no short
-display name is given. singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets units of the value dimensions of a SingleTSFrame from metadata singletsframe (pd.DataFrame) – STSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. Gets unit of value dimensions in MTSF metrics from Metadata multitsframe (pd.DataFrame) – MTSF with metadata following the convention. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata), if the metric itself does not provide the information. Defaults to True. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to False, since this information should be provided for the metric itself. The information requested from We can retrieve the nested information with the following code For a MULTITSFRAME, The fallbacks described above apply to the whole path, e.g. Let’s say we want to plot the same timeseries above using hdhelpers functionalities.
@@ -171,6 +225,7 @@ multitsframe (pd.DataFrame) – MTSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. metric_info (str | Spec) – Information to retrieve, interpreted as a glom Spec: e.g. a field name, a dotted path like “sensor.manufacturer” into sub dictionaries or any other glom Spec. See “How to query nested information?” in the first steps. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the metric, its value dimension “value” nor “value_dimensions_shared” provide the information. Defaults to True. A dotted path reaches into sub dictionaries: series (pd.Series) – Series with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. value_dim_info (str | Spec) – Information to retrieve, interpreted as a glom Spec: e.g. a field name, a dotted path like “sensor.manufacturer” into sub dictionaries or any other glom Spec. See “How to query nested information?” in the first steps. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to True. singletsframe (pd.DataFrame) – STSF with metadata following the convention. value_dim_info (str | Spec) – Name of information to retrieve. Note that value_dim_info is interpreted as a glom Spec. value_dim_info (str | Spec) – Information to retrieve, interpreted as a glom Spec: e.g. a field name, a dotted path like “sensor.manufacturer” into sub dictionaries or any other glom Spec. See “How to query nested information?” in the first steps. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the value dimension, the metric nor “value_dimensions_shared” provide the information. Defaults to True. singletsframe (pd.DataFrame) – STSF with metadata following the convention. metric_info (str | Spec) – Name of information to retrieve. Note that metric_info is interpreted as a glom Spec. metric_info (str | Spec) – Information to retrieve, interpreted as a glom Spec: e.g. a field name, a dotted path like “sensor.manufacturer” into sub dictionaries or any other glom Spec. See “How to query nested information?” in the first steps. include_inherited (bool) – Whether to fall back to the properties a metric inherits from objects higher up in an asset hierarchy (given under “inherited” in the metric metadata) as last resort, i.e. if neither the metric, its value dimension “value” nor “value_dimensions_shared” provide the information. Defaults to True.Functions
+
Functions
+
Functions
Functions
Functions
+
Functions
+
Functions
Functions
Functions
+
Functions
+
Functions
+
Functions
+
Functions
+
Functions
+
Functions
Functions
Functions
+
Functions
get_metric_info() for SingleTSFrames: instead of a dictionary keyed
by metric this directly returns the requested information for the one metric.Functions
Functions
+
Functions
+
Functions
+
Functions
+
Navigation
First steps¶
How to get metadata with hdhelpers?¶
-{
- "__hd_wrapped_data_object__":"SERIES",
+ "__hd_wrapped_data_object__": "SERIES",
"__metadata__": {
- "single_metric_dataset_metadata": {
- "ref_interval_end_timestamp":"2020-01-01T08:20:00.000Z",
- "ref_interval_start_timestamp": "2020-01-01T08:10:00.000Z"
+ "dataset_metadata": {
+ "metric_key": "external_id",
+ "single_metric": "water.north.level",
+ "ref_interval_start_timestamp": "2020-01-01T08:10:00+00:00",
+ "ref_interval_end_timestamp": "2020-01-01T08:20:00+00:00",
+ "ref_interval_type": "closed"
},
- "single_metric_metadata": {
- "structured_metadata": {
- "metric": {
- "short_display_name": "Water Level",
- "unit": "cm"
- }
+ "metrics": [
+ {
+ "external_id": "water.north.level",
+ "name": "Water level north",
+ "short_display_name": "Water Level",
+ "value_dimensions": [{"column": "value", "unit": "cm"}],
+ "inherited": {"latitude": 51.43, "longitude": 7.58}
}
- }
+ ]
},
"__data__": {
"2020-01-01T08:10:00+00:00": 1,
"2020-01-01T08:15:00+00:00": 2,
"2020-01-01T08:16:00+00:00": 3,
- "2020-01-01T08:17:00+00:00": 4,
+ "2020-01-01T08:17:00+00:00": 4
}
}
from hdhelpers.metadata import get_series_name, get_series_unit
+
from hdhelpers.metadata import get_series_info, get_series_name, get_series_unit
def main(*, series):
# entrypoint function for this component
# ***** DO NOT EDIT LINES ABOVE *****
- name = get_series_name(series)
- unit = get_series_unit(series)
+ name = get_series_name(series) # "Water level north"
+ unit = get_series_unit(series) # "cm"
+ latitude = get_series_info(series, "latitude") # 51.43, inherited from the hierarchy
...
Where does the information come from?¶
+get_units, get_series_unit, get_singlets_units) is looked up per value dimension:
+
+"value": in the metric,"value_dimensions_shared".get_metric_info and get_singlets_metric_info
+
+"value","value_dimensions_shared","inherited" in the metric metadata), e.g. the latitude of the plant a sensor belongs to.get_series_info and get_singlets_info use the order of the dedicated information, followed
+by the inherited properties as last resort.include_inherited=False.
+In contrast, the functions for dedicated information do not use them by default, since an
+inherited property with the same label usually does not describe the metric itself, e.g. a
+property “unit” of a plant. Pass include_inherited=True to use them as last resort anyway.How to use hdhelpers for plotting? (tbd)¶
How to use hdhelpers for plotting? (tbd)fig = go.Figure([go.Scatter(x=series.index, y=series.values, marker={"color": resolve_color("ki.vision")})])
- start, end = get_queried_interval(series=series)
+ start, end = get_queried_interval(series)
fig.update_xaxes(range=(start, end))
- full_title = get_series_name(series=series, default_title="Level")
+ full_title = get_series_name(series) or "Level"
fig.update_layout(yaxis_title=full_title)
set_dt_ticks(fig)
@@ -134,6 +170,7 @@
Table of Contents
Introductionhetida designer.
-
Further InformationFirst steps
@@ -74,18 +75,21 @@ Functions
hdhelpers.metadata.get_names().hdhelpers.metadata.get_names().
+For metadata following the current convention, the display name is looked up on the value dimension, on
+the metric (for the value dimension “value”) and in “value_dimensions_shared” before falling back to the
+name. Fields provided as null count as missing.
Functions
Functions
get_units() or
+get_display_names() do not fall back to inherited properties by default.
Functions
Functions
Functions
Functions
Functions
Functions
Functions
Functions
hdhelpers.metadata.get_display_names().hdhelpers.metadata.get_display_names().
+For metadata following the current convention, each of short display name, display name and name is
+looked up on the value dimension, on the metric (for the value dimension “value”) and in
+“value_dimensions_shared” before falling back to the next one. Fields provided as null count as missing.
Functions
Functions
Functions
Functionshdhelpers.metadata.get_singlets_metric_info(singletsframe, metric_info, *, include_inherited=True)¶
get_metric_info() for SingleTSFrames: instead of a dictionary keyed
-by metric this directly returns the requested information for the one metric.get_metric_info(), it falls back to the value dimension “value”, to
+“value_dimensions_shared” and finally to the properties the metric inherits.
Functions
Functions
Functions
Functions
Where does the information come from?include_inherited=True to use them as last resort anyway.
How to query nested information?¶
+get_metric_info, get_singlets_metric_info,
+get_series_info and get_singlets_info is interpreted as a glom spec. A string is a
+path in which dots separate the keys, so it also reaches into sub dictionaries. Let’s say the
+metric of the series above additionally provides comments and information on its sensor (the
+conventions allow additional fields at every level), and the label of one of its inherited
+properties contains a dot:{
+ "external_id": "water.north.level",
+ "name": "Water level north",
+ "sensor": {
+ "manufacturer": "Acme",
+ "calibration": {"date": "2026-03-01", "offset": 0.02}
+ },
+ "comments": [
+ {"content": "Gauge maintenance"},
+ {"content": "Sensor replaced"}
+ ],
+ "value_dimensions": [{"column": "value", "unit": "cm"}],
+ "inherited": {"latitude": 51.43, "operator.name": "Ruhrverband"}
+}
+
from glom import Path
+from hdhelpers.metadata import get_series_info
+
+def main(*, series):
+ # entrypoint function for this component
+ # ***** DO NOT EDIT LINES ABOVE *****
+
+ manufacturer = get_series_info(series, "sensor.manufacturer") # "Acme"
+ calibration_date = get_series_info(series, "sensor.calibration.date") # "2026-03-01"
+ latitude = get_series_info(series, "inherited.latitude") # 51.43, never the metric's own
+ firmware = get_series_info(series, "sensor.firmware") # None, not provided
+
+ # any other glom spec, e.g. the content of each comment
+ comments = get_series_info(series, ("comments", ["content"]))
+ # ["Gauge maintenance", "Sensor replaced"]
+
+ # a key which itself contains dots requires a glom Path
+ operator = get_series_info(series, Path("operator.name")) # "Ruhrverband"
+
+ ...
+
get_metric_info(multitsframe, "sensor.manufacturer") provides the
+manufacturer keyed by metric accordingly."sensor.manufacturer" is looked
+up in the metric first, then in its value dimension "value" and so on. A path which is not
+present yields None instead of an error. Note that a plain string like "operator.name" is
+always split at its dots, so it does not find the inherited property "operator.name".How to use hdhelpers for plotting? (tbd)¶
Table of Contents
Further InformationFirst steps
@@ -174,7 +175,7 @@ FunctionsParameters:
Functions... "external_id": "external_first",
... "unit": "m",
... "display_name": "first display name",
+... "sensor": {"manufacturer": "Acme"},
... "value_dimensions": [
... {
... "column": "temp",
@@ -226,6 +228,14 @@
FunctionsTrue
>>> result = get_metric_info(multitsframe, "sensor.manufacturer")
+>>> result["first"]
+'Acme'
+>>> result["second"] is None
+True
+
@@ -355,7 +365,7 @@
FunctionsParameters:
FunctionsParameters:
FunctionsParameters:
How to query nested information?"value" and so on. A path which is not
present yields None instead of an error. Note that a plain string like
"operator.name" is
always split at its dots, so it does not find the inherited property "operator.name".
A spec may also transform the information, e.g. ("name", str.upper). Since information
+provided as null counts as missing, such a spec may receive null: on the metric itself and, if
+the metric does not provide the information, on the levels it falls back to. Errors raised by
+the spec are not caught. So either let it handle None, or rather transform the information
+after retrieving it.
Entries which do not adhere to the conventions are skipped: metrics which are no objects or
+whose field named by the metric key is no string, and value dimensions which are no objects or
+whose column is no string. "value_dimensions" and "value_dimensions_shared" which are no
+lists count as empty, a metric key which is no string as missing, i.e. "id" is used.
Metadata may provide a default_timezone for each metric and for the dataset metadata: the
+time zone the timeseries should be evaluated in by default. It belongs to the data, e.g. it is
+the time zone of the plant a sensor belongs to, and it is meant for computations:
+Components evaluating timeseries in calendar intervals should respect it. E.g. a component
+aggregating daily values should bucket the days from midnight to midnight of this time zone,
+so its results depend on it.
It is not meant for displaying timestamps. That is what the time zone of the plot target
+settings is for, which the hetida platform provides for the view, e.g. the time zone of a
+dashboard or of the user looking at a plot. modify_timezone uses it if no to_timezone
+is given. Both may differ: A report on a plant in Germany may aggregate days in
+"Europe/Berlin" and nevertheless show its timestamps to a viewer in New York in
+"America/New_York".
get_series_default_timezone, get_singlets_default_timezone and
+get_default_timezones (keyed by metric) provide the default time zone of a metric. If the
+metric does not provide one, they fall back to the one of the dataset metadata, which applies
+to all metrics without their own. If neither provides one, they return None. The time zone is
+an IANA time zone name like "Europe/Berlin", which respects daylight saving time, or a
+fixed UTC offset like "+02:00". pandas accepts both.
from hdhelpers.helpers import modify_timezone
+from hdhelpers.metadata import get_series_default_timezone
+
+def main(*, series):
+ # entrypoint function for this component
+ # ***** DO NOT EDIT LINES ABOVE *****
+
+ # compute: each day starts at midnight of the default time zone of the data,
+ # e.g. at midnight in "Europe/Berlin", and has 23 or 25 hours when switching
+ # from or to daylight saving time
+ timezone = get_series_default_timezone(series) or "UTC"
+ daily_sums = modify_timezone(series, to_timezone=timezone).resample("1D").sum()
+
+ # view: show the timestamps in the time zone of the plot target settings
+ daily_sums = modify_timezone(daily_sums)
+
+ ...
+Note the explicit fallback or "UTC" in the example: Without it, modify_timezone would
+use the time zone of the plot target settings if the metadata provide no default time zone,
+so the results of the computation would depend on who looks at them.
For a MULTITSFRAME, each metric may have its own default time zone:
+from hdhelpers.metadata import get_default_timezones
+
+def main(*, multitsframe):
+ # entrypoint function for this component
+ # ***** DO NOT EDIT LINES ABOVE *****
+
+ timezones = get_default_timezones(multitsframe)
+ daily_sums = {
+ metric: group.set_index("timestamp")["value"]
+ .tz_convert(timezones[metric] or "UTC")
+ .resample("1D")
+ .sum()
+ for metric, group in multitsframe.groupby("metric")
+ }
+
+ ...
+Let’s say we want to plot the same timeseries above using hdhelpers functionalities. @@ -235,6 +297,7 @@