ApiLogicServer 14.2.20__py3-none-any.whl → 14.3.7__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 (100) hide show
  1. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/METADATA +2 -2
  2. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/RECORD +90 -69
  3. api_logic_server_cli/api_logic_server.py +5 -1
  4. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  5. api_logic_server_cli/cli.py +5 -2
  6. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  7. api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
  8. api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
  9. api_logic_server_cli/create_from_model/api_logic_server_utils.py +4 -0
  10. api_logic_server_cli/create_from_model/ont_build.py +53 -19
  11. api_logic_server_cli/create_from_model/ont_create.py +14 -5
  12. api_logic_server_cli/fragments/declare_logic.py +72 -0
  13. api_logic_server_cli/{prototypes/manager/system/genai/create_db_models_inserts/logic_discovery_prefix.py → fragments/declare_logic_begin.py} +2 -1
  14. api_logic_server_cli/fragments/declare_logic_end.py +52 -0
  15. api_logic_server_cli/genai/genai.py +25 -8
  16. api_logic_server_cli/genai/genai_logic_builder.py +14 -11
  17. api_logic_server_cli/genai/genai_svcs.py +104 -7
  18. api_logic_server_cli/manager.py +20 -16
  19. api_logic_server_cli/model_migrator/model_migrator_start.py +1 -1
  20. api_logic_server_cli/model_migrator/reposreader.py +9 -1
  21. api_logic_server_cli/model_migrator/rule_obj.py +24 -6
  22. api_logic_server_cli/prototypes/base/api/api_discovery/ontimize_api.py +4 -1
  23. api_logic_server_cli/prototypes/base/api/system/expression_parser.py +10 -4
  24. api_logic_server_cli/prototypes/base/config/activate_logicbank.py +8 -4
  25. api_logic_server_cli/prototypes/base/database/bind_dbs.py +1 -1
  26. api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -5
  27. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +32 -8
  28. api_logic_server_cli/prototypes/base/integration/system/RowDictMapper.py +33 -16
  29. api_logic_server_cli/prototypes/base/logic/declare_logic.py +9 -3
  30. api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +217 -0
  31. api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +22 -13
  32. api_logic_server_cli/prototypes/genai_demo/api/customize_api.py +9 -11
  33. api_logic_server_cli/prototypes/genai_demo/database/.DS_Store +0 -0
  34. api_logic_server_cli/prototypes/genai_demo/database/db.sqlite +0 -0
  35. api_logic_server_cli/prototypes/genai_demo/database/models.py +52 -42
  36. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/OrderB2B.py +4 -6
  37. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/__pycache__/OrderB2B.cpython-312.pyc +0 -0
  38. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/row_dict_maps_readme.md +3 -0
  39. api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/declare_logic.cpython-312.pyc +0 -0
  40. api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/load_verify_rules.cpython-312.pyc +0 -0
  41. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +58 -62
  42. api_logic_server_cli/prototypes/genai_demo/logic/load_verify_rules.py +216 -0
  43. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/__init__.cpython-312.pyc +0 -0
  44. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/auto_discovery.cpython-312.pyc +0 -0
  45. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/error_testing.cpython-312.pyc +0 -0
  46. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/auto_discovery.py +52 -0
  47. api_logic_server_cli/prototypes/genai_demo/logic/readme_declare_logic.md +172 -0
  48. api_logic_server_cli/prototypes/genai_demo/security/__pycache__/declare_security.cpython-312.pyc +0 -0
  49. api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +86 -53
  50. api_logic_server_cli/prototypes/manager/.vscode/launch.json +1 -1
  51. api_logic_server_cli/prototypes/manager/README.md +19 -4
  52. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +4 -1
  53. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +34 -26
  54. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +3 -0
  55. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/.DS_Store +0 -0
  56. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/000_you_are.prompt +1 -0
  57. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/001_logic_training.prompt +314 -0
  58. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt +150 -0
  59. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/003_create_db_models.response +134 -0
  60. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/004_iteratio_logic.prompt +131 -0
  61. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/005_create_db_models.response-example +141 -0
  62. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/create_db_models.py +105 -0
  63. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/db.dbml +70 -0
  64. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/readme.md +6 -0
  65. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/response.json +178 -0
  66. 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
  67. 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
  68. 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
  69. 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
  70. 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
  71. 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
  72. 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
  73. 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
  74. 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
  75. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +194 -0
  76. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/003_create_db_models.response +298 -0
  77. api_logic_server_cli/prototypes/{genai_demo/database/chatgpt/sample_ai.sqlite → manager/system/genai/examples/time_tracking_billing/db.sqlite} +0 -0
  78. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +61 -0
  79. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +29 -11
  80. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/iteration.prompt +2 -1
  81. api_logic_server_cli/prototypes/nw_no_cust/venv_setup/system_note.txt +1 -1
  82. api_logic_server_cli/prototypes/ont_app/templates/home_tree_template.html +9 -0
  83. api_logic_server_cli/prototypes/ont_app/templates/tree_routing.jinja +32 -0
  84. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
  85. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +4 -2
  86. api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py +1 -1
  87. api_logic_server_cli/model_migrator/system/custom_endpoint.py +0 -545
  88. api_logic_server_cli/prototypes/base/database/test_data/z_test_data_rows.py +0 -98
  89. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/copilot_models.cpython-312.pyc +0 -0
  90. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/sample_ai_models.cpython-312.pyc +0 -0
  91. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.chatgpt +0 -16
  92. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.sql +0 -66
  93. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_items.sqlite +0 -0
  94. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.py +0 -156
  95. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.sqlite +0 -0
  96. api_logic_server_cli/prototypes/genai_demo/logic/cocktail-napkin.jpg +0 -0
  97. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/LICENSE +0 -0
  98. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/WHEEL +0 -0
  99. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/entry_points.txt +0 -0
  100. {ApiLogicServer-14.2.20.dist-info → ApiLogicServer-14.3.7.dist-info}/top_level.txt +0 -0
