st-graph-workbench 0.1.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.
Files changed (91) hide show
  1. st_graph_workbench/__init__.py +115 -0
  2. st_graph_workbench/component/__init__.py +1 -0
  3. st_graph_workbench/component/_expansion_contract.py +106 -0
  4. st_graph_workbench/component/_ids.py +44 -0
  5. st_graph_workbench/component/_typing.py +6 -0
  6. st_graph_workbench/component/_warnings.py +8 -0
  7. st_graph_workbench/component/commands.py +705 -0
  8. st_graph_workbench/component/component.py +990 -0
  9. st_graph_workbench/component/elements.py +191 -0
  10. st_graph_workbench/component/events.py +70 -0
  11. st_graph_workbench/component/expansion.py +1207 -0
  12. st_graph_workbench/component/expansion_provider.py +170 -0
  13. st_graph_workbench/component/icons.py +54 -0
  14. st_graph_workbench/component/layouts.py +100 -0
  15. st_graph_workbench/component/records.py +369 -0
  16. st_graph_workbench/component/styles.py +267 -0
  17. st_graph_workbench/component/types.py +83 -0
  18. st_graph_workbench/component/validation.py +147 -0
  19. st_graph_workbench/frontend/build/111-76b489c917f19c7113b7.js +1 -0
  20. st_graph_workbench/frontend/build/477-3b51cf5f6d2483b89d23.js +1 -0
  21. st_graph_workbench/frontend/build/527-15ddbbbebc4c095e1a00.js +1 -0
  22. st_graph_workbench/frontend/build/576-875a5e1c17a808590401.js +1 -0
  23. st_graph_workbench/frontend/build/644-fd47658f645d99a1478f.js +1 -0
  24. st_graph_workbench/frontend/build/95-b2593eb4f78f89f4f83a.js +1 -0
  25. st_graph_workbench/frontend/build/THIRD_PARTY_LICENSES.txt +651 -0
  26. st_graph_workbench/frontend/build/component.html +905 -0
  27. st_graph_workbench/frontend/build/icons/account_balance.svg +1 -0
  28. st_graph_workbench/frontend/build/icons/alternate_email.svg +1 -0
  29. st_graph_workbench/frontend/build/icons/analytics.svg +1 -0
  30. st_graph_workbench/frontend/build/icons/assured_workload.svg +1 -0
  31. st_graph_workbench/frontend/build/icons/badge.svg +1 -0
  32. st_graph_workbench/frontend/build/icons/business.svg +1 -0
  33. st_graph_workbench/frontend/build/icons/campaign.svg +1 -0
  34. st_graph_workbench/frontend/build/icons/cell_tower.svg +1 -0
  35. st_graph_workbench/frontend/build/icons/chat.svg +1 -0
  36. st_graph_workbench/frontend/build/icons/cloud.svg +1 -0
  37. st_graph_workbench/frontend/build/icons/credit_card.svg +1 -0
  38. st_graph_workbench/frontend/build/icons/description.svg +1 -0
  39. st_graph_workbench/frontend/build/icons/directions_car.svg +1 -0
  40. st_graph_workbench/frontend/build/icons/dns.svg +1 -0
  41. st_graph_workbench/frontend/build/icons/email.svg +1 -0
  42. st_graph_workbench/frontend/build/icons/factory.svg +1 -0
  43. st_graph_workbench/frontend/build/icons/flag.svg +1 -0
  44. st_graph_workbench/frontend/build/icons/flight.svg +1 -0
  45. st_graph_workbench/frontend/build/icons/folder.svg +1 -0
  46. st_graph_workbench/frontend/build/icons/group.svg +1 -0
  47. st_graph_workbench/frontend/build/icons/home.svg +1 -0
  48. st_graph_workbench/frontend/build/icons/inventory.svg +1 -0
  49. st_graph_workbench/frontend/build/icons/key.svg +1 -0
  50. st_graph_workbench/frontend/build/icons/language.svg +1 -0
  51. st_graph_workbench/frontend/build/icons/laptop.svg +1 -0
  52. st_graph_workbench/frontend/build/icons/link.svg +1 -0
  53. st_graph_workbench/frontend/build/icons/local_shipping.svg +1 -0
  54. st_graph_workbench/frontend/build/icons/lock.svg +1 -0
  55. st_graph_workbench/frontend/build/icons/medical_information.svg +1 -0
  56. st_graph_workbench/frontend/build/icons/medical_services.svg +1 -0
  57. st_graph_workbench/frontend/build/icons/monitor.svg +1 -0
  58. st_graph_workbench/frontend/build/icons/person.svg +1 -0
  59. st_graph_workbench/frontend/build/icons/phone.svg +1 -0
  60. st_graph_workbench/frontend/build/icons/photo_camera.svg +1 -0
  61. st_graph_workbench/frontend/build/icons/place.svg +1 -0
  62. st_graph_workbench/frontend/build/icons/request_quote.svg +1 -0
  63. st_graph_workbench/frontend/build/icons/router.svg +1 -0
  64. st_graph_workbench/frontend/build/icons/schedule.svg +1 -0
  65. st_graph_workbench/frontend/build/icons/science.svg +1 -0
  66. st_graph_workbench/frontend/build/icons/sell.svg +1 -0
  67. st_graph_workbench/frontend/build/icons/shopping_cart.svg +1 -0
  68. st_graph_workbench/frontend/build/icons/sim_card.svg +1 -0
  69. st_graph_workbench/frontend/build/icons/smartphone.svg +1 -0
  70. st_graph_workbench/frontend/build/icons/storage.svg +1 -0
  71. st_graph_workbench/frontend/build/icons/store.svg +1 -0
  72. st_graph_workbench/frontend/build/icons/swap_horiz.svg +1 -0
  73. st_graph_workbench/frontend/build/icons/train.svg +1 -0
  74. st_graph_workbench/frontend/build/icons/translate.svg +1 -0
  75. st_graph_workbench/frontend/build/icons/videocam.svg +1 -0
  76. st_graph_workbench/frontend/build/icons/wallet.svg +1 -0
  77. st_graph_workbench/frontend/build/icons/warehouse.svg +1 -0
  78. st_graph_workbench/frontend/build/icons/wifi.svg +1 -0
  79. st_graph_workbench/frontend/build/index-04eff230345e2c3afcce.js +2 -0
  80. st_graph_workbench/frontend/build/index-04eff230345e2c3afcce.js.LICENSE.txt +9 -0
  81. st_graph_workbench/frontend/build/style.css +1268 -0
  82. st_graph_workbench/py.typed +0 -0
  83. st_graph_workbench/pyproject.toml +7 -0
  84. st_graph_workbench-0.1.0.dist-info/METADATA +561 -0
  85. st_graph_workbench-0.1.0.dist-info/RECORD +91 -0
  86. st_graph_workbench-0.1.0.dist-info/WHEEL +4 -0
  87. st_graph_workbench-0.1.0.dist-info/licenses/LICENSE +202 -0
  88. st_graph_workbench-0.1.0.dist-info/licenses/NOTICE +14 -0
  89. st_graph_workbench-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +31 -0
  90. st_graph_workbench-0.1.0.dist-info/licenses/licenses/material-symbols.txt +202 -0
  91. st_graph_workbench-0.1.0.dist-info/licenses/licenses/st-link-analysis.txt +21 -0
