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.
Files changed (45) hide show
  1. api_logic_server_cli/api_logic_server.py +2 -2
  2. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  3. api_logic_server_cli/genai/genai_svcs.py +4 -3
  4. api_logic_server_cli/manager.py +7 -5
  5. api_logic_server_cli/prototypes/base/docs/training/react_map.prompt.md +13 -0
  6. api_logic_server_cli/prototypes/base/docs/training/react_tree.prompt.md +10 -0
  7. api_logic_server_cli/prototypes/base/integration/mcp/examples/mcp_context_results.txt +142 -0
  8. api_logic_server_cli/prototypes/base/integration/mcp/mcp_client_executor.py +65 -29
  9. api_logic_server_cli/prototypes/manager/system/Manager_workspace.code-workspace +3 -3
  10. api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/Admin-App-Resource-Learning-Prompt.md +3 -2
  11. api_logic_server_cli/prototypes/manager/system/genai/app_templates/app_learning/Admin-App-js-Learning-Prompt.md +4 -14
  12. api_logic_server_cli/prototypes/nw/api/api_discovery/authentication_expose_api_models.py +53 -0
  13. api_logic_server_cli/prototypes/nw/api/api_discovery/auto_discovery.py +27 -0
  14. api_logic_server_cli/prototypes/nw/api/api_discovery/count_orders_by_month.html +76 -0
  15. api_logic_server_cli/prototypes/nw/api/api_discovery/count_orders_by_month.sql +1 -0
  16. api_logic_server_cli/prototypes/nw/api/api_discovery/dashboard_services.py +143 -0
  17. api_logic_server_cli/prototypes/nw/api/api_discovery/mcp_discovery.py +97 -0
  18. api_logic_server_cli/prototypes/nw/api/api_discovery/new_service.py +21 -0
  19. api_logic_server_cli/prototypes/nw/api/api_discovery/newer_service.py +21 -0
  20. api_logic_server_cli/prototypes/nw/api/api_discovery/number_of_sales_per_category.html +76 -0
  21. api_logic_server_cli/prototypes/nw/api/api_discovery/number_of_sales_per_category.sql +1 -0
  22. api_logic_server_cli/prototypes/nw/api/api_discovery/ontimize_api.py +495 -0
  23. api_logic_server_cli/prototypes/nw/api/api_discovery/sales_by_category.html +76 -0
  24. api_logic_server_cli/prototypes/nw/api/api_discovery/sales_by_category.sql +1 -0
  25. api_logic_server_cli/prototypes/nw/api/api_discovery/system.py +77 -0
  26. api_logic_server_cli/prototypes/nw/database/database_discovery/graphics_services.py +173 -0
  27. api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -0
  28. api_logic_server_cli/prototypes/nw/ui/reference_react_app/DEPARTMENT_TREE_VIEW.md +66 -0
  29. api_logic_server_cli/prototypes/nw/ui/reference_react_app/package.json +4 -0
  30. api_logic_server_cli/prototypes/nw/ui/reference_react_app/public/index.html +3 -0
  31. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/App.js +8 -1
  32. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/CustomLayout.js +20 -0
  33. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Department.js +511 -24
  34. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/DepartmentTree.js +147 -0
  35. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Employee.js +230 -18
  36. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/LandingPage.js +264 -0
  37. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/Supplier.js +359 -121
  38. api_logic_server_cli/prototypes/nw/ui/reference_react_app/src/index.js +1 -0
  39. {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/METADATA +1 -1
  40. {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/RECORD +44 -23
  41. api_logic_server_cli/prototypes/base/docs/training/admin_app_unused.md +0 -156
  42. {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/WHEEL +0 -0
  43. {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/entry_points.txt +0 -0
  44. {apilogicserver-15.0.35.dist-info → apilogicserver-15.0.38.dist-info}/licenses/LICENSE +0 -0
  45. {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.35" # last public release: 15.00.25 (15.00.12)
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/01/2024 - 15.00.35: minor bug in mgr symlink creation, nw cards \n"\
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: June 30, 2025 16:42:30
2
- last_created_project_name: samples/nw_sample_nocust
3
- last_created_version: 15.00.34
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: dict) -> dict:
909
+ def string_to_lines(dict_long_string: list) -> dict:
910
910
  result = dict_long_string
911
- if isinstance(result[1]['content'], str):
912
- result[1]['content'] = result[1]['content'].split('\n')
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:
@@ -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
- file_src = f"https://raw.githubusercontent.com/ApiLogicServer/ApiLogicServer-src/main/api_logic_server_cli/prototypes/manager/README.md"
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(file_src) # , params=params)
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
- pass # just fall back to using the pip-installed version
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'Failed to copy manager readme from installed: {e}')
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. secure API calls may not work as expected.")
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
- print_print = each_context_result
454
- if isinstance(each_context_result, dict):
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\n")
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.wordWrap": false,
8
+ "debug.console.wornw graphsdWrap": false,
9
9
  "workbench.editorAssociations": {
10
10
  "*.md": "vscode.markdown.preview.editor"
11
11
  },
12
- "workbench.colorTheme": "Office Theme (Word)",
13
- "workbench.preferredLightColorTheme": "Office Theme (Word)"
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): # must match system/genai/prompt_inserts/response_format.prompt
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;