@@ -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
- return self.env.get_template(template_name)
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
- routing = self.load_routing("routing.jinja", entity_name, each_entity)
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(f'templates')
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" and column.name in tg["fks"] and column.name not in ["Id","id"] and len(tg["fks"]) == 1 and not exclude:
590
- tab_name, tab_var = self.get_tab_attrs(entity=entity, parent_entity=parent_entity, fk_tab=tg)
591
- return self.table_cell_render.render(tab_var)
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"].lower().split("(")[0] in ["bigint", "int","integer", "numeric","decimal"]:
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"] and fk["direction"] == "toone" and len(fk["attrs"]) == 1 and not exclude:
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"] and fk["direction"] == "toone" and len(fk["attrs"]) == 1 and not exclude:
727
- return self.gen_pick_list_col(col_var, fk, entity)
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" and col_type in ["BIT","BOOLEAN"]:
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 name.endswith("AMT") or name.endswith("AMOUNT") or name.endswith("TOTAL") or name in ["BALANCE","CREDITLIMIT","FREIGHT"]:
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
- if name.endswith("DT") or name.endswith("DATE"):
1108
+ elif name.endswith("DT") or name.endswith("DATE"):
1075
1109
  return "DATE" if col_type == "DATE" else "TIMESTAMP"
1076
- if name == "DISCOUNT":
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.append(app_model_attribute)
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 = "currency"
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
- rv = "text"
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
+
@@ -176,6 +176,19 @@ 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}')
188
+ genai_demo_response_path = Path('system/genai/temp/response.json')
189
+ with open(genai_demo_response_path, 'w') as response_file:
190
+ json.dump(response_dict, response_file, indent=4)
191
+
179
192
  else: # for retry from corrected response... eg system/genai/temp/chatgpt_retry.response
180
193
  self.resolved_model = "(n/a: model not used for repaired response)"
181
194
  log.debug(f'\nUsing [corrected] response from: {self.project.genai_repaired_response}')
@@ -303,7 +316,7 @@ class GenAI(object):
303
316
  raw_prompt = file.read()
304
317
  prompt = self.get_prompt__with_inserts(raw_prompt=raw_prompt, for_iteration=False) # insert db-specific logic
305
318
  self.logic_enabled = False
306
- if 'LogicBank' in prompt: # if prompt has logic, we need to insert the training
319
+ if 'LogicBank' in prompt and K_LogicBankOff not in prompt: # if prompt has logic, we need to insert the training
307
320
  prompt_messages.extend( self.get_prompt_learning_requests())
