ApiLogicServer 14.4.0__py3-none-any.whl → 14.5.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 (107) hide show
  1. api_logic_server_cli/add_cust/add_cust.py +283 -0
  2. api_logic_server_cli/api_logic_server.py +15 -237
  3. api_logic_server_cli/api_logic_server_info.yaml +3 -3
  4. api_logic_server_cli/cli.py +38 -28
  5. api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
  6. api_logic_server_cli/create_from_model/__pycache__/dbml.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 +47 -0
  10. api_logic_server_cli/create_from_model/dbml.py +113 -58
  11. api_logic_server_cli/create_from_model/ont_build.py +83 -60
  12. api_logic_server_cli/create_from_model/ont_create.py +2 -1
  13. api_logic_server_cli/database/basic_demo.sqlite +0 -0
  14. api_logic_server_cli/database/basic_demo.txt +1 -0
  15. api_logic_server_cli/database/basic_demo_wg.sqlite +0 -0
  16. api_logic_server_cli/manager.py +3 -2
  17. api_logic_server_cli/prototypes/base/.vscode/launch.json +3 -2
  18. api_logic_server_cli/prototypes/base/config/config.py +66 -11
  19. api_logic_server_cli/prototypes/base/config/default.env +7 -1
  20. api_logic_server_cli/prototypes/base/database/test_data/readme.md +2 -1
  21. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +5 -2
  22. api_logic_server_cli/prototypes/base/integration/n8n/n8n_producer.py +68 -21
  23. api_logic_server_cli/prototypes/base/integration/n8n/n8n_readme.md +19 -0
  24. api_logic_server_cli/prototypes/base/test/basic/server_test.py +1 -1
  25. api_logic_server_cli/prototypes/basic_demo/README.md +29 -52
  26. api_logic_server_cli/prototypes/basic_demo/customizations/api/.DS_Store +0 -0
  27. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/mcp_server_executor.py +138 -0
  28. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/openapi.py +92 -0
  29. api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/proper_update_def.json +71 -0
  30. api_logic_server_cli/prototypes/basic_demo/customizations/config/default.env +13 -0
  31. api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
  32. api_logic_server_cli/prototypes/basic_demo/customizations/database/models.py +131 -0
  33. api_logic_server_cli/prototypes/basic_demo/customizations/integration/.DS_Store +0 -0
  34. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/.DS_Store +0 -0
  35. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/1_langchain_loader.py +71 -0
  36. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/README_mcp.md +13 -0
  37. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_client_executor.py +295 -0
  38. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_schema.txt +47 -0
  39. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_server_discovery.json +9 -0
  40. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_flow.png +0 -0
  41. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_orchestration.yaml +49 -0
  42. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/wny mcp flows.png +0 -0
  43. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/natlang_to_api.py +73 -0
  44. api_logic_server_cli/prototypes/{nw_no_cust → basic_demo/customizations}/integration/mcp/resources/curl.txt +2 -1
  45. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP Overview.png +0 -0
  46. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Arch.png +0 -0
  47. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Overview_Executor.png +0 -0
  48. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/1 - prompt_messages_array.json +10 -0
  49. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/2 - completion_tool_context.json +12 -0
  50. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/llm_schema.txt +38 -0
  51. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_2.yaml +17393 -0
  52. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_3_relaxed.yaml +109 -0
  53. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_server.py +51 -0
  54. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_serverZ.py +72 -0
  55. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/validate_jsonapi.py +64 -0
  56. api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/swagger_converter.py +65 -0
  57. api_logic_server_cli/prototypes/{nw_no_cust/integration/mcp → basic_demo/customizations/integration/mcp/z_old}/3_executor_test_agent.py +20 -6
  58. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/3_executor_test_agent.py +52 -0
  59. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/README_functon.md +201 -0
  60. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/ai_plugin.json +17 -0
  61. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/nw-swagger_3.json +1731 -0
  62. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/snippets.txt +5 -0
  63. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3 genai_demo_with_get.json +1731 -0
  64. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3.json +1782 -0
  65. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo.json +264 -0
  66. api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo_with_update.json +1782 -0
  67. api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +62 -44
  68. api_logic_server_cli/prototypes/basic_demo/customizations/security/declare_security.py +11 -12
  69. api_logic_server_cli/prototypes/basic_demo/customizations/ui/admin/admin.yaml +166 -0
  70. api_logic_server_cli/prototypes/basic_demo/iteration/api/{customize_api.py → api_discovery/order_b2b.py} +17 -23
  71. api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
  72. api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderB2B.py +6 -5
  73. api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderShipping.py +4 -4
  74. api_logic_server_cli/prototypes/basic_demo/iteration/logic/declare_logic.py +69 -43
  75. api_logic_server_cli/prototypes/basic_demo/iteration/ui/admin/admin.yaml +125 -50
  76. api_logic_server_cli/prototypes/manager/README.md +4 -0
  77. api_logic_server_cli/prototypes/manager/README_X.md +663 -0
  78. api_logic_server_cli/prototypes/nw_no_cust/Tutorial.md +45 -26
  79. api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/openapi.py +130 -0
  80. api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/proper_update_def.json +71 -0
  81. api_logic_server_cli/prototypes/nw_no_cust/config/default.env +13 -0
  82. api_logic_server_cli/prototypes/ont_app/ontimize_seed/package-lock.json +9725 -1180
  83. api_logic_server_cli/prototypes/ont_app/ontimize_seed/package.json +3 -6
  84. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/shared/app.services.config.ts +1 -1
  85. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/css/app.scss +4 -0
  86. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/en.json +1 -1
  87. api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/es.json +14 -12
  88. api_logic_server_cli/prototypes/ont_app/templates/app_config.jinja +1 -1
  89. api_logic_server_cli/prototypes/ont_app/templates/date_template.html +1 -1
  90. api_logic_server_cli/prototypes/ont_app/templates/textarea_template.html +1 -1
  91. api_logic_server_cli/prototypes/ont_app/templates/timestamp_template.html +1 -1
  92. api_logic_server_cli/prototypes/sample_ai/logic/declare_logic.py +30 -13
  93. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/METADATA +2 -2
  94. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/RECORD +101 -60
  95. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/WHEEL +1 -1
  96. api_logic_server_cli/prototypes/basic_demo/apply_customizations.ps1 +0 -17
  97. api_logic_server_cli/prototypes/basic_demo/apply_customizations.sh +0 -14
  98. api_logic_server_cli/prototypes/basic_demo/apply_iteration.ps1 +0 -20
  99. api_logic_server_cli/prototypes/basic_demo/apply_iteration.sh +0 -15
  100. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/1_langchain_loader.py +0 -19
  101. api_logic_server_cli/prototypes/nw_no_cust/integration/mcp/README.md +0 -17
  102. /api_logic_server_cli/prototypes/{nw_no_cust → basic_demo/customizations}/integration/mcp/2_gpt_mcp_prompt.txt +0 -0
  103. /api_logic_server_cli/prototypes/{nw_no_cust → basic_demo/customizations}/integration/mcp/resources/nw_swagger_3.yaml +0 -0
  104. /api_logic_server_cli/prototypes/{nw_no_cust → basic_demo/customizations}/integration/mcp/run_executor.py +0 -0
  105. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/entry_points.txt +0 -0
  106. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/licenses/LICENSE +0 -0
  107. {apilogicserver-14.4.0.dist-info → apilogicserver-14.5.0.dist-info}/top_level.txt +0 -0
