ApiLogicServer 14.3.20__py3-none-any.whl → 14.3.25__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 +4 -3
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +38 -0
- api_logic_server_cli/database/nw-gold.sqlite +0 -0
- api_logic_server_cli/genai/genai.py +2 -1
- api_logic_server_cli/genai/genai_graphics.py +165 -0
- api_logic_server_cli/genai/genai_logic_builder.py +2 -2
- api_logic_server_cli/genai/genai_svcs.py +8 -0
- api_logic_server_cli/prototypes/base/config/config.py +58 -30
- api_logic_server_cli/prototypes/base/docs/graphics/readme.md +12 -0
- 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/manager/README.md +30 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +2 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +68 -60
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services.py +41 -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/graphics_templates/service_template_jsonapi_rpc.jinja +37 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_unused.jinja +38 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +18 -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 +8 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/sqlite_inserts.prompt +2 -0
- api_logic_server_cli/prototypes/manager/webgenai/docker-compose.yml +27 -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
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/METADATA +1 -1
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/RECORD +38 -26
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.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 → apilogicserver-14.3.25.dist-info}/entry_points.txt +0 -0
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-14.3.20.dist-info → apilogicserver-14.3.25.dist-info}/top_level.txt +0 -0
api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example
CHANGED
|
@@ -2,140 +2,148 @@
|
|
|
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": "sales_by_month = (session.query(func.strftime('%Y-%m', Order.date_shipped).label('Month'), func.sum(Item.amount)) .join(Item, Order.id == Item.order_id) .group_by(func.strftime('%Y-%m', Order.date_shipped)) .order_by(func.strftime('%Y-%m', Order.date_shipped)))",
|
|
75
|
+
"classes_used": "Order, Item",
|
|
76
|
+
"name": "sales_by_month",
|
|
77
|
+
"html_code": "function drawSalesByMonthChart(result) {\n const labels = result.map(entry => entry.Month);\n const data = result.map(entry => entry.TotalSales);\n const ctx = document.getElementById('salesByMonthChart').getContext('2d');\n new Chart(ctx, {\n type: 'bar',\n data: {\n labels: labels,\n datasets: [{\n label: 'Sales by Month',\n data: data,\n backgroundColor: '#42A5F5',\n borderColor: '#1E88E5',\n borderWidth: 1\n }]\n },\n options: {\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero: true\n }\n }]\n }\n }\n });\n}"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"test_data": "This is the test data generated for Customers, Orders, Items, and Products in the new system.",
|
|
73
81
|
"test_data_rows": [
|
|
74
82
|
{
|
|
75
|
-
"test_data_row_variable": "
|
|
76
|
-
"code": "
|
|
83
|
+
"test_data_row_variable": "customer1",
|
|
84
|
+
"code": "customer1 = Customer(name=\"Alice\", balance=1000.00, credit_limit=5000.00)"
|
|
77
85
|
},
|
|
78
86
|
{
|
|
79
|
-
"test_data_row_variable": "
|
|
80
|
-
"code": "
|
|
87
|
+
"test_data_row_variable": "customer2",
|
|
88
|
+
"code": "customer2 = Customer(name=\"Bob\", balance=2000.00, credit_limit=3000.00)"
|
|
81
89
|
},
|
|
82
90
|
{
|
|
83
|
-
"test_data_row_variable": "
|
|
84
|
-
"code": "
|
|
91
|
+
"test_data_row_variable": "customer3",
|
|
92
|
+
"code": "customer3 = Customer(name=\"Charlie\", balance=500.00, credit_limit=2000.00)"
|
|
85
93
|
},
|
|
86
94
|
{
|
|
87
|
-
"test_data_row_variable": "
|
|
88
|
-
"code": "
|
|
95
|
+
"test_data_row_variable": "customer4",
|
|
96
|
+
"code": "customer4 = Customer(name=\"Diana\", balance=0.00, credit_limit=1000.00)"
|
|
89
97
|
},
|
|
90
98
|
{
|
|
91
|
-
"test_data_row_variable": "
|
|
92
|
-
"code": "
|
|
99
|
+
"test_data_row_variable": "product1",
|
|
100
|
+
"code": "product1 = Product(name=\"Gadget\", unit_price=150.00)"
|
|
93
101
|
},
|
|
94
102
|
{
|
|
95
|
-
"test_data_row_variable": "
|
|
96
|
-
"code": "
|
|
103
|
+
"test_data_row_variable": "product2",
|
|
104
|
+
"code": "product2 = Product(name=\"Widget\", unit_price=90.00)"
|
|
97
105
|
},
|
|
98
106
|
{
|
|
99
|
-
"test_data_row_variable": "
|
|
100
|
-
"code": "
|
|
107
|
+
"test_data_row_variable": "product3",
|
|
108
|
+
"code": "product3 = Product(name=\"Thingamajig\", unit_price=75.00)"
|
|
101
109
|
},
|
|
102
110
|
{
|
|
103
|
-
"test_data_row_variable": "
|
|
104
|
-
"code": "
|
|
111
|
+
"test_data_row_variable": "product4",
|
|
112
|
+
"code": "product4 = Product(name=\"Doodad\", unit_price=110.00)"
|
|
105
113
|
},
|
|
106
114
|
{
|
|
107
|
-
"test_data_row_variable": "
|
|
108
|
-
"code": "
|
|
115
|
+
"test_data_row_variable": "order1",
|
|
116
|
+
"code": "order1 = Order(notes=\"First Order\", customer_id=1, date_shipped=date(2023, 3, 22), amount_total=300.00)"
|
|
109
117
|
},
|
|
110
118
|
{
|
|
111
|
-
"test_data_row_variable": "
|
|
112
|
-
"code": "
|
|
119
|
+
"test_data_row_variable": "order2",
|
|
120
|
+
"code": "order2 = Order(notes=\"Second Order\", customer_id=2, date_shipped=None, amount_total=0.00)"
|
|
113
121
|
},
|
|
114
122
|
{
|
|
115
|
-
"test_data_row_variable": "
|
|
116
|
-
"code": "
|
|
123
|
+
"test_data_row_variable": "order3",
|
|
124
|
+
"code": "order3 = Order(notes=\"Pending Shipment\", customer_id=3, date_shipped=None, amount_total=300.00)"
|
|
117
125
|
},
|
|
118
126
|
{
|
|
119
|
-
"test_data_row_variable": "
|
|
120
|
-
"code": "
|
|
127
|
+
"test_data_row_variable": "order4",
|
|
128
|
+
"code": "order4 = Order(notes=\"Urgent Order\", customer_id=4, date_shipped=date(2023, 7, 15), amount_total=220.00)"
|
|
121
129
|
},
|
|
122
130
|
{
|
|
123
|
-
"test_data_row_variable": "
|
|
124
|
-
"code": "
|
|
131
|
+
"test_data_row_variable": "item1",
|
|
132
|
+
"code": "item1 = Item(order_id=1, product_id=1, quantity=2, amount=300.00, unit_price=150.00)"
|
|
125
133
|
},
|
|
126
134
|
{
|
|
127
|
-
"test_data_row_variable": "
|
|
128
|
-
"code": "
|
|
135
|
+
"test_data_row_variable": "item2",
|
|
136
|
+
"code": "item2 = Item(order_id=2, product_id=2, quantity=1, amount=90.00, unit_price=90.00)"
|
|
129
137
|
},
|
|
130
138
|
{
|
|
131
|
-
"test_data_row_variable": "
|
|
132
|
-
"code": "
|
|
139
|
+
"test_data_row_variable": "item3",
|
|
140
|
+
"code": "item3 = Item(order_id=3, product_id=4, quantity=2, amount=220.00, unit_price=110.00)"
|
|
133
141
|
},
|
|
134
142
|
{
|
|
135
|
-
"test_data_row_variable": "
|
|
136
|
-
"code": "
|
|
143
|
+
"test_data_row_variable": "item4",
|
|
144
|
+
"code": "item4 = Item(order_id=4, product_id=3, quantity=4, amount=300.00, unit_price=75.00)"
|
|
137
145
|
}
|
|
138
146
|
],
|
|
139
|
-
"test_data_sqlite": "INSERT INTO
|
|
140
|
-
"name": "
|
|
147
|
+
"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);",
|
|
148
|
+
"name": "CustomerOrderSystem"
|
|
141
149
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
import logging
|
|
3
|
+
from flask_jwt_extended import get_jwt, jwt_required, verify_jwt_in_request
|
|
4
|
+
from config.config import Config, Args
|
|
5
|
+
from security.system.authorization import Security
|
|
6
|
+
import api.system.api_utils as api_utils
|
|
7
|
+
from typing import List
|
|
8
|
+
import safrs
|
|
9
|
+
import sqlalchemy
|
|
10
|
+
from flask import request, jsonify
|
|
11
|
+
from safrs import jsonapi_rpc, SAFRSAPI
|
|
12
|
+
from sqlalchemy.ext.hybrid import hybrid_property
|
|
13
|
+
from sqlalchemy.orm import object_mapper
|
|
14
|
+
from sqlalchemy import extract, func
|
|
15
|
+
from database import models
|
|
16
|
+
from flask_cors import cross_origin
|
|
17
|
+
from logic_bank.rule_bank.rule_bank import RuleBank
|
|
18
|
+
import integration.system.RowDictMapper as row_dict_mapper
|
|
19
|
+
|
|
20
|
+
# Customize this file to add endpoints/services, using SQLAlchemy as required
|
|
21
|
+
# Separate from expose_api_models.py, to simplify merge if project rebuilt
|
|
22
|
+
# Called by api_logic_server_run.py
|
|
23
|
+
|
|
24
|
+
app_logger = logging.getLogger("api_logic_server_app")
|
|
25
|
+
|
|
26
|
+
# called by api_logic_server_run.py, to customize api (new end points, services).
|
|
27
|
+
# separate from expose_api_models.py, to simplify merge if project recreated
|
|
28
|
+
|
|
29
|
+
def add_service(app, api, project_dir, swagger_host: str, PORT: str, method_decorators = []):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
api.expose_object(GraphicsServices) # Swagger-visible services
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
Illustrates #als: apis for graphics
|
|
37
|
+
|
|
38
|
+
* Custom service - visible in swagger
|
|
39
|
+
"""
|
|
40
|
+
class GraphicsServices(safrs.JABase):
|
|
41
|
+
pass
|
|
@@ -0,0 +1,76 @@
|
|
|
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>Bar Chart Example</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<canvas id="myChart" width="400" height="200"></canvas>
|
|
11
|
+
<script>
|
|
12
|
+
// Input data
|
|
13
|
+
fetch('http://localhost:5656/api/GraphicsServices/{{ name }}', {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
headers: {
|
|
16
|
+
'accept': 'application/vnd.api+json',
|
|
17
|
+
'Content-Type': 'application/json'
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify({}) // Add any required payload here
|
|
20
|
+
})
|
|
21
|
+
.then(response => {
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
24
|
+
}
|
|
25
|
+
return response.json();
|
|
26
|
+
})
|
|
27
|
+
.then(result => {
|
|
28
|
+
const labels = [];
|
|
29
|
+
const data = [];
|
|
30
|
+
const type = result.meta.result.chart_type;
|
|
31
|
+
const title = result.meta.result.title;
|
|
32
|
+
const columns = result.meta.result.columns;
|
|
33
|
+
console.log(JSON.stringify(result));
|
|
34
|
+
result.meta.result.results.forEach(item => {
|
|
35
|
+
labels.push(item[columns[0]]);
|
|
36
|
+
data.push(parseFloat(item[columns[1]]));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Create the bar chart
|
|
40
|
+
const ctx = document.getElementById('myChart').getContext('2d');
|
|
41
|
+
const myChart = new Chart(ctx, {
|
|
42
|
+
type: type,
|
|
43
|
+
data: {
|
|
44
|
+
labels: labels,
|
|
45
|
+
datasets: [{
|
|
46
|
+
label: title,
|
|
47
|
+
data: data,
|
|
48
|
+
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
|
49
|
+
borderColor: 'rgba(75, 192, 192, 1)',
|
|
50
|
+
borderWidth: 1
|
|
51
|
+
}]
|
|
52
|
+
},
|
|
53
|
+
options: {
|
|
54
|
+
responsive: true,
|
|
55
|
+
plugins: {
|
|
56
|
+
legend: {
|
|
57
|
+
position: 'top',
|
|
58
|
+
},
|
|
59
|
+
title: {
|
|
60
|
+
display: true,
|
|
61
|
+
text: title
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
scales: {
|
|
65
|
+
y: {
|
|
66
|
+
beginAtZero: true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
})
|
|
72
|
+
.catch(error => console.error('Error fetching data:', error));
|
|
73
|
+
|
|
74
|
+
</script>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Sales by Region</title>
|
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<h1>Sales by Region</h1>
|
|
12
|
+
<p>This page displays a bar chart of sales by region.</p>
|
|
13
|
+
|
|
14
|
+
<div style="display: flex; flex-direction: row; width: 50%; height: 100vh;">
|
|
15
|
+
<iframe id="iframeTarget1" src="http://localhost:5656/sales_by_region" style="flex: 1; border: none;"></iframe>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
</html>
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
height: 100%;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
}
|
|
18
|
+
canvas {
|
|
19
|
+
display: block;
|
|
20
|
+
}
|
|
21
|
+
</style>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<canvas id="salesChart" width="200" height="200"></canvas>
|
|
25
|
+
<script>
|
|
26
|
+
const labels = [];
|
|
27
|
+
const data = [];
|
|
28
|
+
result = {{ result | tojson }};
|
|
29
|
+
console.log(result);
|
|
30
|
+
const type = result.chart_type;
|
|
31
|
+
const title = result.title;
|
|
32
|
+
const columns = result.columns;
|
|
33
|
+
console.log(JSON.stringify(result));
|
|
34
|
+
result.results.forEach(item => {
|
|
35
|
+
labels.push(item[columns[0]]);
|
|
36
|
+
data.push(parseFloat(item[columns[1]]));
|
|
37
|
+
});
|
|
38
|
+
const ctx = document.getElementById('salesChart').getContext('2d');
|
|
39
|
+
const salesChart = new Chart(ctx, {
|
|
40
|
+
type: type,
|
|
41
|
+
data: {
|
|
42
|
+
labels: labels,
|
|
43
|
+
datasets: [{
|
|
44
|
+
label: title,
|
|
45
|
+
data: data,
|
|
46
|
+
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
|
47
|
+
borderColor: 'rgba(75, 192, 192, 1)',
|
|
48
|
+
borderWidth: 1
|
|
49
|
+
}]
|
|
50
|
+
},
|
|
51
|
+
options: {
|
|
52
|
+
responsive: true,
|
|
53
|
+
maintainAspectRatio: false,
|
|
54
|
+
scales: {
|
|
55
|
+
y: {
|
|
56
|
+
beginAtZero: true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
@classmethod
|
|
3
|
+
@jsonapi_rpc(http_methods=['POST','GET', 'OPTIONS'])
|
|
4
|
+
def {{name}}(self, *args, **kwargs):
|
|
5
|
+
"""
|
|
6
|
+
Illustrates:
|
|
7
|
+
* Complex query with multiple joins, for graphics
|
|
8
|
+
* That Thomas is a duck.
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
if request.method == 'OPTIONS':
|
|
12
|
+
return jsonify({ "result": "ok" })
|
|
13
|
+
|
|
14
|
+
from database.models import {{classes_used}}
|
|
15
|
+
db = safrs.DB
|
|
16
|
+
session = db.session # sqlalchemy.orm.scoping.scoped_session
|
|
17
|
+
# Security.set_user_sa() # an endpoint that requires no auth header (see also @bypass_security)
|
|
18
|
+
|
|
19
|
+
# SQLAlchemy query
|
|
20
|
+
query = {{sqlalchemy_query}}
|
|
21
|
+
# Execute query and fetch results
|
|
22
|
+
results = query.all()
|
|
23
|
+
from decimal import Decimal
|
|
24
|
+
columns = ['RegionDescription', 'TotalSales'] # {{ xAxis }} {{ yAxis }}
|
|
25
|
+
results = [{columns[0]: row[0], columns[1]: str(Decimal(row[1]).quantize(Decimal('0.01')))} for row in results]
|
|
26
|
+
title = "Sales by Region" # {{ title }}
|
|
27
|
+
chart_type = 'bar' # {{ chart_type }}
|
|
28
|
+
json_results = {
|
|
29
|
+
"results": results,
|
|
30
|
+
"columns": columns,
|
|
31
|
+
"title": title,
|
|
32
|
+
"chart_type": "bar",
|
|
33
|
+
"xAxis": columns[0],
|
|
34
|
+
"yAxis": columns[1],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return json_results
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from flask import request, jsonify
|
|
2
|
+
import logging
|
|
3
|
+
import safrs
|
|
4
|
+
from sqlalchemy.orm import aliased
|
|
5
|
+
from sqlalchemy import extract, func
|
|
6
|
+
from database.models import {{classes_used}}
|
|
7
|
+
|
|
8
|
+
app_logger = logging.getLogger("api_logic_server_app")
|
|
9
|
+
|
|
10
|
+
def add_service(app, api, project_dir, swagger_host: str, PORT: str, method_decorators = []):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
@app.route('/{{name}}, methods=["GET","OPTIONS"]')
|
|
14
|
+
def {{name}}():
|
|
15
|
+
"""
|
|
16
|
+
Illustrates:
|
|
17
|
+
* Complex query with multiple joins
|
|
18
|
+
|
|
19
|
+
Test it with:
|
|
20
|
+
|
|
21
|
+
curl -X GET "http://localhost:5656/{{name}}"
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
if request.method == 'OPTIONS':
|
|
25
|
+
return jsonify({ "result": "ok" })
|
|
26
|
+
|
|
27
|
+
from api.api_discovery.graphics_services import GraphicsServices
|
|
28
|
+
results = GraphicsServices.{{ name }}()
|
|
29
|
+
# Render the HTML template with the results
|
|
30
|
+
# Ensure the template is located in the "templates" directory relative to the project root.
|
|
31
|
+
from jinja2 import Environment, FileSystemLoader
|
|
32
|
+
from flask import Response
|
|
33
|
+
env = Environment(loader = FileSystemLoader('templates')) # create a templates folder in root path and add jinja files
|
|
34
|
+
template = env.get_template('{{ name }}.jinja')
|
|
35
|
+
output = template.render(result=results)
|
|
36
|
+
print(output)
|
|
37
|
+
|
|
38
|
+
return output
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
Create WGResult Graphics for prompts like 'graph sales by region'.
|
|
3
|
+
* use 'sales by region' to create a WGResult Graphic.sqlalchemy_query
|
|
4
|
+
* Be sure to use class names, not table names
|
|
5
|
+
* for example, using Northwind:
|
|
6
|
+
# SQLAlchemy query for Sales by Category
|
|
7
|
+
sales_by_category = (
|
|
8
|
+
session.query(
|
|
9
|
+
Category.CategoryName,
|
|
10
|
+
func.sum(OrderDetail.Quantity * OrderDetail.UnitPrice * (1 - OrderDetail.Discount)).label("TotalSales")
|
|
11
|
+
)
|
|
12
|
+
.join(Product, category.Id == Product.CategoryId)
|
|
13
|
+
.join(OrderDetail, Product.Id == OrderDetail.ProductId)
|
|
14
|
+
.join(Order, OrderDetail.OrderId == Order.Id)
|
|
15
|
+
.filter(Order.ShippedDate.isnot(None)) # Consider only shipped orders
|
|
16
|
+
.group_by(Category.CategoryName)
|
|
17
|
+
.order_by(func.sum(OrderDetail.Quantity * OrderDetail.UnitPrice * (1 - OrderDetail.Discount)).desc())
|
|
18
|
+
)
|
|
@@ -17,10 +17,18 @@ class TestDataRow(BaseModel):
|
|
|
17
17
|
test_data_row_variable: str # the Python test data row variable
|
|
18
18
|
code: str # Python code to create a test data row instance
|
|
19
19
|
|
|
20
|
+
class Graphic(BaseModel):
|
|
21
|
+
sqlalchemy_query: str # sqlalchemy query using group by, returns result = { "result": [ ("name", "value") ] }
|
|
22
|
+
sql_query: str # sql query using group by, returns result = { "result": [ ("name", "value") ] }
|
|
23
|
+
classes_used: str # comma-delimited list of classes used in sqlalchemy_query
|
|
24
|
+
name: str # suggested Python name for sqlalchemy_query
|
|
25
|
+
html_code: str # create a java script app to show a bar chart from sqlalchemy_query result
|
|
26
|
+
|
|
20
27
|
class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt
|
|
21
28
|
# response: str # result
|
|
22
29
|
models : List[Model] # list of sqlalchemy classes in the response
|
|
23
30
|
rules : List[Rule] # list rule declarations
|
|
31
|
+
graphics: List[Graphic] # list of Graphic objects
|
|
24
32
|
test_data: str
|
|
25
33
|
test_data_rows: List[TestDataRow] # list of test data rows
|
|
26
34
|
test_data_sqlite: str # test data as sqlite INSERT statements
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Run WebGenAI locally from docker container
|
|
2
|
+
|
|
3
|
+
# cd webgenai
|
|
4
|
+
|
|
5
|
+
# docker compose up
|
|
6
|
+
# docker compose down
|
|
7
|
+
|
|
8
|
+
name: webgenai
|
|
9
|
+
services:
|
|
10
|
+
web_genai:
|
|
11
|
+
stdin_open: true
|
|
12
|
+
tty: true
|
|
13
|
+
container_name: webgenai
|
|
14
|
+
ports:
|
|
15
|
+
- 8282:80
|
|
16
|
+
volumes:
|
|
17
|
+
- ./webg_temp:/tmp
|
|
18
|
+
- ./webg_config:/config
|
|
19
|
+
- ./webg_projects:/opt/projects
|
|
20
|
+
image: apilogicserver/web_genai
|
|
21
|
+
user: root
|
|
22
|
+
environment:
|
|
23
|
+
- APILOGICPROJECT_EXTERNAL_PORT=8282
|
|
24
|
+
- APILOGICPROJECT_PORT=5657
|
|
25
|
+
- APILOGICPROJECT_EXTERNAL_HOST=localhost
|
|
26
|
+
- GENAI_LOGIC_APIKEY=<paste license here from registration email>
|
|
27
|
+
- APILOGICSERVER_CHATGPT_APIKEY=<sk-proj-your-openai-key-here>
|