dataface 0.3.2.dev22__py3-none-any.whl → 0.3.2.dev111__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dataface/__init__.py +70 -23
- dataface/agent_api/__init__.py +162 -68
- dataface/agent_api/_paths.py +9 -3
- dataface/agent_api/docs/yaml-reference.md +240 -106
- dataface/agent_api/init.py +1 -1
- dataface/agent_api/project_session.py +2 -1
- dataface/agent_api/serve.py +115 -0
- dataface/ai/agent.py +28 -2
- dataface/cli/_project.py +100 -1
- dataface/cli/commands/board_artifact.py +5 -2
- dataface/cli/commands/describe.py +7 -3
- dataface/cli/commands/docs.py +12 -3
- dataface/cli/commands/init.py +7 -14
- dataface/cli/commands/inspect.py +6 -1
- dataface/cli/commands/mcp_init.py +23 -43
- dataface/cli/commands/migrate.py +8 -3
- dataface/cli/commands/query.py +11 -8
- dataface/cli/commands/render.py +20 -9
- dataface/cli/commands/search.py +8 -2
- dataface/cli/commands/serve.py +26 -76
- dataface/cli/commands/skills_init.py +2 -19
- dataface/cli/commands/validate.py +7 -2
- dataface/cli/filesystem_project.py +5 -4
- dataface/cli/main.py +2 -19
- dataface/core/__init__.py +73 -28
- dataface/core/{compile/colors.py → colors.py} +20 -5
- dataface/core/compile/compiler.py +97 -34
- dataface/core/compile/data_table.py +7 -2
- dataface/core/compile/enrich.py +18 -44
- dataface/core/compile/face_warnings.py +46 -8
- dataface/core/compile/format.py +47 -1
- dataface/core/compile/inherit_graph.py +3 -3
- dataface/core/compile/inherit_registry.yaml +1 -1
- dataface/core/compile/migrations.py +112 -9
- dataface/core/compile/models/chart/normalized/kpi.py +0 -4
- dataface/core/compile/models/chart/resolved/_base.py +28 -10
- dataface/core/compile/models/chart/resolved/callout.py +2 -3
- dataface/core/compile/models/chart/resolved/kpi.py +7 -3
- dataface/core/compile/models/chart/resolved/table.py +16 -2
- dataface/core/compile/models/markers.py +1 -1
- dataface/core/compile/models/primitives.py +175 -2
- dataface/core/compile/models/source.py +5 -5
- dataface/core/compile/models/style/authored/__init__.py +32 -8
- dataface/core/compile/models/style/authored/_base.py +126 -69
- dataface/core/compile/models/style/authored/table.py +61 -3
- dataface/core/compile/models/style/resolved/__init__.py +12 -2
- dataface/core/compile/models/style/resolved/_base.py +142 -57
- dataface/core/compile/models/style/resolved/_cartesian.py +6 -11
- dataface/core/compile/models/style/resolved/_cascade.py +141 -54
- dataface/core/compile/models/style/resolved/area.py +1 -1
- dataface/core/compile/models/style/resolved/bar.py +1 -1
- dataface/core/compile/models/style/resolved/line.py +1 -1
- dataface/core/compile/models/style/resolved/scatter.py +3 -2
- dataface/core/compile/models/style/theme/__init__.py +36 -14
- dataface/core/compile/models/style/theme/_chart_base.py +17 -9
- dataface/core/compile/models/style/theme/axis.py +432 -312
- dataface/core/compile/models/style/theme/charts.py +10 -6
- dataface/core/compile/models/style/theme/kpi.py +2 -1
- dataface/core/compile/models/style/theme/page.py +5 -2
- dataface/core/compile/models/style/theme/table.py +2 -2
- dataface/core/compile/models/vega_lite/config.py +1 -1
- dataface/core/compile/normalize_charts.py +6 -2
- dataface/core/compile/normalize_queries.py +21 -7
- dataface/core/compile/resolve/__init__.py +2 -0
- dataface/core/compile/resolve/_axes.py +147 -44
- dataface/core/compile/resolve/_channels.py +1 -1
- dataface/core/compile/resolve/_dispatch.py +1 -1
- dataface/core/compile/resolve/_domain.py +49 -32
- dataface/core/compile/resolve/_kwargs.py +39 -12
- dataface/core/compile/resolve/_marks.py +8 -3
- dataface/core/compile/resolve/_palette.py +7 -3
- dataface/core/compile/resolve/_table.py +124 -33
- dataface/core/compile/resolve/area.py +48 -18
- dataface/core/compile/resolve/bar.py +245 -64
- dataface/core/compile/resolve/geo.py +10 -3
- dataface/core/compile/resolve/heatmap.py +16 -8
- dataface/core/compile/resolve/line.py +35 -12
- dataface/core/compile/resolve/pie.py +4 -1
- dataface/core/compile/resolve/pie_attachment.py +1 -1
- dataface/core/compile/resolve/scatter.py +25 -13
- dataface/core/compile/resolve/simple.py +25 -2
- dataface/core/compile/schema_renderers/prompt.py +1 -1
- dataface/core/compile/schema_renderers/textmate_grammar.py +8 -1
- dataface/core/compile/source_map.py +211 -28
- dataface/core/compile/style_cascade.py +126 -55
- dataface/core/compile/yaml_error_formatter.py +1 -1
- dataface/core/connections.py +10 -3
- dataface/core/dashboard.py +24 -18
- dataface/core/defaults/themes/_base.yaml +49 -39
- dataface/core/defaults/themes/cream.yaml +4 -4
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +1 -1
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +1 -1
- dataface/core/defaults/themes/editorial.yaml +6 -6
- dataface/core/defaults/themes/neon.yaml +15 -8
- dataface/core/defaults/themes/plain.yaml +6 -4
- dataface/core/defaults/themes/stark.yaml +16 -7
- dataface/core/defaults/themes/vivid.yaml +15 -8
- dataface/core/diagnostics/__init__.py +2 -2
- dataface/core/diagnostics/codes_compile.py +13 -31
- dataface/core/diagnostics/codes_execute.py +79 -0
- dataface/core/diagnostics/codes_render.py +87 -28
- dataface/core/diagnostics/diagnostic.py +30 -0
- dataface/core/diagnostics/hints.py +19 -0
- dataface/core/dialects/base.py +17 -0
- dataface/core/dialects/postgres.py +14 -0
- dataface/core/dialects/redshift.py +5 -0
- dataface/core/execute/__init__.py +59 -14
- dataface/core/execute/adapters/dbt_adapter.py +4 -1
- dataface/core/execute/adapters/dbt_adapter_factory.py +19 -0
- dataface/core/execute/adapters/dbt_utils.py +84 -20
- dataface/core/execute/adapters/postgres_connection_manager.py +48 -0
- dataface/core/execute/adapters/sql_adapter.py +72 -4
- dataface/core/execute/dbt_jinja.py +65 -10
- dataface/core/execute/executor.py +69 -16
- dataface/core/font_measure.py +28 -0
- dataface/core/fonts.py +8 -5
- dataface/core/inspect/__init__.py +46 -6
- dataface/core/project.py +13 -1
- dataface/core/render/chart/data_table_attachment.py +14 -12
- dataface/core/render/chart/emitters/_cartesian.py +21 -18
- dataface/core/render/chart/emitters/_channels.py +56 -8
- dataface/core/render/chart/emitters/_label_overlap.py +18 -15
- dataface/core/render/chart/emitters/_measured_label_padding.py +4 -4
- dataface/core/render/chart/emitters/area.py +3 -3
- dataface/core/render/chart/emitters/bar.py +60 -35
- dataface/core/render/chart/emitters/geo.py +8 -13
- dataface/core/render/chart/emitters/heatmap.py +20 -26
- dataface/core/render/chart/emitters/line.py +2 -2
- dataface/core/render/chart/emitters/scatter.py +7 -30
- dataface/core/render/chart/features/baseline.py +22 -9
- dataface/core/render/chart/features/endpoint_labels.py +55 -32
- dataface/core/render/chart/features/mirror_axis.py +12 -9
- dataface/core/render/chart/features/value_labels.py +1 -1
- dataface/core/render/chart/kpi.py +11 -90
- dataface/core/render/chart/presentation.py +6 -8
- dataface/core/render/chart/rendering.py +3 -12
- dataface/core/render/chart/session.py +10 -0
- dataface/core/render/chart/spec.py +3 -0
- dataface/core/render/chart/spec_builders.py +18 -18
- dataface/core/render/chart/table.py +81 -36
- dataface/core/render/chart/table_support.py +2 -53
- dataface/core/render/chart/time_unit_detect.py +3 -2
- dataface/core/render/chart/translate.py +82 -44
- dataface/core/render/chart/type_inference.py +45 -25
- dataface/core/render/chart/validation.py +91 -0
- dataface/core/render/chart/vega_lite.py +30 -11
- dataface/core/render/chart/vl_field_maps.py +111 -76
- dataface/core/render/converters/chart.py +16 -6
- dataface/core/render/faces.py +3 -2
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -93
- dataface/core/render/fonts/_font_face.css +10 -0
- dataface/core/render/layout_sizing.py +267 -238
- dataface/core/render/numeral_expr.py +80 -0
- dataface/core/render/warnings/bar_band_width_too_narrow.py +64 -6
- dataface/core/render/warnings/layered_chart_shared_y_axis_scale_mismatch.py +25 -3
- dataface/core/render/warnings/likely_currency_or_percent_missing_formatter.py +12 -6
- dataface/core/render/warnings/palette_unsupported.py +6 -0
- dataface/core/render/warnings/pie_dominant_segment.py +1 -0
- dataface/core/render/warnings/pie_too_many_segments.py +1 -0
- dataface/core/render/warnings/point_map_out_of_projection.py +1 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +1 -0
- dataface/core/render/warnings/redundant_encoding.py +20 -1
- dataface/core/render/warnings/registry.py +7 -3
- dataface/core/render/warnings/temporal_single_point.py +1 -0
- dataface/core/render/warnings/too_many_color_categories.py +1 -0
- dataface/core/render/warnings/too_many_x_categories.py +18 -2
- dataface/core/render/warnings/value_labels_crowd_width.py +9 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +1 -0
- dataface/core/render_format.py +18 -0
- dataface/core/text/format_d3.py +56 -0
- dataface/core/text/numeral_scale.py +218 -0
- dataface/core/utils.py +103 -10
- dataface/data/schemas/board-resolved/board-resolved.schema.json +9487 -7978
- dataface/data/schemas/yaml/0.3.2.json +1 -0
- dataface/data/schemas/yaml/manifest.json +7 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/METADATA +1 -1
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/RECORD +185 -177
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/WHEEL +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/entry_points.txt +0 -0
- {dataface-0.3.2.dev22.dist-info → dataface-0.3.2.dev111.dist-info}/licenses/LICENSE +0 -0
|
@@ -100,6 +100,58 @@ class MigrationRegistry:
|
|
|
100
100
|
)
|
|
101
101
|
return cls(moves, catalog=catalog)
|
|
102
102
|
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_suffix_renames(
|
|
105
|
+
cls,
|
|
106
|
+
root_model: type[BaseModel],
|
|
107
|
+
source_schema: str,
|
|
108
|
+
target_schema: str,
|
|
109
|
+
renames: Iterable[tuple[YamlKeyPath, YamlKeyPath]],
|
|
110
|
+
*,
|
|
111
|
+
catalog: YamlSchemaCatalog,
|
|
112
|
+
) -> MigrationRegistry:
|
|
113
|
+
"""Build a move registry for tail-only path renames, one entry per
|
|
114
|
+
occurrence a shared style class is reused at.
|
|
115
|
+
|
|
116
|
+
``MovedFrom`` (``from_model``) declares one fixed ``old_path`` per
|
|
117
|
+
field *declaration*, which is correct only for a field that occurs at
|
|
118
|
+
a single position in the tree. A cascade style class like
|
|
119
|
+
``AxisLabelStyle`` is reused at hundreds of absolute positions (every
|
|
120
|
+
chart family's ``axis_x``/``axis_y``, every nested face, every theme
|
|
121
|
+
override) — one literal ``old_path`` cannot describe all of them.
|
|
122
|
+
``renames`` instead pairs a *new-path tail* with the corresponding
|
|
123
|
+
*old-path tail*; this walks every occurrence of the new tail and
|
|
124
|
+
substitutes the old tail at that same position, keeping the shared
|
|
125
|
+
prefix. Candidates that don't structurally exist in both frozen
|
|
126
|
+
schemas (a coincidental tail match on an unrelated field, e.g. a mark
|
|
127
|
+
family also named ``line``) are silently dropped rather than passed
|
|
128
|
+
to ``Move`` — that keeps this safe to run over the whole field tree
|
|
129
|
+
without hand-enumerating every real position.
|
|
130
|
+
"""
|
|
131
|
+
old_schema = catalog.schema_for(source_schema)
|
|
132
|
+
new_schema = catalog.schema_for(target_schema)
|
|
133
|
+
# Walk the field tree once and reuse it for every rename tuple —
|
|
134
|
+
# re-walking per rename is O(renames * tree_size) and the per-variant
|
|
135
|
+
# axis/scale models made tree_size large enough for that to matter.
|
|
136
|
+
paths = [path for path, _field in _walk_fields(root_model)]
|
|
137
|
+
seen: set[tuple[YamlKeyPath, YamlKeyPath]] = set()
|
|
138
|
+
moves: list[Move] = []
|
|
139
|
+
for new_tail, old_tail in renames:
|
|
140
|
+
width = len(new_tail)
|
|
141
|
+
for path in paths:
|
|
142
|
+
if len(path) < width or path[-width:] != new_tail:
|
|
143
|
+
continue
|
|
144
|
+
old_path = path[:-width] + old_tail
|
|
145
|
+
key = (old_path, path)
|
|
146
|
+
if key in seen:
|
|
147
|
+
continue
|
|
148
|
+
seen.add(key)
|
|
149
|
+
if _schema_path_exists(old_schema, old_path) and _schema_path_exists(
|
|
150
|
+
new_schema, path
|
|
151
|
+
):
|
|
152
|
+
moves.append(Move(source_schema, target_schema, old_path, path))
|
|
153
|
+
return cls(moves, catalog=catalog)
|
|
154
|
+
|
|
103
155
|
def transition_from(self, identifier: str) -> tuple[Move, ...]:
|
|
104
156
|
if identifier not in self._moves_by_source:
|
|
105
157
|
return ()
|
|
@@ -173,6 +225,37 @@ def migrate_face_yaml_text(yaml_text: str) -> str:
|
|
|
173
225
|
return migrate_yaml_text(yaml_text, catalog=catalog, registry=registry)
|
|
174
226
|
|
|
175
227
|
|
|
228
|
+
# 0.3.1 -> 0.3.2: the axis/scale model split (per-variant AxisXStyle/AxisYStyle,
|
|
229
|
+
# BaseScaleStyle.continuous nesting) renamed several theme-cascade fields.
|
|
230
|
+
# Every rename here is a pure tail substitution — same shared prefix, only the
|
|
231
|
+
# trailing segment(s) change — so from_suffix_renames derives every occurrence
|
|
232
|
+
# a rename applies at. Leaf renames that land *inside* the "labels"/"scale"
|
|
233
|
+
# containers run before the "labels"/"line" container renames themselves: a
|
|
234
|
+
# leaf move creates its destination container on demand, and the later
|
|
235
|
+
# container move must find that destination still empty (or absent) to avoid
|
|
236
|
+
# a spurious MigrationConflictError against an unrelated field that also sets
|
|
237
|
+
# the old container. See dataface/AGENTS.md's "Two validation boundaries" —
|
|
238
|
+
# these are structural moves; categorical_orient (deleted), ticks.interval
|
|
239
|
+
# (reinterpreted vocabulary, not renamed), and the band/point/quantize scale
|
|
240
|
+
# groups (deleted) are semantic changes with no lossless move and are left to
|
|
241
|
+
# raise UnsupportedSchemaError for manual migration.
|
|
242
|
+
_AXIS_RESHAPE_RENAMES: tuple[tuple[YamlKeyPath, YamlKeyPath], ...] = (
|
|
243
|
+
(("labels", "format"), ("format",)),
|
|
244
|
+
(("scale", "values"), ("values",)),
|
|
245
|
+
(("scale", "continuous", "domain"), ("scale", "domain")),
|
|
246
|
+
(("scale", "continuous", "zero"), ("scale", "zero")),
|
|
247
|
+
(("scale", "continuous", "type"), ("scale", "type")),
|
|
248
|
+
(("scale", "continuous", "domain_strategy"), ("scale", "domain_strategy")),
|
|
249
|
+
(("scale", "continuous", "log", "base"), ("scale", "base")),
|
|
250
|
+
(("scale", "continuous", "pow", "exponent"), ("scale", "exponent")),
|
|
251
|
+
(("scale", "continuous", "symlog", "constant"), ("scale", "constant")),
|
|
252
|
+
(("scale", "padding"), ("scale", "band_padding_inner")),
|
|
253
|
+
(("ticks", "length"), ("ticks", "size")),
|
|
254
|
+
(("labels",), ("label",)),
|
|
255
|
+
(("line",), ("domain",)),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
176
259
|
@cache
|
|
177
260
|
def _face_migration_context() -> tuple[YamlSchemaCatalog, MigrationRegistry]:
|
|
178
261
|
"""Load and validate the package's immutable migration metadata once."""
|
|
@@ -182,7 +265,17 @@ def _face_migration_context() -> tuple[YamlSchemaCatalog, MigrationRegistry]:
|
|
|
182
265
|
)
|
|
183
266
|
|
|
184
267
|
catalog = load_yaml_schema_catalog()
|
|
185
|
-
|
|
268
|
+
marker_registry = MigrationRegistry.from_model(AuthoredFace, catalog=catalog)
|
|
269
|
+
axis_reshape_registry = MigrationRegistry.from_suffix_renames(
|
|
270
|
+
AuthoredFace,
|
|
271
|
+
"0.3.1",
|
|
272
|
+
"0.3.2",
|
|
273
|
+
_AXIS_RESHAPE_RENAMES,
|
|
274
|
+
catalog=catalog,
|
|
275
|
+
)
|
|
276
|
+
registry = MigrationRegistry(
|
|
277
|
+
marker_registry.moves + axis_reshape_registry.moves, catalog=catalog
|
|
278
|
+
)
|
|
186
279
|
return catalog, registry
|
|
187
280
|
|
|
188
281
|
|
|
@@ -223,14 +316,20 @@ def migrate_mapping(
|
|
|
223
316
|
while identifier != current:
|
|
224
317
|
moves = registry.transition_from(identifier)
|
|
225
318
|
if not moves:
|
|
226
|
-
|
|
227
|
-
if not current_errors:
|
|
228
|
-
break
|
|
229
|
-
raise _unsupported_schema_error(current_errors[0])
|
|
319
|
+
break
|
|
230
320
|
for move in moves:
|
|
231
321
|
_apply_move(result, move)
|
|
232
322
|
identifier = moves[0].target_schema
|
|
233
323
|
|
|
324
|
+
# A transition's declared moves may not cover every field a source schema
|
|
325
|
+
# allowed (a deleted field has no lossless Move — see MovedFrom's
|
|
326
|
+
# docstring). Recognizing an older schema and exhausting its transition
|
|
327
|
+
# chain is not proof the result is current-compatible; check explicitly
|
|
328
|
+
# rather than trust silently reaching the current identifier.
|
|
329
|
+
current_errors = _current_schema_rejections(result, catalog)
|
|
330
|
+
if current_errors:
|
|
331
|
+
raise _unsupported_schema_error(current_errors[0])
|
|
332
|
+
|
|
234
333
|
if not allow_expired:
|
|
235
334
|
warnings.warn(
|
|
236
335
|
"Dataface migrated this YAML in memory; run `dft migrate` to update "
|
|
@@ -268,10 +367,7 @@ def migrate_yaml_text(
|
|
|
268
367
|
while identifier != catalog.versions[0]:
|
|
269
368
|
moves = registry.transition_from(identifier)
|
|
270
369
|
if not moves:
|
|
271
|
-
|
|
272
|
-
if not current_errors:
|
|
273
|
-
break
|
|
274
|
-
raise _unsupported_schema_error(current_errors[0])
|
|
370
|
+
break
|
|
275
371
|
for move in moves:
|
|
276
372
|
for parent, key, bindings in list(_source_locations(staged, move.old_path)):
|
|
277
373
|
if isinstance(parent, list):
|
|
@@ -294,6 +390,13 @@ def migrate_yaml_text(
|
|
|
294
390
|
removals.add(".".join(source))
|
|
295
391
|
_apply_move(staged, move)
|
|
296
392
|
identifier = moves[0].target_schema
|
|
393
|
+
|
|
394
|
+
# See migrate_mapping's identical check: a transition's declared moves
|
|
395
|
+
# may not cover every field the source schema allowed.
|
|
396
|
+
current_errors = _current_schema_rejections(staged, catalog)
|
|
397
|
+
if current_errors:
|
|
398
|
+
raise _unsupported_schema_error(current_errors[0])
|
|
399
|
+
|
|
297
400
|
return set_face_values(
|
|
298
401
|
yaml_text,
|
|
299
402
|
{**updates, **dict.fromkeys(removals)},
|
|
@@ -7,7 +7,6 @@ from typing import Any, Literal
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
|
|
9
9
|
from dataface.core.compile.models.chart.authored import KpiSupportConfig
|
|
10
|
-
from dataface.core.compile.models.primitives import FormatConfig
|
|
11
10
|
from dataface.core.compile.models.style.authored import KpiChartStylePatch
|
|
12
11
|
|
|
13
12
|
from ._base import _BaseChartFields
|
|
@@ -34,9 +33,6 @@ class KpiChart(_BaseChartFields):
|
|
|
34
33
|
default="stacked",
|
|
35
34
|
description="Layout variant, passed through from authored KpiChart.variant.",
|
|
36
35
|
)
|
|
37
|
-
format: str | FormatConfig | None = Field(
|
|
38
|
-
default=None, description="Number format override."
|
|
39
|
-
)
|
|
40
36
|
background: str | dict[str, Any] | None = Field(
|
|
41
37
|
default=None,
|
|
42
38
|
description=(
|
|
@@ -29,7 +29,7 @@ from dataface.core.compile.models.query.normalized import AnyQuery
|
|
|
29
29
|
from dataface.core.compile.models.style.resolved import (
|
|
30
30
|
ResolvedLegendStyle,
|
|
31
31
|
)
|
|
32
|
-
from dataface.core.compile.models.style.theme.board import PaddingStyle
|
|
32
|
+
from dataface.core.compile.models.style.theme.board import PaddingStyle, TitleStyle
|
|
33
33
|
from dataface.core.compile.models.vega_lite.contracts import Projection
|
|
34
34
|
|
|
35
35
|
|
|
@@ -94,23 +94,41 @@ class _BaseResolvedChartFields(BaseModel):
|
|
|
94
94
|
legend: ResolvedLegendStyle = Field(
|
|
95
95
|
description="Baked board-level legend style from the style cascade.",
|
|
96
96
|
)
|
|
97
|
-
layout_padding: PaddingStyle
|
|
98
|
-
default=None,
|
|
97
|
+
layout_padding: PaddingStyle = Field(
|
|
99
98
|
description=(
|
|
100
|
-
"Effective per-chart layout padding baked in at
|
|
101
|
-
"Combines board.charts.padding with any
|
|
102
|
-
"override.
|
|
103
|
-
"(e.g. unit tests). render_layout_item falls back to board-level padding "
|
|
104
|
-
"when None."
|
|
99
|
+
"Effective per-chart layout padding, baked in at construction time by "
|
|
100
|
+
"every family resolver. Combines board.charts.padding with any "
|
|
101
|
+
"chart-local style.<family>.padding override."
|
|
105
102
|
),
|
|
106
103
|
)
|
|
107
104
|
|
|
108
105
|
|
|
109
106
|
class _SharedResolvedChartFields(_BaseResolvedChartFields):
|
|
110
|
-
"""Adds title/subtitle for all families except KPI and Callout."""
|
|
107
|
+
"""Adds title/subtitle/background for all families except KPI and Callout."""
|
|
111
108
|
|
|
112
|
-
title: str | None = Field(
|
|
109
|
+
title: str | None = Field(
|
|
110
|
+
default=None,
|
|
111
|
+
description=(
|
|
112
|
+
"Jinja-resolved chart title, raw (no title-case transform). Also "
|
|
113
|
+
"backs non-VL consumers (e.g. the data-chart-title wire attribute) "
|
|
114
|
+
"that have always emitted the authored text as-is."
|
|
115
|
+
),
|
|
116
|
+
)
|
|
113
117
|
subtitle: str | None = Field(default=None, description="Chart subtitle.")
|
|
118
|
+
background: str = Field(
|
|
119
|
+
description=(
|
|
120
|
+
"Effective chart background: chart-local style.<family>.background "
|
|
121
|
+
"when authored, otherwise the board/face background."
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
title_style: TitleStyle = Field(
|
|
125
|
+
description=(
|
|
126
|
+
"Effective title style — chart-local style.<family>.title merged "
|
|
127
|
+
"onto the board title style. Render-time VL assembly reads "
|
|
128
|
+
"title_style.font.case to case-transform `title` (VL presentation "
|
|
129
|
+
"only), and title_style.overflow for title-wrap mode."
|
|
130
|
+
),
|
|
131
|
+
)
|
|
114
132
|
|
|
115
133
|
|
|
116
134
|
class _CartesianResolvedChartFields(_SharedResolvedChartFields):
|
|
@@ -74,10 +74,9 @@ class ResolvedCalloutChart(BaseModel):
|
|
|
74
74
|
default_factory=dict,
|
|
75
75
|
description="Always empty — callout has no data channels.",
|
|
76
76
|
)
|
|
77
|
-
layout_padding: PaddingStyle
|
|
78
|
-
default=None,
|
|
77
|
+
layout_padding: PaddingStyle = Field(
|
|
79
78
|
description=(
|
|
80
|
-
"Effective per-chart layout padding baked in at
|
|
79
|
+
"Effective per-chart layout padding, baked in at construction time. "
|
|
81
80
|
"Structural parity with _BaseResolvedChartFields so render_layout_item "
|
|
82
81
|
"can access chart.layout_padding without isinstance guards."
|
|
83
82
|
),
|
|
@@ -7,10 +7,10 @@ from typing import Literal
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
|
|
9
9
|
from dataface.core.compile.models.chart.authored import KpiSupportConfig
|
|
10
|
+
from dataface.core.compile.models.primitives import FormatConfig
|
|
10
11
|
from dataface.core.compile.models.style.resolved import ResolvedKpiStyle
|
|
11
12
|
|
|
12
13
|
from ._base import _BaseResolvedChartFields
|
|
13
|
-
from ._layer import FormatState
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class ResolvedKpiChart(_BaseResolvedChartFields):
|
|
@@ -50,9 +50,13 @@ class ResolvedKpiChart(_BaseResolvedChartFields):
|
|
|
50
50
|
default="stacked",
|
|
51
51
|
description="Layout variant — dispatches to a distinct SVG emit path.",
|
|
52
52
|
)
|
|
53
|
-
format:
|
|
53
|
+
format: FormatConfig | None = Field(
|
|
54
54
|
default=None,
|
|
55
|
-
description=
|
|
55
|
+
description=(
|
|
56
|
+
"Final headline value format — resolve() has already applied the "
|
|
57
|
+
"narrative-notation and SI-compaction defaults against this "
|
|
58
|
+
"chart's row; render consumes it as-is."
|
|
59
|
+
),
|
|
56
60
|
)
|
|
57
61
|
style: ResolvedKpiStyle = Field(
|
|
58
62
|
description="KPI family style slice.",
|
|
@@ -39,7 +39,15 @@ class ResolvedTableChart(_SharedResolvedChartFields):
|
|
|
39
39
|
# not from source_chart.style (which no longer exists on Resolved*Chart).
|
|
40
40
|
columns: dict[str, TableColumnConfig] | None = Field(
|
|
41
41
|
default=None,
|
|
42
|
-
description=
|
|
42
|
+
description=(
|
|
43
|
+
"Final per-column display config (width, format, label, …), keyed by "
|
|
44
|
+
"column name. Complete for every column resolve can see: defaults, "
|
|
45
|
+
"explicit overrides, and runtime FK links are already merged. Excludes "
|
|
46
|
+
"pivot measure columns (see `column_defaults`) — their real key space "
|
|
47
|
+
"only exists after render's pivot transform runs. None means no "
|
|
48
|
+
"column-level config or runtime link facts apply — render falls back "
|
|
49
|
+
"to plain query columns."
|
|
50
|
+
),
|
|
43
51
|
)
|
|
44
52
|
header_overflow: Literal["clip", "truncate", "wrap-two", "wrap"] | None = Field(
|
|
45
53
|
default=None,
|
|
@@ -47,7 +55,13 @@ class ResolvedTableChart(_SharedResolvedChartFields):
|
|
|
47
55
|
)
|
|
48
56
|
column_defaults: TableColumnDefaultsConfig | None = Field(
|
|
49
57
|
default=None,
|
|
50
|
-
description=
|
|
58
|
+
description=(
|
|
59
|
+
"Table-level column defaults, kept only for render's two render-native "
|
|
60
|
+
"column key spaces — pivot leaf/measure columns and transpose's "
|
|
61
|
+
"__metric__/__value__ pair — neither of which exists until a "
|
|
62
|
+
"render-time data transform runs. Every other column (explicit or "
|
|
63
|
+
"query-inferred) already has defaults merged into `columns`."
|
|
64
|
+
),
|
|
51
65
|
)
|
|
52
66
|
style: ResolvedTableStyle = Field(
|
|
53
67
|
description="Table family style slice.",
|
|
@@ -104,7 +104,7 @@ class SkipInheritSlots:
|
|
|
104
104
|
``_ChartStyleBase`` must not inherit from the root cascade.
|
|
105
105
|
|
|
106
106
|
**On a leaf scalar field**: prevents the leaf from being added to the graph
|
|
107
|
-
via slot expansion. Use on
|
|
107
|
+
via slot expansion. Use on axis style fields that the style resolver does
|
|
108
108
|
not cascade — e.g. ``grid.dash``, ``ticks.count``, ``scale``, ``position`` —
|
|
109
109
|
so the inherit graph only declares what the resolver actually does.
|
|
110
110
|
|
|
@@ -8,12 +8,13 @@ cannot live in either without creating a circular import.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import re
|
|
11
|
-
from typing import Annotated, Any, Literal
|
|
11
|
+
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
|
12
12
|
|
|
13
13
|
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
14
14
|
|
|
15
15
|
from dataface.core.compile.models.factories import (
|
|
16
16
|
_PatchBase,
|
|
17
|
+
build_patch_model_ext,
|
|
17
18
|
register_as_own_patch,
|
|
18
19
|
register_patch,
|
|
19
20
|
)
|
|
@@ -410,7 +411,158 @@ class FontColorStrokeStyle(StrokeStyle):
|
|
|
410
411
|
)
|
|
411
412
|
|
|
412
413
|
|
|
413
|
-
|
|
414
|
+
# Vega/Vega-Lite's built-in named color schemes (https://vega.github.io/vega/docs/schemes/),
|
|
415
|
+
# the only strings ``ScaleTargetConfig.palette`` forwards straight through to VL's
|
|
416
|
+
# ``scale.scheme``. Sampled-discrete shorthand like "blues-9" is not a real scheme
|
|
417
|
+
# name — VL fails deep inside rendering ("Cannot read properties of undefined")
|
|
418
|
+
# rather than rejecting it up front, so this is validated at compile time instead.
|
|
419
|
+
#
|
|
420
|
+
# Empirically verified against this repo's pinned vl-convert (not hand-copied from
|
|
421
|
+
# docs alone): every name below was round-tripped through
|
|
422
|
+
# ``vlc.vegalite_to_svg`` with a quantitative ``scale.scheme`` encoding and
|
|
423
|
+
# confirmed to render — see ``test_vega_scheme_names_parity.py``, which re-runs
|
|
424
|
+
# that sweep and fails if this set and the bundled renderer ever diverge.
|
|
425
|
+
VEGA_SCHEME_NAMES: frozenset[str] = frozenset(
|
|
426
|
+
{
|
|
427
|
+
# Categorical
|
|
428
|
+
"accent",
|
|
429
|
+
"category10",
|
|
430
|
+
"category20",
|
|
431
|
+
"category20b",
|
|
432
|
+
"category20c",
|
|
433
|
+
"dark2",
|
|
434
|
+
"observable10",
|
|
435
|
+
"paired",
|
|
436
|
+
"pastel1",
|
|
437
|
+
"pastel2",
|
|
438
|
+
"set1",
|
|
439
|
+
"set2",
|
|
440
|
+
"set3",
|
|
441
|
+
"tableau10",
|
|
442
|
+
"tableau20",
|
|
443
|
+
# Sequential (single-hue)
|
|
444
|
+
"blues",
|
|
445
|
+
"tealblues",
|
|
446
|
+
"teals",
|
|
447
|
+
"greens",
|
|
448
|
+
"browns",
|
|
449
|
+
"greys",
|
|
450
|
+
"oranges",
|
|
451
|
+
"purples",
|
|
452
|
+
"reds",
|
|
453
|
+
"warmgreys",
|
|
454
|
+
# Sequential (multi-hue)
|
|
455
|
+
"viridis",
|
|
456
|
+
"magma",
|
|
457
|
+
"inferno",
|
|
458
|
+
"plasma",
|
|
459
|
+
"cividis",
|
|
460
|
+
"turbo",
|
|
461
|
+
"bluegreen",
|
|
462
|
+
"bluepurple",
|
|
463
|
+
"goldgreen",
|
|
464
|
+
"goldorange",
|
|
465
|
+
"goldred",
|
|
466
|
+
"greenblue",
|
|
467
|
+
"orangered",
|
|
468
|
+
"purplebluegreen",
|
|
469
|
+
"purpleblue",
|
|
470
|
+
"purplered",
|
|
471
|
+
"redpurple",
|
|
472
|
+
"yellowgreenblue",
|
|
473
|
+
"yellowgreen",
|
|
474
|
+
"yelloworangebrown",
|
|
475
|
+
"yelloworangered",
|
|
476
|
+
# Diverging
|
|
477
|
+
"blueorange",
|
|
478
|
+
"brownbluegreen",
|
|
479
|
+
"purplegreen",
|
|
480
|
+
"pinkyellowgreen",
|
|
481
|
+
"purpleorange",
|
|
482
|
+
"redblue",
|
|
483
|
+
"redgrey",
|
|
484
|
+
"redyellowblue",
|
|
485
|
+
"redyellowgreen",
|
|
486
|
+
"spectral",
|
|
487
|
+
# Cyclical
|
|
488
|
+
"rainbow",
|
|
489
|
+
"sinebow",
|
|
490
|
+
# For dark backgrounds
|
|
491
|
+
"darkblue",
|
|
492
|
+
"darkgold",
|
|
493
|
+
"darkgreen",
|
|
494
|
+
"darkmulti",
|
|
495
|
+
"darkred",
|
|
496
|
+
# For light backgrounds
|
|
497
|
+
"lightgreyred",
|
|
498
|
+
"lightgreyteal",
|
|
499
|
+
"lightmulti",
|
|
500
|
+
"lightorange",
|
|
501
|
+
"lighttealblue",
|
|
502
|
+
}
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
class ScaleTargetPaletteValidationMixin(BaseModel):
|
|
507
|
+
"""Shared authored-input validator for ``ScaleTargetConfig.palette``.
|
|
508
|
+
|
|
509
|
+
Inherited by both ``ScaleTargetConfig`` and ``ScaleTargetConfigPatch`` so
|
|
510
|
+
chart-local authored patches reject a bad palette at the compile boundary
|
|
511
|
+
(where ``normalize_face()`` wraps the resulting ``pydantic.ValidationError``
|
|
512
|
+
into a proper ``ERR-VALIDATION-FIELD`` diagnostic) instead of only at
|
|
513
|
+
resolve time, where the raw pydantic error leaks past Dataface's error
|
|
514
|
+
formatting — ``build_patch_model_ext`` only carries over validators that
|
|
515
|
+
live on the patch model's base class (mirrors ``ScaleDomainValidationMixin``).
|
|
516
|
+
"""
|
|
517
|
+
|
|
518
|
+
model_config = ConfigDict(extra="forbid")
|
|
519
|
+
|
|
520
|
+
@model_validator(mode="before")
|
|
521
|
+
@classmethod
|
|
522
|
+
def _validate_named_palette(cls, data: object) -> object:
|
|
523
|
+
"""A string palette is either a literal Vega scheme name (forwarded
|
|
524
|
+
as-is to VL's ``scale.scheme`` by geo/heatmap gradient rendering) or a
|
|
525
|
+
Dataface named palette (resolved to hex stops downstream by
|
|
526
|
+
``resolve_palette_stops``). Reject anything that resolves as neither.
|
|
527
|
+
|
|
528
|
+
A dash-suffixed name whose prefix is a real scheme (``"blues-9"``,
|
|
529
|
+
VL's sampled-discrete shorthand, which Dataface does not forward) gets
|
|
530
|
+
a specific hint naming the continuous scheme instead of the generic
|
|
531
|
+
unknown-palette message — that shape is the exact mistake this
|
|
532
|
+
validator exists to catch: an author reaching for N discrete buckets
|
|
533
|
+
that Dataface doesn't support yet, not a random typo.
|
|
534
|
+
|
|
535
|
+
``mode="before"`` on the raw dict (not ``field_validator``) so this
|
|
536
|
+
runs unchanged on both ``ScaleTargetConfig`` and the generated
|
|
537
|
+
``ScaleTargetConfigPatch`` — a field-scoped validator would need the
|
|
538
|
+
field to exist on this mixin itself, which a patch base never does.
|
|
539
|
+
"""
|
|
540
|
+
if not isinstance(data, dict):
|
|
541
|
+
return data
|
|
542
|
+
value = data.get("palette")
|
|
543
|
+
if not isinstance(value, str) or value in VEGA_SCHEME_NAMES:
|
|
544
|
+
return data
|
|
545
|
+
from dataface.core.compile.palette import ( # noqa: PLC0415
|
|
546
|
+
UnknownPaletteError,
|
|
547
|
+
palette as resolve_palette,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
try:
|
|
551
|
+
resolve_palette(value)
|
|
552
|
+
except UnknownPaletteError as exc:
|
|
553
|
+
base, _, suffix = value.rpartition("-")
|
|
554
|
+
if base in VEGA_SCHEME_NAMES and suffix.isdigit():
|
|
555
|
+
raise ValueError(
|
|
556
|
+
f"palette {value!r} requests a {suffix}-step discrete "
|
|
557
|
+
f"variant of the '{base}' scheme. Dataface does not support "
|
|
558
|
+
"bucketed/quantized color scales yet — use the continuous "
|
|
559
|
+
f"scheme '{base}' instead."
|
|
560
|
+
) from None
|
|
561
|
+
raise ValueError(str(exc)) from None
|
|
562
|
+
return data
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class ScaleTargetConfig(ScaleTargetPaletteValidationMixin):
|
|
414
566
|
"""Scale configuration for a single style target (background or color).
|
|
415
567
|
|
|
416
568
|
Used as a chart channel annotation (``ColumnScaleConfig``) and as the
|
|
@@ -447,6 +599,27 @@ class ScaleTargetConfig(BaseModel):
|
|
|
447
599
|
)
|
|
448
600
|
|
|
449
601
|
|
|
602
|
+
# Patch type for ScaleTargetConfig — generated (and registered) here, right after
|
|
603
|
+
# the compiled class, so every later recursive build_patch_model_ext call that
|
|
604
|
+
# encounters ScaleTargetConfig as a nested field (StaticGradientColorStyle.gradient,
|
|
605
|
+
# ColumnScaleConfig.color/background) reuses this exact registered class instead of
|
|
606
|
+
# a vanilla auto-generated one with no palette validation.
|
|
607
|
+
if TYPE_CHECKING:
|
|
608
|
+
|
|
609
|
+
class ScaleTargetConfigPatch(ScaleTargetConfig):
|
|
610
|
+
pass
|
|
611
|
+
|
|
612
|
+
else:
|
|
613
|
+
|
|
614
|
+
class _ScaleTargetConfigPatchBase(_PatchBase, ScaleTargetPaletteValidationMixin):
|
|
615
|
+
"""Base for ScaleTargetConfigPatch: extra="forbid" from _PatchBase plus
|
|
616
|
+
the palette validation from ScaleTargetPaletteValidationMixin."""
|
|
617
|
+
|
|
618
|
+
ScaleTargetConfigPatch = register_patch(ScaleTargetConfig)(
|
|
619
|
+
build_patch_model_ext(ScaleTargetConfig, base_cls=_ScaleTargetConfigPatchBase)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
|
|
450
623
|
class CategoricalColorStyle(BaseModel):
|
|
451
624
|
"""Categorical palette config: per-series colors and single-series ink list."""
|
|
452
625
|
|
|
@@ -875,7 +875,11 @@ class RedshiftSourceConfig(
|
|
|
875
875
|
):
|
|
876
876
|
"""Redshift source configuration.
|
|
877
877
|
|
|
878
|
-
Field names match dbt profiles.yml exactly
|
|
878
|
+
Field names match dbt profiles.yml exactly, except `autocommit`: Dataface
|
|
879
|
+
only reads on this connection, and a non-autocommit session holds
|
|
880
|
+
AccessShareLock on every table it read until the connection closes,
|
|
881
|
+
blocking any writer that needs ACCESS EXCLUSIVE. `build_adapter` forces
|
|
882
|
+
it on unconditionally, so it isn't part of the authored surface.
|
|
879
883
|
|
|
880
884
|
Read-only posture: no native driver flag. Bind to a SELECT-only IAM role
|
|
881
885
|
or database user at the Redshift/IAM layer.
|
|
@@ -957,10 +961,6 @@ class RedshiftSourceConfig(
|
|
|
957
961
|
str | None,
|
|
958
962
|
Field(description="AWS region containing the Redshift target."),
|
|
959
963
|
] = None
|
|
960
|
-
autocommit: Annotated[
|
|
961
|
-
bool | None,
|
|
962
|
-
Field(description="Whether Redshift statements autocommit."),
|
|
963
|
-
] = True
|
|
964
964
|
access_key_id: Annotated[
|
|
965
965
|
str | None,
|
|
966
966
|
Field(description="AWS access key identifier."),
|
|
@@ -42,25 +42,36 @@ from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
42
42
|
# Regression coverage: test_style_authored_bootstrap_order.py.
|
|
43
43
|
from dataface.core.compile.models.style.authored._base import (
|
|
44
44
|
AreaMarkStylePatch,
|
|
45
|
-
AxisDomainStylePatch,
|
|
46
|
-
AxisElementStylePatch,
|
|
47
45
|
AxisGridStylePatch,
|
|
48
|
-
|
|
46
|
+
AxisLabelStylePatch,
|
|
47
|
+
AxisLineStylePatch,
|
|
49
48
|
AxisTicksStylePatch,
|
|
49
|
+
AxisTitleStylePatch,
|
|
50
|
+
AxisXStylePatch,
|
|
51
|
+
AxisYStylePatch,
|
|
52
|
+
BandAxisStylePatch,
|
|
50
53
|
BarMarkStylePatch,
|
|
54
|
+
BaseAxisGridStylePatch,
|
|
55
|
+
BaseAxisStylePatch,
|
|
56
|
+
BaseScaleStylePatch,
|
|
51
57
|
DataTableStylePatch,
|
|
58
|
+
DimensionLabelStylePatch,
|
|
59
|
+
DimensionTicksStylePatch,
|
|
52
60
|
EndpointLabelsConfig,
|
|
53
61
|
EndpointLabelsConfigPatch,
|
|
54
62
|
GlobalMarksStylePatch,
|
|
55
63
|
LegendStylePatch,
|
|
56
64
|
LineMarkStylePatch,
|
|
65
|
+
MeasureGridStylePatch,
|
|
57
66
|
PaddingStylePatch,
|
|
58
67
|
PointMarkStylePatch,
|
|
59
|
-
|
|
68
|
+
QuantitativeAxisStylePatch,
|
|
69
|
+
ScaleContinuousStylePatch,
|
|
60
70
|
StylePatch,
|
|
61
71
|
TitleStylePatch,
|
|
62
72
|
TooltipStylePatch,
|
|
63
73
|
TotalStylePatch,
|
|
74
|
+
XScaleStylePatch,
|
|
64
75
|
)
|
|
65
76
|
from dataface.core.compile.models.style.authored.area import AreaChartStylePatch
|
|
66
77
|
from dataface.core.compile.models.style.authored.bar import BarChartStylePatch
|
|
@@ -99,6 +110,7 @@ from dataface.core.compile.models.style.authored.table import (
|
|
|
99
110
|
TableColumnConfig,
|
|
100
111
|
TableColumnDefaultsConfig,
|
|
101
112
|
_validate_glyph_pair as _validate_glyph_pair,
|
|
113
|
+
fill_table_column_defaults,
|
|
102
114
|
)
|
|
103
115
|
|
|
104
116
|
__all__ = [
|
|
@@ -106,11 +118,23 @@ __all__ = [
|
|
|
106
118
|
"_validate_glyph_pair",
|
|
107
119
|
"AreaChartStylePatch",
|
|
108
120
|
"AreaMarkStylePatch",
|
|
109
|
-
"AxisDomainStylePatch",
|
|
110
|
-
"AxisElementStylePatch",
|
|
111
121
|
"AxisGridStylePatch",
|
|
112
|
-
"
|
|
122
|
+
"AxisLabelStylePatch",
|
|
123
|
+
"AxisLineStylePatch",
|
|
113
124
|
"AxisTicksStylePatch",
|
|
125
|
+
"AxisTitleStylePatch",
|
|
126
|
+
"AxisXStylePatch",
|
|
127
|
+
"AxisYStylePatch",
|
|
128
|
+
"BandAxisStylePatch",
|
|
129
|
+
"BaseAxisGridStylePatch",
|
|
130
|
+
"BaseAxisStylePatch",
|
|
131
|
+
"BaseScaleStylePatch",
|
|
132
|
+
"DimensionLabelStylePatch",
|
|
133
|
+
"DimensionTicksStylePatch",
|
|
134
|
+
"MeasureGridStylePatch",
|
|
135
|
+
"QuantitativeAxisStylePatch",
|
|
136
|
+
"ScaleContinuousStylePatch",
|
|
137
|
+
"XScaleStylePatch",
|
|
114
138
|
"BarChartStylePatch",
|
|
115
139
|
"BarMarkStylePatch",
|
|
116
140
|
"CalloutChartStylePatch",
|
|
@@ -131,7 +155,6 @@ __all__ = [
|
|
|
131
155
|
"PieChartStylePatch",
|
|
132
156
|
"PointMapChartStylePatch",
|
|
133
157
|
"PointMarkStylePatch",
|
|
134
|
-
"ScaleStylePatch",
|
|
135
158
|
"ScatterChartStylePatch",
|
|
136
159
|
"SparkBarBarStylePatch",
|
|
137
160
|
"SparkBarChartLabelStylePatch",
|
|
@@ -146,6 +169,7 @@ __all__ = [
|
|
|
146
169
|
"TableColumnDefaultsConfig",
|
|
147
170
|
"TitleStylePatch",
|
|
148
171
|
"TooltipStylePatch",
|
|
172
|
+
"fill_table_column_defaults",
|
|
149
173
|
"TotalStylePatch",
|
|
150
174
|
]
|
|
151
175
|
|