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.
Files changed (75) hide show
  1. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/METADATA +2 -2
  2. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/RECORD +73 -54
  3. api_logic_server_cli/api_logic_server.py +47 -10
  4. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  5. api_logic_server_cli/cli.py +9 -3
  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/client.py +24 -0
  16. api_logic_server_cli/genai/genai.py +37 -17
  17. api_logic_server_cli/genai/genai_logic_builder.py +21 -35
  18. api_logic_server_cli/genai/genai_svcs.py +109 -13
  19. api_logic_server_cli/genai/genai_utils.py +0 -1
  20. api_logic_server_cli/model_migrator/model_migrator_start.py +1 -1
  21. api_logic_server_cli/model_migrator/reposreader.py +9 -1
  22. api_logic_server_cli/model_migrator/rule_obj.py +24 -6
  23. api_logic_server_cli/prototypes/base/api/api_discovery/ontimize_api.py +4 -1
  24. api_logic_server_cli/prototypes/base/config/activate_logicbank.py +8 -4
  25. api_logic_server_cli/prototypes/base/config/config.py +10 -6
  26. api_logic_server_cli/prototypes/base/database/bind_dbs.py +2 -1
  27. api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -5
  28. api_logic_server_cli/prototypes/base/logic/declare_logic.py +8 -3
  29. api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +216 -0
  30. api_logic_server_cli/prototypes/base/logic/logic_discovery/auto_discovery.py +23 -11
  31. api_logic_server_cli/prototypes/genai_demo/database/models.py +11 -55
  32. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +29 -21
  33. api_logic_server_cli/prototypes/manager/.vscode/launch.json +3 -3
  34. api_logic_server_cli/prototypes/manager/README.md +25 -10
  35. api_logic_server_cli/prototypes/manager/system/genai/create_db_models_inserts/create_db_models_imports.py +1 -0
  36. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +19 -18
  37. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/.DS_Store +0 -0
  38. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/000_you_are.prompt +1 -0
  39. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/001_logic_training.prompt +314 -0
  40. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/002_create_db_models.prompt +150 -0
  41. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/003_create_db_models.response +134 -0
  42. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/004_iteratio_logic.prompt +131 -0
  43. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/005_create_db_models.response-example +141 -0
  44. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/create_db_models.py +105 -0
  45. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/db.dbml +70 -0
  46. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/readme.md +6 -0
  47. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_iteration_discount/response.json +178 -0
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +194 -0
  58. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/003_create_db_models.response +298 -0
  59. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/db.sqlite +0 -0
  60. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +8 -0
  61. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +14 -10
  62. api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/iteration.prompt +2 -1
  63. api_logic_server_cli/prototypes/nw_no_cust/venv_setup/system_note.txt +1 -1
  64. api_logic_server_cli/prototypes/ont_app/templates/home_tree_template.html +9 -0
  65. api_logic_server_cli/prototypes/ont_app/templates/tree_routing.jinja +32 -0
  66. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
  67. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -1
  68. api_logic_server_cli/tools/mini_skel/logic/load_verify_rules.py +1 -1
  69. api_logic_server_cli/tools/mini_skel/run.py +1 -0
  70. api_logic_server_cli/model_migrator/system/custom_endpoint.py +0 -545
  71. api_logic_server_cli/prototypes/base/database/test_data/z_test_data_rows.py +0 -98
  72. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/LICENSE +0 -0
  73. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/WHEEL +0 -0
  74. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/entry_points.txt +0 -0
  75. {ApiLogicServer-14.2.2.dist-info → ApiLogicServer-14.3.0.dist-info}/top_level.txt +0 -0
@@ -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
- prompt="File or dir (determines project name)",
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 project_name == 'genai_demo' and create_utils.does_file_contain(search_for="Customer", in_file=models_py_path):
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')
@@ -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
- 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"] != 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"] 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
+
@@ -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
- in_logic = False
499
- translated_logic = "\n # Logic from GenAI: (or, use your IDE w/ code completion)\n"
500
- translated_logic += genai_svcs.get_code(self.response_dict.rules)
501
- if self.logic_enabled == False:
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
- translated_logic += "\n # End Logic from GenAI\n\n"
505
- utils.insert_lines_at(lines=translated_logic,
506
- file_name=logic_file,
507
- at='discover_logic()',
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
- genai_svcs.rebuild_test_data_for_project(
540
- use_project_path = self.project.project_directory_path,
541
- project = self.project,
542
- use_existing_response = True,
543
- response = response_file)
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
- 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')
@@ -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
- if use_svs := True:
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
- 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
- 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:
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
- 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)
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: