|
1 | 1 | """Utilities for saving structures to disk."""
|
2 | 2 | import csv
|
3 | 3 | import os
|
| 4 | +from typing import Union |
4 | 5 |
|
5 | 6 | import numpy as np
|
6 | 7 |
|
@@ -134,3 +135,82 @@ def save_dicts_to_csv(filename, in_dicts, do_sort=True):
|
134 | 135 |
|
135 | 136 | except Exception as e:
|
136 | 137 | log_exception(e, "When {} saving to csv".format(filename))
|
| 138 | + |
| 139 | + |
| 140 | +def data_dict_from_attr_list( |
| 141 | + input_item, attr_list: list, friendly_names: Union["list[str]", None] = None |
| 142 | +): |
| 143 | + """ |
| 144 | + From a list of attributes, return a dictionary. |
| 145 | +
|
| 146 | + Each item in attr_list should be a tuple containing |
| 147 | + attributes, keys, or None. |
| 148 | + The elements of the tuple are then accessed iteratively, like |
| 149 | + item.tuple_el1.tuple_el2... |
| 150 | + If the element is an attribute, it is directly retrieved. |
| 151 | + If the element is a key in a dictionary, that is retrieved. |
| 152 | + If the element is None, it indicates a break. |
| 153 | + (This last option can be used to get functions without calling them, |
| 154 | + or to get a full dictionary instead of pulling out the key, value pairs.) |
| 155 | +
|
| 156 | + The output also depends on what is retrieved, if a dictionary or a function. |
| 157 | + Functions are called with no arguments. |
| 158 | + Dictionaries have key value pairs, that are stored in the output dictionary. |
| 159 | + Both of these can be avoided by passing the last element of the tuple as None. |
| 160 | +
|
| 161 | + As an example: |
| 162 | + item.results = {"addition": {"1 + 1": 2}} |
| 163 | + item.data.running_speed = [0.5, 1.4, 1.5] |
| 164 | + attr_list = [("results", "addition", None)] |
| 165 | + this_fn(attr_list) = {"results_addition": {"1 + 1" = 2}} |
| 166 | + attr_list = [("results", "addition"), ("data", "running_speed")] |
| 167 | + this_fn(attr_list) = {"1 + 1": 2, "data_running_speed": [0.5, 1.4, 1.5]} |
| 168 | +
|
| 169 | + Parameters |
| 170 | + ---------- |
| 171 | + input_item : Any |
| 172 | + The item to get data attributes from. |
| 173 | + attr_list : list |
| 174 | + The list of attributes to retrieve. |
| 175 | + friendly_names : list of str, optional |
| 176 | + What to name each retrieved attribute, (default None). |
| 177 | + Must be the same size as attr_list or None. |
| 178 | +
|
| 179 | + Returns |
| 180 | + ------- |
| 181 | + dict |
| 182 | + The retrieved attributes. |
| 183 | +
|
| 184 | + Raises |
| 185 | + ------ |
| 186 | + ValueError |
| 187 | + attr_list and friendly_names are not the same size. |
| 188 | +
|
| 189 | + """ |
| 190 | + if friendly_names is not None and len(friendly_names) != len(attr_list): |
| 191 | + raise ValueError("friendly_names and attr_list must be the same length") |
| 192 | + |
| 193 | + data_out = {} |
| 194 | + for i, attr_tuple in enumerate(attr_list): |
| 195 | + item = input_item |
| 196 | + for a in attr_tuple: |
| 197 | + if a is None: |
| 198 | + break |
| 199 | + item = ( |
| 200 | + getattr(item, a) if isinstance(a, str) and hasattr(item, a) else item[a] |
| 201 | + ) |
| 202 | + if callable(item): |
| 203 | + item = item() |
| 204 | + if isinstance(item, dict): |
| 205 | + for key, value in item.items(): |
| 206 | + data_out[key] = value |
| 207 | + else: |
| 208 | + non_none_attrs = [x for x in attr_tuple if x is not None] |
| 209 | + if friendly_names is None: |
| 210 | + key = "_".join(non_none_attrs) |
| 211 | + else: |
| 212 | + key = friendly_names[i] |
| 213 | + if key is None: |
| 214 | + key = "_".join(non_none_attrs) |
| 215 | + data_out[key] = item |
| 216 | + return data_out |
0 commit comments