feat(gfql): Expose ring layout functions via GFQL call()
Summary
Expose ring/radial layout functions via GFQL call() to enable time-series and hierarchical graph visualizations through the JSON wire protocol.
Background
Ring layouts exist in the Plotter API but are NOT currently exposed via GFQL call_safelist.py:
time_ring_layout()- Time-based radial layout (datetime64 column)ring_categorical_layout()- Categorical radial layoutring_continuous_layout()- Continuous radial layout
These layouts are already documented in the LLM guide (docs/source/gfql/spec/llm_guide.md) but cannot be used via GFQL JSON queries.
Motivation
Ring layouts are valuable for:
- Time-series analysis: Events arranged chronologically in concentric rings
- Hierarchical structures: Levels/depths visualized as rings
- Categorical grouping: Categories organized radially
Currently users must use the Plotter API directly - GFQL users cannot access these layouts.
Implementation Tasks
1. Add to call_safelist.py (~30 min)
File: graphistry/compute/gfql/call_safelist.py
Add entries for all three ring layout functions:
'time_ring_layout': {
'allowed_params': {
'time_col', 'num_rings', 'time_start', 'time_end',
'time_unit', 'min_r', 'max_r', 'reverse', 'play_ms', 'engine'
},
'required_params': set(),
'param_validators': {
'time_col': is_string_or_none,
'num_rings': is_int_or_none,
'time_unit': is_string_or_none,
'min_r': is_number,
'max_r': is_number,
'reverse': is_bool,
'play_ms': is_int,
'engine': is_string
},
'description': 'Radial graph layout where nodes are positioned based on a datetime64 column'
},
'ring_categorical_layout': {
'allowed_params': {
'ring_col', 'num_rings', 'min_r', 'max_r', 'reverse',
'play_ms', 'format_axis', 'engine'
},
'required_params': set(),
'param_validators': {
'ring_col': is_string_or_none,
'num_rings': is_int_or_none,
'min_r': is_number,
'max_r': is_number,
'reverse': is_bool,
'play_ms': is_int,
'engine': is_string
},
'description': 'Radial layout based on categorical node attribute'
},
'ring_continuous_layout': {
'allowed_params': {
'ring_col', 'num_rings', 'min_r', 'max_r', 'reverse',
'play_ms', 'format_axis', 'engine'
},
'required_params': set(),
'param_validators': {
'ring_col': is_string_or_none,
'num_rings': is_int_or_none,
'min_r': is_number,
'max_r': is_number,
'reverse': is_bool,
'play_ms': is_int,
'engine': is_string
},
'description': 'Radial layout based on continuous node attribute'
}
2. Add Tests (~60 min)
File: graphistry/tests/compute/test_gfql_ring_layouts.py
Test cases:
def test_time_ring_layout_basic():
"""Test time_ring_layout via GFQL call()"""
g = graphistry.nodes(pd.DataFrame({
'id': ['a', 'b', 'c'],
'time': pd.to_datetime(['2024-01-01', '2024-01-15', '2024-02-01'])
}))
# Via GFQL
chain = {
"type": "Chain",
"chain": [{"type": "Call", "function": "time_ring_layout", "params": {"time_col": "time"}}]
}
g2 = g.chain(chain)
assert 'x' in g2._nodes.columns
assert 'y' in g2._nodes.columns
def test_ring_categorical_layout():
"""Test ring_categorical_layout via GFQL call()"""
# ... similar test
def test_ring_continuous_layout():
"""Test ring_continuous_layout via GFQL call()"""
# ... similar test
3. Run Tests (~15 min)
WITH_BUILD=0 WITH_LINT=0 WITH_TYPECHECK=0 WITH_TEST=1 \
./test-cpu-local-minimal.sh graphistry/tests/compute/test_gfql_ring_layouts.py -xvs
4. Update Documentation (~15 min)
LLM guide already documents these (see commit 059af3bb), but may need to update notes:
Current note in docs/source/gfql/spec/llm_guide.md:
Note: Ring layouts documented but not yet exposed via GFQL call_safelist
After implementation: Remove the note or update to indicate they're now available.
Acceptance Criteria
- [ ] All 3 ring layout functions added to
call_safelist.py - [ ] Parameter validation implemented for all functions
- [ ] Tests pass for all 3 layouts (CPU tests sufficient)
- [ ] GFQL JSON queries can successfully invoke ring layouts
- [ ] Documentation updated if needed
- [ ] No regressions in existing tests
Estimated Effort
Total: 2-3 hours
- Add to safelist: 30 min
- Write tests: 60 min
- Run tests + validation: 30 min
- Documentation updates: 15 min
References
- Implementation:
graphistry/layout/ring/time.py,graphistry/layouts.py - Existing methods:
time_ring_layout(),ring_categorical_layout(),ring_continuous_layout() - Documentation:
docs/source/gfql/spec/llm_guide.md(lines 444-460) - Related PR: #807 (LLM guide enhancements)
Related Issues
Part of GFQL layout completeness - follows FA2 default guidance established in #807