@@ -80,7 +80,7 @@ class OntBuilder(object):
80
80
  self.currency_symbol_position="left" # "right"
81
81
  self.thousand_separator="," # "."
82
82
  self.decimal_separator="." # ","
83
- self.date_format="LL" #not sure what this means
83
+ self.date_format="YYYY-DD-MM" #not sure what this means
84
84
  self.edit_on_mode = "dblclick" # edit or click
85
85
  self.include_translation = False
86
86
  self.row_height = "medium"
@@ -156,14 +156,15 @@ class OntBuilder(object):
156
156
  with contextlib.suppress(Exception):
157
157
  return self.template_env.get_template(template_name)
158
158
  use_local=True
159
- if use_local:
160
- with contextlib.suppress(Exception):
161
- return self.local_env.get_template(template_name)
162
159
  try:
160
+ if use_local:
161
+ with contextlib.suppress(Exception):
162
+ return self.local_env.get_template(template_name)
163
+
163
164
  return self.env.get_template(template_name)
164
165
  except Exception as e:
165
166
  log.error(f"Error loading template {template_name} - {e}")
166
- return None
167
+ return None
167
168
 
168
169
 
169
170
  def build_application(self, show_messages: bool = True):
@@ -201,7 +202,7 @@ class OntBuilder(object):
201
202
  for setting_name, each_setting in app_model.settings.style_guide.items():
202
203
  #style guide