308
321
  self.logic_enabled = True
309
322
  if prompt.startswith('You are a '): # if it's a preset, we need to insert the prompt
@@ -494,13 +507,12 @@ class GenAI(object):
494
507
  return return_line
495
508
 
496
509
  logic_file = self.project.project_directory_path.joinpath('logic/declare_logic.py')
497
- if True: # self.project.is_genai_demo == False: translate logic
510
+ if self.logic_enabled:
511
+ translated_logic = genai_svcs.get_code_update_logic_file(rule_list = self.response_dict.rules,
512
+ logic_file_path = logic_file)
513
+ else: # prompt contains LogicBankOff (eg, LBX - some demo thing)
498
514
  translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
499
- translated_logic += genai_svcs.get_code(self.response_dict.rules)
500
- if self.logic_enabled == False:
501
- translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
502
- translated_logic += "\n # LogicBank Disabled \n"
503
- translated_logic += "\n # End Logic from GenAI\n\n"
515
+ translated_logic += "\n # LogicBank Disabled \n"
504
516
  utils.insert_lines_at(lines=translated_logic,
505
517
  file_name=logic_file,
506
518
  at='discover_logic()',
@@ -541,7 +553,12 @@ class GenAI(object):
541
553
  is_genai_demo = False
542
554
  if os.getenv('APILOGICPROJECT_IS_GENAI_DEMO') is not None or self.project.project_name == 'genai_demo':
543
555
  self.project.project_directory_path.joinpath('docs/project_is_genai_demo.txt').touch()
544
- # and DON'T create test data (db.sqlite already set up in recurive copy)
556
+ # and DON'T create test data (db.sqlite already set up in recursive copy)
557
+ project_docs_response = self.project.project_directory_path.joinpath('docs/response.json')
558
+ with open(project_docs_response, "w") as response_file: # WebG uses this for wg_rules
559
+ json.dump(self.response_dict, response_file, indent=4)
560
+ pass # not possible on create_db_models, since project paths not yet set by api_logic_server
561
+
545
562
  else: # normal path
546
563
  genai_svcs.rebuild_test_data_for_project(
547
564
  use_project_path = self.project.project_directory_path,
@@ -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
- self.get_and_save_response_data(response=response, file=each_file) # save raw response to docs/logic
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')
@@ -354,21 +356,22 @@ class GenAILogic(object):
354
356
  """
355
357
  translated_logic = ""
356
358
  if file.suffix == '.py': # for logic files (not suggestions - they are .txt)
357
- manager_root = Path(os.getcwd()).parent
358
- with open(manager_root.joinpath('system/genai/create_db_models_inserts/logic_discovery_prefix.py'), "r") as logic_prefix_file:
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:
359
362
  logic_prefix = logic_prefix_file.read()
360
- 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
361
364
  translated_logic += f'\n # Logic from GenAI {str(datetime.datetime.now().strftime("%B %d, %Y %H:%M:%S"))}:\n\n'
362
365
 
363
- rule_code = genai_svcs.get_code(rule_list) # get code from logic
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)
364
373
  translated_logic += rule_code
365
374
  translated_logic += "\n # End Logic from GenAI\n\n"
366
-
367
- # logic_file_name = file.stem + '.py'
368
- # logic_file_path = self.project.project_directory_path.joinpath(f'logic/logic_discovery/{logic_file_name}')
369
- with open(file, "w") as logic_file:
370
- logic_file.write(translated_logic)
371
- log.debug(f'.. stored logic code: {file}')
372
375
  pass
373
376
 
374
377
  def get_headers_with_openai_api_key(self) -> dict:
@@ -71,16 +71,99 @@ try: # this is just for WebGenAI
71
71
  except Exception as exc:
72
72
  pass # this is just for WebGenAI, ok to ignore error
73
73
 
74
- def get_code(rule_list: List[DotMap]) -> str:
75
- """returns code snippet for rules from rule
74
+ def get_code_update_logic_file(rule_list: List[DotMap], logic_file_path: Path = None) -> str:
75
+ """returns code snippet for rules from rule, updates rules if logic_file_path provided
76
+
77
+ * see avoid_collisions_on_rule
76
78
 
77
79
  Args:
78
80
  rule_list (List[DotMap]): list of rules from ChatGPT in DotMap format
81
+ logic_file_path (Path): if provided, update default rule file, with provisions for model named `Rule`
79
82
 
80
83
  Returns:
81
84
  str: the rule code
82
85
  """
83
86
 
87
+ import re
88
+
89
+ patterns = [ re.compile(r"derive=(\w+).*$"),
90
+ re.compile(r"from_parent=(\w+).*$"),
91
+ re.compile(r"Rule\.constraint\(validate=(\w+).*$"),
92
+ re.compile(r"as_sum_of=(\w+).*$"),
93
+ re.compile(r"as_count_of=(\w+).*$")
94
+ ]
95
+
96
+ def get_imports(each_line: str, imports: set):
97
+ """updates imports by extracting the class names to import
98
+
99
+ eg, Rule.constraint(validate=Customer) -> Customer
100
+
101
+ Args:
102
+ each_line (str): the ChatGPT code
103
+ imports (set): the set of imported classes (updated in place)
104
+ """
105
+
106
+ for each_pattern in patterns:
107
+ match = each_pattern.search(each_line)
108
+ if match:
109
+ the_class_name = match.group(1)
110
+ log.debug(f'.. found class: {the_class_name} in: {each_line}')
111
+ imports.add(the_class_name)
112
+ else:
113
+ pass
114
+ # log.debug(f'.. no classes found in: {each_line}')
115
+ return
116
+
117
+ def insert_logic_into_file_and_avoid_collisions_on_rule(translated_logic: str, imports: set, logic_file_path: Path = None) -> None:
118
+ """ Update logic file with rule code (if provided), with collision avoidance on models named Rule
119
+
120
+ If there's a model named `Rule`, that collides with LogicBank.Rule. So, change LogicBank.Rule refs:
121
+
122
+ 1. `from logic_bank.logic_bank import Rule`
123
+ * to: from logic_bank.logic_bank import Rule as LogicBankRule
124
+ 2. `Rule.early_row_event_all_classes(early_row_event_all_classes=handle_all)`
125
+ * to: LogicBankRule.early_row_event_all_classes
126
+ * not used in logic_files, so project is None
127
+ 3. Users' logic (e.g, Rule.constraint) --> LogicBank.Rule.constraint
128
+
129
+ Beware of these cases:
130
+ 1. als create - this is not used (but logic/declare_logic.py must exist)
131
+ 2. als genai - uses this, with discovery stuff & Rule.early_event at the end
132
+ 3. als genai-logic - logic/logic_discovery files, no discovery stuff at end
133
+ 4. webG logic - operates differently, to create logic/wg_rules files (for diagnostics)
134
+
135
+ Args:
136
+ rule_list (List[DotMap]): list of rules from ChatGPT in DotMap format
137
+ project (Project): a Project object (unless creating a logic file)
138
+ imports (set): the set of imported classes
139
+ """
140
+ insert_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
141
+ insert_logic += translated_logic
142
+ insert_logic += "\n # End Logic from GenAI\n\n"
143
+
144
+ utils.insert_lines_at(lines=insert_logic,
145
+ file_name=logic_file_path,
146
+ at='discover_logic()',
147
+ after=True)
148
+
149
+ if 'Rule' not in imports:
150
+ return
151
+ if logic_file_path is None: # this needs review for WebGenAI
152
+ log.debug(f'.. .. WebGenAI - avoid_collisions_on_rule: {logic_file_path}')
153
+ return
154
+ # find and replace `Rule` in declare_logic.py:
155
+ utils.replace_string_in_file(search_for='from logic_bank.logic_bank import Rule',
156
+ replace_with='from logic_bank.logic_bank import Rule as LogicBankRule',
157
+ in_file=logic_file_path)
158
+ utils.replace_string_in_file(search_for='Rule.early_row_event_all_classes',
159
+ replace_with='LogicBankRule.early_row_event_all_classes',
160
+ in_file=logic_file_path)
161
+ utils.replace_string_in_file(search_for=' Rule.',
162
+ replace_with=' LogicBankRule.',
163
+ in_file=logic_file_path)
164
+
165
+
166
+
84
167
  def remove_logic_halluncinations(each_line: str) -> str:
85
168
  """remove hallucinations from logic
86
169
 
@@ -110,6 +193,8 @@ def get_code(rule_list: List[DotMap]) -> str:
110
193
  pass
111
194
  elif 'Rule.constraint' in each_line:
112
195
  pass
196
+ elif 'Rule.after_flush_row_event' in each_line:
197
+ pass
113
198
  elif 'Rule.allocate' in each_line:
114
199
  pass
115
200
  elif 'Rule.calculate' in each_line:
@@ -119,7 +204,9 @@ def get_code(rule_list: List[DotMap]) -> str:
119
204
  log.debug(f'.. removed hallucination: {each_line}')
120
205
  return return_line
121
206
 
207
+
122
208
  translated_logic = ""
209
+ imports = set()
123
210
  for each_rule in rule_list:
124
211
  comment_line = each_rule.description
125
212
  translated_logic += f'\n # {comment_line}\n'
@@ -129,11 +216,19 @@ def get_code(rule_list: List[DotMap]) -> str:
129
216
  for each_line in code_lines:
130
217
  if 'declare_logic.py' not in each_line:
131
218
  each_repaired_line = remove_logic_halluncinations(each_line=each_line)
219
+ get_imports(each_line = each_repaired_line, imports=imports)
132
220
  if not each_repaired_line.startswith(' '): # sometimes in indents, sometimes not
133
221
  each_repaired_line = ' ' + each_repaired_line
134
222
  if 'def declare_logic' not in each_repaired_line:
135
- translated_logic += each_repaired_line + '\n'
136
- return translated_logic
223
+ translated_logic += each_repaired_line + '\n'
224
+
225
+ # from database.models import Customer, Order, Item, Product
226
+ return_translated_logic = ' from database.models import ' + ', '.join(imports) + '\n' + translated_logic
227
+ insert_logic_into_file_and_avoid_collisions_on_rule(
228
+ imports=imports,
229
+ translated_logic=return_translated_logic,
230
+ logic_file_path=logic_file_path)
231
+ return return_translated_logic
137
232
 
138
233
  def rebuild_test_data_for_project(response: str = 'docs/response.json',
139
234
  project: Project = None,
@@ -168,7 +263,6 @@ def rebuild_test_data_for_project(response: str = 'docs/response.json',
168
263
  existing_models = rebuild_project
169
264
  del existing_models['rules']
170
265
  del existing_models['test_data']
171
- del existing_models['test_data_rows']
172
266
  del existing_models['test_data_sqlite']
173
267
  rebuild_request.append({"role": "user", "content": json.dumps(existing_models)})
174
268
  with open(get_manager_path().joinpath('system/genai/prompt_inserts/rebuild_test_data.prompt'), 'r') as file:
@@ -436,6 +530,8 @@ def fix_and_write_model_file(response_dict: DotMap, save_dir: str, post_error:
436
530
  check_for_row_name = False
437
531
  if 'Base.metadata.create_all(engine)' in each_fixed_line:
438
532
  each_fixed_line = each_fixed_line.replace('Base.metadata.create_all(engine)', '# Base.metadata.create_all(engine)')
533
+ if ',00' in each_fixed_line:
534
+ each_fixed_line = each_fixed_line.replace(',00', ',0')
439
535
  return each_fixed_line
440
536
 
441
537
  row_names = list()
@@ -767,8 +863,9 @@ def call_chatgpt(messages: List[Dict[str, str]], api_version: str, using: str) -
767
863
  model = api_version
768
864
  if model == "": # default from CLI is '', meaning fall back to env variable or system default...
769
865
  model = os.getenv("APILOGICSERVER_CHATGPT_MODEL")
770
- if model is None or model == "*": # system default chatgpt model
771
- model = "gpt-4o-2024-08-06"
866
+ if model is None or model == "*": # system default chatgpt model
867
+ model = "gpt-4o-2024-08-06" # 33 sec
868
+ # model = "o3-mini" # 130 sec
772
869
  with open(Path(using).joinpath('request.json'), "w") as request_file: # save for debug
773
870
  json.dump(messages, request_file, indent=4)
774
871
  log.info(f'.. saved request: {using}/request.json')