@@ -0,0 +1,115 @@
1
+ from st_graph_workbench.component.component import (
2
+ AnalysisAction,
3
+ CrudAction,
4
+ EditAction,
5
+ ElementsSync,
6
+ NodeAction,
7
+ PerformanceProfile,
8
+ SelectionMode,
9
+ ViewportAction,
10
+ graph_workbench,
11
+ )
12
+ from st_graph_workbench.component.styles import NodeStyle, EdgeStyle, StyleRule
13
+ from st_graph_workbench.component.events import Event
14
+ from st_graph_workbench.component.elements import (
15
+ delete_elements,
16
+ get_element,
17
+ update_element_data,
18
+ upsert_elements,
19
+ )
20
+ from st_graph_workbench.component.records import (
21
+ dataframe_to_records,
22
+ records_to_dataframe,
23
+ )
24
+ from st_graph_workbench.component.types import (
25
+ ConnectedDragConfig,
26
+ Element,
27
+ ElementId,
28
+ ElementIdInput,
29
+ Elements,
30
+ GraphCommand,
31
+ GraphEvent,
32
+ Layout,
33
+ ProgressiveLoadConfig,
34
+ Record,
35
+ ToolbarConfig,
36
+ )
37
+ from st_graph_workbench.component.commands import (
38
+ GraphCommandValidationError,
39
+ add_elements_command,
40
+ apply_graph_command,
41
+ apply_graph_commands,
42
+ clear_graph_command,
43
+ delete_elements_command,
44
+ set_elements_command,
45
+ update_data_command,
46
+ upsert_elements_command,
47
+ validate_graph_commands,
48
+ viewport_command,
49
+ )
50
+ from st_graph_workbench.component.validation import (
51
+ ElementValidationError,
52
+ validate_elements,
53
+ )
54
+ from st_graph_workbench.component.expansion import (
55
+ ExpansionConfig,
56
+ ExpansionController,
57
+ ExpansionOperation,
58
+ ExpansionProvider,
59
+ ExpansionRequest,
60
+ ExpansionResponse,
61
+ )
62
+ from st_graph_workbench.component.expansion_provider import InMemoryExpansionProvider
63
+
64
+ __all__ = [
65
+ "ConnectedDragConfig",
66
+ "ExpansionConfig",
67
+ "ExpansionController",
68
+ "ExpansionOperation",
69
+ "ExpansionProvider",
70
+ "ExpansionRequest",
71
+ "ExpansionResponse",
72
+ "InMemoryExpansionProvider",
73
+ "graph_workbench",
74
+ "NodeStyle",
75
+ "EdgeStyle",
76
+ "StyleRule",
77
+ "Event",
78
+ "AnalysisAction",
79
+ "CrudAction",
80
+ "dataframe_to_records",
81
+ "delete_elements",
82
+ "Element",
83
+ "ElementId",
84
+ "ElementIdInput",
85
+ "ElementValidationError",
86
+ "Elements",
87
+ "EditAction",
88
+ "ElementsSync",
89
+ "GraphCommand",
90
+ "GraphCommandValidationError",
91
+ "GraphEvent",
92
+ "Layout",
93
+ "NodeAction",
94
+ "PerformanceProfile",
95
+ "ProgressiveLoadConfig",
96
+ "ToolbarConfig",
97
+ "SelectionMode",
98
+ "add_elements_command",
99
+ "apply_graph_command",
100
+ "apply_graph_commands",
101
+ "clear_graph_command",
102
+ "delete_elements_command",
103
+ "get_element",
104
+ "set_elements_command",
105
+ "update_data_command",
106
+ "update_element_data",
107
+ "upsert_elements",
108
+ "upsert_elements_command",
109
+ "records_to_dataframe",
110
+ "Record",
111
+ "validate_elements",
112
+ "validate_graph_commands",
113
+ "ViewportAction",
114
+ "viewport_command",
115
+ ]
@@ -0,0 +1 @@
1
+ """Internal modules for the st-graph-workbench Streamlit component."""
@@ -0,0 +1,106 @@
1
+ """Shared record and filter validation for expansion controllers and providers."""
2
+
3
+ from copy import deepcopy
4
+ from datetime import datetime, timezone
5
+ import json
6
+ import math
7
+ from typing import Any
8
+
9
+ from ._ids import normalize_id
10
+ from .types import Elements
11
+ from .validation import validate_elements
12
+
13
+
14
+ def positive_limit(value: int, name: str) -> int:
15
+ if isinstance(value, bool) or not isinstance(value, int) or value < 1:
16
+ raise ValueError(f"{name} must be a positive integer.")
17
+ return value
18
+
19
+
20
+ def normalize_elements(elements: Elements, *, strict: bool = True) -> Elements:
21
+ validate_elements(elements, strict=strict)
22
+ elements = deepcopy(elements)
23
+ for group in ("nodes", "edges"):
24
+ elements.setdefault(group, [])
25
+ for record in elements[group]:
26
+ data = record["data"]
27
+ fields = (
28
+ ("id", "source", "target") if group == "edges" else ("id", "parent")
29
+ )
30
+ for name in fields:
31
+ if name == "parent" and data.get(name) in (None, ""):
32
+ data.pop(name, None)
33
+ elif name in data:
34
+ data[name] = normalize_id(data[name], option_name=name)
35
+ try:
36
+ json.dumps(elements, allow_nan=False)
37
+ except (TypeError, ValueError) as error:
38
+ raise ValueError(
39
+ "Expansion records must contain JSON-compatible finite values."
40
+ ) from error
41
+ for node in elements["nodes"]:
42
+ if "position" not in node:
43
+ continue
44
+ position = node["position"]
45
+ if not isinstance(position, dict) or any(
46
+ isinstance(position.get(axis), bool)
47
+ or not isinstance(position.get(axis), (int, float))
48
+ or not math.isfinite(float(position[axis]))
49
+ for axis in ("x", "y")
50
+ ):
51
+ raise ValueError(
52
+ f"Node {node['data']['id']} position requires finite numeric x and y."
53
+ )
54
+ return elements
55
+
56
+
57
+ def parse_instant(value: Any) -> datetime:
58
+ instant = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
59
+ return instant.replace(tzinfo=timezone.utc) if instant.tzinfo is None else instant
60
+
61
+
62
+ def normalize_query(value: dict[str, Any] | None) -> dict[str, Any]:
63
+ if value is not None and not isinstance(value, dict):
64
+ raise ValueError("Expansion filters must be a dictionary.")
65
+ result = deepcopy(value or {})
66
+ unknown = set(result) - {
67
+ "direction",
68
+ "relationships",
69
+ "attributes",
70
+ "time_field",
71
+ "time_from",
72
+ "time_to",
73
+ }
74
+ if unknown:
75
+ raise ValueError(f"Unknown expansion filters: {sorted(unknown)}")
76
+ result.setdefault("direction", "both")
77
+ if result["direction"] not in ("both", "incoming", "outgoing"):
78
+ raise ValueError("direction must be both, incoming or outgoing.")
79
+ relationships = result.get("relationships", [])
80
+ if not isinstance(relationships, list) or any(
81
+ not isinstance(x, str) for x in relationships
82
+ ):
83
+ raise ValueError("relationships must be a list of strings.")
84
+ result["relationships"] = sorted(set(relationships))
85
+ result.setdefault("attributes", {})
86
+ result.setdefault("time_field", "observed_at")
87
+ if not isinstance(result["attributes"], dict):
88
+ raise ValueError("attributes must be a dictionary of exact-match properties.")
89
+ if not isinstance(result["time_field"], str) or not result["time_field"].strip():
90
+ raise ValueError("time_field must be a nonempty property name.")
91
+ bounds = {}
92
+ for name in ("time_from", "time_to"):
93
+ if result.get(name) in (None, ""):
94
+ continue
95
+ try:
96
+ if not isinstance(result[name], str):
97
+ raise ValueError
98
+ bounds[name] = parse_instant(result[name])
99
+ except (TypeError, ValueError) as error:
100
+ raise ValueError(
101
+ f"{name} must be an ISO-8601 date or timestamp."
102
+ ) from error
103
+ if len(bounds) == 2 and bounds["time_from"] > bounds["time_to"]:
104
+ raise ValueError("time_from must not be later than time_to.")
105
+ json.dumps(result, allow_nan=False)
106
+ return result
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from collections.abc import Iterable, Mapping
5
+
6
+ from st_graph_workbench.component.types import ElementIdInput
7
+
8
+
9
+ def normalize_id(value: object, *, option_name: str) -> str:
10
+ """Normalize one supported scalar identifier to a string."""
11
+ if value is None or value == "":
12
+ raise TypeError(f"`{option_name}` values cannot be empty.")
13
+ if isinstance(value, str | bool | int):
14
+ return str(value)
15
+ if isinstance(value, float) and math.isfinite(value):
16
+ return str(value)
17
+ raise TypeError(
18
+ f"`{option_name}` values must be non-empty string, finite number, "
19
+ f"or boolean IDs, not `{type(value).__name__}`."
20
+ )
21
+
22
+
23
+ def normalize_id_list(values: ElementIdInput, *, option_name: str) -> list[str]:
24
+ """Normalize one scalar ID or an iterable of IDs to string IDs."""
25
+ if values is None:
26
+ return []
27
+ if isinstance(values, str | bool | int | float):
28
+ return [normalize_id(values, option_name=option_name)]
29
+ if isinstance(values, Mapping):
30
+ raise TypeError(
31
+ f"`{option_name}` must be one scalar ID or an iterable of ID values, "
32
+ "not a dict."
33
+ )
34
+ if not isinstance(values, Iterable):
35
+ raise TypeError(
36
+ f"`{option_name}` must be one scalar ID or an iterable of ID values, "
37
+ f"not `{type(values).__name__}`."
38
+ )
39
+ return [normalize_id(value, option_name=option_name) for value in values]
40
+
41
+
42
+ def normalize_id_set(values: ElementIdInput, *, option_name: str) -> set[str]:
43
+ """Normalize one scalar ID or an iterable of IDs to a set of string IDs."""
44
+ return set(normalize_id_list(values, option_name=option_name))
@@ -0,0 +1,6 @@
1
+ from typing import Any, Set, get_args
2
+
3
+
4
+ def literal_choices(literal_alias: Any) -> Set[str]:
5
+ """Return string values declared by a `typing.Literal` alias."""
6
+ return {str(choice) for choice in get_args(literal_alias)}
@@ -0,0 +1,8 @@
1
+ import warnings
2
+
3
+
4
+ class GraphWorkbenchDeprecationWarning(DeprecationWarning):
5
+ """Warning category for deprecated st-graph-workbench compatibility APIs."""
6
+
7
+
8
+ warnings.simplefilter("once", GraphWorkbenchDeprecationWarning)