planar 0.5.0__py3-none-any.whl → 0.8.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.
- planar/_version.py +1 -1
- planar/ai/agent.py +155 -283
- planar/ai/agent_base.py +170 -0
- planar/ai/agent_utils.py +7 -0
- planar/ai/pydantic_ai.py +638 -0
- planar/ai/test_agent_serialization.py +1 -1
- planar/app.py +64 -20
- planar/cli.py +39 -27
- planar/config.py +45 -36
- planar/db/db.py +2 -1
- planar/files/storage/azure_blob.py +343 -0
- planar/files/storage/base.py +7 -0
- planar/files/storage/config.py +70 -7
- planar/files/storage/s3.py +6 -6
- planar/files/storage/test_azure_blob.py +435 -0
- planar/logging/formatter.py +17 -4
- planar/logging/test_formatter.py +327 -0
- planar/registry_items.py +2 -1
- planar/routers/agents_router.py +3 -1
- planar/routers/files.py +11 -2
- planar/routers/models.py +14 -1
- planar/routers/test_agents_router.py +1 -1
- planar/routers/test_files_router.py +49 -0
- planar/routers/test_routes_security.py +5 -7
- planar/routers/test_workflow_router.py +270 -3
- planar/routers/workflow.py +95 -36
- planar/rules/models.py +36 -39
- planar/rules/test_data/account_dormancy_management.json +223 -0
- planar/rules/test_data/airline_loyalty_points_calculator.json +262 -0
- planar/rules/test_data/applicant_risk_assessment.json +435 -0
- planar/rules/test_data/booking_fraud_detection.json +407 -0
- planar/rules/test_data/cellular_data_rollover_system.json +258 -0
- planar/rules/test_data/clinical_trial_eligibility_screener.json +437 -0
- planar/rules/test_data/customer_lifetime_value.json +143 -0
- planar/rules/test_data/import_duties_calculator.json +289 -0
- planar/rules/test_data/insurance_prior_authorization.json +443 -0
- planar/rules/test_data/online_check_in_eligibility_system.json +254 -0
- planar/rules/test_data/order_consolidation_system.json +375 -0
- planar/rules/test_data/portfolio_risk_monitor.json +471 -0
- planar/rules/test_data/supply_chain_risk.json +253 -0
- planar/rules/test_data/warehouse_cross_docking.json +237 -0
- planar/rules/test_rules.py +750 -6
- planar/scaffold_templates/planar.dev.yaml.j2 +6 -6
- planar/scaffold_templates/planar.prod.yaml.j2 +9 -5
- planar/scaffold_templates/pyproject.toml.j2 +1 -1
- planar/security/auth_context.py +21 -0
- planar/security/{jwt_middleware.py → auth_middleware.py} +70 -17
- planar/security/authorization.py +9 -15
- planar/security/tests/test_auth_middleware.py +162 -0
- planar/sse/proxy.py +4 -9
- planar/test_app.py +92 -1
- planar/test_cli.py +81 -59
- planar/test_config.py +17 -14
- planar/testing/fixtures.py +325 -0
- planar/testing/planar_test_client.py +5 -2
- planar/utils.py +41 -1
- planar/workflows/execution.py +1 -1
- planar/workflows/orchestrator.py +5 -0
- planar/workflows/serialization.py +12 -6
- planar/workflows/step_core.py +3 -1
- planar/workflows/test_serialization.py +9 -1
- {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/METADATA +30 -5
- planar-0.8.0.dist-info/RECORD +166 -0
- planar/.__init__.py.un~ +0 -0
- planar/._version.py.un~ +0 -0
- planar/.app.py.un~ +0 -0
- planar/.cli.py.un~ +0 -0
- planar/.config.py.un~ +0 -0
- planar/.context.py.un~ +0 -0
- planar/.db.py.un~ +0 -0
- planar/.di.py.un~ +0 -0
- planar/.engine.py.un~ +0 -0
- planar/.files.py.un~ +0 -0
- planar/.log_context.py.un~ +0 -0
- planar/.log_metadata.py.un~ +0 -0
- planar/.logging.py.un~ +0 -0
- planar/.object_registry.py.un~ +0 -0
- planar/.otel.py.un~ +0 -0
- planar/.server.py.un~ +0 -0
- planar/.session.py.un~ +0 -0
- planar/.sqlalchemy.py.un~ +0 -0
- planar/.task_local.py.un~ +0 -0
- planar/.test_app.py.un~ +0 -0
- planar/.test_config.py.un~ +0 -0
- planar/.test_object_config.py.un~ +0 -0
- planar/.test_sqlalchemy.py.un~ +0 -0
- planar/.test_utils.py.un~ +0 -0
- planar/.util.py.un~ +0 -0
- planar/.utils.py.un~ +0 -0
- planar/ai/.__init__.py.un~ +0 -0
- planar/ai/._models.py.un~ +0 -0
- planar/ai/.agent.py.un~ +0 -0
- planar/ai/.agent_utils.py.un~ +0 -0
- planar/ai/.events.py.un~ +0 -0
- planar/ai/.files.py.un~ +0 -0
- planar/ai/.models.py.un~ +0 -0
- planar/ai/.providers.py.un~ +0 -0
- planar/ai/.pydantic_ai.py.un~ +0 -0
- planar/ai/.pydantic_ai_agent.py.un~ +0 -0
- planar/ai/.pydantic_ai_provider.py.un~ +0 -0
- planar/ai/.step.py.un~ +0 -0
- planar/ai/.test_agent.py.un~ +0 -0
- planar/ai/.test_agent_serialization.py.un~ +0 -0
- planar/ai/.test_providers.py.un~ +0 -0
- planar/ai/.utils.py.un~ +0 -0
- planar/ai/providers.py +0 -1088
- planar/ai/test_agent.py +0 -1298
- planar/ai/test_providers.py +0 -463
- planar/db/.db.py.un~ +0 -0
- planar/files/.config.py.un~ +0 -0
- planar/files/.local.py.un~ +0 -0
- planar/files/.local_filesystem.py.un~ +0 -0
- planar/files/.model.py.un~ +0 -0
- planar/files/.models.py.un~ +0 -0
- planar/files/.s3.py.un~ +0 -0
- planar/files/.storage.py.un~ +0 -0
- planar/files/.test_files.py.un~ +0 -0
- planar/files/storage/.__init__.py.un~ +0 -0
- planar/files/storage/.base.py.un~ +0 -0
- planar/files/storage/.config.py.un~ +0 -0
- planar/files/storage/.context.py.un~ +0 -0
- planar/files/storage/.local_directory.py.un~ +0 -0
- planar/files/storage/.test_local_directory.py.un~ +0 -0
- planar/files/storage/.test_s3.py.un~ +0 -0
- planar/human/.human.py.un~ +0 -0
- planar/human/.test_human.py.un~ +0 -0
- planar/logging/.__init__.py.un~ +0 -0
- planar/logging/.attributes.py.un~ +0 -0
- planar/logging/.formatter.py.un~ +0 -0
- planar/logging/.logger.py.un~ +0 -0
- planar/logging/.otel.py.un~ +0 -0
- planar/logging/.tracer.py.un~ +0 -0
- planar/modeling/.mixin.py.un~ +0 -0
- planar/modeling/.storage.py.un~ +0 -0
- planar/modeling/orm/.planar_base_model.py.un~ +0 -0
- planar/object_config/.object_config.py.un~ +0 -0
- planar/routers/.__init__.py.un~ +0 -0
- planar/routers/.agents_router.py.un~ +0 -0
- planar/routers/.crud.py.un~ +0 -0
- planar/routers/.decision.py.un~ +0 -0
- planar/routers/.event.py.un~ +0 -0
- planar/routers/.file_attachment.py.un~ +0 -0
- planar/routers/.files.py.un~ +0 -0
- planar/routers/.files_router.py.un~ +0 -0
- planar/routers/.human.py.un~ +0 -0
- planar/routers/.info.py.un~ +0 -0
- planar/routers/.models.py.un~ +0 -0
- planar/routers/.object_config_router.py.un~ +0 -0
- planar/routers/.rule.py.un~ +0 -0
- planar/routers/.test_object_config_router.py.un~ +0 -0
- planar/routers/.test_workflow_router.py.un~ +0 -0
- planar/routers/.workflow.py.un~ +0 -0
- planar/rules/.decorator.py.un~ +0 -0
- planar/rules/.runner.py.un~ +0 -0
- planar/rules/.test_rules.py.un~ +0 -0
- planar/security/.jwt_middleware.py.un~ +0 -0
- planar/sse/.constants.py.un~ +0 -0
- planar/sse/.example.html.un~ +0 -0
- planar/sse/.hub.py.un~ +0 -0
- planar/sse/.model.py.un~ +0 -0
- planar/sse/.proxy.py.un~ +0 -0
- planar/testing/.client.py.un~ +0 -0
- planar/testing/.memory_storage.py.un~ +0 -0
- planar/testing/.planar_test_client.py.un~ +0 -0
- planar/testing/.predictable_tracer.py.un~ +0 -0
- planar/testing/.synchronizable_tracer.py.un~ +0 -0
- planar/testing/.test_memory_storage.py.un~ +0 -0
- planar/testing/.workflow_observer.py.un~ +0 -0
- planar/workflows/.__init__.py.un~ +0 -0
- planar/workflows/.builtin_steps.py.un~ +0 -0
- planar/workflows/.concurrency_tracing.py.un~ +0 -0
- planar/workflows/.context.py.un~ +0 -0
- planar/workflows/.contrib.py.un~ +0 -0
- planar/workflows/.decorators.py.un~ +0 -0
- planar/workflows/.durable_test.py.un~ +0 -0
- planar/workflows/.errors.py.un~ +0 -0
- planar/workflows/.events.py.un~ +0 -0
- planar/workflows/.exceptions.py.un~ +0 -0
- planar/workflows/.execution.py.un~ +0 -0
- planar/workflows/.human.py.un~ +0 -0
- planar/workflows/.lock.py.un~ +0 -0
- planar/workflows/.misc.py.un~ +0 -0
- planar/workflows/.model.py.un~ +0 -0
- planar/workflows/.models.py.un~ +0 -0
- planar/workflows/.notifications.py.un~ +0 -0
- planar/workflows/.orchestrator.py.un~ +0 -0
- planar/workflows/.runtime.py.un~ +0 -0
- planar/workflows/.serialization.py.un~ +0 -0
- planar/workflows/.step.py.un~ +0 -0
- planar/workflows/.step_core.py.un~ +0 -0
- planar/workflows/.sub_workflow_runner.py.un~ +0 -0
- planar/workflows/.sub_workflow_scheduler.py.un~ +0 -0
- planar/workflows/.test_concurrency.py.un~ +0 -0
- planar/workflows/.test_concurrency_detection.py.un~ +0 -0
- planar/workflows/.test_human.py.un~ +0 -0
- planar/workflows/.test_lock_timeout.py.un~ +0 -0
- planar/workflows/.test_orchestrator.py.un~ +0 -0
- planar/workflows/.test_race_conditions.py.un~ +0 -0
- planar/workflows/.test_serialization.py.un~ +0 -0
- planar/workflows/.test_suspend_deserialization.py.un~ +0 -0
- planar/workflows/.test_workflow.py.un~ +0 -0
- planar/workflows/.tracing.py.un~ +0 -0
- planar/workflows/.types.py.un~ +0 -0
- planar/workflows/.util.py.un~ +0 -0
- planar/workflows/.utils.py.un~ +0 -0
- planar/workflows/.workflow.py.un~ +0 -0
- planar/workflows/.workflow_wrapper.py.un~ +0 -0
- planar/workflows/.wrappers.py.un~ +0 -0
- planar-0.5.0.dist-info/RECORD +0 -289
- {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/WHEEL +0 -0
- {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/entry_points.txt +0 -0
planar/rules/models.py
CHANGED
@@ -4,7 +4,7 @@ import json
|
|
4
4
|
from typing import Annotated, Literal, Type
|
5
5
|
from uuid import uuid4
|
6
6
|
|
7
|
-
from pydantic import BaseModel, Field
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
8
8
|
|
9
9
|
from planar.modeling.field_helpers import JsonSchema
|
10
10
|
from planar.object_config.object_config import (
|
@@ -12,14 +12,18 @@ from planar.object_config.object_config import (
|
|
12
12
|
)
|
13
13
|
|
14
14
|
|
15
|
-
class
|
15
|
+
class JDMBaseModel(BaseModel):
|
16
|
+
model_config = ConfigDict(extra="forbid")
|
17
|
+
|
18
|
+
|
19
|
+
class Position(JDMBaseModel):
|
16
20
|
"""Represents the x,y coordinates of a node in the graph"""
|
17
21
|
|
18
|
-
x: int
|
19
|
-
y: int
|
22
|
+
x: int | float
|
23
|
+
y: int | float
|
20
24
|
|
21
25
|
|
22
|
-
class InputOutputNodeContent(
|
26
|
+
class InputOutputNodeContent(JDMBaseModel):
|
23
27
|
"""Content structure for input or output nodes containing JSON schema"""
|
24
28
|
|
25
29
|
schema_: str = Field(
|
@@ -27,15 +31,15 @@ class InputOutputNodeContent(BaseModel):
|
|
27
31
|
) # JSON string representation of the input schema
|
28
32
|
|
29
33
|
|
30
|
-
class RuleTableInput(
|
34
|
+
class RuleTableInput(JDMBaseModel):
|
31
35
|
"""Represents an input column in a decision table"""
|
32
36
|
|
33
37
|
id: str
|
34
|
-
field: str # The field name from the input schema
|
38
|
+
field: str | None = None # The field name from the input schema
|
35
39
|
name: str
|
36
40
|
|
37
41
|
|
38
|
-
class RuleTableOutput(
|
42
|
+
class RuleTableOutput(JDMBaseModel):
|
39
43
|
"""Represents an output column in a decision table"""
|
40
44
|
|
41
45
|
id: str
|
@@ -43,23 +47,11 @@ class RuleTableOutput(BaseModel):
|
|
43
47
|
name: str
|
44
48
|
|
45
49
|
|
46
|
-
class
|
47
|
-
"""Represents a single rule row in a decision table with dynamic values"""
|
48
|
-
|
49
|
-
rule_id: str = Field(
|
50
|
-
alias="_id"
|
51
|
-
) # Using alias to maintain compatibility with _id field
|
52
|
-
# Note: Additional dynamic keys will be present based on input/output column IDs
|
53
|
-
# These are handled using model_extra="allow" configuration
|
54
|
-
|
55
|
-
model_config = {"extra": "allow", "populate_by_name": True}
|
56
|
-
|
57
|
-
|
58
|
-
class DecisionTableNodeContent(BaseModel):
|
50
|
+
class DecisionTableNodeContent(JDMBaseModel):
|
59
51
|
"""Content structure for decision table nodes"""
|
60
52
|
|
61
|
-
hitPolicy: Literal["first"] # Policy for rule evaluation
|
62
|
-
rules: list[
|
53
|
+
hitPolicy: Literal["first"] | Literal["collect"] # Policy for rule evaluation
|
54
|
+
rules: list[dict[str, str]] # List of rule rows
|
63
55
|
inputs: list[RuleTableInput] # Input column definitions
|
64
56
|
outputs: list[RuleTableOutput] # Output column definitions
|
65
57
|
|
@@ -73,7 +65,7 @@ class DecisionTableNodeContent(BaseModel):
|
|
73
65
|
executionMode: Literal["single", "loop"] | None # Execution mode for the table
|
74
66
|
|
75
67
|
|
76
|
-
class InputNode(
|
68
|
+
class InputNode(JDMBaseModel):
|
77
69
|
"""Represents an input node in the JDM graph"""
|
78
70
|
|
79
71
|
id: str
|
@@ -83,7 +75,7 @@ class InputNode(BaseModel):
|
|
83
75
|
position: Position
|
84
76
|
|
85
77
|
|
86
|
-
class DecisionTableNode(
|
78
|
+
class DecisionTableNode(JDMBaseModel):
|
87
79
|
"""Represents a decision table node in the JDM graph"""
|
88
80
|
|
89
81
|
id: str
|
@@ -93,7 +85,7 @@ class DecisionTableNode(BaseModel):
|
|
93
85
|
position: Position
|
94
86
|
|
95
87
|
|
96
|
-
class OutputNode(
|
88
|
+
class OutputNode(JDMBaseModel):
|
97
89
|
"""Represents an output node in the JDM graph"""
|
98
90
|
|
99
91
|
id: str
|
@@ -103,13 +95,13 @@ class OutputNode(BaseModel):
|
|
103
95
|
position: Position
|
104
96
|
|
105
97
|
|
106
|
-
class FunctionNodeContent(
|
98
|
+
class FunctionNodeContent(JDMBaseModel):
|
107
99
|
"""Content structure for a function node containing JavaScript source code."""
|
108
100
|
|
109
101
|
source: str
|
110
102
|
|
111
103
|
|
112
|
-
class FunctionNode(
|
104
|
+
class FunctionNode(JDMBaseModel):
|
113
105
|
"""Represents a JavaScript function node in the JDM graph."""
|
114
106
|
|
115
107
|
id: str
|
@@ -119,11 +111,13 @@ class FunctionNode(BaseModel):
|
|
119
111
|
position: Position
|
120
112
|
|
121
113
|
|
122
|
-
#
|
114
|
+
# The gorules site sometimes uses this legacy node in their (presumably outdated) examples.
|
115
|
+
# so safe to ignore since our go rules editor frontend won't generate this node.
|
116
|
+
#
|
117
|
+
# class LegacyFunctionNode(JDMBaseModel):
|
123
118
|
# """Represents a legacy JavaScript function node in the JDM graph.
|
124
119
|
# The GoRules examples (such as https://editor.gorules.io/?template=shipping-fees use a legacy fn node)
|
125
120
|
# """
|
126
|
-
|
127
121
|
# id: str
|
128
122
|
# type: Literal["functionNode"]
|
129
123
|
# name: str
|
@@ -131,21 +125,22 @@ class FunctionNode(BaseModel):
|
|
131
125
|
# position: Position
|
132
126
|
|
133
127
|
|
134
|
-
class SwitchStatement(
|
128
|
+
class SwitchStatement(JDMBaseModel):
|
135
129
|
"""Represents a statement in a switch node."""
|
136
130
|
|
137
131
|
id: str
|
138
132
|
condition: str
|
133
|
+
isDefault: bool
|
139
134
|
|
140
135
|
|
141
|
-
class SwitchNodeContent(
|
136
|
+
class SwitchNodeContent(JDMBaseModel):
|
142
137
|
"""Content structure for a switch node."""
|
143
138
|
|
144
139
|
hitPolicy: Literal["collect", "first"]
|
145
140
|
statements: list[SwitchStatement]
|
146
141
|
|
147
142
|
|
148
|
-
class SwitchNode(
|
143
|
+
class SwitchNode(JDMBaseModel):
|
149
144
|
"""Represents a switch node in the JDM graph."""
|
150
145
|
|
151
146
|
id: str
|
@@ -155,7 +150,7 @@ class SwitchNode(BaseModel):
|
|
155
150
|
position: Position
|
156
151
|
|
157
152
|
|
158
|
-
class Expression(
|
153
|
+
class Expression(JDMBaseModel):
|
159
154
|
"""Represents a single expression in an expression node."""
|
160
155
|
|
161
156
|
id: str
|
@@ -163,7 +158,7 @@ class Expression(BaseModel):
|
|
163
158
|
value: str
|
164
159
|
|
165
160
|
|
166
|
-
class ExpressionNodeContent(
|
161
|
+
class ExpressionNodeContent(JDMBaseModel):
|
167
162
|
"""Content structure for an expression node."""
|
168
163
|
|
169
164
|
expressions: list[Expression]
|
@@ -175,7 +170,7 @@ class ExpressionNodeContent(BaseModel):
|
|
175
170
|
)
|
176
171
|
|
177
172
|
|
178
|
-
class ExpressionNode(
|
173
|
+
class ExpressionNode(JDMBaseModel):
|
179
174
|
"""Represents an expression node in the JDM graph."""
|
180
175
|
|
181
176
|
id: str
|
@@ -198,21 +193,23 @@ JDMNode = (
|
|
198
193
|
JDMNodeWithType = Annotated[JDMNode, Field(discriminator="type")]
|
199
194
|
|
200
195
|
|
201
|
-
class JDMEdge(
|
196
|
+
class JDMEdge(JDMBaseModel):
|
202
197
|
"""Represents an edge connecting nodes in the JDM graph"""
|
203
198
|
|
204
199
|
id: str
|
205
200
|
type: Literal["edge"]
|
206
201
|
sourceId: str # ID of the source node
|
207
202
|
targetId: str # ID of the target node
|
203
|
+
sourceHandle: str | None = None
|
208
204
|
|
209
205
|
|
210
|
-
class JDMGraph(
|
206
|
+
class JDMGraph(JDMBaseModel):
|
211
207
|
"""
|
212
208
|
Complete JDM (JSON Decision Model) graph structure.
|
213
209
|
This represents a GoRules decision flow with input, processing, and output nodes.
|
214
210
|
"""
|
215
211
|
|
212
|
+
contentType: Literal["application/vnd.gorules.decision"] | None = None
|
216
213
|
nodes: list[JDMNodeWithType] # All nodes in the graph
|
217
214
|
edges: list[JDMEdge] # All edges connecting the nodes
|
218
215
|
|
@@ -287,7 +284,7 @@ def create_jdm_graph(rule: Rule) -> JDMGraph:
|
|
287
284
|
name="decisionTable1",
|
288
285
|
content=DecisionTableNodeContent(
|
289
286
|
hitPolicy="first",
|
290
|
-
rules=[
|
287
|
+
rules=[{"_id": str(uuid4()), **rule_values}],
|
291
288
|
inputs=[
|
292
289
|
RuleTableInput(
|
293
290
|
id=input_column_id,
|
@@ -0,0 +1,223 @@
|
|
1
|
+
{
|
2
|
+
"nodes": [
|
3
|
+
{
|
4
|
+
"type": "inputNode",
|
5
|
+
"content": {
|
6
|
+
"schema": ""
|
7
|
+
},
|
8
|
+
"id": "ip1",
|
9
|
+
"name": "request",
|
10
|
+
"position": {
|
11
|
+
"x": 110,
|
12
|
+
"y": 173.5
|
13
|
+
}
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"type": "expressionNode",
|
17
|
+
"content": {
|
18
|
+
"expressions": [
|
19
|
+
{
|
20
|
+
"id": "e1-1",
|
21
|
+
"key": "daysSinceLastActivity",
|
22
|
+
"value": "date('now') - date(lastActivityDate)"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"id": "e1-2",
|
26
|
+
"key": "daysToThreshold",
|
27
|
+
"value": "dormancyThreshold - $.daysSinceLastActivity"
|
28
|
+
}
|
29
|
+
],
|
30
|
+
"passThrough": true,
|
31
|
+
"inputField": null,
|
32
|
+
"outputPath": null,
|
33
|
+
"executionMode": "single"
|
34
|
+
},
|
35
|
+
"id": "ex1",
|
36
|
+
"name": "calculate_dormancy",
|
37
|
+
"position": {
|
38
|
+
"x": 430,
|
39
|
+
"y": 173.5
|
40
|
+
}
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"type": "decisionTableNode",
|
44
|
+
"content": {
|
45
|
+
"hitPolicy": "first",
|
46
|
+
"rules": [
|
47
|
+
{
|
48
|
+
"_id": "r1-1",
|
49
|
+
"i1-1": "< 0",
|
50
|
+
"o1-1": "'dormant'"
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"_id": "r1-2",
|
54
|
+
"i1-1": "< 30",
|
55
|
+
"o1-1": "'at_risk'"
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"_id": "r1-3",
|
59
|
+
"i1-1": "< 60",
|
60
|
+
"o1-1": "'warning'"
|
61
|
+
},
|
62
|
+
{
|
63
|
+
"_id": "r1-4",
|
64
|
+
"i1-1": "",
|
65
|
+
"o1-1": "'active'"
|
66
|
+
}
|
67
|
+
],
|
68
|
+
"inputs": [
|
69
|
+
{
|
70
|
+
"id": "i1-1",
|
71
|
+
"name": "Days To Threshold",
|
72
|
+
"field": "daysToThreshold"
|
73
|
+
}
|
74
|
+
],
|
75
|
+
"outputs": [
|
76
|
+
{
|
77
|
+
"id": "o1-1",
|
78
|
+
"name": "Status",
|
79
|
+
"field": "dormancyStatus"
|
80
|
+
}
|
81
|
+
],
|
82
|
+
"passThrough": true,
|
83
|
+
"inputField": null,
|
84
|
+
"outputPath": null,
|
85
|
+
"executionMode": "single"
|
86
|
+
},
|
87
|
+
"id": "dt1",
|
88
|
+
"name": "dormancy_status",
|
89
|
+
"position": {
|
90
|
+
"x": 750,
|
91
|
+
"y": 173.5
|
92
|
+
}
|
93
|
+
},
|
94
|
+
{
|
95
|
+
"type": "switchNode",
|
96
|
+
"content": {
|
97
|
+
"hitPolicy": "first",
|
98
|
+
"statements": [
|
99
|
+
{
|
100
|
+
"id": "s1-1",
|
101
|
+
"condition": "dormancyStatus != 'active'",
|
102
|
+
"isDefault": false
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"id": "s1-2",
|
106
|
+
"condition": "",
|
107
|
+
"isDefault": true
|
108
|
+
}
|
109
|
+
]
|
110
|
+
},
|
111
|
+
"id": "sw1",
|
112
|
+
"name": "route_by_status",
|
113
|
+
"position": {
|
114
|
+
"x": 1070,
|
115
|
+
"y": 173.5
|
116
|
+
}
|
117
|
+
},
|
118
|
+
{
|
119
|
+
"type": "decisionTableNode",
|
120
|
+
"content": {
|
121
|
+
"hitPolicy": "first",
|
122
|
+
"rules": [
|
123
|
+
{
|
124
|
+
"_id": "r2-1",
|
125
|
+
"i2-1": "'dormant'",
|
126
|
+
"i2-2": "'premium'",
|
127
|
+
"o2-1": "'fee_waiver'",
|
128
|
+
"o2-2": "'high'"
|
129
|
+
},
|
130
|
+
{
|
131
|
+
"_id": "r2-2",
|
132
|
+
"i2-1": "'dormant'",
|
133
|
+
"i2-2": "",
|
134
|
+
"o2-1": "'auto_close'",
|
135
|
+
"o2-2": "'medium'"
|
136
|
+
},
|
137
|
+
{
|
138
|
+
"_id": "r2-3",
|
139
|
+
"i2-1": "'at_risk'",
|
140
|
+
"i2-2": "'premium'",
|
141
|
+
"o2-1": "'account_review'",
|
142
|
+
"o2-2": "'high'"
|
143
|
+
},
|
144
|
+
{
|
145
|
+
"_id": "r2-4",
|
146
|
+
"i2-1": "'at_risk'",
|
147
|
+
"i2-2": "",
|
148
|
+
"o2-1": "'final_notice'",
|
149
|
+
"o2-2": "'high'"
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"_id": "r2-5",
|
153
|
+
"i2-1": "'warning'",
|
154
|
+
"i2-2": "",
|
155
|
+
"o2-1": "'notification'",
|
156
|
+
"o2-2": "'medium'"
|
157
|
+
}
|
158
|
+
],
|
159
|
+
"inputs": [
|
160
|
+
{
|
161
|
+
"id": "i2-1",
|
162
|
+
"name": "Status",
|
163
|
+
"field": "dormancyStatus"
|
164
|
+
},
|
165
|
+
{
|
166
|
+
"id": "i2-2",
|
167
|
+
"name": "Customer Tier",
|
168
|
+
"field": "customerTier"
|
169
|
+
}
|
170
|
+
],
|
171
|
+
"outputs": [
|
172
|
+
{
|
173
|
+
"id": "o2-1",
|
174
|
+
"name": "Action",
|
175
|
+
"field": "recommendedAction"
|
176
|
+
},
|
177
|
+
{
|
178
|
+
"id": "o2-2",
|
179
|
+
"name": "Priority",
|
180
|
+
"field": "actionPriority"
|
181
|
+
}
|
182
|
+
],
|
183
|
+
"passThrough": false,
|
184
|
+
"inputField": null,
|
185
|
+
"outputPath": null,
|
186
|
+
"executionMode": "single"
|
187
|
+
},
|
188
|
+
"id": "dt2",
|
189
|
+
"name": "determine_action",
|
190
|
+
"position": {
|
191
|
+
"x": 1390,
|
192
|
+
"y": 173.5
|
193
|
+
}
|
194
|
+
}
|
195
|
+
],
|
196
|
+
"edges": [
|
197
|
+
{
|
198
|
+
"id": "ed1",
|
199
|
+
"sourceId": "ip1",
|
200
|
+
"targetId": "ex1",
|
201
|
+
"type": "edge"
|
202
|
+
},
|
203
|
+
{
|
204
|
+
"id": "ed2",
|
205
|
+
"sourceId": "ex1",
|
206
|
+
"targetId": "dt1",
|
207
|
+
"type": "edge"
|
208
|
+
},
|
209
|
+
{
|
210
|
+
"id": "ed3",
|
211
|
+
"sourceId": "dt1",
|
212
|
+
"targetId": "sw1",
|
213
|
+
"type": "edge"
|
214
|
+
},
|
215
|
+
{
|
216
|
+
"id": "ed4",
|
217
|
+
"sourceId": "sw1",
|
218
|
+
"targetId": "dt2",
|
219
|
+
"sourceHandle": "s1-1",
|
220
|
+
"type": "edge"
|
221
|
+
}
|
222
|
+
]
|
223
|
+
}
|
@@ -0,0 +1,262 @@
|
|
1
|
+
{
|
2
|
+
"nodes": [
|
3
|
+
{
|
4
|
+
"type": "inputNode",
|
5
|
+
"content": {
|
6
|
+
"schema": ""
|
7
|
+
},
|
8
|
+
"id": "ip1",
|
9
|
+
"name": "request",
|
10
|
+
"position": {
|
11
|
+
"x": 110,
|
12
|
+
"y": 114
|
13
|
+
}
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"type": "decisionTableNode",
|
17
|
+
"content": {
|
18
|
+
"hitPolicy": "first",
|
19
|
+
"rules": [
|
20
|
+
{
|
21
|
+
"_id": "r1-1",
|
22
|
+
"i1-1": "'First'",
|
23
|
+
"o1-1": "3"
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"_id": "r1-2",
|
27
|
+
"i1-1": "'Business'",
|
28
|
+
"o1-1": "2"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"_id": "r1-3",
|
32
|
+
"i1-1": "'Premium Economy'",
|
33
|
+
"o1-1": "1.5"
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"_id": "r1-4",
|
37
|
+
"i1-1": "'Economy Plus'",
|
38
|
+
"o1-1": "1.25"
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"_id": "r1-5",
|
42
|
+
"i1-1": "",
|
43
|
+
"o1-1": "1"
|
44
|
+
}
|
45
|
+
],
|
46
|
+
"inputs": [
|
47
|
+
{
|
48
|
+
"id": "i1-1",
|
49
|
+
"name": "Fare Class",
|
50
|
+
"field": "booking.fareClass"
|
51
|
+
}
|
52
|
+
],
|
53
|
+
"outputs": [
|
54
|
+
{
|
55
|
+
"id": "o1-1",
|
56
|
+
"name": "Fare Multiplier",
|
57
|
+
"field": "points.fareMultiplier"
|
58
|
+
}
|
59
|
+
],
|
60
|
+
"passThrough": true,
|
61
|
+
"inputField": null,
|
62
|
+
"outputPath": null,
|
63
|
+
"executionMode": "single"
|
64
|
+
},
|
65
|
+
"id": "dt1",
|
66
|
+
"name": "fareClassMultiplier",
|
67
|
+
"position": {
|
68
|
+
"x": 430,
|
69
|
+
"y": 114
|
70
|
+
}
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"type": "decisionTableNode",
|
74
|
+
"content": {
|
75
|
+
"hitPolicy": "first",
|
76
|
+
"rules": [
|
77
|
+
{
|
78
|
+
"_id": "r2-1",
|
79
|
+
"i2-1": "'Domestic'",
|
80
|
+
"i2-2": "< 500",
|
81
|
+
"o2-1": "250"
|
82
|
+
},
|
83
|
+
{
|
84
|
+
"_id": "r2-2",
|
85
|
+
"i2-1": "'Domestic'",
|
86
|
+
"i2-2": "[500..1000]",
|
87
|
+
"o2-1": "500"
|
88
|
+
},
|
89
|
+
{
|
90
|
+
"_id": "r2-3",
|
91
|
+
"i2-1": "'Domestic'",
|
92
|
+
"i2-2": "> 1000",
|
93
|
+
"o2-1": "750"
|
94
|
+
},
|
95
|
+
{
|
96
|
+
"_id": "r2-4",
|
97
|
+
"i2-1": "'International'",
|
98
|
+
"i2-2": "< 2000",
|
99
|
+
"o2-1": "1000"
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"_id": "r2-5",
|
103
|
+
"i2-1": "'International'",
|
104
|
+
"i2-2": "[2000..5000]",
|
105
|
+
"o2-1": "2000"
|
106
|
+
},
|
107
|
+
{
|
108
|
+
"_id": "r2-6",
|
109
|
+
"i2-1": "'International'",
|
110
|
+
"i2-2": "> 5000",
|
111
|
+
"o2-1": "3000"
|
112
|
+
},
|
113
|
+
{
|
114
|
+
"_id": "r2-7",
|
115
|
+
"i2-1": "",
|
116
|
+
"i2-2": "",
|
117
|
+
"o2-1": "500"
|
118
|
+
}
|
119
|
+
],
|
120
|
+
"inputs": [
|
121
|
+
{
|
122
|
+
"id": "i2-1",
|
123
|
+
"name": "Route Type",
|
124
|
+
"field": "booking.routeType"
|
125
|
+
},
|
126
|
+
{
|
127
|
+
"id": "i2-2",
|
128
|
+
"name": "Distance (miles)",
|
129
|
+
"field": "booking.distance"
|
130
|
+
}
|
131
|
+
],
|
132
|
+
"outputs": [
|
133
|
+
{
|
134
|
+
"id": "o2-1",
|
135
|
+
"name": "Base Points",
|
136
|
+
"field": "points.basePoints"
|
137
|
+
}
|
138
|
+
],
|
139
|
+
"passThrough": true,
|
140
|
+
"inputField": null,
|
141
|
+
"outputPath": null,
|
142
|
+
"executionMode": "single"
|
143
|
+
},
|
144
|
+
"id": "dt2",
|
145
|
+
"name": "routeBasePoints",
|
146
|
+
"position": {
|
147
|
+
"x": 750,
|
148
|
+
"y": 114
|
149
|
+
}
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"type": "decisionTableNode",
|
153
|
+
"content": {
|
154
|
+
"hitPolicy": "first",
|
155
|
+
"rules": [
|
156
|
+
{
|
157
|
+
"_id": "r3-1",
|
158
|
+
"i3-1": "'Platinum'",
|
159
|
+
"o3-1": "2"
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"_id": "r3-2",
|
163
|
+
"i3-1": "'Gold'",
|
164
|
+
"o3-1": "1.5"
|
165
|
+
},
|
166
|
+
{
|
167
|
+
"_id": "r3-3",
|
168
|
+
"i3-1": "'Silver'",
|
169
|
+
"o3-1": "1.25"
|
170
|
+
},
|
171
|
+
{
|
172
|
+
"_id": "r3-4",
|
173
|
+
"i3-1": "",
|
174
|
+
"o3-1": "1"
|
175
|
+
}
|
176
|
+
],
|
177
|
+
"inputs": [
|
178
|
+
{
|
179
|
+
"id": "i3-1",
|
180
|
+
"name": "Member Status",
|
181
|
+
"field": "member.status"
|
182
|
+
}
|
183
|
+
],
|
184
|
+
"outputs": [
|
185
|
+
{
|
186
|
+
"id": "o3-1",
|
187
|
+
"name": "Status Multiplier",
|
188
|
+
"field": "points.statusMultiplier"
|
189
|
+
}
|
190
|
+
],
|
191
|
+
"passThrough": true,
|
192
|
+
"inputField": null,
|
193
|
+
"outputPath": null,
|
194
|
+
"executionMode": "single"
|
195
|
+
},
|
196
|
+
"id": "dt3",
|
197
|
+
"name": "memberStatusMultiplier",
|
198
|
+
"position": {
|
199
|
+
"x": 1070,
|
200
|
+
"y": 114
|
201
|
+
}
|
202
|
+
},
|
203
|
+
{
|
204
|
+
"type": "expressionNode",
|
205
|
+
"content": {
|
206
|
+
"expressions": [
|
207
|
+
{
|
208
|
+
"id": "e1-1",
|
209
|
+
"key": "seasonalPromotion",
|
210
|
+
"value": "booking.isSeasonalPromotion == true ? 1.5 : 1"
|
211
|
+
},
|
212
|
+
{
|
213
|
+
"id": "e1-2",
|
214
|
+
"key": "calculatedPoints",
|
215
|
+
"value": "floor(points.basePoints * points.fareMultiplier * points.statusMultiplier * $.seasonalPromotion)"
|
216
|
+
},
|
217
|
+
{
|
218
|
+
"id": "e1-3",
|
219
|
+
"key": "totalPoints",
|
220
|
+
"value": "$.calculatedPoints"
|
221
|
+
}
|
222
|
+
],
|
223
|
+
"passThrough": false,
|
224
|
+
"inputField": null,
|
225
|
+
"outputPath": null,
|
226
|
+
"executionMode": "single"
|
227
|
+
},
|
228
|
+
"id": "ex1",
|
229
|
+
"name": "calculateTotalPoints",
|
230
|
+
"position": {
|
231
|
+
"x": 1390,
|
232
|
+
"y": 114
|
233
|
+
}
|
234
|
+
}
|
235
|
+
],
|
236
|
+
"edges": [
|
237
|
+
{
|
238
|
+
"id": "ed1",
|
239
|
+
"sourceId": "ip1",
|
240
|
+
"targetId": "dt1",
|
241
|
+
"type": "edge"
|
242
|
+
},
|
243
|
+
{
|
244
|
+
"id": "ed2",
|
245
|
+
"sourceId": "dt1",
|
246
|
+
"targetId": "dt2",
|
247
|
+
"type": "edge"
|
248
|
+
},
|
249
|
+
{
|
250
|
+
"id": "ed3",
|
251
|
+
"sourceId": "dt2",
|
252
|
+
"targetId": "dt3",
|
253
|
+
"type": "edge"
|
254
|
+
},
|
255
|
+
{
|
256
|
+
"id": "ed4",
|
257
|
+
"sourceId": "dt3",
|
258
|
+
"targetId": "ex1",
|
259
|
+
"type": "edge"
|
260
|
+
}
|
261
|
+
]
|
262
|
+
}
|