203
204
  self.set_style(setting_name, each_setting)
204
- self.global_values[setting_name] = each_setting
205
+ self.global_values[setting_name] = each_setting if setting_name is not DotMap() else None
205
206
 
206
207
  '''
207
208
  # Breaking change - added to app.config.ts - values may not be on older version
@@ -210,23 +211,25 @@ class OntBuilder(object):
210
211
  applicationLocales = ["en","es"]
211
212
  startSessionPath = "/auth/login"
212
213
  '''
213
- if getattr(self.global_values,"serviceType",None) is None:
214
+ if getattr(self.global_values,"serviceType",None) is None or getattr(self.global_values,"serviceType",None) == DotMap():
214
215
  self.global_values["serviceType"] = "JSONAPI"
215
- if getattr(self.global_values,"locale",None) is None:
216
- self.global_values["locale"] = ["en","es"]
217
- if getattr(self.global_values,"applicationLocales",None) is None:
218
- self.global_values["applicationLocales"] = "en"
219
- if getattr(self.global_values,"startSessionPath",None) is None:
220
- self.global_values["startSessionPath"] = "/auth/login"
221
- if getattr(self.global_values,"exclude_listpicker",None) is None:
216
+ if getattr(self.global_values,"locale",None) is None or getattr(self.global_values,"locale",None) == DotMap():
217
+ self.global_values["locale"] = "en"
218
+ if getattr(self.global_values,"applicationLocales",None) is None or getattr(self.global_values,"applicationLocales",None) == DotMap():
219
+ self.global_values["applicationLocales"] = ['en','es']
220
+
221
+ if getattr(self.global_values,"exclude_listpicker",None) is None or getattr(self.global_values,"exclude_listpicker",None) == DotMap():
222
222
  self.global_values["exclude_listpicker"] = False
223
+
224
+ self.global_values["startSessionPath"] = '/auth/login'
225
+ self.global_values["api_endpoint"] = self.apiEndpoint
223
226
 
224
227
  # If the application yaml has been included = we will use the values from the yaml
225
228
  if "application" in app_model:
226
229
  for app in app_model.application:
227
230
  # yaml may have multiple apps = only work on the one selected app-build --app={app}
228
- if self.app != app:
229
- continue
231
+ #if self.app != app:
232
+ # continue
230
233
  menu_group = app_model.application[app]["menu_group"]
231
234
  for mg in menu_group:
232
235
  for mi in menu_group[mg]["menu_item"]:
@@ -271,7 +274,8 @@ class OntBuilder(object):
271
274
  # Generates KeyCloak or SQL Auth - if already set - do not overwrite - use rebuild=from
272
275
  self.gen_auth_components(app_path, keycloak_args, self.use_keycloak,overwrite=False)
273
276
  rv_app_config = self.gen_app_config()
274
- rv_environment = self.environment_template.render(apiEndpoint=self.apiEndpoint)
277
+ apiEndpoint = self.global_values.api_endpoint or self.apiEndpoint
278
+ rv_environment = self.environment_template.render(apiEndpoint=apiEndpoint)
275
279
  write_root_file(
276
280
  app_path=app_path,
277
281
  dir_name="environments",
@@ -318,20 +322,20 @@ class OntBuilder(object):
318
322
  )
319
323
 
320
324
  def build_entity_list_from_app(self):
321
- entity_list = self.app_model.entities.items()
325
+ entity_list = self.app_model.entities
322
326
  if "application" in self.app_model:
323
- entities = []
327
+ entities = {}
324
328
  for app in self.app_model.application:
325
329
  # yaml may have multiple apps = only work on the one selected app-build --app={app}
326
- if self.app != app:
327
- continue
330
+ #if self.app != app:
331
+ # continue
328
332
  menu_group = self.app_model.application[app]["menu_group"]
329
333
  for mg in menu_group:
330
334
  for mi in menu_group[mg]["menu_item"]:
331
335
  each_entity = self.app_model.entities[mi]
332
- for each_entity_name, each_entity in entity_list:
336
+ for each_entity_name, each_entity in entity_list.items():
333
337
  if each_entity_name == mi:
334
- entities.append((mi,each_entity))
338
+ entities[mi] = each_entity
335
339
  return entities
336
340
 
337
341
  return entity_list
@@ -439,7 +443,7 @@ class OntBuilder(object):
439
443
  def build_entity_favorites(self):
440
444
  entity_favorites = []
441
445
  entity_list = self.build_entity_list_from_app()
