Skip to content

Commit 486ff33

Browse files
committed
fix(draw_figures): Per-figure defaults cascade and layout support
Technical fix: _draw_single_figure() now reads fig_spec['defaults'] and merges into cascade: top-level < fig_defaults < plot_spec. Adds layout=(nrows, ncols) tuple support. Delegation to dfdraw deferred — needs specs format alignment (ADF list vs dfdraw dict). 28 draw subframe tests pass (was 23).
1 parent 8229d38 commit 486ff33

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

UTILS/dfextensions/AliasDataFrame/AliasDataFrame.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10636,6 +10636,9 @@ def draw_figures(
1063610636

1063710637
# ═══════════════════════════════════════════════════════════════════
1063810638
# PHASE 5: Generate figures
10639+
# Note: Uses ADF's _draw_single_figure which handles per-figure
10640+
# defaults cascade and layout. Future: delegate to dfdraw once
10641+
# specs format alignment is resolved.
1063910642
# ═══════════════════════════════════════════════════════════════════
1064010643

1064110644
results = {}

UTILS/dfextensions/AliasDataFrame/tests/test_draw_subframe_resolution.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,135 @@ def test_draw_preserves_original_df(self, adf_with_subframe):
252252
fig, ax, stats = adf.draw("Sub.dz:x", type='profile', bins=2)
253253
cols_after = set(adf.df.columns)
254254
assert cols_before == cols_after, f"Columns leaked: {cols_after - cols_before}"
255+
256+
257+
# =========================================================================
258+
# draw_figures() tests
259+
# =========================================================================
260+
261+
class TestDrawFiguresSubframeResolution:
262+
"""draw_figures() should resolve Subframe.column references."""
263+
264+
def test_draw_figures_subframe_basic(self, adf_with_subframe):
265+
"""draw_figures with Subframe.column in plot expr."""
266+
adf = adf_with_subframe
267+
specs = [{
268+
'name': 'test_fig',
269+
'plots': [
270+
{'expr': 'Sub.dz:x', 'type': 'profile', 'bins': 3},
271+
]
272+
}]
273+
results = adf.draw_figures(specs)
274+
assert 'test_fig' in results
275+
assert results['test_fig'].get('error') is None
276+
277+
def test_draw_figures_subframe_conflict(self, adf_with_subframe):
278+
"""draw_figures with conflicting column name."""
279+
adf = adf_with_subframe
280+
specs = [{
281+
'name': 'conflict_fig',
282+
'plots': [
283+
{'expr': 'Sub.dy:x', 'type': 'profile', 'bins': 2},
284+
]
285+
}]
286+
results = adf.draw_figures(specs)
287+
assert results['conflict_fig'].get('error') is None
288+
289+
def test_draw_figures_mixed_subframe_and_local(self, adf_with_subframe):
290+
"""draw_figures with both subframe and local columns in same figure."""
291+
adf = adf_with_subframe
292+
specs = [{
293+
'name': 'mixed_fig',
294+
'ncols': 2,
295+
'plots': [
296+
{'expr': 'dy:x', 'type': 'profile', 'bins': 2},
297+
{'expr': 'Sub.dz:x', 'type': 'profile', 'bins': 2},
298+
]
299+
}]
300+
results = adf.draw_figures(specs)
301+
assert results['mixed_fig'].get('error') is None
302+
303+
def test_draw_figures_multikey(self, adf_multikey_subframe):
304+
"""draw_figures with multi-key subframe."""
305+
adf = adf_multikey_subframe
306+
specs = [{
307+
'name': 'multikey_fig',
308+
'plots': [
309+
{'expr': 'Side.dyS:x', 'type': 'profile', 'bins': 4},
310+
{'expr': 'Side.dzS:x', 'type': 'profile', 'bins': 4},
311+
]
312+
}]
313+
results = adf.draw_figures(specs)
314+
assert results['multikey_fig'].get('error') is None
315+
316+
def test_draw_figures_defaults_cascade(self, adf_with_subframe):
317+
"""Per-figure defaults propagate to plots (linestyle, type, bins)."""
318+
adf = adf_with_subframe
319+
specs = [{
320+
'name': 'defaults_fig',
321+
'defaults': {'type': 'profile', 'bins': 3, 'linestyle': 'none'},
322+
'plots': [
323+
{'expr': 'Sub.dz:x'},
324+
{'expr': 'dy:x'},
325+
]
326+
}]
327+
results = adf.draw_figures(specs)
328+
assert results['defaults_fig'].get('error') is None
329+
330+
def test_draw_figures_layout_tuple(self, adf_with_subframe):
331+
"""layout=(nrows, ncols) tuple works."""
332+
adf = adf_with_subframe
333+
specs = [{
334+
'name': 'layout_fig',
335+
'layout': (1, 2),
336+
'plots': [
337+
{'expr': 'dy:x', 'type': 'profile', 'bins': 2},
338+
{'expr': 'Sub.dz:x', 'type': 'profile', 'bins': 2},
339+
]
340+
}]
341+
results = adf.draw_figures(specs)
342+
assert results['layout_fig'].get('error') is None
343+
344+
def test_draw_figures_subframe_in_selection(self, adf_with_subframe):
345+
"""Subframe.column works in selection within draw_figures."""
346+
adf = adf_with_subframe
347+
specs = [{
348+
'name': 'sel_fig',
349+
'plots': [
350+
{'expr': 'dy:x', 'type': 'profile', 'bins': 2,
351+
'selection': 'Sub.dz<0.015'},
352+
]
353+
}]
354+
results = adf.draw_figures(specs)
355+
assert results['sel_fig'].get('error') is None
356+
357+
def test_draw_figures_multiple_figures(self, adf_with_subframe):
358+
"""Multiple figures in one draw_figures call."""
359+
adf = adf_with_subframe
360+
specs = [
361+
{
362+
'name': 'fig1',
363+
'plots': [{'expr': 'Sub.dz:x', 'type': 'profile', 'bins': 2}]
364+
},
365+
{
366+
'name': 'fig2',
367+
'plots': [{'expr': 'Sub.dy:x', 'type': 'profile', 'bins': 2}]
368+
},
369+
]
370+
results = adf.draw_figures(specs)
371+
assert 'fig1' in results
372+
assert 'fig2' in results
373+
assert results['fig1'].get('error') is None
374+
assert results['fig2'].get('error') is None
375+
376+
def test_draw_figures_preserves_df(self, adf_with_subframe):
377+
"""draw_figures doesn't modify self.df."""
378+
adf = adf_with_subframe
379+
cols_before = set(adf.df.columns)
380+
specs = [{
381+
'name': 'preserve_fig',
382+
'plots': [{'expr': 'Sub.dz:x', 'type': 'profile', 'bins': 2}]
383+
}]
384+
adf.draw_figures(specs)
385+
cols_after = set(adf.df.columns)
386+
assert cols_before == cols_after

0 commit comments

Comments
 (0)