simstack 0.2.0__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.
- simstack/__init__.py +0 -0
- simstack/_version.py +34 -0
- simstack/core/__init__.py +0 -0
- simstack/core/artifacts.py +447 -0
- simstack/core/asnyc_helper.py +14 -0
- simstack/core/config_file.py +60 -0
- simstack/core/context.py +167 -0
- simstack/core/data.py +139 -0
- simstack/core/definitions.py +21 -0
- simstack/core/engine.py +116 -0
- simstack/core/find_simstack_modules.py +43 -0
- simstack/core/hash.py +177 -0
- simstack/core/model_table.py +199 -0
- simstack/core/node.py +911 -0
- simstack/core/node_runner.py +254 -0
- simstack/core/node_table.py +380 -0
- simstack/core/recompute_artifacts.py +120 -0
- simstack/core/reset_db.py +12 -0
- simstack/core/resources.py +101 -0
- simstack/core/run_node.py +69 -0
- simstack/core/runner.py +435 -0
- simstack/core/simstack_result.py +41 -0
- simstack/core/submit_node.py +143 -0
- simstack/core/task_id.py +22 -0
- simstack/data/__init__.py +0 -0
- simstack/methods/__init__.py +0 -0
- simstack/methods/get_file.py +10 -0
- simstack/methods/multiple_executor.py +307 -0
- simstack/models/__init__.py +37 -0
- simstack/models/array_list.py +48 -0
- simstack/models/array_storage.py +54 -0
- simstack/models/artifact_models.py +127 -0
- simstack/models/base_types.py +208 -0
- simstack/models/charts_artifact.py +411 -0
- simstack/models/dataset.py +432 -0
- simstack/models/dataset_metadata.py +303 -0
- simstack/models/file_instance.py +77 -0
- simstack/models/file_list.py +321 -0
- simstack/models/files.py +229 -0
- simstack/models/log_entry_model.py +37 -0
- simstack/models/models.py +40 -0
- simstack/models/node_registry.py +105 -0
- simstack/models/pandas_model.py +210 -0
- simstack/models/parameters.py +388 -0
- simstack/models/pickle_models.py +311 -0
- simstack/models/runner_model.py +39 -0
- simstack/models/simple_table.py +32 -0
- simstack/models/simstack_model.py +97 -0
- simstack/models/slurm_info.py +17 -0
- simstack/models/table_artifact.py +87 -0
- simstack/util/__init__.py +0 -0
- simstack/util/async_zip_utils.py +18 -0
- simstack/util/cleaned_json_schema.py +20 -0
- simstack/util/config_reader.py +273 -0
- simstack/util/custom_model_dump.py +75 -0
- simstack/util/db.py +457 -0
- simstack/util/db_logger.py +117 -0
- simstack/util/default_from_dict.py +57 -0
- simstack/util/directory_iterator.py +216 -0
- simstack/util/file_hashing.py +68 -0
- simstack/util/files.py +396 -0
- simstack/util/find_python_files.py +33 -0
- simstack/util/generate_ui_schema.py +80 -0
- simstack/util/geometry_utils.py +118 -0
- simstack/util/get_module_path.py +26 -0
- simstack/util/git_pull.py +65 -0
- simstack/util/git_repository_status.py +93 -0
- simstack/util/import_module.py +65 -0
- simstack/util/importer.py +163 -0
- simstack/util/make_graph.py +107 -0
- simstack/util/make_table.py +827 -0
- simstack/util/minimal_route_finder.py +72 -0
- simstack/util/mock_db.py +144 -0
- simstack/util/module_path_checker.py +38 -0
- simstack/util/path_manager.py +234 -0
- simstack/util/project_root_finder.py +36 -0
- simstack/util/queue_watcher.py +262 -0
- simstack/util/route_finder.py +74 -0
- simstack/util/runner_util.py +162 -0
- simstack/util/safe_code_executor.py +194 -0
- simstack/util/sample_config.py +98 -0
- simstack/util/sensitive_content_filter.py +48 -0
- simstack/util/setup_logging.py +49 -0
- simstack/util/submit_to_watchdog.py +106 -0
- simstack/util/ui_tools.py +213 -0
- simstack-0.2.0.dist-info/METADATA +361 -0
- simstack-0.2.0.dist-info/RECORD +91 -0
- simstack-0.2.0.dist-info/WHEEL +5 -0
- simstack-0.2.0.dist-info/entry_points.txt +10 -0
- simstack-0.2.0.dist-info/licenses/LICENSE +21 -0
- simstack-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from odmantic import Model, Reference
|
|
2
|
+
from pydantic import model_validator
|
|
3
|
+
|
|
4
|
+
from simstack.models import simstack_model
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@simstack_model
|
|
8
|
+
class IntData(Model):
|
|
9
|
+
field_name: str = "int"
|
|
10
|
+
value: int
|
|
11
|
+
|
|
12
|
+
@model_validator(mode="before")
|
|
13
|
+
@classmethod
|
|
14
|
+
def ensure_fieldname(cls, data):
|
|
15
|
+
"""Ensure fieldname is set for existing documents"""
|
|
16
|
+
if isinstance(data, dict) and "field_name" not in data:
|
|
17
|
+
data["field_name"] = "int"
|
|
18
|
+
return data
|
|
19
|
+
|
|
20
|
+
def make_table_entries(
|
|
21
|
+
self,
|
|
22
|
+
max_recursion_level=1,
|
|
23
|
+
drop_id=True,
|
|
24
|
+
current_level=0,
|
|
25
|
+
visited=None,
|
|
26
|
+
field_prefix="",
|
|
27
|
+
):
|
|
28
|
+
return {self.field_name: self.value}
|
|
29
|
+
|
|
30
|
+
def make_column_defs_instance(
|
|
31
|
+
self,
|
|
32
|
+
table_name=None,
|
|
33
|
+
max_recursion_level=1,
|
|
34
|
+
drop_id=True,
|
|
35
|
+
current_level=0,
|
|
36
|
+
visited=None,
|
|
37
|
+
field_prefix="",
|
|
38
|
+
):
|
|
39
|
+
return [
|
|
40
|
+
{
|
|
41
|
+
"field": self.field_name,
|
|
42
|
+
"headerName": self.field_name,
|
|
43
|
+
"type": "numericColumn",
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def ui_schema(cls, **kwargs) -> dict:
|
|
49
|
+
return {
|
|
50
|
+
"ui:field": "IntDataField"
|
|
51
|
+
} # This tells RJSF to use the custom component
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@simstack_model
|
|
55
|
+
class FloatData(Model):
|
|
56
|
+
field_name: str = "float"
|
|
57
|
+
value: float
|
|
58
|
+
|
|
59
|
+
@model_validator(mode="before")
|
|
60
|
+
@classmethod
|
|
61
|
+
def ensure_fieldname(cls, data):
|
|
62
|
+
"""Ensure fieldname is set for existing documents"""
|
|
63
|
+
if isinstance(data, dict) and "field_name" not in data:
|
|
64
|
+
data["field_name"] = "float"
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
def make_table_entries(
|
|
68
|
+
self,
|
|
69
|
+
max_recursion_level=1,
|
|
70
|
+
drop_id=True,
|
|
71
|
+
current_level=0,
|
|
72
|
+
visited=None,
|
|
73
|
+
field_prefix="",
|
|
74
|
+
):
|
|
75
|
+
return {self.field_name: self.value}
|
|
76
|
+
|
|
77
|
+
def make_column_defs_instance(
|
|
78
|
+
self,
|
|
79
|
+
table_name=None,
|
|
80
|
+
max_recursion_level=1,
|
|
81
|
+
drop_id=True,
|
|
82
|
+
current_level=0,
|
|
83
|
+
visited=None,
|
|
84
|
+
field_prefix="",
|
|
85
|
+
):
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
"field": self.field_name,
|
|
89
|
+
"headerName": self.field_name,
|
|
90
|
+
"type": "numericColumn",
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def ui_schema(cls, **kwargs) -> dict:
|
|
96
|
+
return {
|
|
97
|
+
"ui:field": "FloatDataField"
|
|
98
|
+
} # This tells RJSF to use the custom component
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@simstack_model
|
|
102
|
+
class StringData(Model):
|
|
103
|
+
field_name: str = "text"
|
|
104
|
+
value: str
|
|
105
|
+
|
|
106
|
+
@model_validator(mode="before")
|
|
107
|
+
@classmethod
|
|
108
|
+
def ensure_fieldname(cls, data):
|
|
109
|
+
"""Ensure fieldname is set for existing documents"""
|
|
110
|
+
if isinstance(data, dict) and "field_name" not in data:
|
|
111
|
+
data["field_name"] = "text"
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
def make_table_entries(
|
|
115
|
+
self,
|
|
116
|
+
max_recursion_level=1,
|
|
117
|
+
drop_id=True,
|
|
118
|
+
current_level=0,
|
|
119
|
+
visited=None,
|
|
120
|
+
field_prefix="",
|
|
121
|
+
):
|
|
122
|
+
return {self.field_name: self.value}
|
|
123
|
+
|
|
124
|
+
def make_column_defs_instance(
|
|
125
|
+
self,
|
|
126
|
+
table_name=None,
|
|
127
|
+
max_recursion_level=1,
|
|
128
|
+
drop_id=True,
|
|
129
|
+
current_level=0,
|
|
130
|
+
visited=None,
|
|
131
|
+
field_prefix="",
|
|
132
|
+
):
|
|
133
|
+
return [
|
|
134
|
+
{
|
|
135
|
+
"field": self.field_name,
|
|
136
|
+
"headerName": self.field_name,
|
|
137
|
+
"type": "textColumn",
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def ui_schema(cls, **kwargs) -> dict:
|
|
143
|
+
return {
|
|
144
|
+
"ui:field": "StringDataField"
|
|
145
|
+
} # This tells RJSF to use the custom component
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@simstack_model
|
|
149
|
+
class BooleanData(Model):
|
|
150
|
+
field_name: str = "boolean"
|
|
151
|
+
value: bool
|
|
152
|
+
|
|
153
|
+
@model_validator(mode="before")
|
|
154
|
+
@classmethod
|
|
155
|
+
def ensure_fieldname(cls, data):
|
|
156
|
+
"""Ensure fieldname is set for existing documents"""
|
|
157
|
+
if isinstance(data, dict) and "field_name" not in data:
|
|
158
|
+
data["field_name"] = "boolean"
|
|
159
|
+
return data
|
|
160
|
+
|
|
161
|
+
def make_table_entries(
|
|
162
|
+
self,
|
|
163
|
+
max_recursion_level=1,
|
|
164
|
+
drop_id=True,
|
|
165
|
+
current_level=0,
|
|
166
|
+
visited=None,
|
|
167
|
+
field_prefix="",
|
|
168
|
+
):
|
|
169
|
+
return {self.field_name: self.value}
|
|
170
|
+
|
|
171
|
+
def make_column_defs_instance(
|
|
172
|
+
self,
|
|
173
|
+
table_name=None,
|
|
174
|
+
max_recursion_level=1,
|
|
175
|
+
drop_id=True,
|
|
176
|
+
current_level=0,
|
|
177
|
+
visited=None,
|
|
178
|
+
field_prefix="",
|
|
179
|
+
):
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
"field": self.field_name,
|
|
183
|
+
"headerName": self.field_name,
|
|
184
|
+
"type": "booleanColumn",
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
@classmethod
|
|
189
|
+
def ui_schema(cls, **kwargs) -> dict:
|
|
190
|
+
return {
|
|
191
|
+
"ui:field": "BooleanDataField"
|
|
192
|
+
} # This tells RJSF to use the custom component
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@simstack_model
|
|
196
|
+
class BinaryOperationInput(Model):
|
|
197
|
+
arg1: FloatData = Reference()
|
|
198
|
+
arg2: FloatData = Reference()
|
|
199
|
+
|
|
200
|
+
def __str__(self):
|
|
201
|
+
return f"BinaryOperationInput(arg1={self.arg1}, arg2={self.arg2})"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@simstack_model
|
|
205
|
+
class IteratorInput(Model):
|
|
206
|
+
start: int
|
|
207
|
+
stop: int
|
|
208
|
+
generator: str = "range"
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
from typing import List, Union, Literal, Optional, Dict, Any
|
|
2
|
+
|
|
3
|
+
from odmantic import Model, Field, EmbeddedModel, ObjectId
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Chart Series Definitions
|
|
7
|
+
class AGChartSeriesBase(EmbeddedModel):
|
|
8
|
+
"""Base class for AG-Charts series configuration."""
|
|
9
|
+
|
|
10
|
+
type: str = Field(..., description="Chart series type")
|
|
11
|
+
xKey: str = Field(..., description="Key for x-axis data")
|
|
12
|
+
yKey: str = Field(..., description="Key for y-axis data")
|
|
13
|
+
visible: Optional[bool] = Field(
|
|
14
|
+
default=True, description="Whether series is visible"
|
|
15
|
+
)
|
|
16
|
+
showInLegend: Optional[bool] = Field(default=True, description="Show in legend")
|
|
17
|
+
title: Optional[str] = Field(default=None, description="Series title")
|
|
18
|
+
# Chart data
|
|
19
|
+
data: List[Dict[str, Any]] = Field(default_factory=list, description="Chart data")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AGLineSeriesConfig(AGChartSeriesBase):
|
|
23
|
+
"""AG-Charts line series configuration."""
|
|
24
|
+
|
|
25
|
+
type: Literal["line"] = "line"
|
|
26
|
+
strokeWidth: Optional[float] = Field(default=2, description="Line stroke width")
|
|
27
|
+
strokeOpacity: Optional[float] = Field(default=1, description="Line stroke opacity")
|
|
28
|
+
lineDash: Optional[List[float]] = Field(
|
|
29
|
+
default=None, description="Line dash pattern"
|
|
30
|
+
)
|
|
31
|
+
marker: Optional[Dict[str, Any]] = Field(
|
|
32
|
+
default=None, description="Marker configuration"
|
|
33
|
+
)
|
|
34
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
35
|
+
default=None, description="Tooltip configuration"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AGBarSeriesConfig(AGChartSeriesBase):
|
|
40
|
+
"""AG-Charts bar series configuration."""
|
|
41
|
+
|
|
42
|
+
type: Literal["bar"] = "bar"
|
|
43
|
+
fillOpacity: Optional[float] = Field(default=1, description="Bar fill opacity")
|
|
44
|
+
strokeWidth: Optional[float] = Field(default=0, description="Bar stroke width")
|
|
45
|
+
cornerRadius: Optional[float] = Field(default=0, description="Bar corner radius")
|
|
46
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
47
|
+
default=None, description="Tooltip configuration"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AGColumnSeriesConfig(AGChartSeriesBase):
|
|
52
|
+
"""AG-Charts column series configuration."""
|
|
53
|
+
|
|
54
|
+
type: Literal["column"] = "column"
|
|
55
|
+
fillOpacity: Optional[float] = Field(default=1, description="Column fill opacity")
|
|
56
|
+
strokeWidth: Optional[float] = Field(default=0, description="Column stroke width")
|
|
57
|
+
cornerRadius: Optional[float] = Field(default=0, description="Column corner radius")
|
|
58
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
59
|
+
default=None, description="Tooltip configuration"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AGAreaSeriesConfig(AGChartSeriesBase):
|
|
64
|
+
"""AG-Charts area series configuration."""
|
|
65
|
+
|
|
66
|
+
type: Literal["area"] = "area"
|
|
67
|
+
fillOpacity: Optional[float] = Field(default=0.8, description="Area fill opacity")
|
|
68
|
+
strokeWidth: Optional[float] = Field(default=2, description="Area stroke width")
|
|
69
|
+
marker: Optional[Dict[str, Any]] = Field(
|
|
70
|
+
default=None, description="Marker configuration"
|
|
71
|
+
)
|
|
72
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
73
|
+
default=None, description="Tooltip configuration"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class AGScatterSeriesConfig(AGChartSeriesBase):
|
|
78
|
+
"""AG-Charts scatter series configuration."""
|
|
79
|
+
|
|
80
|
+
type: Literal["scatter"] = "scatter"
|
|
81
|
+
marker: Optional[Dict[str, Any]] = Field(
|
|
82
|
+
default=None, description="Marker configuration"
|
|
83
|
+
)
|
|
84
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
85
|
+
default=None, description="Tooltip configuration"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class AGPieSeriesConfig(EmbeddedModel):
|
|
90
|
+
"""AG-Charts pie series configuration."""
|
|
91
|
+
|
|
92
|
+
type: Literal["pie"] = "pie"
|
|
93
|
+
angleKey: str = Field(..., description="Key for pie slice angles")
|
|
94
|
+
radiusKey: Optional[str] = Field(
|
|
95
|
+
default=None, description="Key for pie slice radius"
|
|
96
|
+
)
|
|
97
|
+
labelKey: Optional[str] = Field(
|
|
98
|
+
default=None, description="Key for pie slice labels"
|
|
99
|
+
)
|
|
100
|
+
visible: Optional[bool] = Field(
|
|
101
|
+
default=True, description="Whether series is visible"
|
|
102
|
+
)
|
|
103
|
+
showInLegend: Optional[bool] = Field(default=True, description="Show in legend")
|
|
104
|
+
title: Optional[str] = Field(default=None, description="Series title")
|
|
105
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
106
|
+
default=None, description="Tooltip configuration"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class AGDonutSeriesConfig(EmbeddedModel):
|
|
111
|
+
"""AG-Charts donut series configuration."""
|
|
112
|
+
|
|
113
|
+
type: Literal["donut"] = "donut"
|
|
114
|
+
angleKey: str = Field(..., description="Key for donut slice angles")
|
|
115
|
+
radiusKey: Optional[str] = Field(
|
|
116
|
+
default=None, description="Key for donut slice radius"
|
|
117
|
+
)
|
|
118
|
+
labelKey: Optional[str] = Field(
|
|
119
|
+
default=None, description="Key for donut slice labels"
|
|
120
|
+
)
|
|
121
|
+
innerRadiusRatio: Optional[float] = Field(
|
|
122
|
+
default=0.6, description="Inner radius ratio"
|
|
123
|
+
)
|
|
124
|
+
visible: Optional[bool] = Field(
|
|
125
|
+
default=True, description="Whether series is visible"
|
|
126
|
+
)
|
|
127
|
+
showInLegend: Optional[bool] = Field(default=True, description="Show in legend")
|
|
128
|
+
title: Optional[str] = Field(default=None, description="Series title")
|
|
129
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
130
|
+
default=None, description="Tooltip configuration"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Union type for all series configurations
|
|
135
|
+
AGChartSeries = Union[
|
|
136
|
+
AGLineSeriesConfig,
|
|
137
|
+
AGBarSeriesConfig,
|
|
138
|
+
AGColumnSeriesConfig,
|
|
139
|
+
AGAreaSeriesConfig,
|
|
140
|
+
AGScatterSeriesConfig,
|
|
141
|
+
AGPieSeriesConfig,
|
|
142
|
+
AGDonutSeriesConfig,
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Axis Configurations
|
|
147
|
+
class AGChartAxisConfig(EmbeddedModel):
|
|
148
|
+
"""AG-Charts axis configuration."""
|
|
149
|
+
|
|
150
|
+
type: Literal["category", "number", "time", "log"] = Field(
|
|
151
|
+
..., description="Axis type"
|
|
152
|
+
)
|
|
153
|
+
position: Literal["top", "right", "bottom", "left"] = Field(
|
|
154
|
+
..., description="Axis position"
|
|
155
|
+
)
|
|
156
|
+
title: Optional[str] = Field(default=None, description="Axis title")
|
|
157
|
+
min: Optional[float] = Field(default=None, description="Minimum axis value")
|
|
158
|
+
max: Optional[float] = Field(default=None, description="Maximum axis value")
|
|
159
|
+
tick: Optional[Dict[str, Any]] = Field(
|
|
160
|
+
default=None, description="Tick configuration"
|
|
161
|
+
)
|
|
162
|
+
label: Optional[Dict[str, Any]] = Field(
|
|
163
|
+
default=None, description="Label configuration"
|
|
164
|
+
)
|
|
165
|
+
gridStyle: Optional[List[Dict[str, Any]]] = Field(
|
|
166
|
+
default=None, description="Grid line style"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Legend Configuration
|
|
171
|
+
class AGChartLegendConfig(EmbeddedModel):
|
|
172
|
+
"""AG-Charts legend configuration."""
|
|
173
|
+
|
|
174
|
+
enabled: bool = Field(default=True, description="Enable legend")
|
|
175
|
+
position: Literal["top", "right", "bottom", "left"] = Field(
|
|
176
|
+
default="right", description="Legend position"
|
|
177
|
+
)
|
|
178
|
+
spacing: float = Field(default=20, description="Legend spacing")
|
|
179
|
+
item: Dict[str, Any] = Field(
|
|
180
|
+
default_factory=dict, description="Legend item configuration"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Title Configuration
|
|
185
|
+
class AGChartTitleConfig(EmbeddedModel):
|
|
186
|
+
"""AG-Charts title configuration."""
|
|
187
|
+
|
|
188
|
+
text: str = Field("Chart Title", description="Title text")
|
|
189
|
+
# fontSize: Optional[int] = Field(default=16, description="Title font size")
|
|
190
|
+
# fontWeight: Optional[str] = Field(default="bold", description="Title font weight")
|
|
191
|
+
# color: Optional[str] = Field(default=None, description="Title color")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Subtitle Configuration
|
|
195
|
+
class AGChartSubtitleConfig(EmbeddedModel):
|
|
196
|
+
"""AG-Charts subtitle configuration."""
|
|
197
|
+
|
|
198
|
+
text: str = Field(..., description="Subtitle text")
|
|
199
|
+
fontSize: Optional[int] = Field(default=12, description="Subtitle font size")
|
|
200
|
+
color: Optional[str] = Field(default=None, description="Subtitle color")
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# Main Chart Model
|
|
204
|
+
class ChartArtifactModel(Model):
|
|
205
|
+
"""AG-Charts configuration model."""
|
|
206
|
+
|
|
207
|
+
parent_id: ObjectId = Field(default=None, description="ID of the node registry")
|
|
208
|
+
|
|
209
|
+
# Chart data
|
|
210
|
+
data: List[Dict[str, Any]] = Field(default_factory=list, description="Chart data")
|
|
211
|
+
|
|
212
|
+
# Chart configuration
|
|
213
|
+
title: AGChartTitleConfig = Field(..., description="Chart title configuration")
|
|
214
|
+
# subtitle: AGChartSubtitleConfig = Field(..., description="Chart subtitle configuration")
|
|
215
|
+
|
|
216
|
+
# Series configuration
|
|
217
|
+
series: List[AGChartSeries] = Field(
|
|
218
|
+
default_factory=list, description="Chart series configurations"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Axes configuration
|
|
222
|
+
axes: List[AGChartAxisConfig] = Field(
|
|
223
|
+
default_factory=list, description="Chart axes configurations"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# # Legend configuration
|
|
227
|
+
legend: AGChartLegendConfig = Field(
|
|
228
|
+
default=AGChartLegendConfig(), description="Legend configuration"
|
|
229
|
+
)
|
|
230
|
+
#
|
|
231
|
+
# Chart styling and behavior
|
|
232
|
+
width: int = Field(default=800, description="Chart width in pixels")
|
|
233
|
+
height: int = Field(default=400, description="Chart height in pixels")
|
|
234
|
+
padding: Optional[Dict[str, int]] = Field(default=None, description="Chart padding")
|
|
235
|
+
background: Optional[Dict[str, Any]] = Field(
|
|
236
|
+
default=None, description="Background configuration"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Animation
|
|
240
|
+
animation: Optional[Dict[str, Any]] = Field(
|
|
241
|
+
default=None, description="Animation configuration"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Tooltip
|
|
245
|
+
tooltip: Optional[Dict[str, Any]] = Field(
|
|
246
|
+
default=None, description="Global tooltip configuration"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# Theme
|
|
250
|
+
theme: Optional[str] = Field(default="ag-default", description="Chart theme")
|
|
251
|
+
|
|
252
|
+
# Additional options
|
|
253
|
+
options: Optional[Dict[str, Any]] = Field(
|
|
254
|
+
default_factory=dict, description="Additional chart options"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# Helper functions for creating specific chart types
|
|
259
|
+
def create_simple_line_chart(
|
|
260
|
+
data: List[Dict[str, Any]],
|
|
261
|
+
x_key: str,
|
|
262
|
+
y_key: str,
|
|
263
|
+
title: Optional[str] = None,
|
|
264
|
+
parent_id: ObjectId = None,
|
|
265
|
+
) -> ChartArtifactModel:
|
|
266
|
+
"""Create a simple line chart."""
|
|
267
|
+
chart_title = (
|
|
268
|
+
AGChartTitleConfig(text=title) if title else AGChartTitleConfig(text="Chart")
|
|
269
|
+
)
|
|
270
|
+
# subtitle = AGChartSubtitleConfig(text="Subtitle")
|
|
271
|
+
|
|
272
|
+
series = [
|
|
273
|
+
AGLineSeriesConfig(
|
|
274
|
+
type="line", xKey=x_key, yKey=y_key, title=y_key.title(), data=data
|
|
275
|
+
)
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
axes = [
|
|
279
|
+
AGChartAxisConfig(type="number", position="bottom", title=x_key.title()),
|
|
280
|
+
AGChartAxisConfig(type="number", position="left", title=y_key.title()),
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
return ChartArtifactModel(
|
|
284
|
+
parent_id=parent_id,
|
|
285
|
+
data=data,
|
|
286
|
+
title=chart_title,
|
|
287
|
+
# subtitle=subtitle,
|
|
288
|
+
series=series,
|
|
289
|
+
axes=axes,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def create_simple_bar_chart(
|
|
294
|
+
data: List[Dict[str, Any]],
|
|
295
|
+
x_key: str,
|
|
296
|
+
y_key: str,
|
|
297
|
+
title: Optional[str] = None,
|
|
298
|
+
parent_id: Optional[ObjectId] = None,
|
|
299
|
+
) -> ChartArtifactModel:
|
|
300
|
+
"""Create a simple bar chart."""
|
|
301
|
+
chart_title = AGChartTitleConfig(text=title) if title else None
|
|
302
|
+
|
|
303
|
+
series = [
|
|
304
|
+
AGColumnSeriesConfig(type="column", xKey=x_key, yKey=y_key, title=y_key.title())
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
axes = [
|
|
308
|
+
AGChartAxisConfig(type="category", position="bottom", title=x_key.title()),
|
|
309
|
+
AGChartAxisConfig(type="number", position="left", title=y_key.title()),
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
return ChartArtifactModel(
|
|
313
|
+
parent_id=parent_id, data=data, title=chart_title, series=series, axes=axes
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def create_simple_pie_chart(
|
|
318
|
+
data: List[Dict[str, Any]],
|
|
319
|
+
angle_key: str,
|
|
320
|
+
label_key: str,
|
|
321
|
+
title: Optional[str] = None,
|
|
322
|
+
parent_id: Optional[ObjectId] = None,
|
|
323
|
+
) -> ChartArtifactModel:
|
|
324
|
+
"""Create a simple pie chart."""
|
|
325
|
+
chart_title = AGChartTitleConfig(text=title) if title else None
|
|
326
|
+
|
|
327
|
+
series = [
|
|
328
|
+
AGPieSeriesConfig(
|
|
329
|
+
type="pie", angleKey=angle_key, labelKey=label_key, title="Distribution"
|
|
330
|
+
)
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
return ChartArtifactModel(
|
|
334
|
+
parent_id=parent_id, data=data, title=chart_title, series=series
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def create_simple_area_chart(
|
|
339
|
+
data: List[Dict[str, Any]],
|
|
340
|
+
x_key: str,
|
|
341
|
+
y_key: str,
|
|
342
|
+
title: Optional[str] = None,
|
|
343
|
+
parent_id: Optional[ObjectId] = None,
|
|
344
|
+
) -> ChartArtifactModel:
|
|
345
|
+
"""Create a simple area chart."""
|
|
346
|
+
chart_title = AGChartTitleConfig(text=title) if title else None
|
|
347
|
+
|
|
348
|
+
series = [
|
|
349
|
+
AGAreaSeriesConfig(type="area", xKey=x_key, yKey=y_key, title=y_key.title())
|
|
350
|
+
]
|
|
351
|
+
|
|
352
|
+
axes = [
|
|
353
|
+
AGChartAxisConfig(type="category", position="bottom", title=x_key.title()),
|
|
354
|
+
AGChartAxisConfig(type="number", position="left", title=y_key.title()),
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
return ChartArtifactModel(
|
|
358
|
+
parent_id=parent_id, data=data, title=chart_title, series=series, axes=axes
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def create_simple_scatter_chart(
|
|
363
|
+
data: List[Dict[str, Any]],
|
|
364
|
+
x_key: str,
|
|
365
|
+
y_key: str,
|
|
366
|
+
title: Optional[str] = None,
|
|
367
|
+
parent_id: Optional[ObjectId] = None,
|
|
368
|
+
) -> ChartArtifactModel:
|
|
369
|
+
"""Create a simple scatter chart."""
|
|
370
|
+
chart_title = AGChartTitleConfig(text=title) if title else None
|
|
371
|
+
|
|
372
|
+
series = [
|
|
373
|
+
AGScatterSeriesConfig(
|
|
374
|
+
type="scatter", xKey=x_key, yKey=y_key, title=y_key.title()
|
|
375
|
+
)
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
axes = [
|
|
379
|
+
AGChartAxisConfig(type="number", position="bottom", title=x_key.title()),
|
|
380
|
+
AGChartAxisConfig(type="number", position="left", title=y_key.title()),
|
|
381
|
+
]
|
|
382
|
+
|
|
383
|
+
return ChartArtifactModel(
|
|
384
|
+
parent_id=parent_id, data=data, title=chart_title, series=series, axes=axes
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def create_simple_donut_chart(
|
|
389
|
+
data: List[Dict[str, Any]],
|
|
390
|
+
angle_key: str,
|
|
391
|
+
label_key: str,
|
|
392
|
+
title: Optional[str] = None,
|
|
393
|
+
inner_radius_ratio: float = 0.6,
|
|
394
|
+
parent_id: Optional[ObjectId] = None,
|
|
395
|
+
) -> ChartArtifactModel:
|
|
396
|
+
"""Create a simple donut chart."""
|
|
397
|
+
chart_title = AGChartTitleConfig(text=title) if title else None
|
|
398
|
+
|
|
399
|
+
series = [
|
|
400
|
+
AGDonutSeriesConfig(
|
|
401
|
+
type="donut",
|
|
402
|
+
angleKey=angle_key,
|
|
403
|
+
labelKey=label_key,
|
|
404
|
+
innerRadiusRatio=inner_radius_ratio,
|
|
405
|
+
title="Distribution",
|
|
406
|
+
)
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
return ChartArtifactModel(
|
|
410
|
+
parent_id=parent_id, data=data, title=chart_title, series=series
|
|
411
|
+
)
|