442
- for each_entity_name, each_entity in entity_list:
446
+ for each_entity_name, each_entity in entity_list.items():
443
447
  datatype = 'INTEGER'
444
448
  pkey_datatype = 'INTEGER'
445
449
  primary_key = each_entity["primary_key"]
@@ -505,7 +509,7 @@ class OntBuilder(object):
505
509
 
506
510
  def get_entity(self, entity_name):
507
511
  entity_list = self.build_entity_list_from_app()
508
- for each_entity_name, each_entity in entity_list:
512
+ for each_entity_name, each_entity in entity_list.items():
509
513
  if each_entity_name == entity_name:
510
514
  return each_entity
511
515
 
@@ -542,9 +546,9 @@ class OntBuilder(object):
542
546
  return template.render(entity_vars)
543
547
 
544
548
  def load_home_template(self, template_name: str, entity: any, entity_name:str, entity_favorites: any) -> str:
545
- template = self.get_template(template_name)
549
+ template = self.get_template(template_name) or self.get_template("home_template.html")
546
550
  entity_vars = self.get_entity_vars(entity_name=entity_name, entity=entity)
547
- entity_vars["row_columns"] = self.get_entity_columns(entity)
551
+ entity_vars["row_columns"] = self.get_entity_columns(entity, entity_vars=entity_vars)
548
552
  entity_vars["has_tabs"] = False
549
553
  if template_name.endswith("_expand.html"):
550
554
  self.gen_expanded_template(entity, entity_favorites, entity_vars)
@@ -572,13 +576,14 @@ class OntBuilder(object):
572
576
  entity_vars["single_tab_panel"] = self.single_tab_panel.render(tab_vars)
573
577
  entity_vars["has_tabs"] = True
574
578
 
575
- def get_entity_columns(self, entity):
579
+ def get_entity_columns(self, entity, entity_vars: dict = None) -> list:
576
580
  row_cols = []
577
- for column in entity.columns:
578
- if column.get("exclude", "false") == "true":
579
- continue
580
- rv = self.gen_home_columns(entity, entity, column)
581
- row_cols.append(rv)
581
+
582
+ visible_columns = entity_vars["visibleColumns"].split(";") if entity_vars else entity.columns
583
+ for col in visible_columns:
584
+ if column := find_column(entity, col):
585
+ rv = self.gen_home_columns(entity, entity, column)
586
+ row_cols.append(rv)
582
587
  return row_cols
583
588
 
584
589
  def get_entity_vars(self, entity_name:str, entity, page_name: str = "home") -> dict:
@@ -589,7 +594,7 @@ class OntBuilder(object):
589
594
  fav_column = find_column(entity,favorite)
590
595
  if page := self.get_page(page_name, entity.type):
591
596
  cols = page.visible_columns.replace(",",";",100)
592
- visible_columns = page.visible_columns.replace(",",";",100)
597
+ visible_columns = page.visible_columns.replace(" ","",100).replace(",",";",100)
593
598
  else:
594
599
  cols = self.get_columns(entity)
595
600
  visible_columns = self.get_visible_columns(entity, True)
@@ -652,8 +657,7 @@ class OntBuilder(object):
652
657
 
653
658
  def get_columns(self, entity) -> str:
654
659
  cols = []
655
- for column in entity.columns:
656
- cols.append(column.name)
660
+ cols.extend(column.name for column in entity.columns)
657
661
  return ";".join(cols)
658
662
 
659
663
  def get_page(self, page_name, entity_name: str) -> dict:
@@ -661,8 +665,8 @@ class OntBuilder(object):
661
665
  if "application" in self.app_model:
662
666
  for app in self.app_model.application:
663
667
  # yaml may have multiple apps = only work on the one selected app-build --app={app}
664
- if self.app != app:
665
- continue
668
+ #if self.app != app:
669
+ # continue
666
670
  menu_group = self.app_model.application[app]["menu_group"]
667
671
  for mg in menu_group:
668
672
  for mi in menu_group[mg]["menu_item"]:
@@ -679,13 +683,13 @@ class OntBuilder(object):
679
683
  if "application" in self.app_model:
680
684
  for app in self.app_model.application:
681
685
  # yaml may have multiple apps = only work on the one selected app-build --app={app}
682
- if self.app != app:
683
- continue
686
+ #if self.app != app:
687
+ # continue
684
688
  menu_group = self.app_model.application[app]["menu_group"]
685
689
  for mg in menu_group:
686
690
  entities = []
