ApiLogicServer 14.3.20__py3-none-any.whl → 14.4.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.
- api_logic_server_cli/api_logic_server.py +5 -14
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +52 -5
- api_logic_server_cli/create_from_model/__pycache__/create_db_from_model.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/create_db_from_model.py +2 -0
- api_logic_server_cli/create_from_model/ont_build.py +19 -14
- api_logic_server_cli/create_from_model/ont_create.py +5 -5
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/.DS_Store +0 -0
- api_logic_server_cli/database/nw-gold-fix.sql +62 -0
- api_logic_server_cli/database/nw-gold.sqlite +0 -0
- api_logic_server_cli/fragments/docker-compose.yml +27 -0
- api_logic_server_cli/genai/genai.py +43 -11
- api_logic_server_cli/genai/genai_graphics.py +379 -0
- api_logic_server_cli/genai/genai_logic_builder.py +2 -2
- api_logic_server_cli/genai/genai_svcs.py +24 -8
- api_logic_server_cli/manager.py +19 -10
- api_logic_server_cli/prototypes/.DS_Store +0 -0
- api_logic_server_cli/prototypes/base/.DS_Store +0 -0
- api_logic_server_cli/prototypes/base/.vscode/launch.json +19 -0
- api_logic_server_cli/prototypes/base/api/expose_api_models.py +3 -1
- api_logic_server_cli/prototypes/base/api_logic_server_run.py +5 -2
- api_logic_server_cli/prototypes/base/config/activate_logicbank.py +1 -0
- api_logic_server_cli/prototypes/base/config/config.py +95 -24
- api_logic_server_cli/prototypes/base/config/logging.yml +1 -0
- api_logic_server_cli/prototypes/base/config/server_setup.py +33 -1
- api_logic_server_cli/prototypes/base/database/test_data/readme.md +3 -1
- api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +7 -2
- api_logic_server_cli/prototypes/base/docs/graphics/readme.md +12 -0
- api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +314 -0
- api_logic_server_cli/prototypes/base/docs/training/logic_example.py +41 -0
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +7 -3
- api_logic_server_cli/prototypes/base/integration/system/FlaskKafka.py +5 -1
- api_logic_server_cli/prototypes/base/security/authentication_provider/keycloak/auth_provider.py +1 -1
- api_logic_server_cli/prototypes/base/security/declare_security.py +4 -0
- api_logic_server_cli/prototypes/base/ui/admin/admin_loader.py +3 -1
- api_logic_server_cli/prototypes/base/ui/templates/bar_chart.jinja +64 -0
- api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +1 -1
- api_logic_server_cli/prototypes/manager/README.md +56 -5
- api_logic_server_cli/prototypes/manager/system/genai/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +0 -8
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +90 -60
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/002_create_db_models.prompt +4 -4
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/003_create_db_models.response +77 -47
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/dashboard_services.jinja +83 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_dashboard_WIP.py +34 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_api_xxx.py +32 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db.jinja +46 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db_each_method.jinja +36 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/html_template.jinja +76 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/index.html +19 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/sales_by_region.jinja +63 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +22 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics_request.prompt +5 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +15 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +2 -0
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.ps1 +100 -0
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +116 -0
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/readme.md +7 -0
- api_logic_server_cli/prototypes/manager/system/style-guide.yaml +2 -2
- api_logic_server_cli/prototypes/manager/webgenai/README.md +6 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/count_orders_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/order_count_by_month.prompt +1 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/request copy.json +892 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/request.json +6 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/response.json +17 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/response.yaml +59 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/sales_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -4
- api_logic_server_cli/prototypes/nw/ui/app_model_custom.yaml +851 -1082
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/count_orders_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request copy.json +892 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/request.json +6 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.json +17 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/response.yaml +59 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_employee.prompt +1 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/1_langchain_loader.py +19 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/2_gpt_mcp_prompt.txt +19 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/3_executor_test_agent.py +38 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/README.md +17 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/resources/curl.txt +4 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/resources/nw_swagger_3.yaml +16660 -0
- api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/run_executor.py +23 -0
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/nginx/nginx.conf +2 -2
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/package.json +6 -6
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/app.config.ts +2 -1
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.prod.ts +5 -5
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.ts +5 -5
- api_logic_server_cli/prototypes/ont_app/templates/app_config.jinja +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/detail_template.html +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/new_template.html +16 -16
- apilogicserver-14.4.0.dist-info/METADATA +76 -0
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/RECORD +102 -59
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/WHEEL +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/zsqlite_inserts_iterations.prompt +0 -29
- api_logic_server_cli/prototypes/manager/webgenai/docker-compose-webg.yml +0 -33
- api_logic_server_cli/prototypes/manager/webgenai/webg_config/license.json +0 -6
- api_logic_server_cli/prototypes/manager/webgenai/webg_config/web_genai.txt +0 -13
- apilogicserver-14.3.20.dist-info/METADATA +0 -167
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/entry_points.txt +0 -0
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.4.0.dist-info}/top_level.txt +0 -0
|
@@ -44,8 +44,12 @@ def kafka_producer():
|
|
|
44
44
|
if "client.id" not in conf:
|
|
45
45
|
conf["client.id"] = socket.gethostname()
|
|
46
46
|
# conf = {'bootstrap.servers': 'localhost:9092', 'client.id': socket.gethostname()}
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
try:
|
|
48
|
+
producer = Producer(conf)
|
|
49
|
+
logger.debug(f'\nKafka producer connected')
|
|
50
|
+
except Exception as ke:
|
|
51
|
+
logger.debug(f'Kafka producer error: {ke}')
|
|
52
|
+
producer = None
|
|
49
53
|
|
|
50
54
|
from sqlalchemy.inspection import inspect
|
|
51
55
|
|
|
@@ -64,7 +68,7 @@ def get_primary_key(logic_row: LogicRow):
|
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
def send_kafka_message(kafka_topic: str, kafka_key: str = None, msg: str="", json_root_name: str = "",
|
|
67
|
-
|
|
71
|
+
logic_row: LogicRow = None, row_dict_mapper: RowDictMapper = None, payload: dict = None):
|
|
68
72
|
""" Send Kafka message regarding logic_row, mapped by row_dict_mapper
|
|
69
73
|
|
|
70
74
|
* Typically called from declare_logic event
|
|
@@ -64,7 +64,11 @@ class FlaskKafka():
|
|
|
64
64
|
logger.info(f" - FlaskKafka._start: begin polling (v {__version__}), with \n -- conf: {self.conf} \n -- topics: {topics}")
|
|
65
65
|
consumer = Consumer(self.conf)
|
|
66
66
|
consumer.subscribe(topics=list(topics))
|
|
67
|
-
while True:
|
|
67
|
+
while True and len(topics) > 0:
|
|
68
|
+
if self.interrupt_event.is_set():
|
|
69
|
+
logger.info("Kafka thread interrupted")
|
|
70
|
+
break
|
|
71
|
+
|
|
68
72
|
msg = consumer.poll(1.0)
|
|
69
73
|
logger.debug(f' - KafkaConnect._start - consuming consumer.poll(1.0): {msg}')
|
|
70
74
|
if msg is None:
|
api_logic_server_cli/prototypes/base/security/authentication_provider/keycloak/auth_provider.py
CHANGED
|
@@ -78,7 +78,7 @@ class Authentication_Provider(Abstract_Authentication_Provider):
|
|
|
78
78
|
from flask import jsonify, request
|
|
79
79
|
from config.config import Args # circular import error if at top
|
|
80
80
|
|
|
81
|
-
jwks_uri = Args.instance.
|
|
81
|
+
jwks_uri = Args.instance.keycloak_base_url + '/protocol/openid-connect/certs'
|
|
82
82
|
for i in range(100):
|
|
83
83
|
# we retry a couple of times in case there are connection problems
|
|
84
84
|
try:
|
|
@@ -34,6 +34,8 @@ class Roles():
|
|
|
34
34
|
admin = "CS_ADMIN"
|
|
35
35
|
public="public" # p1/p (no roles, but gets public)
|
|
36
36
|
sa="sa"
|
|
37
|
+
default_roles_kcals = "default-roles-kcals"
|
|
38
|
+
uma_authorization = "uma_authorization"
|
|
37
39
|
|
|
38
40
|
DefaultRolePermission(to_role=Roles.sa, can_read=True, can_update=True, can_insert=True, can_delete=True)
|
|
39
41
|
DefaultRolePermission(to_role=Roles.tenant, can_read=True, can_delete=True)
|
|
@@ -43,3 +45,5 @@ DefaultRolePermission(to_role=Roles.teller, can_read=True, can_insert=True,can_u
|
|
|
43
45
|
DefaultRolePermission(to_role=Roles.customer, can_read=True, can_insert=True,can_update=True, can_delete=False)
|
|
44
46
|
DefaultRolePermission(to_role=Roles.read_only, can_read=True, can_insert=False,can_update=False, can_delete=False)
|
|
45
47
|
DefaultRolePermission(to_role=Roles.public, can_read=True, can_insert=False,can_update=False, can_delete=False)
|
|
48
|
+
DefaultRolePermission(to_role=Roles.default_roles_kcals, can_read=True, can_insert=True,can_update=True, can_delete=False)
|
|
49
|
+
DefaultRolePermission(to_role=Roles.uma_authorization, can_read=True, can_insert=True,can_update=True, can_delete=False)
|
|
@@ -140,7 +140,7 @@ def admin_events(flask_app: Flask, args: Args, validation_error: ValidationError
|
|
|
140
140
|
if "keycloak" in provider_name:
|
|
141
141
|
s = (f'\n'
|
|
142
142
|
f' keycloak:\n'
|
|
143
|
-
f' url: {args.
|
|
143
|
+
f' url: {args.keycloak_base}\n'
|
|
144
144
|
f' realm: {args.keycloak_realm}\n'
|
|
145
145
|
f' clientId: {args.keycloak_client_id}\n'
|
|
146
146
|
)
|
|
@@ -148,6 +148,8 @@ def admin_events(flask_app: Flask, args: Args, validation_error: ValidationError
|
|
|
148
148
|
elif "sql" in provider_name:
|
|
149
149
|
sql_auth_config = f'\n endpoint: {args.http_scheme}://{args.swagger_host}:{args.swagger_port}/{args.api_prefix[1:]}/auth/login\n'
|
|
150
150
|
content = content.replace("'{system-default}'", sql_auth_config)
|
|
151
|
+
elif getattr(Config.SECURITY_PROVIDER, 'auth_config', None):
|
|
152
|
+
content = content.replace("'{system-default}'", Config.SECURITY_PROVIDER.auth_config)
|
|
151
153
|
else:
|
|
152
154
|
sys.exit(f"ERROR[admin_loader]: unknown security type: {Config.SECURITY_PROVIDER}")
|
|
153
155
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Sales by Region</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
display: flex;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
height: 100%;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
canvas {
|
|
20
|
+
display: block;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<canvas id="salesChart" width="200" height="200"></canvas>
|
|
26
|
+
<script>
|
|
27
|
+
const labels = [];
|
|
28
|
+
const data = [];
|
|
29
|
+
result = {{ result | tojson }};
|
|
30
|
+
console.log(result);
|
|
31
|
+
const type = result.chart_type;
|
|
32
|
+
const title = result.title;
|
|
33
|
+
const columns = result.columns;
|
|
34
|
+
console.log(JSON.stringify(result));
|
|
35
|
+
result.results.forEach(item => {
|
|
36
|
+
labels.push(item[columns[0]]);
|
|
37
|
+
data.push(parseFloat(item[columns[1]]));
|
|
38
|
+
});
|
|
39
|
+
const ctx = document.getElementById('salesChart').getContext('2d');
|
|
40
|
+
const salesChart = new Chart(ctx, {
|
|
41
|
+
type: type,
|
|
42
|
+
data: {
|
|
43
|
+
labels: labels,
|
|
44
|
+
datasets: [{
|
|
45
|
+
label: title,
|
|
46
|
+
data: data,
|
|
47
|
+
backgroundColor: {{ color | tojson }},
|
|
48
|
+
borderColor: 'rgba(75, 192, 192, 1)',
|
|
49
|
+
borderWidth: 1
|
|
50
|
+
}]
|
|
51
|
+
},
|
|
52
|
+
options: {
|
|
53
|
+
responsive: true,
|
|
54
|
+
maintainAspectRatio: false,
|
|
55
|
+
scales: {
|
|
56
|
+
y: {
|
|
57
|
+
beginAtZero: true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
@@ -98,7 +98,7 @@ Then, try your own databases [(db-url examples here)](https://apilogicserver.git
|
|
|
98
98
|
|
|
99
99
|
<br>You can do this with or without signup:
|
|
100
100
|
|
|
101
|
-
1. If you have signed up, this will create and open a project called `genai_demo` from `genai_demo.prompt` (available in left Explorer pane):
|
|
101
|
+
1. If you have signed up (see *To obtain a ChatGPT API Key*, below), this will create and open a project called `genai_demo` from `genai_demo.prompt` (available in left Explorer pane):
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
104
|
als genai --using=system/genai/examples/genai_demo/genai_demo.prompt --project-name=genai_demo
|
|
@@ -122,6 +122,20 @@ Verify it's operating properly:
|
|
|
122
122
|
|
|
123
123
|
</br>
|
|
124
124
|
|
|
125
|
+
|
|
126
|
+
<details markdown>
|
|
127
|
+
|
|
128
|
+
<summary> To obtain a ChatGPT API Key</summary>
|
|
129
|
+
|
|
130
|
+
<br>GenAI-Logic uses OpenAI, which requires an Open API Key:
|
|
131
|
+
|
|
132
|
+
1. Obtain one from [here](https://platform.openai.com/account/api-keys) or [here](https://platform.openai.com/api-keys)
|
|
133
|
+
2. Authorize payments [here](https://platform.openai.com/settings/organization/billing/overview)
|
|
134
|
+
|
|
135
|
+
</details>
|
|
136
|
+
|
|
137
|
+
</br>
|
|
138
|
+
|
|
125
139
|
<details markdown>
|
|
126
140
|
|
|
127
141
|
<summary> What Just Happened? Next Steps...</summary>
|
|
@@ -151,20 +165,28 @@ Verify it's operating properly:
|
|
|
151
165
|
|
|
152
166
|
<summary> You can iterate the logic and data model</summary>
|
|
153
167
|
|
|
154
|
-
<br>
|
|
168
|
+
<br>The approach for an iteration is to create a new project from an existing one:
|
|
169
|
+
|
|
170
|
+
1. add another prompt to an existing projects `docs` directory, specifying your changes
|
|
171
|
+
2. use `als genai`, specifying
|
|
172
|
+
* `--using` existing projects `docs` directory, and
|
|
173
|
+
* `--project-name` as the output project
|
|
174
|
+
|
|
175
|
+
**Logic iterations** are particuarly useful. For example, here we take the basic check-credit logic, and add:
|
|
155
176
|
|
|
156
177
|
> Provide a 10% discount when buying more than 10 carbon neutral products.<br><br>The Item carbon neutral is copied from the Product carbon neutral
|
|
157
178
|
|
|
158
|
-
Explore [genai_demo_iteration_discount](system/genai/examples/genai_demo/genai_demo_iteration_discount). This will add carbon_neutral to the data model, and update the logic to provide the discount:
|
|
179
|
+
Explore [genai_demo_iteration_discount](system/genai/examples/genai_demo/genai_demo_iteration_discount). It's an iteration of basic_demo (see system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt). This will add carbon_neutral to the data model, and update the logic to provide the discount:
|
|
159
180
|
|
|
160
181
|
```bash title='Iterate Business Logic'
|
|
161
182
|
# Iterate with data model and logic
|
|
162
|
-
als genai --project-name='
|
|
183
|
+
als genai --project-name='genai_demo_with_discount' --using=system/genai/examples/genai_demo/genai_demo_iteration_discount
|
|
163
184
|
# open Docs/db.dbml
|
|
164
185
|
```
|
|
186
|
+
|
|
165
187
|
<br>
|
|
166
188
|
|
|
167
|
-
You can add new columns/tables, while keeping the prior model intact:
|
|
189
|
+
You can perform **model iterations:** add new columns/tables, while keeping the prior model intact. First, we create a project with no logic, perhaps just to see the screens (this step is optional, provided just to illustrate that iterations create new projects from existing ones):
|
|
168
190
|
|
|
169
191
|
```bash title='Iterate Without Logic'
|
|
170
192
|
# Step 1 - create without logic
|
|
@@ -172,6 +194,8 @@ als genai --project-name='genai_demo_no_logic' --using=system/genai/examples/gen
|
|
|
172
194
|
# open Docs/db.dbml
|
|
173
195
|
```
|
|
174
196
|
|
|
197
|
+
Then, we would create another prompt in the docs directory with our model changes. We've already created these for you in `system/genai/examples/genai_demo/genai_demo_iteration` - we use that to alter the data model (see `system/genai/examples/genai_demo/genai_demo_iteration/004_iteration_renames_logic.prompt`):
|
|
198
|
+
|
|
175
199
|
```bash title='Iterate With Logic'
|
|
176
200
|
# Iterate with data model and logic
|
|
177
201
|
als genai --project-name='genai_demo_with_logic' --using=system/genai/examples/genai_demo/genai_demo_iteration
|
|
@@ -488,6 +512,18 @@ als create --project-name=sample_ai --from-model=sample_ai.py --db-url=sqlite
|
|
|
488
512
|
|
|
489
513
|
4. This will create your database, create an API Logic Project from it, and launch your IDE.
|
|
490
514
|
|
|
515
|
+
5. Create business logic
|
|
516
|
+
|
|
517
|
+
* You can create logic with either your IDE (and code completion), or Natural Language
|
|
518
|
+
* To use Natural Language:
|
|
519
|
+
|
|
520
|
+
1. Use the CoPilot chat,
|
|
521
|
+
2. Paste the logic above
|
|
522
|
+
3. Copy it to `logic/declare_logic.py` after `discover_logic()`
|
|
523
|
+
|
|
524
|
+
* Alert: Table and Column Names may require correction to conform to the model
|
|
525
|
+
* Alert: you may to apply [defaulting](https://apilogicserver.github.io/Docs/Logic-Use/#insert-defaulting), and initialize derived attributes in your database
|
|
526
|
+
|
|
491
527
|
</details>
|
|
492
528
|
</br>
|
|
493
529
|
|
|
@@ -544,6 +580,21 @@ ApiLogicServer create --project-name=samples/nw_sample_nocust --db-url=nw
|
|
|
544
580
|
|
|
545
581
|
|
|
546
582
|
|
|
583
|
+
## Explore WebGenAI
|
|
584
|
+
|
|
585
|
+
In addition to the CLI examples above, you can use [WebGenAI](https://apilogicserver.github.io/Docs/WebGenAI/) - a web interface for creating projects from prompts. You can install WebGenAI on a server, so that created projects are easy to review with colleagues.
|
|
586
|
+
|
|
587
|
+
To try WebGenAI:
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
cd webgenai
|
|
591
|
+
docker compose up
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
You will be directed to the registration process. You will also require a ChatGPT API Key as described above.
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
|
|
547
598
|
|
|
548
599
|
|
|
549
600
|
## Appendix: Quick Basic Demo
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,8 +2,6 @@ Create a system with customers, orders, items and products.
|
|
|
2
2
|
|
|
3
3
|
Include a notes field for orders.
|
|
4
4
|
|
|
5
|
-
Use LogicBank to enforce business logic.
|
|
6
|
-
|
|
7
5
|
Use case: Check Credit
|
|
8
6
|
1. The Customer's balance is less than the credit limit
|
|
9
7
|
2. The Customer's balance is the sum of the Order amount_total where date_shipped is null
|
|
@@ -13,9 +11,3 @@ Use case: Check Credit
|
|
|
13
11
|
|
|
14
12
|
Use case: App Integration
|
|
15
13
|
1. Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None.
|
|
16
|
-
|
|
17
|
-
Ensure each customer and product has a unique name.
|
|
18
|
-
|
|
19
|
-
Ensure each Item quantity is not null.
|
|
20
|
-
|
|
21
|
-
Ensure each order has a valid customer_id that exists in the Customer table.
|
api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example
CHANGED
|
@@ -2,140 +2,170 @@
|
|
|
2
2
|
"models": [
|
|
3
3
|
{
|
|
4
4
|
"classname": "Customer",
|
|
5
|
-
"code": "class Customer(Base):\n __tablename__ = 'customer'\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String, unique=True)\n balance = Column(DECIMAL)\n credit_limit = Column(DECIMAL)",
|
|
6
|
-
"description": "
|
|
5
|
+
"code": "class Customer(Base):\n __tablename__ = 'customer'\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String, unique=True)\n balance = Column(DECIMAL)\n credit_limit = Column(DECIMAL)",
|
|
6
|
+
"description": "Defines the Customer entity with a unique name, balance, and credit limit.",
|
|
7
7
|
"name": "Customer"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"classname": "Order",
|
|
11
|
-
"code": "class Order(Base):\n __tablename__ = 'order'\n id = Column(Integer, primary_key=True, autoincrement=True)\n customer_id = Column(Integer, ForeignKey('customer.id'))\n date_shipped = Column(Date)\n amount_total = Column(DECIMAL)
|
|
12
|
-
"description": "
|
|
11
|
+
"code": "class Order(Base):\n __tablename__ = 'order'\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n notes = Column(String)\n customer_id = Column(Integer, ForeignKey('customer.id'), nullable=False)\n date_shipped = Column(Date)\n amount_total = Column(DECIMAL)",
|
|
12
|
+
"description": "Defines the Order entity which belongs to a customer. Includes notes and amount total.",
|
|
13
13
|
"name": "Order"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"classname": "Item",
|
|
17
|
-
"code": "class Item(Base):\n __tablename__ = 'item'\n id = Column(Integer, primary_key=True, autoincrement=True)\n order_id = Column(Integer, ForeignKey('order.id'))\n product_id = Column(Integer, ForeignKey('product.id'))\n quantity = Column(Integer, nullable=False)\n
|
|
18
|
-
"description": "
|
|
17
|
+
"code": "class Item(Base):\n __tablename__ = 'item'\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n order_id = Column(Integer, ForeignKey('order.id'))\n product_id = Column(Integer, ForeignKey('product.id'), nullable=False)\n quantity = Column(Integer, nullable=False)\n amount = Column(DECIMAL)\n unit_price = Column(DECIMAL)",
|
|
18
|
+
"description": "Defines the Item entity with quantity, amounts, and unit price details.",
|
|
19
19
|
"name": "Item"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"classname": "Product",
|
|
23
|
-
"code": "class Product(Base):\n __tablename__ = 'product'\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String)\n unit_price = Column(DECIMAL)",
|
|
24
|
-
"description": "
|
|
23
|
+
"code": "class Product(Base):\n __tablename__ = 'product'\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n name = Column(String, unique=True)\n unit_price = Column(DECIMAL)",
|
|
24
|
+
"description": "Defines the Product entity with a unique name and unit price.",
|
|
25
25
|
"name": "Product"
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
28
|
"rules": [
|
|
29
29
|
{
|
|
30
30
|
"name": "Customer Balance Constraint",
|
|
31
|
-
"description": "
|
|
32
|
-
"use_case": "
|
|
31
|
+
"description": "Ensure the customer's balance is less than their credit limit.",
|
|
32
|
+
"use_case": "Check Credit",
|
|
33
33
|
"entity": "Customer",
|
|
34
|
-
"code": "Rule.constraint(validate=Customer
|
|
34
|
+
"code": "Rule.constraint(validate=Customer, as_condition=lambda row: row.balance <= row.credit_limit, error_msg=\"Customer balance ({row.balance}) exceeds credit limit ({row.credit_limit})\")"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"name": "Customer Balance Derivation",
|
|
38
|
-
"description": "
|
|
39
|
-
"use_case": "
|
|
38
|
+
"description": "Derive the customer's balance as the sum of order totals where not yet shipped.",
|
|
39
|
+
"use_case": "Check Credit",
|
|
40
40
|
"entity": "Customer",
|
|
41
41
|
"code": "Rule.sum(derive=Customer.balance, as_sum_of=Order.amount_total, where=lambda row: row.date_shipped is None)"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
|
-
"name": "Order
|
|
45
|
-
"description": "
|
|
46
|
-
"use_case": "
|
|
44
|
+
"name": "Order Total Derivation",
|
|
45
|
+
"description": "Derive the order's total amount from the sum of item amounts.",
|
|
46
|
+
"use_case": "Check Credit",
|
|
47
47
|
"entity": "Order",
|
|
48
48
|
"code": "Rule.sum(derive=Order.amount_total, as_sum_of=Item.amount)"
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
"name": "Item Amount
|
|
52
|
-
"description": "
|
|
53
|
-
"use_case": "
|
|
51
|
+
"name": "Item Amount Calculation",
|
|
52
|
+
"description": "Calculate item amount based on quantity and unit price.",
|
|
53
|
+
"use_case": "Check Credit",
|
|
54
54
|
"entity": "Item",
|
|
55
55
|
"code": "Rule.formula(derive=Item.amount, as_expression=lambda row: row.quantity * row.unit_price)"
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
|
-
"name": "
|
|
59
|
-
"description": "
|
|
60
|
-
"use_case": "
|
|
58
|
+
"name": "Item Unit Price Copy",
|
|
59
|
+
"description": "Copy unit price from product to item.",
|
|
60
|
+
"use_case": "Check Credit",
|
|
61
61
|
"entity": "Item",
|
|
62
62
|
"code": "Rule.copy(derive=Item.unit_price, from_parent=Product.unit_price)"
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
"name": "Order Kafka
|
|
66
|
-
"description": "
|
|
65
|
+
"name": "Order Kafka Notification",
|
|
66
|
+
"description": "Send order details to Kafka if order is shipped.",
|
|
67
67
|
"use_case": "App Integration",
|
|
68
68
|
"entity": "Order",
|
|
69
|
-
"code": "Rule.after_flush_row_event(on_class=Order, calling=kafka_producer.send_row_to_kafka, if_condition=lambda row: row.date_shipped is not None, with_args={
|
|
69
|
+
"code": "Rule.after_flush_row_event(on_class=Order, calling=kafka_producer.send_row_to_kafka, if_condition=lambda row: row.date_shipped is not None, with_args={'topic': 'order_shipping'})"
|
|
70
70
|
}
|
|
71
71
|
],
|
|
72
|
-
"
|
|
72
|
+
"graphics": [
|
|
73
|
+
{
|
|
74
|
+
"sqlalchemy_query": "session.query(Product.name, func.sum(Item.amount).label('TotalSales')).join(Item, Product.id == Item.product_id).filter(Order.date_shipped.isnot(None)).group_by(Product.name).order_by(func.sum(Item.amount).desc())",
|
|
75
|
+
"sql_query": "SELECT Product.name, SUM(Item.amount) AS TotalSales FROM Product JOIN Item ON Product.id = Item.product_id JOIN Order ON Item.order_id = Order.id WHERE Order.date_shipped IS NOT NULL GROUP BY Product.name ORDER BY TotalSales DESC",
|
|
76
|
+
"classes_used": "Product, Item, Order",
|
|
77
|
+
"class_x_axis": "Product",
|
|
78
|
+
"name": "sales_by_product",
|
|
79
|
+
"prompt": "Graph Sales by Product",
|
|
80
|
+
"title": "Sales by Product",
|
|
81
|
+
"xAxis": "Product",
|
|
82
|
+
"yAxis": "Total Sales",
|
|
83
|
+
"dashboard": true,
|
|
84
|
+
"graph_type": "Bar",
|
|
85
|
+
"html_code": "var data = result.result; var categories = data.map(item => item[0]); var totals = data.map(item => item[1]); var ctx = document.getElementById('salesByProductChart').getContext('2d'); new Chart(ctx, {type: 'bar', data: {labels: categories, datasets: [{label: 'Sales by Product', data: totals}]}});"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"sqlalchemy_query": "session.query(Customer.name, func.sum(Order.amount_total).label('TotalCustomerSales')).join(Order, Customer.id == Order.customer_id).filter(Order.date_shipped.isnot(None)).group_by(Customer.name).order_by(func.sum(Order.amount_total).desc())",
|
|
89
|
+
"sql_query": "SELECT Customer.name, SUM(Order.amount_total) AS TotalCustomerSales FROM Customer JOIN Order ON Customer.id = Order.customer_id WHERE Order.date_shipped IS NOT NULL GROUP BY Customer.name ORDER BY TotalCustomerSales DESC",
|
|
90
|
+
"classes_used": "Customer, Order",
|
|
91
|
+
"class_x_axis": "Customer",
|
|
92
|
+
"name": "sales_by_customer",
|
|
93
|
+
"prompt": "Graph Sales by Customer",
|
|
94
|
+
"title": "Sales by Customer",
|
|
95
|
+
"xAxis": "Customer",
|
|
96
|
+
"yAxis": "Total Sales",
|
|
97
|
+
"dashboard": true,
|
|
98
|
+
"graph_type": "Bar",
|
|
99
|
+
"html_code": "var data = result.result; var customers = data.map(item => item[0]); var sales = data.map(item => item[1]); var ctx = document.getElementById('salesByCustomerChart').getContext('2d'); new Chart(ctx, {type: 'bar', data: {labels: customers, datasets: [{label: 'Sales by Customer', data: sales}]}});"
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"test_data": "This is the test data generated for Customers, Orders, Items, and Products in the new system.",
|
|
73
103
|
"test_data_rows": [
|
|
74
104
|
{
|
|
75
|
-
"test_data_row_variable": "
|
|
76
|
-
"code": "
|
|
105
|
+
"test_data_row_variable": "customer1",
|
|
106
|
+
"code": "customer1 = Customer(name=\"Alice\", balance=90.00, credit_limit=5000.00)"
|
|
77
107
|
},
|
|
78
108
|
{
|
|
79
|
-
"test_data_row_variable": "
|
|
80
|
-
"code": "
|
|
109
|
+
"test_data_row_variable": "customer2",
|
|
110
|
+
"code": "customer2 = Customer(name=\"Bob\", balance=0.00, credit_limit=3000.00)"
|
|
81
111
|
},
|
|
82
112
|
{
|
|
83
|
-
"test_data_row_variable": "
|
|
84
|
-
"code": "
|
|
113
|
+
"test_data_row_variable": "customer3",
|
|
114
|
+
"code": "customer3 = Customer(name=\"Charlie\", balance=220.00, credit_limit=2000.00)"
|
|
85
115
|
},
|
|
86
116
|
{
|
|
87
|
-
"test_data_row_variable": "
|
|
88
|
-
"code": "
|
|
117
|
+
"test_data_row_variable": "customer4",
|
|
118
|
+
"code": "customer4 = Customer(name=\"Diana\", balance=0.00, credit_limit=1000.00)"
|
|
89
119
|
},
|
|
90
120
|
{
|
|
91
|
-
"test_data_row_variable": "
|
|
92
|
-
"code": "
|
|
121
|
+
"test_data_row_variable": "product1",
|
|
122
|
+
"code": "product1 = Product(name=\"Gadget\", unit_price=150.00)"
|
|
93
123
|
},
|
|
94
124
|
{
|
|
95
|
-
"test_data_row_variable": "
|
|
96
|
-
"code": "
|
|
125
|
+
"test_data_row_variable": "product2",
|
|
126
|
+
"code": "product2 = Product(name=\"Widget\", unit_price=90.00)"
|
|
97
127
|
},
|
|
98
128
|
{
|
|
99
|
-
"test_data_row_variable": "
|
|
100
|
-
"code": "
|
|
129
|
+
"test_data_row_variable": "product3",
|
|
130
|
+
"code": "product3 = Product(name=\"Thingamajig\", unit_price=75.00)"
|
|
101
131
|
},
|
|
102
132
|
{
|
|
103
|
-
"test_data_row_variable": "
|
|
104
|
-
"code": "
|
|
133
|
+
"test_data_row_variable": "product4",
|
|
134
|
+
"code": "product4 = Product(name=\"Doodad\", unit_price=110.00)"
|
|
105
135
|
},
|
|
106
136
|
{
|
|
107
|
-
"test_data_row_variable": "
|
|
108
|
-
"code": "
|
|
137
|
+
"test_data_row_variable": "order1",
|
|
138
|
+
"code": "order1 = Order(notes=\"First Order\", customer_id=2, date_shipped=date(2023, 3, 22), amount_total=300.00)"
|
|
109
139
|
},
|
|
110
140
|
{
|
|
111
|
-
"test_data_row_variable": "
|
|
112
|
-
"code": "
|
|
141
|
+
"test_data_row_variable": "order2",
|
|
142
|
+
"code": "order2 = Order(notes=\"Second Order\", customer_id=1, date_shipped=None, amount_total=90.00)"
|
|
113
143
|
},
|
|
114
144
|
{
|
|
115
|
-
"test_data_row_variable": "
|
|
116
|
-
"code": "
|
|
145
|
+
"test_data_row_variable": "order3",
|
|
146
|
+
"code": "order3 = Order(notes=\"Pending Shipment\", customer_id=3, date_shipped=None, amount_total=220.00)"
|
|
117
147
|
},
|
|
118
148
|
{
|
|
119
|
-
"test_data_row_variable": "
|
|
120
|
-
"code": "
|
|
149
|
+
"test_data_row_variable": "order4",
|
|
150
|
+
"code": "order4 = Order(notes=\"Urgent Order\", customer_id=4, date_shipped=date(2023, 7, 15), amount_total=220.00)"
|
|
121
151
|
},
|
|
122
152
|
{
|
|
123
|
-
"test_data_row_variable": "
|
|
124
|
-
"code": "
|
|
153
|
+
"test_data_row_variable": "item1",
|
|
154
|
+
"code": "item1 = Item(order_id=1, product_id=1, quantity=2, amount=300.00, unit_price=150.00)"
|
|
125
155
|
},
|
|
126
156
|
{
|
|
127
|
-
"test_data_row_variable": "
|
|
128
|
-
"code": "
|
|
157
|
+
"test_data_row_variable": "item2",
|
|
158
|
+
"code": "item2 = Item(order_id=2, product_id=2, quantity=1, amount=90.00, unit_price=90.00)"
|
|
129
159
|
},
|
|
130
160
|
{
|
|
131
|
-
"test_data_row_variable": "
|
|
132
|
-
"code": "
|
|
161
|
+
"test_data_row_variable": "item3",
|
|
162
|
+
"code": "item3 = Item(order_id=3, product_id=4, quantity=2, amount=220.00, unit_price=110.00)"
|
|
133
163
|
},
|
|
134
164
|
{
|
|
135
|
-
"test_data_row_variable": "
|
|
136
|
-
"code": "
|
|
165
|
+
"test_data_row_variable": "item4",
|
|
166
|
+
"code": "item4 = Item(order_id=4, product_id=3, quantity=4, amount=300.00, unit_price=75.00)"
|
|
137
167
|
}
|
|
138
168
|
],
|
|
139
|
-
"test_data_sqlite": "INSERT INTO
|
|
140
|
-
"name": "
|
|
169
|
+
"test_data_sqlite": "INSERT INTO customer (name, balance, credit_limit) VALUES ('Alice', 1000.00, 5000.00);\nINSERT INTO customer (name, balance, credit_limit) VALUES ('Bob', 2000.00, 3000.00);\nINSERT INTO customer (name, balance, credit_limit) VALUES ('Charlie', 500.00, 2000.00);\nINSERT INTO customer (name, balance, credit_limit) VALUES ('Diana', 0.00, 1000.00);\n\nINSERT INTO product (name, unit_price) VALUES ('Gadget', 150.00);\nINSERT INTO product (name, unit_price) VALUES ('Widget', 90.00);\nINSERT INTO product (name, unit_price) VALUES ('Thingamajig', 75.00);\nINSERT INTO product (name, unit_price) VALUES ('Doodad', 110.00);\n\nINSERT INTO [order] (notes, customer_id, date_shipped, amount_total) VALUES ('First Order', 1, '2023-03-22', 300.00);\nINSERT INTO [order] (notes, customer_id, date_shipped, amount_total) VALUES ('Second Order', 2, NULL, 0.00);\nINSERT INTO [order] (notes, customer_id, date_shipped, amount_total) VALUES ('Pending Shipment', 3, NULL, 300.00);\nINSERT INTO [order] (notes, customer_id, date_shipped, amount_total) VALUES ('Urgent Order', 4, '2023-07-15', 220.00);\n\nINSERT INTO item (order_id, product_id, quantity, amount, unit_price) VALUES (1, 1, 2, 300.00, 150.00);\nINSERT INTO item (order_id, product_id, quantity, amount, unit_price) VALUES (2, 2, 1, 90.00, 90.00);\nINSERT INTO item (order_id, product_id, quantity, amount, unit_price) VALUES (3, 4, 2, 220.00, 110.00);\nINSERT INTO item (order_id, product_id, quantity, amount, unit_price) VALUES (4, 3, 4, 300.00, 75.00);",
|
|
170
|
+
"name": "CustomerOrderSystem"
|
|
141
171
|
}
|
|
@@ -5,8 +5,6 @@ Create a system with customers, orders, items and products.
|
|
|
5
5
|
|
|
6
6
|
Include a notes field for orders.
|
|
7
7
|
|
|
8
|
-
Use LogicBank to enforce these requirements (do not generate check constraints); be sure to update the data model and *all* test data with any attributes used in the logic:
|
|
9
|
-
|
|
10
8
|
Use case: Check Credit
|
|
11
9
|
1. The Customer's balance is less than the credit limit
|
|
12
10
|
2. The Customer's balance is the sum of the Order amount_total where date_shipped is null
|
|
@@ -17,12 +15,14 @@ Use case: Check Credit
|
|
|
17
15
|
Use case: App Integration
|
|
18
16
|
1. Send the Order to Kafka topic 'order_shipping' if the date_shipped is not None.
|
|
19
17
|
|
|
20
|
-
Ensure each customer
|
|
18
|
+
Ensure each customer has a unique name.
|
|
21
19
|
|
|
22
20
|
Ensure each Item quantity is not null.
|
|
23
21
|
|
|
24
22
|
Ensure each order has a valid customer_id that exists in the Customer table.
|
|
25
|
-
|
|
23
|
+
|
|
24
|
+
Graph sales by month, for dashboard.
|
|
25
|
+
Graph Sales by Product, for dashboard.
|
|
26
26
|
</Requirements>
|
|
27
27
|
|
|
28
28
|
Hints: use autonum keys (for all tables - including for link/join/junction/intersection tables), allow nulls, foreign keys, no check constraints.
|