Source code for simstack.util.make_graph
[docs]
def model_to_graph_data(model, exclude_none=True, exclude_unset=True, exclude=None):
"""
Checks if an odmantic Model contains fields that can be interpreted as graph data
(multiple lists of the same length) and converts it to a format suitable for ag-graph.
Args:
model: An odmantic Model instance
exclude_none (bool): Whether to exclude None values
exclude_unset (bool): Whether to exclude unset values
exclude (set): Fields to exclude
Returns:
dict: A dictionary with graph data if the model contains compatible fields, otherwise None
"""
if exclude is None:
exclude = set()
# Collect all list fields from the model
list_fields = {}
# Get all fields from the model
if hasattr(model, "__fields__"):
fields = model.__fields__
else:
# Fallback if field access pattern is different
fields = {
k: None
for k in dir(model)
if not k.startswith("_") and not callable(getattr(model, k))
}
# Process each field to find lists with numeric or string values
for field_name in fields:
if field_name in exclude:
continue
value = getattr(model, field_name, None)
# Skip None values if exclude_none is True
if value is None and exclude_none:
continue
# Handle unset values if exclude_unset is True
if exclude_unset and value == getattr(
fields.get(field_name), "default", object()
):
continue
# Check if the field is a list with numeric or string values
if isinstance(value, list) and len(value) > 0:
# Check if all elements are numeric or strings
if all(isinstance(item, (int, float, str)) for item in value):
list_fields[field_name] = value
# If we don't have at least two lists, we can't make a graph
if len(list_fields) < 2:
return None
# Check if all lists have the same length
list_lengths = {len(lst) for lst in list_fields.values()}
if len(list_lengths) != 1:
return None # Not all lists have the same length
list_length = next(iter(list_lengths))
if list_length == 0:
return None # Empty lists
# Generate data series for each potential y-axis
series = []
# Use first string list as labels if available, otherwise use index
x_axis_field = None
for field_name, values in list_fields.items():
if all(isinstance(item, str) for item in values):
x_axis_field = field_name
break
# If no string list found, use the first field as x-axis
if x_axis_field is None:
x_axis_field = next(iter(list_fields.keys()))
x_values = list_fields[x_axis_field]
# Create series for each numeric list (except the x-axis)
for field_name, values in list_fields.items():
if field_name != x_axis_field:
# Create a series for this y-axis
series_data = []
for i in range(list_length):
series_data.append({"x": x_values[i], "y": values[i]})
series.append({"name": field_name, "data": series_data})
# Create the graph data structure
graph_data = {
"chart": {
"type": "line" # Default chart type, can be changed
},
"title": {
"text": model.__class__.__name__ # Use model class name as title
},
"xAxis": {"title": {"text": x_axis_field}},
"yAxis": {"title": {"text": "Value"}},
"series": series,
}
return graph_data