ApiLogicServer 15.0.35__py3-none-any.whl → 15.0.38__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 +2 -2
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/genai/genai_svcs.py +4 -3
- api_logic_server_cli/manager.py +7 -5
- api_logic_server_cli/prototypes/base/docs/training/react_map.prompt.md +13 -0
- api_logic_server_cli/prototypes/base/docs/training/react_tree.prompt.md +10 -0
- api_logic_server_cli/prototypes/base/integration/mcp/examples/mcp_context_results.txt +142 -0
- api_logic_server_cli/prototypes/base/integration/mcp/mcp_client_executor.py +65 -29
- api_logic_server_cli/prototypes/manager/system/Manager_workspace.code-workspace +3 -3
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/Admin-App-Resource-Learning-Prompt.md +3 -2
- api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/Admin-App-js-Learning-Prompt.md +4 -14
- api_logic_server_cli/prototypes/nw/api/api_discovery/authentication_expose_api_models.py +53 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/auto_discovery.py +27 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/count_orders_by_month.html +76 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/count_orders_by_month.sql +1 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/dashboard_services.py +143 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/mcp_discovery.py +97 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/new_service.py +21 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/newer_service.py +21 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/number_of_sales_per_category.html +76 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/number_of_sales_per_category.sql +1 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/ontimize_api.py +495 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/sales_by_category.html +76 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/sales_by_category.sql +1 -0
- api_logic_server_cli/prototypes/nw/api/api_discovery/system.py +77 -0
- api_logic_server_cli/prototypes/nw/database/database_discovery/graphics_services.py +173 -0
- api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/DEPARTMENT_TREE_VIEW.md +66 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/package.json +4 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/public/index.html +3 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/App.js +8 -1
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/CustomLayout.js +20 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Department.js +511 -24
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/DepartmentTree.js +147 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Employee.js +230 -18
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/LandingPage.js +264 -0
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Supplier.js +359 -121
- api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/index.js +1 -0
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/METADATA +1 -1
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/RECORD +44 -23
- api_logic_server_cli/prototypes/base/docs/training/admin_app_unused.md +0 -156
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/WHEEL +0 -0
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/entry_points.txt +0 -0
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/top_level.txt +0 -0
|
@@ -12,10 +12,10 @@ ApiLogicServer CLI: given a database url, create [and run] customizable ApiLogic
|
|
|
12
12
|
Called from api_logic_server_cli.py, by instantiating the ProjectRun object.
|
|
13
13
|
'''
|
|
14
14
|
|
|
15
|
-
__version__ = "15.00.
|
|
15
|
+
__version__ = "15.00.38" # last public release: 15.00.36 (15.00.12)
|
|
16
16
|
recent_changes = \
|
|
17
17
|
f'\n\nRecent Changes:\n' +\
|
|
18
|
-
"\t07/
|
|
18
|
+
"\t07/05/2024 - 15.00.38: nw vibe, minor bug in mgr symlink creation, nw cards, mgr readme diagnostics, mcp printer, bug[98] \n"\
|
|
19
19
|
"\t06/30/2024 - 15.00.33: Tech Preview: genai-logic genai-add-app --vibe, bug [96, 97] \n"\
|
|
20
20
|
"\t06/10/2024 - 15.00.12: MCP Security, win fixes for readme, graphics quotes \n"\
|
|
21
21
|
"\t06/08/2024 - 15.00.10: MCP, optional shortening of stacktrace lines, bugfix[92] \n"\
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
last_created_date:
|
|
2
|
-
last_created_project_name:
|
|
3
|
-
last_created_version: 15.00.
|
|
1
|
+
last_created_date: July 05, 2025 07:17:31
|
|
2
|
+
last_created_project_name: ../../../servers/NW_NoCust
|
|
3
|
+
last_created_version: 15.00.37
|
|
@@ -906,10 +906,11 @@ def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str, r
|
|
|
906
906
|
request_path.parent.mkdir(parents=True, exist_ok=True)
|
|
907
907
|
return request_path
|
|
908
908
|
|
|
909
|
-
def string_to_lines(dict_long_string:
|
|
909
|
+
def string_to_lines(dict_long_string: list) -> dict:
|
|
910
910
|
result = dict_long_string
|
|
911
|
-
if
|
|
912
|
-
result[1]['content']
|
|
911
|
+
if len(result) > 1:
|
|
912
|
+
if isinstance(result[1]['content'], str):
|
|
913
|
+
result[1]['content'] = result[1]['content'].split('\n')
|
|
913
914
|
return result
|
|
914
915
|
|
|
915
916
|
try:
|
api_logic_server_cli/manager.py
CHANGED
|
@@ -145,21 +145,23 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
|
145
145
|
copied_path = shutil.copytree(src=from_docker_dir, dst=to_dir, dirs_exist_ok=True)
|
|
146
146
|
|
|
147
147
|
# get latest readme from git (eg, has been updated since pip install)
|
|
148
|
-
|
|
148
|
+
get_readme_url = f"https://raw.githubusercontent.com/ApiLogicServer/ApiLogicServer-src/main/api_logic_server_cli/prototypes/manager/README.md"
|
|
149
|
+
# https://github.com/ApiLogicServer/ApiLogicServer-src/main/api_logic_server_cli/prototypes/manager/README.md
|
|
149
150
|
readme_path = to_dir.joinpath('README.md')
|
|
150
151
|
try:
|
|
151
|
-
r = requests.get(
|
|
152
|
+
r = requests.get(get_readme_url) # , params=params)
|
|
152
153
|
if r.status_code == 200:
|
|
153
154
|
readme_data = r.content.decode('utf-8')
|
|
154
155
|
with open(str(readme_path), "w", encoding="utf-8") as readme_file:
|
|
155
156
|
readme_file.write(readme_data)
|
|
157
|
+
log.debug("✅ Wrote Manager Readme from git")
|
|
156
158
|
except requests.exceptions.ConnectionError as conerr:
|
|
157
159
|
# without this, windows fails if network is down
|
|
158
|
-
|
|
160
|
+
log.debug("❌ Manager Readme from git failed, using pip-installed version")
|
|
159
161
|
except Exception as e: # do NOT fail
|
|
160
|
-
log.error(f'
|
|
162
|
+
log.error(f'❌ Manager Readme from git excp installed: {e}')
|
|
161
163
|
pass # just fall back to using the pip-installed version
|
|
162
|
-
create_utils.copy_md(from_doc_file='Sample-Basic-Tour.md', project = to_dir)
|
|
164
|
+
create_utils.copy_md(from_doc_file='Sample-Basic-Tour.md', project = to_dir) # does this just override whatever?
|
|
163
165
|
|
|
164
166
|
if not samples:
|
|
165
167
|
shutil.rmtree(to_dir.joinpath(f'{docker_volume}system/app_model_editor'))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
I need to enhance a React Admin supplier page to include a professional, interactive world map view that displays suppliers on a real map with proper geography. Requirements:
|
|
2
|
+
|
|
3
|
+
1. **Use a proper mapping library** (like Leaflet.js) - NOT custom SVG drawings
|
|
4
|
+
2. **Real world map** with accurate geography, countries, and coastlines
|
|
5
|
+
3. **Interactive controls**: pan, zoom, mouse wheel support
|
|
6
|
+
4. **Clickable supplier markers** that navigate to supplier detail pages
|
|
7
|
+
5. **Professional UI** with map legend and controls
|
|
8
|
+
6. **Toggle between list and map view**
|
|
9
|
+
7. **Position suppliers** by country with slight offsets to avoid overlap
|
|
10
|
+
|
|
11
|
+
The current supplier data has fields: Id, CompanyName, ContactName, City, Country, etc.
|
|
12
|
+
|
|
13
|
+
Please install the necessary mapping library dependencies and create a production-ready map component that looks professional like Google Maps, not a hand-drawn cartoon map.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Provide an option to see departments either as a list (as now), or as a tree. The tree expands to show related sub departments, as links. If I click on the dept name link, then open the department "show" to the right.
|
|
2
|
+
|
|
3
|
+
TECHNICAL CONSTRAINTS:
|
|
4
|
+
|
|
5
|
+
- Avoid recursion stack overflow: include proper termination conditions
|
|
6
|
+
- Handle mixed data types: use parseInt() for ID comparisons
|
|
7
|
+
- Prevent import conflicts: use aliases for Material-UI components
|
|
8
|
+
- Verify data relationships: check actual field names in backend
|
|
9
|
+
- Use incremental development: start simple, add complexity gradually
|
|
10
|
+
- Test each layer: tree logic → expansion → detail panel → tabs
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
|
|
2
|
+
1. Discovered MCP servers from config file: integration/mcp/../../integration/mcp/mcp_server_discovery.json:{
|
|
3
|
+
"servers": [
|
|
4
|
+
{
|
|
5
|
+
"name": "API Logic Server: basic_demo",
|
|
6
|
+
"base_url": "http://localhost:5656",
|
|
7
|
+
"schema_url": "http://localhost:5656/.well-known/mcp.json"
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Learnings and Schema from discovery schema_url: http://localhost:5656/.well-known/mcp.json:
|
|
14
|
+
{
|
|
15
|
+
"base_url": "http://localhost:5656/api",
|
|
16
|
+
"description": "API Logic Project: basic_demo",
|
|
17
|
+
"learning": "To issue one request per row from a prior step (fan-out), use the syntax:\n\n\"$<stepIndex>[*].<fieldName>\"\n\nFor example, if step 0 returns orders, and you want to send a POST per customer:\n\nBefore (incorrect):\n \"customer_id\": \"{customer_id}\"\n\nAfter (correct):\n \"customer_id\": \"$0[*].customer_id\"\n \nOnly if 'email' is in the Natural language query, send email by issing a POST request to the SysEmail endpoint, setting the subject, message and customer_id in the body. DO NOT include a POST unless the word \u2018email\u2019 appears in the user query.\n\nFormat response as mcp_responseFormat.\n\n<mcp_responseFormat>\nclass QueryParam(BaseModel):\n name: str # attribute name\n op: str # eq, lt, gt\n val: str\n\nclass JsonValues(BaseMopdel):\n name: str # name of attribute\n value: str # value of attribute\n\nclass Resource(BaseModel):\n tool_type: str\n base_url: str\n path: str # specified use case or requirement name (use 'General' if missing)\n method: str # GET, PATCH, POST or DELETE\n body: json # data for P
|
|
18
|
+
"resources":
|
|
19
|
+
[
|
|
20
|
+
{
|
|
21
|
+
"fields": [
|
|
22
|
+
"id",
|
|
23
|
+
"name",
|
|
24
|
+
"balance",
|
|
25
|
+
"credit_limit",
|
|
26
|
+
"email",
|
|
27
|
+
"email_opt_out"
|
|
28
|
+
],
|
|
29
|
+
"filterable": [
|
|
30
|
+
... etc
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
2a. LLM request:
|
|
35
|
+
|
|
36
|
+
Natural language query:
|
|
37
|
+
List the orders date_shipped is null and CreatedOn before 2023-07-14, and send a discount email (subject: 'Discount Offer') to the customer for each one.
|
|
38
|
+
|
|
39
|
+
Learnings_and_Schema:
|
|
40
|
+
{"http://localhost:5656/.well-known/mcp.json": {"base_url": "http://localhost:5656/api", "description": "API Logic Project: basic_demo", "learning": "To issue one request per row from a prior step (fan-out), use the syntax:\n\n\"$<stepIndex>[*].<fieldName>\"\n\nFor example, if step 0 returns orders, and you want to send a POST per customer:\n\nBefore (incorrect):\n \"customer_id\": \"{customer_id}\"\n\nAfter (correct):\n \"customer_id\": \"$0[*].customer_id\"\n \nOnly if 'email' is in the Natural language query, send email by issing a POST request to the SysEmail endpoint, setting the subject, message and customer_id in the body. DO NOT include a POST unless the word \u2018email\u2019 appears in the user query.\n\nFormat response as mcp_responseFormat.\n\n<mcp_responseFormat>\nclass QueryParam(BaseModel):\n name: str # attribute name\n op: str # eq, lt, gt\n val: str\n\nclass JsonValues(BaseMopdel):\n name: str # name of attribute\n value: str # value of attribute\n\nclass Resource(BaseModel):\n tool_type: str\n base_url: str\n path: str # specified use case or requirement name (use 'General' if missing)\n method: str # GET, PATCH, POST or
|
|
41
|
+
... etc from step 1
|
|
42
|
+
|
|
43
|
+
2b. generated tool context from LLM:
|
|
44
|
+
{
|
|
45
|
+
"schema_version": "1.0",
|
|
46
|
+
"resources": [
|
|
47
|
+
{
|
|
48
|
+
"tool_type": "json-api",
|
|
49
|
+
"base_url": "http://localhost:5656/api",
|
|
50
|
+
"path": "/Order",
|
|
51
|
+
"method": "GET",
|
|
52
|
+
"query_params": [
|
|
53
|
+
{
|
|
54
|
+
"name": "date_shipped",
|
|
55
|
+
"op": "eq",
|
|
56
|
+
"val": "null"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "CreatedOn",
|
|
60
|
+
"op": "lt",
|
|
61
|
+
"val": "2023-07-14"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"tool_type": "json-api",
|
|
67
|
+
"base_url": "http://localhost:5656/api",
|
|
68
|
+
"path": "/SysEmail",
|
|
69
|
+
"method": "POST",
|
|
70
|
+
"body": {
|
|
71
|
+
"subject": "Discount Offer",
|
|
72
|
+
"message": "Dear customer, we are offering a discount on your next purchase. Please check your account for more details.",
|
|
73
|
+
"customer_id": "$0[*].customer_id"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
3. MCP Client Executor – Starting Tool Context Execution
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
➡️ MCP execute_api_step[0]:
|
|
84
|
+
Method: GET http://localhost:5656/api/Order
|
|
85
|
+
Query: filter=[{"name": "date_shipped", "op": "eq", "val": null}, {"name": "CreatedOn", "op": "lt", "val": "2023-07-14"}]
|
|
86
|
+
Body: {}
|
|
87
|
+
|
|
88
|
+
Warning: No Flask request context available. secure API calls may not work as expected.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
➡️ MCP execute_api_step[1]:
|
|
92
|
+
Method: POST http://localhost:5656/api/SysEmail
|
|
93
|
+
Query: filter=[]
|
|
94
|
+
Body: {'data': {'type': 'SysEmail', 'attributes': {'subject': 'Discount Offer', 'message': 'Dear customer, we are offering a discount on your next purchase. Please check your account for more details.', 'customer_id': 1}}}
|
|
95
|
+
|
|
96
|
+
Warning: No Flask request context available. secure API calls may not work as expected.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
➡️ MCP execute_api_step[1]:
|
|
100
|
+
Method: POST http://localhost:5656/api/SysEmail
|
|
101
|
+
Query: filter=[]
|
|
102
|
+
Body: {'data': {'type': 'SysEmail', 'attributes': {'subject': 'Discount Offer', 'message': 'Dear customer, we are offering a discount on your next purchase. Please check your account for more details.', 'customer_id': 3}}}
|
|
103
|
+
|
|
104
|
+
Warning: No Flask request context available. secure API calls may not work as expected.
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
➡️ MCP execute_api_step[1]:
|
|
108
|
+
Method: POST http://localhost:5656/api/SysEmail
|
|
109
|
+
Query: filter=[]
|
|
110
|
+
Body: {'data': {'type': 'SysEmail', 'attributes': {'subject': 'Discount Offer', 'message': 'Dear customer, we are offering a discount on your next purchase. Please check your account for more details.', 'customer_id': 5}}}
|
|
111
|
+
|
|
112
|
+
Warning: No Flask request context available. secure API calls may not work as expected.
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
4. MCP Client Executor – Context Results:
|
|
116
|
+
|
|
117
|
+
Step 0 - Results (3 rows):
|
|
118
|
+
| CreatedOn | amount_total | customer_id | date_shipped | notes |
|
|
119
|
+
|------------|--------------|-------------|--------------|------------------|
|
|
120
|
+
| 2023-02-22 | 90.0 | 1 | None | Second Order |
|
|
121
|
+
| 2023-01-22 | 220.0 | 3 | None | Pending Shipment |
|
|
122
|
+
| 2023-01-22 | 220.0 | 5 | None | Silent Shipment |
|
|
123
|
+
|
|
124
|
+
Step 1 - Results (1 rows):
|
|
125
|
+
| CreatedOn | customer_id | message | subject |
|
|
126
|
+
|------------|-------------|--------------------------------------------------------------------------------------------------------------|----------------|
|
|
127
|
+
| 2025-07-02 | 1 | Dear customer, we are offering a discount on your next purchase. Please check your account for more details. | Discount Offer |
|
|
128
|
+
|
|
129
|
+
Step 2 - Results (1 rows):
|
|
130
|
+
| CreatedOn | customer_id | message | subject |
|
|
131
|
+
|------------|-------------|--------------------------------------------------------------------------------------------------------------|----------------|
|
|
132
|
+
| 2025-07-02 | 3 | Dear customer, we are offering a discount on your next purchase. Please check your account for more details. | Discount Offer |
|
|
133
|
+
|
|
134
|
+
Step 3 - Results (1 rows):
|
|
135
|
+
| CreatedOn | customer_id | message | subject |
|
|
136
|
+
|------------|-------------|--------------------------------------------------------------------------------------------------------------|----------------|
|
|
137
|
+
| 2025-07-02 | 5 | Dear customer, we are offering a discount on your next purchase. Please check your account for more details. | Discount Offer |
|
|
138
|
+
|
|
139
|
+
✅ MCP Client Executor – All Steps Executed - Review Results Above
|
|
140
|
+
.. 💡 Suggestion - Copy/Paste Response to a JsonFormatter
|
|
141
|
+
|
|
142
|
+
Test complete.
|
|
@@ -224,28 +224,6 @@ def process_tool_context(tool_context):
|
|
|
224
224
|
# query_param_filter = query_param_filter.replace("date_created", 'CreatedOn') # TODO - why this name?
|
|
225
225
|
return query_param_filter # end get_query_param_filter
|
|
226
226
|
|
|
227
|
-
def print_get_response(query_param_filter, mcp_response):
|
|
228
|
-
""" Print the response from the GET request. """
|
|
229
|
-
log.info("\n3. MCP Server (als) GET filter(query_param_filter):\n" + query_param_filter)
|
|
230
|
-
log.info(" GET Response:\n" + mcp_response.text)
|
|
231
|
-
results : List[Dict] = mcp_response.json()['data']
|
|
232
|
-
# print results in a table format
|
|
233
|
-
if results:
|
|
234
|
-
# Get all unique keys from all result dicts
|
|
235
|
-
keys = set()
|
|
236
|
-
for row in results:
|
|
237
|
-
if isinstance(row, dict):
|
|
238
|
-
keys.update(row.keys())
|
|
239
|
-
keys = list(keys)
|
|
240
|
-
# Print header
|
|
241
|
-
log.info("\n| " + " | ".join(keys) + " |")
|
|
242
|
-
log.info("|" + "|".join(["---"] * len(keys)) + "|")
|
|
243
|
-
# Print rows
|
|
244
|
-
for row in results:
|
|
245
|
-
log.info("| " + " | ".join(str(row.get(k, "")) for k in keys) + " |")
|
|
246
|
-
else:
|
|
247
|
-
log.info("No results found.")
|
|
248
|
-
|
|
249
227
|
def substitute_vars(val, context, row=None, ref_index=None):
|
|
250
228
|
"""
|
|
251
229
|
Substitutes variable references in a value using a provided context.
|
|
@@ -411,7 +389,7 @@ Based on this, generate the next tool_context step(s) as a JSON list.
|
|
|
411
389
|
if has_request_context():
|
|
412
390
|
headers = request.headers # get headers from Flask request context
|
|
413
391
|
else:
|
|
414
|
-
log.info("Warning: No Flask request context available.
|
|
392
|
+
log.info("Warning: No Flask request context available. Some API calls may not work as expected.")
|
|
415
393
|
try:
|
|
416
394
|
resp = requests.request(method, url, headers=headers, json=body if method in ["POST", "PATCH"] else None, params=params)
|
|
417
395
|
resp.raise_for_status()
|
|
@@ -447,17 +425,75 @@ Based on this, generate the next tool_context step(s) as a JSON list.
|
|
|
447
425
|
context_results.append(result)
|
|
448
426
|
step_num += 1
|
|
449
427
|
|
|
428
|
+
def print_json_as_table(json_data, step_index):
|
|
429
|
+
"""Print JSON data in table format showing only data/attributes section with column headers."""
|
|
430
|
+
if not isinstance(json_data, dict):
|
|
431
|
+
log.info(f"\nStep {step_index}: Non-dict result - {json_data}")
|
|
432
|
+
return
|
|
433
|
+
|
|
434
|
+
# Extract data section
|
|
435
|
+
data = json_data.get('data', [])
|
|
436
|
+
if not data:
|
|
437
|
+
log.info(f"\nStep {step_index}: No data found in result")
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
# Handle both single item and list of items
|
|
441
|
+
if isinstance(data, dict):
|
|
442
|
+
data = [data]
|
|
443
|
+
elif not isinstance(data, list):
|
|
444
|
+
log.info(f"\nStep {step_index}: Data is not in expected format")
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
# Extract attributes from all items to get all possible columns
|
|
448
|
+
all_attributes = set()
|
|
449
|
+
attribute_rows = []
|
|
450
|
+
|
|
451
|
+
for item in data:
|
|
452
|
+
if isinstance(item, dict) and 'attributes' in item:
|
|
453
|
+
attributes = item['attributes']
|
|
454
|
+
all_attributes.update(attributes.keys())
|
|
455
|
+
attribute_rows.append(attributes)
|
|
456
|
+
else:
|
|
457
|
+
# If no attributes section, use the item directly
|
|
458
|
+
if isinstance(item, dict):
|
|
459
|
+
all_attributes.update(item.keys())
|
|
460
|
+
attribute_rows.append(item)
|
|
461
|
+
|
|
462
|
+
if not attribute_rows:
|
|
463
|
+
log.info(f"\nStep {step_index}: No attributes found in data")
|
|
464
|
+
return
|
|
465
|
+
|
|
466
|
+
# Sort columns for consistent display
|
|
467
|
+
columns = sorted(list(all_attributes))
|
|
468
|
+
|
|
469
|
+
# Calculate column widths
|
|
470
|
+
col_widths = {}
|
|
471
|
+
for col in columns:
|
|
472
|
+
col_widths[col] = max(len(str(col)),
|
|
473
|
+
max(len(str(row.get(col, ""))) for row in attribute_rows))
|
|
474
|
+
|
|
475
|
+
# Print table header
|
|
476
|
+
log.info(f"\nStep {step_index} - Results ({len(attribute_rows)} rows):")
|
|
477
|
+
header = "| " + " | ".join(col.ljust(col_widths[col]) for col in columns) + " |"
|
|
478
|
+
separator = "|" + "|".join("-" * (col_widths[col] + 2) for col in columns) + "|"
|
|
479
|
+
|
|
480
|
+
log.info(header)
|
|
481
|
+
log.info(separator)
|
|
482
|
+
|
|
483
|
+
# Print table rows
|
|
484
|
+
for row in attribute_rows:
|
|
485
|
+
row_str = "| " + " | ".join(str(row.get(col, "")).ljust(col_widths[col]) for col in columns) + " |"
|
|
486
|
+
log.info(row_str)
|
|
487
|
+
|
|
450
488
|
if print := True: # print context (which is just the GETs)
|
|
451
489
|
log.info("\n\n4. MCP Client Executor – Context Results:")
|
|
452
490
|
for each_context_result in context_results:
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
each_print = json.dumps(each_context_result, indent=4)
|
|
456
|
-
print_line = f"\nStep {context_results.index(each_context_result)}\n{each_print}"
|
|
457
|
-
log.info(print_line)
|
|
491
|
+
step_index = context_results.index(each_context_result)
|
|
492
|
+
print_json_as_table(each_context_result, step_index)
|
|
458
493
|
pass
|
|
459
494
|
|
|
460
|
-
log.info("\n✅ MCP Client Executor – All Steps Executed
|
|
495
|
+
log.info("\n✅ MCP Client Executor – All Steps Executed - Review Results Above")
|
|
496
|
+
log.info(".. 💡 Suggestion - Copy/Paste Response to a JsonFormatter\n")
|
|
461
497
|
|
|
462
498
|
return context_results
|
|
463
499
|
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
}
|
|
6
6
|
],
|
|
7
7
|
"settings": {
|
|
8
|
-
"debug.console.
|
|
8
|
+
"debug.console.wornw graphsdWrap": false,
|
|
9
9
|
"workbench.editorAssociations": {
|
|
10
10
|
"*.md": "vscode.markdown.preview.editor"
|
|
11
11
|
},
|
|
12
|
-
"workbench.colorTheme": "Office Theme (
|
|
13
|
-
"workbench.preferredLightColorTheme": "Office Theme (
|
|
12
|
+
"workbench.colorTheme": "Office Theme (Excel)",
|
|
13
|
+
"workbench.preferredLightColorTheme": "Office Theme (Excel)"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -7,7 +7,7 @@ Generate the {{resource.js}} file for a React Admin application using the follow
|
|
|
7
7
|
### Per-Resource Files (Required)
|
|
8
8
|
|
|
9
9
|
Sample code (follow these guidelines EXACTLY):
|
|
10
|
-
|
|
10
|
+
```
|
|
11
11
|
<sample-code>
|
|
12
12
|
// begin MANDATORY imports (always generated EXACTLY)
|
|
13
13
|
import React from 'react';
|
|
@@ -179,9 +179,10 @@ export default {
|
|
|
179
179
|
create: CustomerCreate,
|
|
180
180
|
edit: CustomerEdit,
|
|
181
181
|
};
|
|
182
|
-
|
|
182
|
+
```
|
|
183
183
|
</sample-code>
|
|
184
184
|
|
|
185
|
+
|
|
185
186
|
For each resource (`Customer`, `Order` etc) and **fully** implement:
|
|
186
187
|
* `CustomerList`
|
|
187
188
|
* `CustomerShow`
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
+
App Wiring
|
|
2
|
+
Sample code for react App.js (follow these guidelines EXACTLY):
|
|
1
3
|
|
|
2
|
-
|
|
3
|
-
### App Wiring
|
|
4
|
-
|
|
5
|
-
Sample code for react `App.js` (follow these guidelines EXACTLY):
|
|
6
|
-
|
|
7
|
-
```jsx
|
|
8
4
|
// begin constant imports (always included) -- generate this code EXACTLY
|
|
9
5
|
import React from 'react';
|
|
10
6
|
import { Admin, Resource, Loading } from 'react-admin'; // val? loading
|
|
@@ -59,13 +55,7 @@ const App = () => {
|
|
|
59
55
|
};
|
|
60
56
|
|
|
61
57
|
export default App;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Response Format
|
|
67
|
-
|
|
58
|
+
Response Format
|
|
68
59
|
Format the response as a JSResponseFormat:
|
|
69
60
|
|
|
70
|
-
class JSResponseFormat(BaseModel):
|
|
71
|
-
code : str # generated javascript code (only)
|
|
61
|
+
class JSResponseFormat(BaseModel): # must match system/genai/prompt_inserts/response_format.prompt code : str # generated javascript code (only)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import safrs
|
|
3
|
+
import importlib
|
|
4
|
+
import pathlib
|
|
5
|
+
import logging as logging
|
|
6
|
+
import flask_sqlalchemy
|
|
7
|
+
|
|
8
|
+
# use absolute path import for easier multi-{app,model,db} support
|
|
9
|
+
force_import = __import__("database.database_discovery.authentication_models")
|
|
10
|
+
database = __import__('database')
|
|
11
|
+
app_logger = logging.getLogger(__name__)
|
|
12
|
+
app_logger.debug("api/expose_api_models.py - endpoint for each table")
|
|
13
|
+
|
|
14
|
+
def add_check_sum(cls):
|
|
15
|
+
"""
|
|
16
|
+
Checksum decorator for each model
|
|
17
|
+
"""
|
|
18
|
+
@safrs.jsonapi_attr
|
|
19
|
+
def _check_sum_(self):
|
|
20
|
+
if isinstance(self, flask_sqlalchemy.model.DefaultMeta) or not hasattr(self, "_check_sum_property"): # property does not exist during initialization
|
|
21
|
+
return None
|
|
22
|
+
return self._check_sum_property
|
|
23
|
+
|
|
24
|
+
@_check_sum_.setter
|
|
25
|
+
def _check_sum_(self, value):
|
|
26
|
+
self._check_sum_property = value
|
|
27
|
+
|
|
28
|
+
cls.S_CheckSum = _check_sum_
|
|
29
|
+
return cls
|
|
30
|
+
|
|
31
|
+
def add_service(app, api, project_dir, swagger_host: str, PORT: str, method_decorators ):
|
|
32
|
+
""" Called by api_discovery to
|
|
33
|
+
* expose API for each model (using introspection)
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
app (_type_): _description_
|
|
37
|
+
api (_type_): _description_
|
|
38
|
+
project_dir (_type_): _description_
|
|
39
|
+
swagger_host (str): _description_
|
|
40
|
+
PORT (str): _description_
|
|
41
|
+
method_decorators (_type_): _description_
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
_type_: _description_
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Get all the subclasses of the Base class and expose them in the api
|
|
48
|
+
for name, obj in inspect.getmembers(database.database_discovery.authentication_models):
|
|
49
|
+
if inspect.isclass(obj) and issubclass(obj, database.models.SAFRSBaseX) and obj is not database.models.SAFRSBaseX:
|
|
50
|
+
app_logger.info(f"Exposing /{name}")
|
|
51
|
+
api.expose_object(add_check_sum(obj), method_decorators= method_decorators)
|
|
52
|
+
|
|
53
|
+
return api
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
app_logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
def discover_services(app, api, project_dir, swagger_host: str, PORT: str):
|
|
8
|
+
"""
|
|
9
|
+
Discover services in the services directory
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
method_decorators : list = []
|
|
13
|
+
services = []
|
|
14
|
+
services_path = Path(__file__).parent
|
|
15
|
+
for root, dirs, files in os.walk(services_path):
|
|
16
|
+
for file in files:
|
|
17
|
+
if file.endswith(".py"):
|
|
18
|
+
spec = importlib.util.spec_from_file_location("module.name", services_path.joinpath(file))
|
|
19
|
+
if file.endswith("auto_discovery.py"):
|
|
20
|
+
pass
|
|
21
|
+
else:
|
|
22
|
+
services.append(file)
|
|
23
|
+
each_service = importlib.util.module_from_spec(spec)
|
|
24
|
+
spec.loader.exec_module(each_service) # runs "bare" module code (e.g., initialization)
|
|
25
|
+
each_service.add_service(app, api, project_dir, swagger_host, PORT, method_decorators) # invoke create function
|
|
26
|
+
app.logger.info(f"..discovered services: {services}")
|
|
27
|
+
return
|
|
@@ -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/count_orders_by_month', {
|
|
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 @@
|
|
|
1
|
+
SELECT strftime('%Y-%m', date(`Order`.OrderDate)) AS OrderMonth, COUNT(`Order`.Id) AS OrderCount FROM `Order` WHERE `Order`.OrderDate IS NOT NULL GROUP BY OrderMonth ORDER BY OrderMonth;
|