687
691
  for mi in menu_group[mg]["menu_item"]:
688
- for entity_name, each_entity in entity_list:
692
+ for entity_name, each_entity in entity_list.items():
689
693
  if entity_name == mi:
690
694
  get_group(menu_groups, mg, each_entity)
691
695
  return menu_groups
@@ -697,6 +701,7 @@ class OntBuilder(object):
697
701
  # Lookup real Entity Table Name Here
698
702
  self.title_translation.append({title: entity_name})
699
703
  def gen_home_columns(self, entity, parent_entity, column):
704
+ # sourcery skip: low-code-quality
700
705
  col_var = self.get_column_attrs(column)
701
706
  if getattr(entity,"tab_groups",None) != None:
702
707
  for tg in entity["tab_groups"]:
@@ -767,9 +772,15 @@ class OntBuilder(object):
767
772
  fks = get_foreign_keys(entity, favorites)
768
773
  row_cols = []
769
774
  defaultValues = {}
770
- for column in entity.columns:
771
- rv = self.get_new_column(column, fks, entity)
772
- row_cols.append(rv)
775
+ if page := self.get_page("new", entity.type):
776
+ visible_columns = page.visible_columns.replace(" ","",100).replace(",",";",100)
777
+ else:
778
+ visible_columns = self.get_visible_columns(entity, True)
779
+ for col in visible_columns.split(";"):
780
+ for column in entity.columns:
781
+ if col == column.name:
782
+ rv = self.get_new_column(column, fks, entity)
783
+ row_cols.append(rv)
773
784
 
774
785
  entity_vars["row_columns"] = row_cols
775
786
  return template.render(entity_vars)
@@ -838,11 +849,17 @@ class OntBuilder(object):
838
849
  entity_vars = self.get_entity_vars(entity_name, entity)
839
850
  fks = get_foreign_keys(entity, favorites)
840
851
  row_cols = []
841
- for column in entity.columns:
842
- if column.get("exclude", "false") == "true":
843
- continue
844
- rv = self.gen_detail_rows(column, fks, entity)
845
- row_cols.append(rv)
852
+ if page := self.get_page("detail", entity.type):
853
+ visible_columns = page.visible_columns.replace(" ","",100).replace(",",";",100)
854
+ else:
855
+ visible_columns = self.get_visible_columns(entity, True)
856
+ for col in visible_columns.split(";"):
857
+ for column in entity.columns:
858
+ if col == column.name:
859
+ if column.get("exclude", "false") == "true":
860
+ continue
861
+ rv = self.gen_detail_rows(column, fks, entity)
862
+ row_cols.append(rv)
846
863
 
847
864
  entity_vars["row_columns"] = row_cols
848
865
  entity_vars["has_tabs"] = len(fks) > 0
@@ -898,12 +915,18 @@ class OntBuilder(object):
898
915
  entity_vars = self.get_entity_vars(entity.type, entity, 'detail')
899
916
  template_var |= entity_vars
900
917
  row_cols = []
901
- for column in entity.columns:
902
- if column.get("exclude", "false") == "true":
903
- continue
904
- rv = self.gen_home_columns(entity,parent_entity, column)
905
- row_cols.append(rv)
906
-
918
+ if page := self.get_page("home", entity.type):
919
+ visible_columns = page.visible_columns.replace(" ","",100).replace(",",";",100)
920
+ else:
921
+ visible_columns = self.get_visible_columns(entity, True)
922
+ for col in visible_columns.split(";"):
923
+ for column in entity.columns:
924
+ if col == column.name:
925
+ if column.get("exclude", "false") == "true":
926
+ continue
927
+ rv = self.gen_home_columns(entity,parent_entity, column)
928
+ row_cols.append(rv)
929
+ template_var['visibleColumns'] = visible_columns
907
930
  template_var["row_columns"] = row_cols
908
931
  return tab_template.render(template_var)
909
932
 
@@ -930,7 +953,7 @@ class OntBuilder(object):
930
953
  tab_name, tab_vars = self.get_tab_attrs(entity, parent_entity, fk_tab)
931
954
  primaryKey = make_keys(entity["primary_key"])
932
955
  entity_list = self.build_entity_list_from_app()
933
- for each_entity_name, each_entity in entity_list:
956
+ for each_entity_name, each_entity in entity_list.items():
934
957
  if each_entity_name == tab_name:
935
958
  template = self.load_tab_template(each_entity,entity,tab_vars, primaryKey )
936
959
  panels.append(template)
