Skip to content

Commit fa13f13

Browse files
Merge pull request #7 from StructuralPython/features/diversify_static_inputs
Features/diversify static inputs
2 parents 4bf9400 + 5040b47 commit fa13f13

File tree

2 files changed

+84
-16
lines changed

2 files changed

+84
-16
lines changed

src/xl_engine/excel_engine.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313

1414
def excel_runner(
1515
xlsx_filepath,
16-
static_inputs: dict[str, list],
16+
static_inputs: dict[str, list[float | int | str]] | list[dict[str, float | int | str]],
1717
dynamic_inputs: dict[str, dict[str, float]],
1818
success_conditions: dict[str, callable],
1919
static_identifier_keys: Optional[list[str]] = None,
2020
result_labels: Optional[dict[str, str]] = None,
21+
static_input_maps: Optional[dict[str, str]] = None,
2122
save_dir: Optional[str] = None,
2223
sheet_idx: int = 0,
2324
) -> None:
@@ -29,13 +30,16 @@ def excel_runner(
2930
True for that iteration, then the workbook with that iteration's inputs is saved
3031
to disk.
3132
32-
'static_inputs': a dictionary keyed by either data labels or cell references. The
33-
values are lists of values (like columns in a table). Each index of the values
34-
represent the data used for one iteration. For keys containing cell references,
33+
'static_inputs': a dictionary where each key is a cell reference and the values are a
34+
list of values to place in the cell OR a list of dictionaries, where each key is a
35+
cell reference and the value is the value to be placed.
36+
Each index in the list (whether the list is the inner or outer collection type)
37+
represents the data used for one iteration. For keys containing cell references,
3538
the cell reference is used as a static input value to the workbook. For keys
3639
that are not cell references, their values are accessible by 'static_identifier_keys',
3740
which will be used for creating the unique filename in the event that the
38-
'save_conditions' are satisfied.
41+
'save_conditions' are satisfied. If the key is not in 'static_identifier_keys',
42+
it will be assumed to be a cell reference.
3943
'dynamic_inputs': a dictionary of dictionaries. The outer keys represent the unique
4044
label to describe the iteration, e.g. the name of the design element.
4145
The values are dictionaries keyed by cell references with single values which will
@@ -55,12 +59,17 @@ def excel_runner(
5559
the result_label dict might look like this: {"B6": "shear utilization ratio"}
5660
The result label will be used in the returned results. If None, then the cell references
5761
will be used instead.
62+
'static_input_maps': A mapping of keys in 'static_inputs' to cell references. This is useful
63+
to provide when your data is keyed by some other process and you do not want to manually
64+
re-map your data to be keyed by cell referdences. By providing 'static_input_maps', this
65+
excel_runner will do that for you.
5866
'save_dir': The directory to store saved workbooks
5967
'sheet_idx': The sheet to modify within the workbook.
6068
"""
61-
62-
demand_cell_ids = list(static_inputs.keys())
63-
iterations = len(static_inputs[demand_cell_ids[0]])
69+
static_inputs = format_static_inputs(static_inputs)
70+
iterations = len(static_inputs)
71+
if static_input_maps is None:
72+
static_input_maps = dict()
6473

6574
main_progress = Progress(
6675
TextColumn("{task.description}"),
@@ -83,18 +92,19 @@ def excel_runner(
8392
dynamic_results = {}
8493
with Live(panel) as live:
8594
for iteration in range(iterations):
95+
static_data = static_inputs[iteration]
8696
demand_cells_to_change = {
87-
cell_id: static_inputs[cell_id][iteration]
88-
for cell_id in demand_cell_ids
89-
if cell_id not in static_identifier_keys
97+
static_input_maps.get(k, k): v
98+
for k, v in static_data.items()
99+
if k not in static_identifier_keys
90100
}
91101
identifier_values = {
92-
cell_id: str(static_inputs[cell_id][iteration])
93-
for cell_id in demand_cell_ids
94-
if cell_id in static_identifier_keys
102+
k: str(v)
103+
for k, v in static_data.items()
104+
if k in static_identifier_keys
95105
}
96106
if identifier_values:
97-
identifiers = "-".join([static_inputs[id_key][iteration] for id_key in static_identifier_keys])
107+
identifiers = "-".join(identifier_values.values())
98108
else:
99109
identifiers = f"{iteration}"
100110
variations_task = variations_progress.add_task("Sheet variations", total=len(dynamic_inputs.items()))
@@ -275,4 +285,27 @@ def valid_excel_reference(cell: str) -> bool:
275285
if match is None:
276286
return False
277287
else:
278-
return True
288+
return True
289+
290+
291+
def format_static_inputs(
292+
static_inputs: dict[str, list[float | int | str]] | list[dict[str, float | int | str]]
293+
) -> list[dict[str, float | int | str]]:
294+
"""
295+
Transforms a dictionary of str keys and list values to a list of dictionaries.
296+
297+
All sub-lists must be the same size.
298+
"""
299+
if isinstance(static_inputs, list) and isinstance(static_inputs[0], dict):
300+
return static_inputs
301+
else:
302+
column_data = [list_data for list_data in static_inputs.values()]
303+
row_data = zip(*column_data)
304+
outer_acc = []
305+
for row in row_data:
306+
inner_acc = {}
307+
for idx, key in enumerate(static_inputs.keys()):
308+
inner_acc.update({key: row[idx]})
309+
outer_acc.append(inner_acc)
310+
return outer_acc
311+

tests/test_excel_engine.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,39 @@ def test_excel_runner():
5757
save_dir=TEST_DATA_DIR / "design"
5858
)
5959
assert results['C01']['successful_key'] is None # No match found
60+
assert results['C02']['successful_key'] == "OptA" # Option A worked passed the criteria
61+
62+
results = xl.excel_runner(
63+
TEST_DATA_DIR / "example_wb.xlsx",
64+
static_inputs={"static": [10, 20], "Labels": ["C01", "C02"]},
65+
dynamic_inputs={
66+
"OptA": {"B2": 22},
67+
"OptB": {"B2": 33},
68+
"OptC": {"B2": 55},
69+
},
70+
success_conditions={"B6": dcr2},
71+
static_identifier_keys=["Labels"],
72+
result_labels={"B6": "meaningful_value"},
73+
static_input_maps={"static": "B1"},
74+
save_dir=TEST_DATA_DIR / "design"
75+
)
76+
assert results['C01']['successful_key'] is None # No match found
77+
assert results['C02']['successful_key'] == "OptA" # Option A worked passed the criteria
78+
79+
80+
results = xl.excel_runner(
81+
TEST_DATA_DIR / "example_wb.xlsx",
82+
static_inputs=[{"static": 10, "Labels": "C01"}, {"static": 20, "Labels": "C02"}],
83+
dynamic_inputs={
84+
"OptA": {"B2": 22},
85+
"OptB": {"B2": 33},
86+
"OptC": {"B2": 55},
87+
},
88+
success_conditions={"B6": dcr2},
89+
static_identifier_keys=["Labels"],
90+
result_labels={"B6": "meaningful_value"},
91+
static_input_maps={"static": "B1"},
92+
save_dir=TEST_DATA_DIR / "design"
93+
)
94+
assert results['C01']['successful_key'] is None # No match found
6095
assert results['C02']['successful_key'] == "OptA" # Option A worked passed the criteria

0 commit comments

Comments
 (0)