ApiLogicServer 14.3.25__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 +4 -14
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +16 -7
- 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/{prototypes/manager/webgenai → fragments}/docker-compose.yml +1 -1
- api_logic_server_cli/genai/genai.py +42 -11
- api_logic_server_cli/genai/genai_graphics.py +252 -38
- api_logic_server_cli/genai/genai_svcs.py +20 -12
- 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 +57 -14
- 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/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/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 +26 -4
- 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 -10
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +32 -10
- 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.py → graphics_services_api_xxx.py} +0 -9
- 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/prompt_inserts/graphics.prompt +7 -3
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +8 -1
- 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/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.25.dist-info → apilogicserver-14.4.0.dist-info}/RECORD +87 -56
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.4.0.dist-info}/WHEEL +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_jsonapi_rpc.jinja +0 -37
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_unused.jinja +0 -38
- apilogicserver-14.3.25.dist-info/METADATA +0 -167
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.4.0.dist-info}/entry_points.txt +0 -0
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.4.0.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.4.0.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
import importlib
|
|
7
7
|
from api_logic_server_cli.genai.genai_utils import call_chatgpt
|
|
8
8
|
import requests
|
|
9
|
-
import os
|
|
9
|
+
import os, time
|
|
10
10
|
import datetime
|
|
11
11
|
import create_from_model.api_logic_server_utils as utils
|
|
12
12
|
import time
|
|
@@ -29,49 +29,101 @@ K_data_model_prompt = "Use SQLAlchemy to create"
|
|
|
29
29
|
log = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
31
|
class GenAIGraphics(object):
|
|
32
|
-
"""
|
|
33
|
-
Adds Graphics to
|
|
34
|
-
* adds `
|
|
35
|
-
* adds
|
|
32
|
+
""" 4/3/2025
|
|
33
|
+
Adds Graphics to projects (genai project or als project):
|
|
34
|
+
* adds `database/database_discovery` file to project (methods on database.models classes)
|
|
35
|
+
* adds `api/api_discovery` file to project (dashboard services - calls db methods, above)
|
|
36
|
+
* adds `docs/graphics` prompt files to **wg** project (graphics prompt files)
|
|
36
37
|
|
|
37
38
|
Invoked from:
|
|
38
39
|
1. **New GenAI Project:** for newly created project (e,g, mgr system/genai/examples/genai_demo/genai_demo.prompt)
|
|
39
|
-
* `--using` is None ==> Docs folder already has WGResponse.graphics[]
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
* `--using` is None ==> Docs folder already has WGResponse.graphics[]
|
|
41
|
+
* see api_logic_server_cli/genai/genai_svcs.py#insert_logic_into_created_project
|
|
42
|
+
2. **Existing Project:** `als genai-graphics [--using]` # existing project
|
|
43
|
+
* `--using` ==> Call ChatGPT for WGResponse.graphics, default = `<project>/docs/graphics/*.prompt`
|
|
42
44
|
* note: dbml not rebuilt after rebuild-from-db
|
|
43
45
|
3. **Existing WG Project:** in-place (do not create new project with new test data)
|
|
44
46
|
* Same as #1, but requires WG UI change ('in place', 'graphics' button, ...) to use genai_graphics cmd
|
|
45
47
|
|
|
48
|
+
Testing:
|
|
49
|
+
* BLT to create manager
|
|
50
|
+
* Test from source: launch.json (in group 3) has `Add Graphics to blt/samples/nw...`
|
|
51
|
+
* Note: uses `<mgr>/system/genai/graphics_templates`
|
|
52
|
+
* Don't forget to copy these back to `api_logic_server_cli/prototypes/manager/system/genai/graphics_templates`
|
|
53
|
+
* Optionally: update `bypass_for_debug` to True to skip ChatGPT call
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
*
|
|
49
|
-
|
|
55
|
+
Persistence model for graphics: docs/response.json (also in docs/graphics/response.json, for als customization)
|
|
56
|
+
* Requirements:
|
|
57
|
+
1. Fail-safe: do not let WG projects fail due to graphics - but alert user, just once
|
|
58
|
+
2. Iterable: do not lose graphics on WG iteration
|
|
59
|
+
* Running design: 4/12
|
|
60
|
+
* Fail-safe: implemented in dashboard_service.py -- for each <graphic.name> query:
|
|
61
|
+
1. if exists(docs/graphics/<graphics.name>.err), bypass the query
|
|
62
|
+
2. wrap each dashboard_service query in a try/except block
|
|
63
|
+
3. if exception,
|
|
64
|
+
* return "graphics failed" to iFrame so user can see it
|
|
65
|
+
* create docs/graphics/<graphics>.err to inhibit future calls
|
|
66
|
+
* For wg (--using == None):
|
|
67
|
+
* iterations build on docs/response.json, so graphics are preserved
|
|
68
|
+
* todo: verify add-rules
|
|
69
|
+
* this code creates docs/graphics/<graphics.name>.prompt
|
|
70
|
+
* preserves graphics when opening project in als mode
|
|
71
|
+
* ALS developers manage their own docs/graphics
|
|
72
|
+
|
|
50
73
|
Open Issues
|
|
51
|
-
* How to
|
|
52
|
-
* How
|
|
53
|
-
*
|
|
74
|
+
* How to enforce licensing? eg, create stubbed api/api_discovery/dashboard_services.py
|
|
75
|
+
* How can user delete or alter the graphics? Activate the Project Summary Graphics button [replace]
|
|
76
|
+
* Graphics for wg projects (not showing)
|
|
77
|
+
* No Graphics (just shows {} )
|
|
54
78
|
|
|
55
79
|
"""
|
|
56
80
|
|
|
57
|
-
def __init__(self, project: Project, using: str, genai_version: str):
|
|
81
|
+
def __init__(self, project: Project, using: str, genai_version: str, replace_with: str):
|
|
58
82
|
"""
|
|
59
83
|
Add graphics to existing projects - [see docs](https://apilogicserver.github.io/Docs/WebGenAI-CLI/#add-graphics-to-existing-projects)
|
|
60
84
|
|
|
61
|
-
|
|
85
|
+
Called to inject graphics into existing project, by:
|
|
86
|
+
1. genai#insert_logic for NEW WG projects (--replace_with = '!new-wg', using already-built docs dir)
|
|
87
|
+
2. cli for EXISTING projects (add from docs/graphics, or update per replace_with)
|
|
62
88
|
|
|
89
|
+
Args:
|
|
90
|
+
project (Project): Project object
|
|
91
|
+
using (str): path to graphics prompt files (set by genai#insert_logic, or None, for existing project)
|
|
92
|
+
replace_with (str): (request type): '!using' (default), '!new-wg', '!delete', '!retry`, or '!request'
|
|
93
|
+
genai_version (str): GenAI version to use
|
|
63
94
|
"""
|
|
64
95
|
|
|
65
96
|
self.project = project
|
|
66
|
-
self.
|
|
97
|
+
self.using = using
|
|
67
98
|
self.manager_path = genai_svcs.get_manager_path()
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
self.start_time = time.time()
|
|
100
|
+
self.replace_with = replace_with
|
|
101
|
+
''' '!using', '!new-wg', '!delete', '!retry`, or '!request Graph Sales... '''
|
|
102
|
+
graphics_response_path = self.project.project_directory_path.joinpath('docs/graphics/response.json') # assume existing project
|
|
103
|
+
if replace_with == '!new-wg': # if new webgenai, response already prepared here
|
|
70
104
|
graphics_response_path = self.project.project_directory_path.joinpath('docs/response.json')
|
|
71
|
-
|
|
72
|
-
|
|
105
|
+
|
|
106
|
+
log.info(f"\nGenAIGraphics start...")
|
|
107
|
+
log.info(f"... args:")
|
|
108
|
+
log.info(f"..... self.replace_with: {self.replace_with}")
|
|
109
|
+
log.info(f"..... self.using: {self.using}")
|
|
110
|
+
log.info(f"..... graphics_response_path: {graphics_response_path}")
|
|
111
|
+
log.info(f"..... self.project.project_directory_actual: {self.project.project_directory_actual}")
|
|
112
|
+
log.info(f"..... self.project.project_directory_path: {str(self.project.project_directory_path)}")
|
|
113
|
+
|
|
114
|
+
if self.replace_with != '!new-wg' and self.replace_with != '!using' : # update existing genai project
|
|
115
|
+
replaced_graphics = self.graphics_replace_with_in_existing_project()
|
|
116
|
+
if self.replace_with == '!delete': # we are done (else create docs/graphics prompts for processing below)
|
|
117
|
+
log.info(f"... update existing genai project - delete graphics")
|
|
118
|
+
return
|
|
119
|
+
log.info(f"... update genai existing project - from docs/graphics prompts with {replaced_graphics}")
|
|
120
|
+
|
|
121
|
+
if replace_with == '!new-wg':
|
|
122
|
+
log.info(f"... NEW WG project - already built: docs/002_create_db_models.prompt")
|
|
123
|
+
else:
|
|
124
|
+
log.info(f"... EXISTING project - process prompts in docs/graphics")
|
|
73
125
|
if bypass_for_debug := False:
|
|
74
|
-
pass
|
|
126
|
+
pass # uses already-built docs/graphics/response.json
|
|
75
127
|
else:
|
|
76
128
|
prompt = genai_svcs.read_and_expand_prompt(self.manager_path.joinpath('system/genai/prompt_inserts/graphics_request.prompt'))
|
|
77
129
|
prompt_lines = prompt.split('\n') # ChatGPT instructions
|
|
@@ -87,74 +139,236 @@ class GenAIGraphics(object):
|
|
|
87
139
|
using = self.project.project_directory_path.joinpath('docs/graphics'),
|
|
88
140
|
api_version=genai_version)
|
|
89
141
|
|
|
90
|
-
self.
|
|
142
|
+
self.create_data_class_methods(graphics_response_path)
|
|
143
|
+
self.create_graphics_dashboard_service(graphics_response_path)
|
|
144
|
+
self.create_genai_graphics_prompts(graphics_response_path)
|
|
145
|
+
log.info(f"\ngenai-graphics completed in [{str(int(time.time() - self.start_time))} secs] \n")
|
|
91
146
|
pass
|
|
92
147
|
|
|
93
|
-
def
|
|
94
|
-
""" Process graphics response from ChatGPT
|
|
148
|
+
def create_data_class_methods(self, graphics_response_path: Path):
|
|
149
|
+
""" Process graphics response from ChatGPT docs/graphics/response.json
|
|
150
|
+
* 'graphics' attributes map directly (by name) to <mgr>/system/genai/graphics_templates
|
|
151
|
+
"""
|
|
95
152
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
shutil.copy(graphic_services_header_path, graphics_services_path)
|
|
153
|
+
shutil.copy(self.manager_path.joinpath('system/genai/graphics_templates/graphics_services_db.jinja'),
|
|
154
|
+
self.project.project_directory_path.joinpath('database/database_discovery/graphics_services.py')) # all the db-class methods are created in this file
|
|
99
155
|
|
|
100
156
|
# open and read the graphics_response_path json file
|
|
101
157
|
assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
|
|
102
158
|
with open(graphics_response_path, 'r') as file:
|
|
103
159
|
graphics_response = json.load(file)
|
|
104
|
-
log.info(f'
|
|
160
|
+
log.info(f'... create_data_class_methods - from {graphics_response_path}')
|
|
105
161
|
graphics = graphics_response['graphics']
|
|
106
162
|
for each_graphic in graphics: # add each service to api/api_discovery
|
|
107
163
|
self.fix_sqlalchemy_query(each_graphic)
|
|
108
164
|
env = Environment(loader=FileSystemLoader(self.manager_path.joinpath('system/genai/graphics_templates')))
|
|
109
165
|
|
|
110
|
-
template = env.get_template('
|
|
166
|
+
template = env.get_template('graphics_services_db_each_method.jinja')
|
|
111
167
|
rendered_result = template.render( **each_graphic )
|
|
112
|
-
with open(self.project.project_directory_path.joinpath(f'
|
|
168
|
+
with open(self.project.project_directory_path.joinpath(f'database/database_discovery/graphics_services.py'), 'a') as out_file:
|
|
113
169
|
out_file.write(rendered_result)
|
|
114
170
|
|
|
171
|
+
log.info(f'..... added db class method: {each_graphic['name']} to database_discovery')
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
""" note it needs the /1 - what is that about?
|
|
176
|
+
curl -X 'GET' \
|
|
177
|
+
'http://localhost:5656/api/Category/sales_by_category' \
|
|
178
|
+
-H 'accept: application/vnd.api+json' \
|
|
179
|
+
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTc0MzY5MTc3OCwianRpIjoiNDBlYzZkNGMtMzk4My00OGEwLTgxMjQtYzQwY2RmYWFiZWRhIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InUxIiwibmJmIjoxNzQzNjkxNzc4LCJleHAiOjE3NDM3MDUwOTh9.WLDxdkp3PIsgUqR0t9-ymQDR0eOAECdQsgS_3YTqAQ0'
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def create_graphics_dashboard_service(self, graphics_response_path: Path):
|
|
183
|
+
""" Process graphics response from ChatGPT graphics_response_path """
|
|
184
|
+
|
|
185
|
+
# open and read the graphics_response_path json file
|
|
186
|
+
assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
|
|
187
|
+
with open(graphics_response_path, 'r') as file:
|
|
188
|
+
graphics_response = json.load(file)
|
|
189
|
+
if 'graphics' not in graphics_response:
|
|
190
|
+
log.error(f'No graphics found in {graphics_response_path}')
|
|
191
|
+
return
|
|
192
|
+
graphics = graphics_response['graphics']
|
|
193
|
+
|
|
194
|
+
env = Environment(loader=FileSystemLoader(self.manager_path.joinpath('system/genai/graphics_templates')))
|
|
195
|
+
iframe_templates= []
|
|
196
|
+
has_iframe = False
|
|
197
|
+
iframe_links = []
|
|
198
|
+
dashboards = []
|
|
199
|
+
cnt = 0
|
|
200
|
+
template = env.get_template('dashboard_services.jinja')
|
|
201
|
+
for each_graphic in graphics: # add each service to api/api_discovery
|
|
202
|
+
cnt += 1
|
|
203
|
+
server = '{server}'
|
|
204
|
+
iframe = f'iframe_{cnt} = iframe_template.format(url=f"{server}chart_graphics/{each_graphic['name']}")\n'
|
|
205
|
+
iframe_templates.append(iframe)
|
|
206
|
+
link = "{"+ f'iframe_{cnt}' + "}"
|
|
207
|
+
iframe_links.append(f'{link}')
|
|
208
|
+
sqlalchemy_query = each_graphic['class_x_axis']
|
|
209
|
+
|
|
210
|
+
# create the dashboard service query (skip if .err file exists, create .err file if query fails)
|
|
211
|
+
# typical failure: xxx
|
|
212
|
+
db = f"""
|
|
213
|
+
previously_failed = Path('docs/graphics/{each_graphic['name']}.err').exists()
|
|
214
|
+
if previously_failed:
|
|
215
|
+
pass # query has previously failed, so skip it
|
|
216
|
+
else:
|
|
217
|
+
try:
|
|
218
|
+
results = models.{sqlalchemy_query}.{each_graphic['name']}(None)
|
|
219
|
+
color = 'rgba(75, 192, 192, 0.2)'
|
|
220
|
+
dashboard{cnt} = template.render(result=results, color=color)
|
|
221
|
+
dashboard_result['{each_graphic['name']}']= dashboard{cnt}
|
|
222
|
+
except Exception as e:
|
|
223
|
+
msg = f"GenAI query creation error on models.{sqlalchemy_query}.{each_graphic['name']}: " + str(e)
|
|
224
|
+
dashboard_result['{each_graphic['name']}'] = msg
|
|
225
|
+
app_logger.error(msg)
|
|
226
|
+
with open('docs/graphics//{each_graphic['name']}.err', 'w') as err_file:
|
|
227
|
+
err_file.write(msg) # this logs the error to prevent future calls
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
dashboards.append(db)
|
|
231
|
+
|
|
232
|
+
rendered_result = template.render(iframe_templates=iframe_templates, iframe_links=" ".join(iframe_links), has_iframe=cnt > 0 , dashboards= dashboards)
|
|
233
|
+
with open(self.project.project_directory_path.joinpath(f'api/api_discovery/dashboard_services.py'), 'w') as out_file:
|
|
234
|
+
out_file.write(rendered_result)
|
|
235
|
+
log.info(f'... create_graphics_dashboard_service - created api/api_discovery/dashboard_services.py')
|
|
236
|
+
|
|
237
|
+
for each_graphic in graphics:
|
|
238
|
+
self.fix_sqlalchemy_query(each_graphic)
|
|
115
239
|
template = env.get_template('html_template.jinja')
|
|
116
240
|
rendered_result = template.render( **each_graphic )
|
|
117
241
|
with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.html'), 'w') as out_file:
|
|
118
242
|
out_file.write(rendered_result)
|
|
119
243
|
|
|
120
244
|
with open(self.project.project_directory_path.joinpath(f'api/api_discovery/{each_graphic['name']}.sql'), 'w') as out_file:
|
|
121
|
-
|
|
245
|
+
sql_query = "System Error: missing sql_query - check WGResult format"
|
|
246
|
+
if 'sql_query' in each_graphic:
|
|
247
|
+
sql_query = each_graphic['sql_query']
|
|
248
|
+
out_file.write(sql_query)
|
|
122
249
|
|
|
123
|
-
log.info(f'
|
|
250
|
+
log.info(f'..... added dashboard query: {each_graphic['name']} to api_discovery')
|
|
251
|
+
|
|
124
252
|
pass
|
|
125
253
|
|
|
254
|
+
def create_genai_graphics_prompts(self, graphics_response_path: Path):
|
|
255
|
+
""" if genai project, create graphics prompt file: docs/graphics/wg_graphics.prompt
|
|
256
|
+
"""
|
|
257
|
+
if self.using is not None:
|
|
258
|
+
return # it's an als request, not a genai project (todo: confirm this works on iterations)
|
|
259
|
+
|
|
260
|
+
# open and read the graphics_response_path json file
|
|
261
|
+
assert graphics_response_path.exists(), f'Graphics response file not found: {graphics_response_path}'
|
|
262
|
+
with open(graphics_response_path, 'r') as file:
|
|
263
|
+
graphics_response = json.load(file)
|
|
264
|
+
log.info(f'... create_genai_graphics_prompts - from {graphics_response_path}')
|
|
265
|
+
graphics = graphics_response['graphics']
|
|
266
|
+
graphics_prompt = ''
|
|
267
|
+
graphics_prompt_count = 0
|
|
268
|
+
for each_graphic in graphics: # add each prompt to docs/graphics
|
|
269
|
+
graphics_prompt_count += 1
|
|
270
|
+
graphics_prompt += each_graphic['prompt'] + '\n'
|
|
271
|
+
with open(self.project.project_directory_path.joinpath(f'docs/graphics/wg_graphics.prompt'), 'w') as out_file:
|
|
272
|
+
out_file.write(graphics_prompt)
|
|
273
|
+
log.info(f'..... added docs/graphics/wg_graphics.prompt')
|
|
274
|
+
pass
|
|
275
|
+
|
|
276
|
+
def graphics_replace_with_in_existing_project(self) -> str:
|
|
277
|
+
"""
|
|
278
|
+
Delete graphics for wg project (als projects - just delete the docs/graphics files)
|
|
279
|
+
1. Update docs/*.prompt to remove lines starting with Graphics
|
|
280
|
+
* Prevents reappearance on iteration
|
|
281
|
+
2. Presume (!) not necessary to delete the graphics[] in docs/*.response files
|
|
282
|
+
3. If delete, rename the api/api_discovery/dashboard_services.py file so it won't be called
|
|
283
|
+
|
|
284
|
+
This does not rebuild / create a new project - operates on current project.
|
|
285
|
+
|
|
286
|
+
If self.replace_with is retry or request, build docs/graphics/wg_graphics.prompt
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
# for all docs/*.prompt files, alter lines where the first characters are 'Graph' (case insensitive)
|
|
290
|
+
docs_dir = self.project.project_directory_path.joinpath('docs')
|
|
291
|
+
replaced_count = 0
|
|
292
|
+
replaced_prompts = ''
|
|
293
|
+
log.info(f"... graphics_replace_with_in_existing_project - self.replace_with: {self.replace_with}")
|
|
294
|
+
|
|
295
|
+
for prompt_file in docs_dir.glob('*.prompt'):
|
|
296
|
+
with open(prompt_file, 'r') as file:
|
|
297
|
+
lines = file.readlines()
|
|
298
|
+
line_number = 0
|
|
299
|
+
with open(prompt_file, 'w') as file:
|
|
300
|
+
for line in lines:
|
|
301
|
+
line_number += 1
|
|
302
|
+
if not line.strip().lower().startswith('graph '):
|
|
303
|
+
file.write(line)
|
|
304
|
+
else:
|
|
305
|
+
replaced_prompts += line
|
|
306
|
+
replaced_count += 1
|
|
307
|
+
if self.replace_with == '!retry': # we are just doing retry - don't replace, use existing wg_graphics
|
|
308
|
+
file.write(line)
|
|
309
|
+
continue
|
|
310
|
+
elif self.replace_with.startswith('!request'): # replacing - replace ==> docs and doc/graphics
|
|
311
|
+
if replaced_count == 1:
|
|
312
|
+
file.write(self.replace_with + '\n')
|
|
313
|
+
graphics_prompt = self.replace_with[8:]
|
|
314
|
+
with open(self.project.project_directory_path.joinpath(f'docs/graphics/wg_graphics.prompt'), 'w') as out_file:
|
|
315
|
+
out_file.write(self.replace_with)
|
|
316
|
+
else:
|
|
317
|
+
assert self.replace_with == '!delete', f"genai_graphics - expected !delete, got: {self.replace_with}"
|
|
318
|
+
pass # removing line
|
|
319
|
+
|
|
320
|
+
log.info(f"..... completed - processed {replaced_count} graph line(s).")
|
|
321
|
+
|
|
322
|
+
# Stop graphics: rename the old dashboard_services.py file to dashboard_services.pyZ
|
|
323
|
+
dashboard_service_path = self.project.project_directory_path.joinpath('api/api_discovery/dashboard_services.py')
|
|
324
|
+
if dashboard_service_path.exists():
|
|
325
|
+
renamed_path = dashboard_service_path.with_suffix('.pyZ')
|
|
326
|
+
dashboard_service_path.rename(renamed_path)
|
|
327
|
+
log.info(f"..... Renamed existing dashboard_services.py to {renamed_path}")
|
|
328
|
+
else:
|
|
329
|
+
log.info(f'.. Note: {dashboard_service_path} not found')
|
|
330
|
+
return replaced_prompts
|
|
331
|
+
|
|
126
332
|
def fix_sqlalchemy_query(self, graphic: Dict):
|
|
127
333
|
""" Fix the SQLAlchemy query for the graphic """
|
|
128
334
|
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\\n', '\n')
|
|
129
335
|
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('\"', '"')
|
|
130
|
-
|
|
336
|
+
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('.isnot', '.is_not')
|
|
131
337
|
|
|
338
|
+
# this part is for readability, not 'fixing'
|
|
339
|
+
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace('session.query', '(session.query')
|
|
340
|
+
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'] + ')'
|
|
341
|
+
graphic['sqlalchemy_query'] = graphic['sqlalchemy_query'].replace(').', ')\n .')
|
|
342
|
+
pass
|
|
132
343
|
|
|
133
344
|
def append_data_model(self) -> List[str]:
|
|
134
345
|
""" Get the data model
|
|
135
346
|
|
|
136
347
|
Returns:
|
|
137
|
-
list:
|
|
348
|
+
list[str]: the data model lines
|
|
138
349
|
"""
|
|
139
350
|
|
|
140
351
|
data_model_lines = []
|
|
352
|
+
data_model_lines.extend(['Here is the data model - please use it to create the graphics:'])
|
|
141
353
|
data_model_path = self.project.project_directory_path.joinpath('database/models.py')
|
|
142
354
|
assert data_model_path.exists(), f"Data model file not found: {data_model_path}"
|
|
143
355
|
with open(data_model_path, 'r') as file:
|
|
144
356
|
prompt_lines = file.readlines()
|
|
145
357
|
data_model_lines.extend(prompt_lines)
|
|
358
|
+
data_model_lines.extend(['End of data model'])
|
|
146
359
|
return data_model_lines
|
|
147
360
|
|
|
148
361
|
def append_graphics_files(self) -> List[str]:
|
|
149
|
-
""" Get graphics files (typically from project)
|
|
362
|
+
""" Get graphics files (typically from project/docs/graphics)
|
|
363
|
+
* 1 file per graphic
|
|
150
364
|
|
|
151
365
|
Returns:
|
|
152
366
|
list: logic_files
|
|
153
367
|
"""
|
|
154
368
|
|
|
155
369
|
graphics_lines = []
|
|
156
|
-
if Path(self.
|
|
157
|
-
for each_file in sorted(Path(self.
|
|
370
|
+
if Path(self.using).is_dir(): # conversation from directory
|
|
371
|
+
for each_file in sorted(Path(self.using).iterdir()):
|
|
158
372
|
if each_file.is_file() and each_file.suffix == '.prompt':
|
|
159
373
|
# read lines from each_file, and append to prompt
|
|
160
374
|
with open(each_file, 'r') as file:
|
|
@@ -42,11 +42,18 @@ class Model(BaseModel):
|
|
|
42
42
|
description: str
|
|
43
43
|
name: str
|
|
44
44
|
|
|
45
|
-
class
|
|
46
|
-
sqlalchemy_query: str # sqlalchemy group by result = { "result": [ ("name", "value) ] }
|
|
45
|
+
class Graphic(BaseModel):
|
|
46
|
+
sqlalchemy_query: str # sqlalchemy query using group by, returns result = { "result": [ ("name", "value") ] }
|
|
47
47
|
sql_query: str # sql query using group by, returns result = { "result": [ ("name", "value") ] }
|
|
48
48
|
classes_used: str # comma-delimited list of classes used in sqlalchemy_query
|
|
49
|
-
|
|
49
|
+
class_x_axis: str # name of class for x axis
|
|
50
|
+
name: str # suggested Python name for sqlalchemy_query - unique
|
|
51
|
+
prompt: str # prompt used to create the graphic
|
|
52
|
+
title: str # expanded name
|
|
53
|
+
xAxis: str # caption for x axis
|
|
54
|
+
yAxis: str # caption for y axis
|
|
55
|
+
dashboard: bool # whether appears on home page
|
|
56
|
+
graph_type: str # Bar, Line, Pie
|
|
50
57
|
html_code: str # create a java script app to show a bar chart from sqlalchemy_query result
|
|
51
58
|
|
|
52
59
|
class TestDataRow(BaseModel):
|
|
@@ -57,7 +64,7 @@ class WGResult(BaseModel): # must match system/genai/prompt_inserts/response_fo
|
|
|
57
64
|
# response: str # result
|
|
58
65
|
models : List[Model] # list of sqlalchemy classes in the response
|
|
59
66
|
rules : List[Rule] # list rule declarations
|
|
60
|
-
graphics: List[
|
|
67
|
+
graphics: List[Graphic] # list of graphs
|
|
61
68
|
test_data: str
|
|
62
69
|
test_data_rows: List[TestDataRow] # list of test data rows
|
|
63
70
|
test_data_sqlite: str # test data as sqlite INSERT statements
|
|
@@ -737,10 +744,10 @@ def get_create_prompt__with_inserts(arg_prompt_inserts: str='', raw_prompt: str=
|
|
|
737
744
|
pre_post = file.read() # eg, Use SQLAlchemy to create a sqlite database named system/genai/temp/create_db_models.sqlite, with
|
|
738
745
|
prompt_result = pre_post.replace('{{prompt}}', raw_prompt)
|
|
739
746
|
if for_iteration:
|
|
740
|
-
# Update the prior response - be sure not to lose classes and test data already created.
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
log.debug(f'.. iteration inserted: Update the prior response')
|
|
747
|
+
# Update the prior response - be sure not to lose classes, attributes, rules and test data already created.
|
|
748
|
+
iteration_prompt = read_and_expand_prompt(get_manager_path().joinpath(f'system/genai/prompt_inserts/iteration.prompt'))
|
|
749
|
+
prompt_result = iteration_prompt + '\n\n' + prompt_result
|
|
750
|
+
log.debug(f'.. iteration inserted: Update the prior response, using prompt_inserts/iteration.prompt')
|
|
744
751
|
#log.debug(f'.... iteration prompt result: {prompt_result}')
|
|
745
752
|
|
|
746
753
|
prompt_lines = prompt_result.split('\n')
|
|
@@ -766,10 +773,11 @@ def get_create_prompt__with_inserts(arg_prompt_inserts: str='', raw_prompt: str=
|
|
|
766
773
|
do_logic = False
|
|
767
774
|
prompt_line_number += 1
|
|
768
775
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
776
|
+
if format_not_requested := False: # FIXME - double format definition on create; others??
|
|
777
|
+
response_format_file_name = get_manager_path().joinpath(f'system/genai/prompt_inserts/response_format.prompt')
|
|
778
|
+
with open(response_format_file_name, 'r') as file:
|
|
779
|
+
response_format = file.readlines()
|
|
780
|
+
prompt_lines.extend(response_format)
|
|
773
781
|
|
|
774
782
|
prompt_result = "\n".join(prompt_lines) # back to a string
|
|
775
783
|
pass
|
api_logic_server_cli/manager.py
CHANGED
|
@@ -12,7 +12,7 @@ import api_logic_server_cli.api_logic_server as PR
|
|
|
12
12
|
|
|
13
13
|
def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
14
14
|
volume: str = "", open_manager: bool = True, samples: bool = True):
|
|
15
|
-
"""Implements als start to create manager - called from api_logic_server_cli/cli.py
|
|
15
|
+
"""Implements `als start` to create manager - called from api_logic_server_cli/cli.py
|
|
16
16
|
|
|
17
17
|
create Manager at os.getcwd(), including:
|
|
18
18
|
|
|
@@ -27,11 +27,11 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
|
27
27
|
Bit tricky to find find cli in subdirectories of the lib path for manager run launches
|
|
28
28
|
|
|
29
29
|
Args:
|
|
30
|
-
clean (bool):
|
|
31
|
-
open_with (str):
|
|
30
|
+
clean (bool): Overlay existing manager (projects and web_genai retained)
|
|
31
|
+
open_with (str): IDE to use
|
|
32
32
|
api_logic_server_path (Path): _description_
|
|
33
33
|
volume (str, optional): _description_. Defaults to "".
|
|
34
|
-
open_manager (bool, optional):
|
|
34
|
+
open_manager (bool, optional): Whether to open IDE at Manager. Defaults to True.
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
37
|
log = logging.getLogger(__name__)
|
|
@@ -68,8 +68,9 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
|
68
68
|
docker_volume = volume + '/'
|
|
69
69
|
|
|
70
70
|
to_dir = Path(os.getcwd())
|
|
71
|
+
""" location for creating (cleaning) the Manager """
|
|
71
72
|
path = Path(__file__)
|
|
72
|
-
|
|
73
|
+
from_dir_proto_mgr = api_logic_server_path.joinpath('prototypes/manager')
|
|
73
74
|
to_dir_str = str(to_dir)
|
|
74
75
|
to_dir_check = Path(to_dir).joinpath('venv')
|
|
75
76
|
if not Path(to_dir).joinpath('venv').exists():
|
|
@@ -85,12 +86,12 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
|
85
86
|
if to_dir_check.exists() and not clean:
|
|
86
87
|
manager_exists = True
|
|
87
88
|
if env_path.exists():
|
|
88
|
-
log.info(f" Using manager at: {to_dir}\n\n")
|
|
89
|
+
log.info(f" Using existing manager (update .env APILOGICSERVER_AUTO_OPEN, only) at: {to_dir}\n\n")
|
|
89
90
|
else:
|
|
90
91
|
log.info(f" Refreshing .env in manager at: {to_dir}\n\n")
|
|
91
|
-
copied_env = shutil.copy(src=
|
|
92
|
+
copied_env = shutil.copy(src=from_dir_proto_mgr.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
|
|
92
93
|
os.remove(to_dir.joinpath('settings.txt'))
|
|
93
|
-
else:
|
|
94
|
+
else: # new Manager, or clean existing manager
|
|
94
95
|
mgr_save_level = log.level
|
|
95
96
|
codegen_logger = logging.getLogger('sqlacodegen_wrapper.sqlacodegen.sqlacodegen.codegen')
|
|
96
97
|
codegen_logger_save_level= codegen_logger.level
|
|
@@ -99,8 +100,16 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
|
|
|
99
100
|
if to_dir_check.exists():
|
|
100
101
|
log.info(f" Cleaning manager at: {to_dir}\n\n")
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
# create the manager system files and shell scripts (samples created below).
|
|
104
|
+
copied_path = shutil.copytree(src=from_dir_proto_mgr, dst=to_dir, dirs_exist_ok=True, ) # issue: not permitted
|
|
105
|
+
copied_env = shutil.copy(src=from_dir_proto_mgr.joinpath('settings.txt'), dst=to_dir.joinpath('.env'))
|
|
106
|
+
web_genai_docker = to_dir.joinpath('webgenai/docker-compose.yml')
|
|
107
|
+
if web_genai_docker.exists():
|
|
108
|
+
log.debug(' .. WebGenAI docker_compose unaltered (license preserved)')
|
|
109
|
+
else:
|
|
110
|
+
log.debug(' .. WebGenAI docker_compose created')
|
|
111
|
+
copied_env = shutil.copy(src=api_logic_server_path.joinpath('fragments/docker-compose.yml'),
|
|
112
|
+
dst=web_genai_docker)
|
|
104
113
|
os.remove(to_dir.joinpath('settings.txt'))
|
|
105
114
|
log.debug(f" .. created manager\n")
|
|
106
115
|
|
|
Binary file
|
|
Binary file
|
|
@@ -22,6 +22,25 @@
|
|
|
22
22
|
"console": "internalConsole",
|
|
23
23
|
"internalConsoleOptions": "openOnSessionStart"
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
"name": "ApiLogicServer DEBUG",
|
|
27
|
+
"type": "debugpy",
|
|
28
|
+
"cwd": "${workspaceFolder}",
|
|
29
|
+
"env": {
|
|
30
|
+
"PYTHONPATH": "",
|
|
31
|
+
"EXPERIMENT": "",
|
|
32
|
+
"PYTHONHASHSEED": "0",
|
|
33
|
+
"APILOGICPROJECT_LOGGING_CONFIG": "config/logging.yml",
|
|
34
|
+
"APILOGICPROJECT_STOP_OK": "True",
|
|
35
|
+
"APILOGICPROJECT_DEBUG": "False"},
|
|
36
|
+
"request": "launch",
|
|
37
|
+
"program": "api_logic_server_run.py",
|
|
38
|
+
"redirectOutput": true,
|
|
39
|
+
"justMyCode": false,
|
|
40
|
+
"args": ["--flask_host=localhost", "--port=5656", "--swagger_port=5656", "--swagger_host=localhost", "--verbose=False"],
|
|
41
|
+
"console": "internalConsole",
|
|
42
|
+
"internalConsoleOptions": "openOnSessionStart"
|
|
43
|
+
},
|
|
25
44
|
{
|
|
26
45
|
"name": " - API Logic Server - VERBOSE",
|
|
27
46
|
"type": "debugpy",
|
|
@@ -4,6 +4,8 @@ import importlib
|
|
|
4
4
|
import pathlib
|
|
5
5
|
import logging as logging
|
|
6
6
|
import flask_sqlalchemy
|
|
7
|
+
from config.config import Args
|
|
8
|
+
from config import config
|
|
7
9
|
|
|
8
10
|
# use absolute path import for easier multi-{app,model,db} support
|
|
9
11
|
database = __import__('database')
|
|
@@ -40,7 +42,7 @@ def expose_models(api, method_decorators = []):
|
|
|
40
42
|
"""
|
|
41
43
|
|
|
42
44
|
debug_inspect_list = inspect.getmembers(database.models)
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
# Get all the subclasses of the Base class and expose them in the api
|
|
45
47
|
for name, obj in inspect.getmembers(database.models):
|
|
46
48
|
if inspect.isclass(obj) and issubclass(obj, database.models.SAFRSBaseX) and obj is not database.models.SAFRSBaseX:
|
|
@@ -31,7 +31,7 @@ import os, logging, logging.config, sys, yaml # failure here means venv probabl
|
|
|
31
31
|
from flask_sqlalchemy import SQLAlchemy
|
|
32
32
|
import json
|
|
33
33
|
from pathlib import Path
|
|
34
|
-
from config.config import Args
|
|
34
|
+
from config.config import Args # sets up logging
|
|
35
35
|
from config import server_setup
|
|
36
36
|
|
|
37
37
|
current_path = os.path.abspath(os.path.dirname(__file__))
|
|
@@ -68,8 +68,11 @@ import ui.admin.admin_loader as AdminLoader
|
|
|
68
68
|
from security.system.authentication import configure_auth
|
|
69
69
|
import oracledb
|
|
70
70
|
|
|
71
|
+
if os.getenv("EXPERIMENT") == '+':
|
|
72
|
+
app_logger = logging.getLogger("api_logic_server_app")
|
|
73
|
+
else:
|
|
74
|
+
app_logger = server_setup.logging_setup()
|
|
71
75
|
|
|
72
|
-
app_logger = server_setup.logging_setup()
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
# ==================================
|
|
@@ -40,6 +40,7 @@ def activate_logicbank(session, constraint_handler):
|
|
|
40
40
|
app_logger.exception(e)
|
|
41
41
|
if not os.environ.get("WG_PROJECT") == "True":
|
|
42
42
|
# Continue if inside WebGenAI
|
|
43
|
+
# see: https://apilogicserver.github.io/Docs/WebGenAI-CLI/#wg_rules-and-ide-rules
|
|
43
44
|
raise e
|
|
44
45
|
logic_logger.setLevel(logic_logger_level)
|
|
45
46
|
|