@@ -1004,7 +1027,7 @@ class OntBuilder(object):
1004
1027
 
1005
1028
  sidebarTemplate = self.sidebar_template
1006
1029
  # sep = ","
1007
- for each_entity_name, each_entity in entities:
1030
+ for each_entity_name, each_entity in entities.items():
1008
1031
  name = each_entity_name
1009
1032
  entity_first_cap = f"{name[:1].upper()}{name[1:]}"
1010
1033
  var = {"entity": name, "entity_first_cap": entity_first_cap}
@@ -1086,7 +1109,7 @@ class OntBuilder(object):
1086
1109
  if tg.direction == "tomany":
1087
1110
  var["tab_name"] = tg.resource
1088
1111
  var["tab_key"] = tg.fks[0]
1089
- if next((e for e in entity_list if e[0] == tg.resource), None):
1112
+ if next((e for e in entity_list if e == tg.resource), None):
1090
1113
  additional_routes += f",{self.detail_route_template.render(var)}"
1091
1114
 
1092
1115
  var["additional_routes"] = additional_routes
@@ -1124,7 +1147,7 @@ class OntBuilder(object):
1124
1147
  menu_separator = ""
1125
1148
  groups = self.get_menu_group()
1126
1149
  if len(groups) == 0:
1127
- for each_entity_name, each_entity in entities:
1150
+ for each_entity_name, each_entity in entities.items():
1128
1151
  group = getattr(each_entity, "group") or "data"
1129
1152
  get_group(groups, group, each_entity)
1130
1153
 
@@ -1310,7 +1333,7 @@ def gen_app_service_config(entities: any) -> str:
1310
1333
  sep = ""
1311
1334
  config = ""
1312
1335
  children = ""
1313
- for each_entity_name, each_entity in entities:
1336
+ for each_entity_name, each_entity in entities.items():
1314
1337
  name = each_entity_name
1315
1338
  title = each_entity["label"].replace("*","") if hasattr(each_entity, "label") and each_entity.label != DotMap() else name
1316
1339
  child = child_template.render(title=title, name=name)
@@ -316,6 +316,7 @@ class OntCreator(object):
316
316
  def style_guide(self) -> DotMap:
317
317
  style_guide = DotMap()
318
318
  # GLOBAL Style settings for all forms
319
+ style_guide.api_endpoint = "http://localhost:5656/api" # "http://localhost:8080"
319
320
  style_guide.mode = "tab" # "dialog"
320
321
  style_guide.pick_style = "list" #"combo" or"list"
321
322
  style_guide.style = "light" # "dark"
@@ -323,7 +324,7 @@ class OntCreator(object):
323
324
  style_guide.currency_symbol_position="left" # "right"
324
325
  style_guide.thousand_separator="," # "."
325
326
  style_guide.decimal_separator="." # ","
326
- style_guide.date_format="LL" #not sure what this means
327
+ style_guide.date_format="YYYY-MM-DD" #not sure what this means
327
328
  style_guide.edit_on_mode = "dblclick" # edit #click
328
329
  style_guide.min_decimal_digits="2"
329
330
  style_guide.max_decimal_digits="4"
@@ -0,0 +1 @@
1
+ see tests/test_databases/basic_demo/basic_demo.sql
@@ -137,7 +137,7 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
137
137
  else:
138
138
  if project.is_docker:
139
139
  log.debug(f" tutorial not created for docker\n\n")
140
- else:
140
+ elif create_manager := False:
141
141
  tutorial_project = PR.ProjectRun(command="tutorial",
142
142
  project_name='./samples',
143
143
  db_url="",
@@ -145,7 +145,8 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
145
145
  open_with="NO_AUTO_OPEN"
146
146
  )
147
147
  tutorial_project = tutorial_project.tutorial(msg="Creating:") ##, create='tutorial')
148
-
148
+ else:
149
+ log.debug(f"For Tutorial, use Northwind and basic_demo\n\n")
149
150
  samples_project = PR.ProjectRun(command= "create", project_name=f'{docker_volume}samples/nw_sample', db_url='nw+', open_with="NO_AUTO_OPEN")
150
151
  log.setLevel(mgr_save_level)
151
152
  log.disabled = False # todo why was it reset?
@@ -95,7 +95,8 @@
95
95
  "internalConsoleOptions": "openOnSessionStart"
96
96
  },
