ApiLogicServer 14.2.2__py3-none-any.whl → 14.3.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.
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/METADATA +2 -2
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/RECORD +73 -54
- api_logic_server_cli/api_logic_server.py +47 -10
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +9 -3
- api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.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/api_logic_server_utils.py +4 -0
- api_logic_server_cli/create_from_model/ont_build.py +53 -19
- api_logic_server_cli/create_from_model/ont_create.py +14 -5
- api_logic_server_cli/fragments/declare_logic.py +72 -0
- api_logic_server_cli/{prototypes/manager/system/genai/create_db_models_inserts/logic_discovery_prefix.py → fragments/declare_logic_begin.py} +2 -1
- api_logic_server_cli/fragments/declare_logic_end.py +52 -0
- api_logic_server_cli/genai/client.py +24 -0
- api_logic_server_cli/genai/genai.py +37 -17
- api_logic_server_cli/genai/genai_logic_builder.py +21 -35
- api_logic_server_cli/genai/genai_svcs.py +109 -13
- api_logic_server_cli/genai/genai_utils.py +0 -1
- api_logic_server_cli/model_migrator/model_migrator_start.py +1 -1
- api_logic_server_cli/model_migrator/reposreader.py +9 -1
- api_logic_server_cli/model_migrator/rule_obj.py +24 -6
- api_logic_server_cli/prototypes/base/api/api_discovery/ontimize_api.py +4 -1
- api_logic_server_cli/prototypes/base/config/activate_logicbank.py +8 -4
- api_logic_server_cli/prototypes/base/config/config.py +10 -6
- api_logic_server_cli/prototypes/base/database/bind_dbs.py +2 -1
- api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -5
- api_logic_server_cli/prototypes/base/logic/declare_logic.py +8 -3
- api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +216 -0
- api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +23 -11
- api_logic_server_cli/prototypes/genai_demo/database/models.py +11 -55
- api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +29 -21
- api_logic_server_cli/prototypes/manager/.vscode/launch.json +3 -3
- api_logic_server_cli/prototypes/manager/README.md +25 -10
- api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +19 -18
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/000_you_are.prompt +1 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/001_logic_training.prompt +314 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt +150 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/003_create_db_models.response +134 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/004_iteratio_logic.prompt +131 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/005_create_db_models.response-example +141 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/create_db_models.py +105 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/db.dbml +70 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/readme.md +6 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/response.json +178 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/genai/examples/genai_demo/wg_dev_merge/wg_genai_demo_no_logic_fixed_from_CLI/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/base_genai_demo_no_logic/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/dev_demo_no_logic_fixed/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/wg_dev_merge/wg_demo_no_logic_fixed/system/genai/examples/genai_demo/wg_dev_merge/wg_genai_demo_no_logic_fixed_from_CLI/logic/declare_logic.py +0 -1
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +194 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/003_create_db_models.response +298 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/db.sqlite +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +8 -0
- api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +14 -10
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/iteration.prompt +2 -1
- api_logic_server_cli/prototypes/nw_no_cust/venv_setup/system_note.txt +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/home_tree_template.html +9 -0
- api_logic_server_cli/prototypes/ont_app/templates/tree_routing.jinja +32 -0
- api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
- api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -1
- api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py +1 -1
- api_logic_server_cli/tools/mini_skel/run.py +1 -0
- api_logic_server_cli/model_migrator/system/custom_endpoint.py +0 -545
- api_logic_server_cli/prototypes/base/database/test_data/z_test_data_rows.py +0 -98
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/LICENSE +0 -0
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/WHEEL +0 -0
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/entry_points.txt +0 -0
- {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/top_level.txt +0 -0
api_logic_server_cli/cli.py
CHANGED
|
@@ -563,8 +563,7 @@ def tutorial(ctx, create):
|
|
|
563
563
|
@main.command("genai", cls=HideDunderCommand)
|
|
564
564
|
@click.option('--using',
|
|
565
565
|
default=f'genai_demo',
|
|
566
|
-
|
|
567
|
-
help="File or dir (determines project name)")
|
|
566
|
+
help="File or dir of prompt")
|
|
568
567
|
@click.option('--db-url', 'db_url',
|
|
569
568
|
default=f'sqlite',
|
|
570
569
|
help="SQLAlchemy Database URL\n")
|
|
@@ -614,6 +613,9 @@ def genai(ctx, using, db_url, repaired_response: str,
|
|
|
614
613
|
"""
|
|
615
614
|
global command
|
|
616
615
|
import api_logic_server_cli.genai.genai as genai_svcs
|
|
616
|
+
if using is None and repaired_response is None:
|
|
617
|
+
log.error("Error - must provide --using or --repaired-response")
|
|
618
|
+
exit(1)
|
|
617
619
|
defaulted_using = using
|
|
618
620
|
if defaulted_using == 'genai_demo': # default to genai_demo.prompt
|
|
619
621
|
defaulted_using = 'system/genai/examples/genai_demo/genai_demo.prompt'
|
|
@@ -1546,12 +1548,16 @@ def add_cust(ctx, bind_key_url_separator: str, api_name: str, project_name: str)
|
|
|
1546
1548
|
log.debug(f"\ncli[add-cust] models_py_path={models_py_path}")
|
|
1547
1549
|
if not models_py_path.exists():
|
|
1548
1550
|
raise Exception("Customizations are northwind/genai-specific - models.py does not exist")
|
|
1551
|
+
|
|
1552
|
+
project_is_genai_demo = False # can't use project.is_genai_demo because this is not the create command...
|
|
1553
|
+
if project.project_directory_path.joinpath('docs/project_is_genai_demo.txt').exists():
|
|
1554
|
+
project_is_genai_demo = True
|
|
1549
1555
|
|
|
1550
1556
|
project.abs_db_url, project.nw_db_status, project.model_file_name = create_utils.get_abs_db_url("0. Using Sample DB", project)
|
|
1551
1557
|
if create_utils.does_file_contain(search_for="CategoryTableNameTest", in_file=models_py_path):
|
|
1552
1558
|
project.add_nw_customizations(do_security=False)
|
|
1553
1559
|
log.info("\nNext step - add authentication:\n $ ApiLogicServer add-auth --db_url=auth\n\n")
|
|
1554
|
-
elif
|
|
1560
|
+
elif project_is_genai_demo and create_utils.does_file_contain(search_for="Customer", in_file=models_py_path):
|
|
1555
1561
|
project.add_genai_customizations(do_security=False)
|
|
1556
1562
|
elif project_name == 'sample_ai' and create_utils.does_file_contain(search_for="CustomerName = Column(Text", in_file=models_py_path):
|
|
1557
1563
|
cocktail_napkin_path = project.project_directory_path.joinpath('logic/cocktail-napkin.jpg')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -92,6 +92,10 @@ def copy_md(project: 'ProjectRun', from_doc_file: str, to_project_file: str = "R
|
|
|
92
92
|
|
|
93
93
|
2. Except if 1st line has ## - then remove indents to retain sections
|
|
94
94
|
|
|
95
|
+
Image references are made absolute (to github).
|
|
96
|
+
|
|
97
|
+
Doc Links are not well displayed in Codespaces, so should me minimized.
|
|
98
|
+
|
|
95
99
|
Args:
|
|
96
100
|
project (ProjectRun): project object (project name, etc)
|
|
97
101
|
from_doc_file (str): eg, Sample-Basic_Demo.md
|
|
@@ -159,8 +159,11 @@ class OntBuilder(object):
|
|
|
159
159
|
if use_local:
|
|
160
160
|
with contextlib.suppress(Exception):
|
|
161
161
|
return self.local_env.get_template(template_name)
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
try:
|
|
163
|
+
return self.env.get_template(template_name)
|
|
164
|
+
except Exception as e:
|
|
165
|
+
log.error(f"Error loading template {template_name} - {e}")
|
|
166
|
+
return None
|
|
164
167
|
|
|
165
168
|
|
|
166
169
|
def build_application(self, show_messages: bool = True):
|
|
@@ -229,11 +232,7 @@ class OntBuilder(object):
|
|
|
229
232
|
self.generate_home_template(app_path, entity_favorites, each_entity_name, each_entity, entity_name)
|
|
230
233
|
self.generate_new_template(app_path, entity_favorites, each_entity_name, each_entity, entity_name)
|
|
231
234
|
self.generate_detail_template(app_path, entity_favorites, each_entity_name, each_entity, entity_name)
|
|
232
|
-
if self.api_endpoint:
|
|
233
|
-
print(f"Ontimize Build for --api-endpoint={self.api_endpoint} - entity: {each_entity_name}")
|
|
234
|
-
return
|
|
235
235
|
self.generate_routing(app_path, each_entity_name, each_entity, entity_name)
|
|
236
|
-
|
|
237
236
|
self.generate_card_home_template(app_path, entity_favorites, each_entity_name, each_entity, entity_name)
|
|
238
237
|
|
|
239
238
|
# menu groups/routing and service config
|
|
@@ -269,7 +268,11 @@ class OntBuilder(object):
|
|
|
269
268
|
source=app_config,
|
|
270
269
|
)
|
|
271
270
|
def generate_routing(self, app_path, each_entity_name, each_entity, entity_name):
|
|
272
|
-
|
|
271
|
+
home_template_name = self.find_template(each_entity, "home_template","home_template.html")
|
|
272
|
+
if home_template_name == "home_tree_template.html":
|
|
273
|
+
routing = self.load_routing("tree_routing.jinja", entity_name, each_entity)
|
|
274
|
+
else:
|
|
275
|
+
routing = self.load_routing("routing.jinja", entity_name, each_entity)
|
|
273
276
|
write_file(app_path, entity_name, "", "-routing.module.ts", routing)
|
|
274
277
|
module = self.load_module("module.jinja", entity_name=each_entity_name, entity=each_entity)
|
|
275
278
|
write_file(app_path, entity_name, "", ".module.ts", module)
|
|
@@ -324,6 +327,11 @@ class OntBuilder(object):
|
|
|
324
327
|
if home_template_name == "grid_template.html":
|
|
325
328
|
home_scss = self.get_template("grid_home.scss").render(entity=each_entity_name)
|
|
326
329
|
ts = self.load_ts("grid_home_template.jinja", each_entity_name, each_entity, entity_favorites)
|
|
330
|
+
elif home_template_name != "home_template.html":
|
|
331
|
+
home_scss = self.get_template("home.scss").render(entity=each_entity_name)
|
|
332
|
+
home_template_nm = home_template_name.split(".")
|
|
333
|
+
ts_template = self.find_template(each_entity, f"{home_template_nm[0]}.jinja","home_template.jinja")
|
|
334
|
+
ts = self.load_ts(ts_template, each_entity_name, each_entity, entity_favorites)
|
|
327
335
|
else:
|
|
328
336
|
home_scss = self.get_template("home.scss").render(entity=each_entity_name)
|
|
329
337
|
ts = self.load_ts("home_template.jinja", each_entity_name, each_entity, entity_favorites)
|
|
@@ -455,7 +463,7 @@ class OntBuilder(object):
|
|
|
455
463
|
# loader=PackageLoader(package_name="APILOGICPROJECT",package_path="/ApiLogicServer/ApiLogicServer-dev/build_and_test/nw/ui/templates"),
|
|
456
464
|
loader=FileSystemLoader(searchpath=f"{templates_path}")
|
|
457
465
|
)
|
|
458
|
-
local_templates_path = self.app_path.joinpath(
|
|
466
|
+
local_templates_path = self.app_path.joinpath('templates')
|
|
459
467
|
local_env = Environment (
|
|
460
468
|
loader=FileSystemLoader(searchpath=f"{local_templates_path}")
|
|
461
469
|
)
|
|
@@ -464,7 +472,7 @@ class OntBuilder(object):
|
|
|
464
472
|
template_env = Environment(
|
|
465
473
|
loader=FileSystemLoader(searchpath=f"{self.template_dir}")
|
|
466
474
|
)
|
|
467
|
-
return (env,local_env, template_env)
|
|
475
|
+
return (env, local_env, template_env)
|
|
468
476
|
|
|
469
477
|
def load_ts(self, template_name: str, entity_name: str, entity: any, favorites: any) -> str:
|
|
470
478
|
# The above code is a Python function that takes a template name as input, retrieves the template
|
|
@@ -586,9 +594,15 @@ class OntBuilder(object):
|
|
|
586
594
|
if getattr(entity,"tab_groups",None) != None:
|
|
587
595
|
for tg in entity["tab_groups"]:
|
|
588
596
|
exclude = tg.get("exclude", False) or self.global_values["exclude_listpicker"] == True
|
|
589
|
-
if tg["direction"] == "toone"
|
|
590
|
-
|
|
591
|
-
|
|
597
|
+
if tg["direction"] == "toone" \
|
|
598
|
+
and column.name in tg["fks"] \
|
|
599
|
+
and column.name not in ["Id","id"] \
|
|
600
|
+
and len(tg["fks"]) == 1 \
|
|
601
|
+
and not exclude:
|
|
602
|
+
col_type = next((col.type for col in entity.columns if col.name == col_var["name"]), None)
|
|
603
|
+
if col_type and col_var["type"] != col_type:
|
|
604
|
+
tab_name, tab_var = self.get_tab_attrs(entity=entity, parent_entity=parent_entity, fk_tab=tg)
|
|
605
|
+
return self.table_cell_render.render(tab_var)
|
|
592
606
|
|
|
593
607
|
name = column.label if hasattr(column, "label") and column.label != DotMap() else column.name
|
|
594
608
|
self.add_title(column["name"], name)
|
|
@@ -606,6 +620,8 @@ class OntBuilder(object):
|
|
|
606
620
|
return self.table_real_template.render(col_var)
|
|
607
621
|
elif template_type == "table_column":
|
|
608
622
|
return self.table_column.render(col_var)
|
|
623
|
+
elif template_type.upper() == "CHECKBOX":
|
|
624
|
+
return self.check_circle_template.render(col_var)
|
|
609
625
|
else:
|
|
610
626
|
if template_type == "TEXTAREA":
|
|
611
627
|
return self.table_textarea_template.render(col_var)
|
|
@@ -629,6 +645,8 @@ class OntBuilder(object):
|
|
|
629
645
|
return "CURRENCY"
|
|
630
646
|
elif column.type in ["BLOB","CLOB", "VARBINARY"]:
|
|
631
647
|
return "IMAGE"
|
|
648
|
+
elif column.type == "BOOLEAN":
|
|
649
|
+
return "BOOLEAN"
|
|
632
650
|
return "TEXT"
|
|
633
651
|
def load_new_template(self, template_name: str,entity_name: str, entity: any, favorites: any) -> str:
|
|
634
652
|
"""
|
|
@@ -662,8 +680,14 @@ class OntBuilder(object):
|
|
|
662
680
|
self.add_title(column["name"], name)
|
|
663
681
|
for fk in fks:
|
|
664
682
|
exclude = fk.get("exclude", "false") == "true"
|
|
665
|
-
if column.name in fk["attrs"]
|
|
683
|
+
if column.name in fk["attrs"] \
|
|
684
|
+
and fk["direction"] == "toone" \
|
|
685
|
+
and len(fk["attrs"]) == 1 \
|
|
686
|
+
and not exclude:
|
|
687
|
+
#and column.template != 'text':
|
|
666
688
|
fk_entity = self.get_entity(fk["resource"])
|
|
689
|
+
#col_type = next((col.type for col in entity.columns if col.name == col_var["name"]), None)
|
|
690
|
+
#if col_type and col_var["type"] != col_type:
|
|
667
691
|
return self.gen_pick_list_col(col_var, fk, entity)
|
|
668
692
|
return self.gen_field_template(column, col_var)
|
|
669
693
|
|
|
@@ -723,8 +747,14 @@ class OntBuilder(object):
|
|
|
723
747
|
for fk in fks:
|
|
724
748
|
# TODO - not sure how to handle multiple fks attrs - so only support 1 for now
|
|
725
749
|
exclude = fk.get("exclude", "false") == "true"
|
|
726
|
-
if column.name in fk["attrs"]
|
|
727
|
-
|
|
750
|
+
if column.name in fk["attrs"] \
|
|
751
|
+
and fk["direction"] == "toone" \
|
|
752
|
+
and len(fk["attrs"]) == 1 \
|
|
753
|
+
and not exclude:
|
|
754
|
+
#and column.template != 'text':
|
|
755
|
+
#col_type = next((col.type for col in entity.columns if col.name == col_var["name"]), None)
|
|
756
|
+
#if col_type and col_var["type"] != col_type:
|
|
757
|
+
return self.gen_pick_list_col(col_var, fk, entity)
|
|
728
758
|
self.add_title(column["name"], name)
|
|
729
759
|
return self.gen_field_template(column, col_var)
|
|
730
760
|
|
|
@@ -912,7 +942,7 @@ class OntBuilder(object):
|
|
|
912
942
|
rv = self.nif_template.render(col_var)
|
|
913
943
|
elif col_type in ["DECIMAL","NUMERIC", "DOUBLE","REAL"]:
|
|
914
944
|
rv = self.real_template.render(col_var)
|
|
915
|
-
elif template_type == "CHECK_CIRCLE"
|
|
945
|
+
elif template_type == "CHECK_CIRCLE" or col_type in ["BIT","BOOLEAN"]:
|
|
916
946
|
rv == self.check_circle_template.render(col_var)
|
|
917
947
|
else:
|
|
918
948
|
rv = self.text_template.render(col_var)
|
|
@@ -1069,11 +1099,15 @@ def get_first_tab_group_entity(entity: any):
|
|
|
1069
1099
|
def calculate_template(column):
|
|
1070
1100
|
col_type = column.type.upper().split("(")[0]
|
|
1071
1101
|
name = column.name.upper()
|
|
1072
|
-
if
|
|
1102
|
+
if col_type in ["INTEGER","INT", "TINYINT", "SMALLINT"]:
|
|
1103
|
+
return "INTEGER"
|
|
1104
|
+
if col_type in ["BIT","BOOLEAN"]:
|
|
1105
|
+
return "CHECKBOX"
|
|
1106
|
+
elif name.endswith("AMT") or name.endswith("AMOUNT") or name.endswith("TOTAL") or name in ["BALANCE","CREDITLIMIT","FREIGHT"]:
|
|
1073
1107
|
return "CURRENCY"
|
|
1074
|
-
|
|
1108
|
+
elif name.endswith("DT") or name.endswith("DATE"):
|
|
1075
1109
|
return "DATE" if col_type == "DATE" else "TIMESTAMP"
|
|
1076
|
-
|
|
1110
|
+
elif name == "DISCOUNT":
|
|
1077
1111
|
return "PERCENT"
|
|
1078
1112
|
template = column.template.upper() if hasattr(column,"template") and column.template != DotMap() else col_type
|
|
1079
1113
|
if template == "TEXT" and col_type in ["DECIMAL","INTEGER","NUMERIC","REAL","FLOAT"]:
|
|
@@ -63,6 +63,10 @@ class OntCreator(object):
|
|
|
63
63
|
self.admin_app = admin_app
|
|
64
64
|
self.app = app
|
|
65
65
|
|
|
66
|
+
def attribute_exists(self, attribute: DotMap, attributes: List[DotMap]) -> bool:
|
|
67
|
+
return any(
|
|
68
|
+
each_attribute.name == attribute.name for each_attribute in attributes
|
|
69
|
+
)
|
|
66
70
|
def create_application(self, show_messages: bool = True):
|
|
67
71
|
""" Iterate over ui/admin/admin.yml, and create app...
|
|
68
72
|
|
|
@@ -124,7 +128,8 @@ class OntCreator(object):
|
|
|
124
128
|
each_attribute=each_attribute,
|
|
125
129
|
each_resource_name=each_resource_name,
|
|
126
130
|
resources=resources)
|
|
127
|
-
app_model_out.entities[each_resource_name].columns
|
|
131
|
+
if not self.attribute_exists(app_model_attribute, app_model_out.entities[each_resource_name].columns):
|
|
132
|
+
app_model_out.entities[each_resource_name].columns.append(app_model_attribute)
|
|
128
133
|
app_model_out.entities[each_resource_name].pop('attributes')
|
|
129
134
|
|
|
130
135
|
app_model_out.entities[each_resource_name].primary_key = []
|
|
@@ -151,8 +156,7 @@ class OntCreator(object):
|
|
|
151
156
|
pass
|
|
152
157
|
if show_messages:
|
|
153
158
|
log.info("\nEdit the add_model.yaml as desired, and ApiLogicServer app-build\n")
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
|
|
156
160
|
def create_model_entity(self, each_resource, resources: list) -> DotMap:
|
|
157
161
|
each_resource.favorite = each_resource.user_key
|
|
158
162
|
each_resource.exclude = "false"
|
|
@@ -227,7 +231,7 @@ class OntCreator(object):
|
|
|
227
231
|
if col_type in ["SERIAL","SERIAL4"]:
|
|
228
232
|
rv = "nif"
|
|
229
233
|
if col_type in ["DECIMAL","NUMERIC"]:
|
|
230
|
-
rv = "
|
|
234
|
+
rv = "real"
|
|
231
235
|
elif col_type in ["DOUBLE", "FLOAT", "REAL"]:
|
|
232
236
|
rv = "real"
|
|
233
237
|
elif col_type in ["DATE","DATETIME","TIME","TIMESTAMP"]:
|
|
@@ -240,8 +244,13 @@ class OntCreator(object):
|
|
|
240
244
|
rv = "image"
|
|
241
245
|
elif col_type in ["BLOB","CLOB","VARBINARY","BINARY","BYTEA","LONGBLOB","MEDIUMBLOB","TINYBLOB"]:
|
|
242
246
|
rv = "textarea"
|
|
247
|
+
elif col_type in ["BOOLEAN","BOOL"]:
|
|
248
|
+
rv = "checkbox"
|
|
243
249
|
else:
|
|
244
|
-
|
|
250
|
+
if "amount" in column.name or "price" in column.name or "rate" in column.name:
|
|
251
|
+
rv = "currency"
|
|
252
|
+
else:
|
|
253
|
+
rv = "text" #char varchar string etc
|
|
245
254
|
else:
|
|
246
255
|
rv = "text"
|
|
247
256
|
return rv
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from logic_bank.exec_row_logic.logic_row import LogicRow
|
|
4
|
+
from logic_bank.extensions.rule_extensions import RuleExtension
|
|
5
|
+
from logic_bank.logic_bank import Rule
|
|
6
|
+
import database.models as models
|
|
7
|
+
import api.system.opt_locking.opt_locking as opt_locking
|
|
8
|
+
from security.system.authorization import Grant, Security
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
def declare_logic():
|
|
14
|
+
''' Declarative multi-table derivations and constraints, extensible with Python.
|
|
15
|
+
|
|
16
|
+
Brief background: see readme_declare_logic.md
|
|
17
|
+
|
|
18
|
+
Your Code Goes Here - Use code completion (Rule.) to declare rules
|
|
19
|
+
'''
|
|
20
|
+
|
|
21
|
+
from logic.logic_discovery.auto_discovery import discover_logic
|
|
22
|
+
discover_logic()
|
|
23
|
+
|
|
24
|
+
def handle_all(logic_row: LogicRow): # #als: TIME / DATE STAMPING, OPTIMISTIC LOCKING
|
|
25
|
+
"""
|
|
26
|
+
This is generic - executed for all classes.
|
|
27
|
+
|
|
28
|
+
Invokes optimistic locking, and checks Grant permissions.
|
|
29
|
+
|
|
30
|
+
Also provides user/date stamping.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
logic_row (LogicRow): from LogicBank - old/new row, state
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
if os.getenv("APILOGICPROJECT_NO_FLASK") is not None:
|
|
37
|
+
print("\ndeclare_logic.py Using TestBase\n")
|
|
38
|
+
return # enables rules to be used outside of Flask, e.g., test data loading
|
|
39
|
+
|
|
40
|
+
if logic_row.is_updated() and logic_row.old_row is not None and logic_row.nest_level == 0:
|
|
41
|
+
opt_locking.opt_lock_patch(logic_row=logic_row)
|
|
42
|
+
|
|
43
|
+
Grant.process_updates(logic_row=logic_row)
|
|
44
|
+
|
|
45
|
+
did_stamping = False
|
|
46
|
+
if enable_stamping := False: # #als: DATE / USER STAMPING
|
|
47
|
+
row = logic_row.row
|
|
48
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedOn"):
|
|
49
|
+
row.CreatedOn = datetime.datetime.now()
|
|
50
|
+
did_stamping = True
|
|
51
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedBy"):
|
|
52
|
+
row.CreatedBy = Security.current_user().id
|
|
53
|
+
# if Config.SECURITY_ENABLED == True else 'public'
|
|
54
|
+
did_stamping = True
|
|
55
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedOn"):
|
|
56
|
+
row.UpdatedOn = datetime.datetime.now()
|
|
57
|
+
did_stamping = True
|
|
58
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedBy"):
|
|
59
|
+
row.UpdatedBy = Security.current_user().id \
|
|
60
|
+
if Config.SECURITY_ENABLED == True else 'public'
|
|
61
|
+
did_stamping = True
|
|
62
|
+
if did_stamping:
|
|
63
|
+
logic_row.log("early_row_event_all_classes - handle_all did stamping")
|
|
64
|
+
Rule.early_row_event_all_classes(early_row_event_all_classes=handle_all)
|
|
65
|
+
|
|
66
|
+
#als rules report
|
|
67
|
+
from api.system import api_utils
|
|
68
|
+
# api_utils.rules_report()
|
|
69
|
+
|
|
70
|
+
app_logger.debug("..logic/declare_logic.py (logic == rules + code)")
|
|
71
|
+
|
|
72
|
+
|
|
@@ -4,7 +4,6 @@ from logic_bank.exec_row_logic.logic_row import LogicRow
|
|
|
4
4
|
from logic_bank.extensions.rule_extensions import RuleExtension
|
|
5
5
|
from logic_bank.logic_bank import Rule
|
|
6
6
|
import database.models as models
|
|
7
|
-
from database.models import *
|
|
8
7
|
import api.system.opt_locking.opt_locking as opt_locking
|
|
9
8
|
from security.system.authorization import Grant, Security
|
|
10
9
|
import logging
|
|
@@ -18,4 +17,6 @@ def declare_logic():
|
|
|
18
17
|
|
|
19
18
|
Your Code Goes Here - Use code completion (Rule.) to declare rules
|
|
20
19
|
'''
|
|
20
|
+
|
|
21
|
+
# this logic is automatically discovered by declare_logic.py#discover_logic()
|
|
21
22
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
from logic.logic_discovery.auto_discovery import discover_logic
|
|
3
|
+
discover_logic()
|
|
4
|
+
|
|
5
|
+
def handle_all(logic_row: LogicRow): # #als: TIME / DATE STAMPING, OPTIMISTIC LOCKING
|
|
6
|
+
"""
|
|
7
|
+
This is generic - executed for all classes.
|
|
8
|
+
|
|
9
|
+
Invokes optimistic locking, and checks Grant permissions.
|
|
10
|
+
|
|
11
|
+
Also provides user/date stamping.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
logic_row (LogicRow): from LogicBank - old/new row, state
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
if os.getenv("APILOGICPROJECT_NO_FLASK") is not None:
|
|
18
|
+
print("\ndeclare_logic.py Using TestBase\n")
|
|
19
|
+
return # enables rules to be used outside of Flask, e.g., test data loading
|
|
20
|
+
|
|
21
|
+
if logic_row.is_updated() and logic_row.old_row is not None and logic_row.nest_level == 0:
|
|
22
|
+
opt_locking.opt_lock_patch(logic_row=logic_row)
|
|
23
|
+
|
|
24
|
+
Grant.process_updates(logic_row=logic_row)
|
|
25
|
+
|
|
26
|
+
did_stamping = False
|
|
27
|
+
if enable_stamping := False: # #als: DATE / USER STAMPING
|
|
28
|
+
row = logic_row.row
|
|
29
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedOn"):
|
|
30
|
+
row.CreatedOn = datetime.datetime.now()
|
|
31
|
+
did_stamping = True
|
|
32
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedBy"):
|
|
33
|
+
row.CreatedBy = Security.current_user().id
|
|
34
|
+
# if Config.SECURITY_ENABLED == True else 'public'
|
|
35
|
+
did_stamping = True
|
|
36
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedOn"):
|
|
37
|
+
row.UpdatedOn = datetime.datetime.now()
|
|
38
|
+
did_stamping = True
|
|
39
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedBy"):
|
|
40
|
+
row.UpdatedBy = Security.current_user().id \
|
|
41
|
+
if Config.SECURITY_ENABLED == True else 'public'
|
|
42
|
+
did_stamping = True
|
|
43
|
+
if did_stamping:
|
|
44
|
+
logic_row.log("early_row_event_all_classes - handle_all did stamping")
|
|
45
|
+
Rule.early_row_event_all_classes(early_row_event_all_classes=handle_all)
|
|
46
|
+
|
|
47
|
+
#als rules report
|
|
48
|
+
from api.system import api_utils
|
|
49
|
+
# api_utils.rules_report()
|
|
50
|
+
|
|
51
|
+
app_logger.debug("..logic/declare_logic.py (logic == rules + code)")
|
|
52
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from openai import AzureOpenAI
|
|
3
|
+
from openai import OpenAI
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_ai_client():
|
|
7
|
+
api_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
|
|
8
|
+
|
|
9
|
+
if not api_key:
|
|
10
|
+
raise Exception("APILOGICSERVER_CHATGPT_APIKEY environment variable not set")
|
|
11
|
+
|
|
12
|
+
azure_endpoint = os.getenv("APILOGICSERVER_CHATGPT_AZURE_ENDPOINT")
|
|
13
|
+
if azure_endpoint:
|
|
14
|
+
api_version = os.getenv("APILOGICSERVER_CHATGPT_AZURE_API_VERSION", "2024-10-21")
|
|
15
|
+
client = AzureOpenAI(
|
|
16
|
+
azure_endpoint = azure_endpoint,
|
|
17
|
+
api_key=api_key,
|
|
18
|
+
api_version=api_version)
|
|
19
|
+
print(f"Using Azure OpenAI, endpoint: {azure_endpoint}, version: {api_version}")
|
|
20
|
+
else:
|
|
21
|
+
client = OpenAI(api_key=api_key)
|
|
22
|
+
|
|
23
|
+
return client
|
|
24
|
+
|
|
@@ -176,6 +176,15 @@ class GenAI(object):
|
|
|
176
176
|
using = 'system/genai/temp',
|
|
177
177
|
api_version=self.project.genai_version)
|
|
178
178
|
response_dict = json.loads(data)
|
|
179
|
+
if os.environ.get("APILOGICPROJECT_IS_GENAI_DEMO") is not None and \
|
|
180
|
+
os.environ.get("APILOGICPROJECT_IS_GENAI_DEMO") == 'True':
|
|
181
|
+
genai_demo_response_path = Path('system/genai/examples/genai_demo/genai_demo.response_example')
|
|
182
|
+
if not genai_demo_response_path.is_file():
|
|
183
|
+
log.debug(f'.. standard genai_demo response not found: {genai_demo_response_path}')
|
|
184
|
+
else:
|
|
185
|
+
with open(genai_demo_response_path, 'r') as response_file:
|
|
186
|
+
response_dict = json.load(response_file)
|
|
187
|
+
log.debug(f'.. used standard genai_demo response: {genai_demo_response_path}')
|
|
179
188
|
else: # for retry from corrected response... eg system/genai/temp/chatgpt_retry.response
|
|
180
189
|
self.resolved_model = "(n/a: model not used for repaired response)"
|
|
181
190
|
log.debug(f'\nUsing [corrected] response from: {self.project.genai_repaired_response}')
|
|
@@ -303,7 +312,7 @@ class GenAI(object):
|
|
|
303
312
|
raw_prompt = file.read()
|
|
304
313
|
prompt = self.get_prompt__with_inserts(raw_prompt=raw_prompt, for_iteration=False) # insert db-specific logic
|
|
305
314
|
self.logic_enabled = False
|
|
306
|
-
if 'LogicBank' in prompt: # if prompt has logic, we need to insert the training
|
|
315
|
+
if 'LogicBank' in prompt and K_LogicBankOff not in prompt: # if prompt has logic, we need to insert the training
|
|
307
316
|
prompt_messages.extend( self.get_prompt_learning_requests())
|
|
308
317
|
self.logic_enabled = True
|
|
309
318
|
if prompt.startswith('You are a '): # if it's a preset, we need to insert the prompt
|
|
@@ -493,19 +502,17 @@ class GenAI(object):
|
|
|
493
502
|
log.debug(f'.. removed hallucination: {each_line}')
|
|
494
503
|
return return_line
|
|
495
504
|
|
|
496
|
-
logic_enabled = True
|
|
497
505
|
logic_file = self.project.project_directory_path.joinpath('logic/declare_logic.py')
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
506
|
+
if self.logic_enabled:
|
|
507
|
+
translated_logic = genai_svcs.get_code_update_logic_file(rule_list = self.response_dict.rules,
|
|
508
|
+
logic_file_path = logic_file)
|
|
509
|
+
else: # prompt contains LogicBankOff (eg, LBX - some demo thing)
|
|
502
510
|
translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
|
|
503
511
|
translated_logic += "\n # LogicBank Disabled \n"
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
after=True)
|
|
512
|
+
utils.insert_lines_at(lines=translated_logic,
|
|
513
|
+
file_name=logic_file,
|
|
514
|
+
at='discover_logic()',
|
|
515
|
+
after=True)
|
|
509
516
|
|
|
510
517
|
readme_lines = \
|
|
511
518
|
f'\n**GenAI Microservice Automation:** after verifying, apply logic:\n' +\
|
|
@@ -535,12 +542,25 @@ class GenAI(object):
|
|
|
535
542
|
|
|
536
543
|
response_file = self.project.project_directory_path.joinpath("docs/response.json")
|
|
537
544
|
if Path(self.project.genai_using).stem == 'logic_suggestions':
|
|
538
|
-
response_file = self.project.project_directory_path.joinpath("docs/logic_suggestions/response.json")
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
545
|
+
response_file = self.project.project_directory_path.joinpath("docs/logic_suggestions/response.json")
|
|
546
|
+
elif not response_file.exists():
|
|
547
|
+
if Path(self.project.genai_repaired_response).is_file():
|
|
548
|
+
shutil.copyfile(self.project.genai_repaired_response, response_file)
|
|
549
|
+
is_genai_demo = False
|
|
550
|
+
if os.getenv('APILOGICPROJECT_IS_GENAI_DEMO') is not None or self.project.project_name == 'genai_demo':
|
|
551
|
+
self.project.project_directory_path.joinpath('docs/project_is_genai_demo.txt').touch()
|
|
552
|
+
# and DON'T create test data (db.sqlite already set up in recursive copy)
|
|
553
|
+
project_docs_response = self.project.project_directory_path.joinpath('docs/response.json')
|
|
554
|
+
with open(project_docs_response, "w") as response_file: # WebG uses this for wg_rules
|
|
555
|
+
json.dump(self.response_dict, response_file, indent=4)
|
|
556
|
+
pass # not possible on create_db_models, since project paths not yet set by api_logic_server
|
|
557
|
+
|
|
558
|
+
else: # normal path
|
|
559
|
+
genai_svcs.rebuild_test_data_for_project(
|
|
560
|
+
use_project_path = self.project.project_directory_path,
|
|
561
|
+
project = self.project,
|
|
562
|
+
use_existing_response = True,
|
|
563
|
+
response = response_file)
|
|
544
564
|
|
|
545
565
|
except: # intentional try/catch/bury - it's just docs, so don't fail
|
|
546
566
|
import traceback
|
|
@@ -84,7 +84,9 @@ class GenAILogic(object):
|
|
|
84
84
|
log.debug(f'.. ChatGPT - saving raw response to: system/genai/temp/chatgpt_original.response')
|
|
85
85
|
response_str = genai_svcs.call_chatgpt(messages=self.messages, api_version=self.project.genai_version, using=self.project.genai_using)
|
|
86
86
|
response = json.loads(response_str)
|
|
87
|
-
|
|
87
|
+
# FIXME - perhaps required for fixup (it is failing)
|
|
88
|
+
# the rules & data models are expected to be in docs... not there
|
|
89
|
+
# self.get_and_save_response_data(response=response, file=each_file) # save raw response to docs/logic
|
|
88
90
|
self.response_dict = DotMap(response)
|
|
89
91
|
rule_list = self.response_dict.rules
|
|
90
92
|
each_code_file = self.project.project_directory_path.joinpath(f'logic/logic_discovery/{each_file.stem}.py')
|
|
@@ -298,30 +300,13 @@ class GenAILogic(object):
|
|
|
298
300
|
suggest_or_get_code_prompt = get_suggest_or_get_code_prompt()
|
|
299
301
|
self.messages.append({"role": "user", "content": suggest_or_get_code_prompt})
|
|
300
302
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
response_dict = json.loads(response_dict_str)
|
|
309
|
-
else:
|
|
310
|
-
debug_key = os.getenv("APILOGICSERVER_CHATGPT_APIKEY")
|
|
311
|
-
client = OpenAI(api_key=os.getenv("APILOGICSERVER_CHATGPT_APIKEY"))
|
|
312
|
-
model = os.getenv("APILOGICSERVER_CHATGPT_MODEL_SUGGESTION")
|
|
313
|
-
if model is None or model == "*": # system default chatgpt model
|
|
314
|
-
model = "gpt-4o-2024-08-06"
|
|
315
|
-
model = 'gpt-4o-mini' # reduces from 40 -> 7 secs
|
|
316
|
-
# 0 = 'you are', 1 = the classes, 2 = rule training
|
|
317
|
-
# FIXME - use gena0_svcs.call_chatgpt()
|
|
318
|
-
completion = client.beta.chat.completions.parse(
|
|
319
|
-
messages=self.messages, response_format=WGResult,
|
|
320
|
-
model=model # for own model, use "ft:gpt-4o-2024-08-06:personal:logicbank:ARY904vS"
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
data = completion.choices[0].message.content
|
|
324
|
-
response_dict = json.loads(data)
|
|
303
|
+
response_dict_str = call_chatgpt(
|
|
304
|
+
messages=self.messages,
|
|
305
|
+
api_version=self.project.genai_version,
|
|
306
|
+
using=self.project.project_directory_path.joinpath('docs/logic_suggestions')
|
|
307
|
+
)
|
|
308
|
+
response_dict = json.loads(response_dict_str)
|
|
309
|
+
|
|
325
310
|
self.response_dict = DotMap(response_dict)
|
|
326
311
|
|
|
327
312
|
# starting creating files in docs/logic_suggestions, starting with response
|
|
@@ -371,21 +356,22 @@ class GenAILogic(object):
|
|
|
371
356
|
"""
|
|
372
357
|
translated_logic = ""
|
|
373
358
|
if file.suffix == '.py': # for logic files (not suggestions - they are .txt)
|
|
374
|
-
manager_root = Path(os.getcwd()).parent
|
|
375
|
-
|
|
359
|
+
manager_root = Path(os.getcwd()).parent # FIXME this moved
|
|
360
|
+
logic_prefix_path = self.project.api_logic_server_dir_path.joinpath('fragments/declare_logic_begin.py')
|
|
361
|
+
with open(logic_prefix_path, "r") as logic_prefix_file:
|
|
376
362
|
logic_prefix = logic_prefix_file.read()
|
|
377
|
-
translated_logic = logic_prefix # imports, your code goes here
|
|
363
|
+
translated_logic = logic_prefix # imports (such as `from logic_bank.logic_bank import Rule``), your code goes here
|
|
378
364
|
translated_logic += f'\n # Logic from GenAI {str(datetime.datetime.now().strftime("%B %d, %Y %H:%M:%S"))}:\n\n'
|
|
379
365
|
|
|
380
|
-
|
|
366
|
+
with open(file, "w") as logic_file: # write the prefix, so get_code can fix the imports
|
|
367
|
+
logic_file.write(translated_logic)
|
|
368
|
+
log.debug(f'.. created logic code: {file}')
|
|
369
|
+
|
|
370
|
+
# update logic file with translated rules (and fix import if there is a Rule table)
|
|
371
|
+
rule_code = genai_svcs.get_code_update_logic_file(rule_list = rule_list,
|
|
372
|
+
logic_file_path = file)
|
|
381
373
|
translated_logic += rule_code
|
|
382
374
|
translated_logic += "\n # End Logic from GenAI\n\n"
|
|
383
|
-
|
|
384
|
-
# logic_file_name = file.stem + '.py'
|
|
385
|
-
# logic_file_path = self.project.project_directory_path.joinpath(f'logic/logic_discovery/{logic_file_name}')
|
|
386
|
-
with open(file, "w") as logic_file:
|
|
387
|
-
logic_file.write(translated_logic)
|
|
388
|
-
log.debug(f'.. stored logic code: {file}')
|
|
389
375
|
pass
|
|
390
376
|
|
|
391
377
|
def get_headers_with_openai_api_key(self) -> dict:
|