97
97
  {
98
- "name": " - test/basic/server_test.py",
98
+ "name": "Test - test/basic/server_test.py",
99
+ // use this for test programs, eg to test the server
99
100
  "type": "debugpy",
100
101
  "request": "launch",
101
102
  "cwd": "${workspaceFolder}",
@@ -112,7 +113,7 @@
112
113
  "request": "launch",
113
114
  "cwd": "${workspaceFolder}/ui/app",
114
115
  "runtimeExecutable": "npm",
115
- "runtimeArgs": ["install"],
116
+ "runtimeArgs": ["install --force"],
116
117
  "console": "integratedTerminal"
117
118
  },
118
119
  {
@@ -223,17 +223,19 @@ class Config:
223
223
  app_logger.info(f'config.py - KAFKA_SERVER: {KAFKA_SERVER}')
224
224
  # N8N Webhook Args (for testing)
225
225
  # see https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=n8n-nodes-base.webhook#path
226
+ # N8N is a workflow automation tool that allows you to connect different applications and automate tasks between them.
226
227
  wh_scheme = "http"
227
228
  wh_server = "localhost" # or cloud.n8n.io...
228
229
  wh_port = 5678
229
- wh_endpoint = "webhook-test"
230
- wh_path = "002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69"
231
- token = "YWRtaW46cA=="
232
- N8N_PRODUCER = {"authorization": f"Basic {token}", "n8n_url": f'"{wh_scheme}://{wh_server}:{wh_port}/{wh_endpoint}/{wh_path}"'}
230
+ wh_endpoint = "webhook-test" # This comes from the WebHook node in n8n
231
+ wh_path = "002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69" # This comes from the WebHook node in n8n
232
+ wh_token = "YWRtaW46cA==" # This is the base64 encoded string of username:password (e.g. admin:password)
233
+ N8N_PRODUCER = {"authorization": f"Basic {wh_token}", "n8n_url": f'"{wh_scheme}://{wh_server}:{wh_port}/{wh_endpoint}/{wh_path}"'}
233
234
  # Or enter the n8n_url directly:
234
- N8N_PRODUCER = {"authorization": f"Basic {token}","n8n_url":"http://localhost:5678/webhook-test/002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69"}
235
+ #N8N_PRODUCER = {"authorization": f"Basic {wh_token}","n8n_url":"http://localhost:5678/webhook-test/002fa0e8-f7aa-4e04-b4e3-e81aa29c6e69"}
235
236
  N8N_PRODUCER = None # comment out to enable N8N producer
236
- # Consumer under consideration
237
+ # See integration/n8n/n8n_readme.md for more details
238
+
237
239
 
238
240
  OPT_LOCKING = "optional"
239
241
  if os.getenv('OPT_LOCKING'): # e.g. export OPT_LOCKING=required
@@ -298,14 +300,19 @@ class Args():
298
300
  self.kafka_producer = Config.KAFKA_PRODUCER
299
301
  self.kafka_consumer = Config.KAFKA_CONSUMER
300
302
  self.kafka_consumer_group = Config.KAFKA_CONSUMER_GROUP
301
- self.n8n_producer = Config.N8N_PRODUCER
302
303
  self.keycloak_base = Config.KEYCLOAK_BASE
303
304
  self.keycloak_realm = Config.KEYCLOAK_REALM
304
305
  self.keycloak_base_url = Config.KEYCLOAK_BASE_URL
305
306
  self.keycloak_client_id = Config.KEYCLOAK_CLIENT_ID
306
307
  self.backtic_as_quote = Config.BACKTIC_AS_QUOTE
307
308
  self.service_type = Config.ONTIMIZE_SERVICE_TYPE
308
-
309
+ self.wh_scheme = Config.wh_scheme
310
+ self.wh_server = Config.wh_server
311
+ self.wh_port = Config.wh_port
312
+ self.wh_endpoint = Config.wh_endpoint
313
+ self.wh_path = Config.wh_path
314
+ self.wh_token = Config.wh_token
315
+ self.n8n_producer = Config.N8N_PRODUCER
309
316
  self.verbose = False
310
317
  self.create_and_run = False
311
318
 
@@ -576,7 +583,56 @@ class Args():
576
583
  def n8n_producer(self, a: str):
577
584
  self.flask_app.config["N8N_PRODUCER"] = a
578
585
 
579
-
586
+ # WebHook Args (used by N8N producer - see n8n_producer above)
587
+ @property
588
+ def wh_scheme(self) -> str:
589
+ """ n8n connect string """
590
+ return self.flask_app.config["WH_SCHEME"]
591
+ @wh_scheme.setter
592
+ def wh_scheme(self, a: str):
593
+ self.flask_app.config["WH_SCHEME"] = a
594
+
595
+ @property
596
+ def wh_server(self) -> str:
597
+ """ n8n connect string """
598
+ return self.flask_app.config["WH_SERVER"]
599
+ @wh_server.setter
600
+ def wh_server(self, a: str):
601
+ self.flask_app.config["WH_SERVER"] = a
602
+
603
+ @property
604
+ def wh_port(self) -> str:
605
+ """ n8n connect string """
606
+ return self.flask_app.config["WH_PORT"]
607
+
608
+ @wh_port.setter
609
+ def wh_port(self, a: str):
610
+ self.flask_app.config["WH_PORT"] = a
611
+
612
+ @property
613
+ def wh_endpoint(self) -> str:
614
+ """ n8n connect string """
615
+ return self.flask_app.config["WH_ENDPOINT"]
616
+ @wh_endpoint.setter
617
+ def wh_endpoint(self, a: str):
618
+ self.flask_app.config["WH_ENDPOINT"] = a
619
+ @property
620
+ def wh_path(self) -> str:
621
+ """ n8n connect string """
622
+ return self.flask_app.config["WH_PATH"]
623
+
624
+ @wh_path.setter
625
+ def wh_path(self, a: str):
626
+ self.flask_app.config["WH_PATH"] = a
627
+ @property
628
+ def wh_token(self) -> str:
629
+ """ n8n connect string """
630
+ return self.flask_app.config["WH_TOKEN"]
631
+ @wh_token.setter
632
+ def wh_token(self, a: str):
633
+ self.flask_app.config["WH_TOKEN"] = a
634
+
635
+
580
636
  def __str__(self) -> str:
581
637
  rtn = f'.. flask_host: {self.flask_host}, port: {self.port}, \n'\
582
638
  f'.. swagger_host: {self.swagger_host}, swagger_port: {self.swagger_port}, \n'\
@@ -695,5 +751,4 @@ class Args():
695
751
  args.swagger_port = 443
696
752
  args.http_scheme = 'https'
697
753
 
698
- return
699
-
754
+ return
@@ -4,4 +4,10 @@ SQLAlCHEMY_ECHO = False
4
4
  # AGGREGATE_DEFAULTS = True
5
5
  # ALL_DEFAULTS = True
6
6
  # APILOGICPROJECT_KAFKA_PRODUCER = "{\"bootstrap.servers\": \"localhost:9092\"}"
7
- # SQLALCHEMY_DATABASE_URI=db.sqlite
7
+ # SQLALCHEMY_DATABASE_URI=db.sqlite
8
+
9
+ SECURITY_ENABLED = false
10
+
11
+ # if using tunnel for mcp, function, or ai_plugin
12
+ # eg, https://tunnel_url.ngrok-free.app
13
+ API_LOGIC_SERVER_TUNNEL = "TUNNEL_URL"
@@ -1,8 +1,9 @@
1
+ This rebuilds test data into `database/test_data/db.sqlite`, for example as used by genai project creation.
2
+
1
3
  ChatGPT sometimes fails to build proper test data that matches the derivation rules.
2
4
 
3
5
  You can rebuild the test data, using Logic Bank rules for proper derivations, to rebuild your `database/db.sqlite` (make a copy first to preserve your existing data).
4
6
 
5
-
6
7
  ```
7
8
  als genai-utils --rebuild-test-data
8
9
  ```
@@ -12,7 +12,7 @@ You do not normally need to alter this file
12
12
  from config.config import Args
13
13
  from confluent_kafka import Producer
14
14
  import socket
15
- import logging
15
+ import logging, os
16
16
  from logic_bank.exec_row_logic.logic_row import LogicRow
17
17
  from integration.system.RowDictMapper import RowDictMapper
18
18
  from flask import jsonify
@@ -82,7 +82,10 @@ def send_kafka_message(kafka_topic: str, kafka_key: str = None, msg: str="", jso
82
82
  json_root_name (str, optional): json name for json payload root; default is logic_row.name
83
83
  """
84
84
 
85
-
85
+ if '1' == os.getenv("APILOGICPROJECT_NO_FLASK", None):
86
+ logger.debug("kafka_producer#send_kafka_message: not safrs class (e.g. database/test_data/test_data_code.py)")
87
+ return
88
+
86
89
  if isinstance(payload, dict):
87
90
  row_obj_dict = payload
88
91
  elif row_dict_mapper is not None: