ApiLogicServer 14.3.0__py3-none-any.whl → 14.3.11__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 (69) hide show
  1. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/METADATA +3 -3
  2. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/RECORD +58 -54
  3. api_logic_server_cli/api_logic_server.py +2 -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__/ont_build.cpython-312.pyc +0 -0
  7. api_logic_server_cli/create_from_model/ont_build.py +9 -9
  8. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/asset-manifest.json +3 -3
  9. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0213.txt +1 -0
  10. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/index.html +1 -1
  11. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.7c8c0e37.js +3 -0
  12. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/{main.bfe80d1d.js.map → main.7c8c0e37.js.map} +1 -1
  13. api_logic_server_cli/database/nw-gold.sqlite +0 -0
  14. api_logic_server_cli/genai/genai.py +13 -3
  15. api_logic_server_cli/genai/genai_svcs.py +2 -0
  16. api_logic_server_cli/manager.py +20 -16
  17. api_logic_server_cli/prototypes/base/api/system/expression_parser.py +10 -4
  18. api_logic_server_cli/prototypes/base/devops/docker-image/env.list +7 -2
  19. api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +31 -8
  20. api_logic_server_cli/prototypes/base/integration/system/RowDictMapper.py +33 -16
  21. api_logic_server_cli/prototypes/base/logic/declare_logic.py +1 -0
  22. api_logic_server_cli/prototypes/base/logic/load_verify_rules.py +2 -1
  23. api_logic_server_cli/prototypes/genai_demo/api/customize_api.py +9 -11
  24. api_logic_server_cli/prototypes/genai_demo/database/.DS_Store +0 -0
  25. api_logic_server_cli/prototypes/genai_demo/database/db.sqlite +0 -0
  26. api_logic_server_cli/prototypes/genai_demo/database/models.py +52 -42
  27. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/OrderB2B.py +4 -6
  28. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/__pycache__/OrderB2B.cpython-312.pyc +0 -0
  29. api_logic_server_cli/prototypes/genai_demo/integration/row_dict_maps/row_dict_maps_readme.md +3 -0
  30. api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/declare_logic.cpython-312.pyc +0 -0
  31. api_logic_server_cli/prototypes/genai_demo/logic/__pycache__/load_verify_rules.cpython-312.pyc +0 -0
  32. api_logic_server_cli/prototypes/genai_demo/logic/declare_logic.py +57 -69
  33. api_logic_server_cli/prototypes/genai_demo/logic/load_verify_rules.py +216 -0
  34. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/__init__.cpython-312.pyc +0 -0
  35. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/auto_discovery.cpython-312.pyc +0 -0
  36. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/__pycache__/error_testing.cpython-312.pyc +0 -0
  37. api_logic_server_cli/prototypes/genai_demo/logic/logic_discovery/auto_discovery.py +52 -0
  38. api_logic_server_cli/prototypes/genai_demo/logic/readme_declare_logic.md +172 -0
  39. api_logic_server_cli/prototypes/genai_demo/security/__pycache__/declare_security.cpython-312.pyc +0 -0
  40. api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +86 -53
  41. api_logic_server_cli/prototypes/manager/.vscode/ApiLogicServer.code-workspace +2 -2
  42. api_logic_server_cli/prototypes/manager/.vscode/launch.json +21 -21
  43. api_logic_server_cli/prototypes/manager/README.md +1 -1
  44. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +4 -1
  45. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +15 -8
  46. api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +3 -0
  47. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/002_create_db_models.prompt +3 -132
  48. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/Invoice Made Ready.png +0 -0
  49. api_logic_server_cli/prototypes/manager/system/genai/examples/time_tracking_billing/readme.md +59 -6
  50. api_logic_server_cli/prototypes/manager/system/genai/learning_requests/logic_bank_api.prompt +22 -1
  51. api_logic_server_cli/prototypes/nw/logic/declare_logic.py +1 -1
  52. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/__pycache__/codegen.cpython-312.pyc +0 -0
  53. api_logic_server_cli/sqlacodegen_wrapper/sqlacodegen/sqlacodegen/codegen.py +2 -1
  54. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/build-0106.txt +0 -1
  55. api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/main.bfe80d1d.js +0 -3
  56. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/copilot_models.cpython-312.pyc +0 -0
  57. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/__pycache__/sample_ai_models.cpython-312.pyc +0 -0
  58. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.chatgpt +0 -16
  59. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.sql +0 -66
  60. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai.sqlite +0 -0
  61. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_items.sqlite +0 -0
  62. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.py +0 -156
  63. api_logic_server_cli/prototypes/genai_demo/database/chatgpt/sample_ai_models.sqlite +0 -0
  64. api_logic_server_cli/prototypes/genai_demo/logic/cocktail-napkin.jpg +0 -0
  65. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/LICENSE +0 -0
  66. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/WHEEL +0 -0
  67. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/entry_points.txt +0 -0
  68. {ApiLogicServer-14.3.0.dist-info → ApiLogicServer-14.3.11.dist-info}/top_level.txt +0 -0
  69. /api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/js/{main.bfe80d1d.js.LICENSE.txt → main.7c8c0e37.js.LICENSE.txt} +0 -0
@@ -185,6 +185,10 @@ class GenAI(object):
185
185
  with open(genai_demo_response_path, 'r') as response_file:
186
186
  response_dict = json.load(response_file)
187
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
+
188
192
  else: # for retry from corrected response... eg system/genai/temp/chatgpt_retry.response
189
193
  self.resolved_model = "(n/a: model not used for repaired response)"
190
194
  log.debug(f'\nUsing [corrected] response from: {self.project.genai_repaired_response}')
@@ -311,8 +315,14 @@ class GenAI(object):
311
315
  log.debug(f'.. from file: {self.project.genai_using}')
312
316
  raw_prompt = file.read()
313
317
  prompt = self.get_prompt__with_inserts(raw_prompt=raw_prompt, for_iteration=False) # insert db-specific logic
314
- self.logic_enabled = False
315
- if 'LogicBank' in prompt and K_LogicBankOff not in prompt: # if prompt has logic, we need to insert the training
318
+ self.logic_enabled = True
319
+ if os.environ.get("APILOGICPROJECT_LOGIC_ENABLED") is not None and \
320
+ os.environ.get("APILOGICPROJECT_LOGIC_ENABLED") == 'False':
321
+ self.logic_enabled = False
322
+ log.info("*** Initial Logic Disabled: {self.logic_enabled}")
323
+ else:
324
+ log.debug(f'.. Initial Logic enabled: {self.logic_enabled}')
325
+ if self.logic_enabled == True or ('LogicBank' in prompt and K_LogicBankOff not in prompt): # if prompt has logic, we need to insert the training
316
326
  prompt_messages.extend( self.get_prompt_learning_requests())
317
327
  self.logic_enabled = True
318
328
  if prompt.startswith('You are a '): # if it's a preset, we need to insert the prompt
@@ -325,7 +335,7 @@ class GenAI(object):
325
335
  active_rules_json_path = Path(self.project.genai_using).joinpath('logic/active_rules.json')
326
336
  # assert active_rules_json_path.exists(), f"Missing active_rules.json: {active_rules_json_path}"
327
337
  if not active_rules_json_path.exists():
328
- log.info("*** Internal error: --active_rules specified, but no --using/logic/active_rules.json found - try to proced")
338
+ log.info("*** Internal error: --active_rules specified, but no --using/logic/active_rules.json found - try to proceed")
329
339
  else:
330
340
  with open(active_rules_json_path, 'r') as file:
331
341
  active_rules_str = file.read()
@@ -193,6 +193,8 @@ def get_code_update_logic_file(rule_list: List[DotMap], logic_file_path: Path =
193
193
  pass
194
194
  elif 'Rule.constraint' in each_line:
195
195
  pass
196
+ elif 'Rule.after_flush_row_event' in each_line:
197
+ pass
196
198
  elif 'Rule.allocate' in each_line:
197
199
  pass
198
200
  elif 'Rule.calculate' in each_line:
@@ -11,14 +11,14 @@ from pathlib import Path
11
11
  import api_logic_server_cli.api_logic_server as PR
12
12
 
13
13
  def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
14
- volume: str = "", open_manager: bool = True):
14
+ volume: str = "", open_manager: bool = True, samples: bool = True):
15
15
  """Implements als start to create manager - called from api_logic_server_cli/cli.py
16
16
 
17
17
  create Manager at os.getcwd(), including:
18
18
 
19
19
  1. .vscode, readme
20
20
  2. System folder (GenAI sample prompts / responses, others TBD)
21
- 3. pre-created samples
21
+ 3. pre-created samples (optional)
22
22
 
23
23
  Example, from CLI in directory containing a `venv` (see https://apilogicserver.github.io/Docs/Manager/):
24
24
  als start
@@ -122,21 +122,25 @@ def create_manager(clean: bool, open_with: str, api_logic_server_path: Path,
122
122
  except: # do NOT fail
123
123
  pass # just fall back to using the pip-installed version
124
124
 
125
- if project.is_docker:
126
- log.debug(f" tutorial not created for docker\n\n")
125
+ if not samples:
126
+ shutil.rmtree(to_dir.joinpath(f'{docker_volume}system/app_model_editor'))
127
+ shutil.rmtree(to_dir.joinpath(f'{docker_volume}system/genai/examples/genai_demo/wg_dev_merge'))
127
128
  else:
128
- tutorial_project = PR.ProjectRun(command="tutorial",
129
- project_name='./samples',
130
- db_url="",
131
- execute=False,
132
- open_with="NO_AUTO_OPEN"
133
- )
134
- tutorial_project = tutorial_project.tutorial(msg="Creating:") ##, create='tutorial')
135
-
136
- samples_project = PR.ProjectRun(command= "create", project_name=f'{docker_volume}samples/nw_sample', db_url='nw+', open_with="NO_AUTO_OPEN")
137
- log.setLevel(mgr_save_level)
138
- log.disabled = False # todo why was it reset?
139
- samples_project = PR.ProjectRun(command= "create", project_name=f'{docker_volume}samples/nw_sample_nocust', db_url='nw', open_with="NO_AUTO_OPEN")
129
+ if project.is_docker:
130
+ log.debug(f" tutorial not created for docker\n\n")
131
+ else:
132
+ tutorial_project = PR.ProjectRun(command="tutorial",
133
+ project_name='./samples',
134
+ db_url="",
135
+ execute=False,
136
+ open_with="NO_AUTO_OPEN"
137
+ )
138
+ tutorial_project = tutorial_project.tutorial(msg="Creating:") ##, create='tutorial')
139
+
140
+ samples_project = PR.ProjectRun(command= "create", project_name=f'{docker_volume}samples/nw_sample', db_url='nw+', open_with="NO_AUTO_OPEN")
141
+ log.setLevel(mgr_save_level)
142
+ log.disabled = False # todo why was it reset?
143
+ samples_project = PR.ProjectRun(command= "create", project_name=f'{docker_volume}samples/nw_sample_nocust', db_url='nw', open_with="NO_AUTO_OPEN")
140
144
  log.info('')
141
145
  log.setLevel(mgr_save_level)
142
146
  log.disabled = False
@@ -153,18 +153,24 @@ def fixup_sort(clz, data):
153
153
  continue
154
154
  return sort
155
155
  def fixup_data(data, sqltypes):
156
+ new_data = None
156
157
  if data:
158
+ new_data = {}
157
159
  for key, value in data.items():
160
+ new_data[key] = value
158
161
  if sqltypes and key in sqltypes and isinstance(value, str):
159
162
  if sqltypes[key] in [-5,2,4,5,-6]: #BIGINT, TINYINT, INT, SMALLINT, INTEGER
160
- data[key] = int(value)
163
+ if new_data[key].isdigit():
164
+ new_data[key] = int(value)
165
+ else:
166
+ del new_data[key]
161
167
  elif sqltypes[key] in [6]: #DECIMAL
162
- data[key] = Decimal(value)
168
+ new_data[key] = Decimal(value)
163
169
  if sqltypes and key in sqltypes and sqltypes[key] in [91,93] and isinstance(value, int): #DATE, TIMESTAMP
164
170
  from datetime import datetime
165
171
  fmt = "%Y-%m-%d" if sqltypes[key] == 91 else "%Y-%m-%d %H:%M:%S"
166
- data[key] = datetime.fromtimestamp(value / 1000) #.strftime(fmt)
167
- return data
172
+ new_data[key] = datetime.fromtimestamp(value / 1000) #.strftime(fmt)
173
+ return new_data
168
174
 
169
175
  def _parseFilter(filter: dict, sqltypes: any):
170
176
  # {filter":{"@basic_expression":{"lop":"BALANCE","op":"<=","rop":35000}}
@@ -40,11 +40,16 @@
40
40
  # whether to invoke dbinit befoce connecting...
41
41
  # APILOGICSERVER_ORACLE_THICK=~/Downloads/instantclient_19_16
42
42
 
43
+ # enables aggregate defaulting, and defaulting for all numerics and strings
44
+ # used to initialize rows prior to logic, to avoid excessive None testing
45
+ # AGGREGATE_DEFAULTS=True
46
+ # ALL_DEFAULTS=True
47
+
43
48
  APILOGICPROJECT_VERBOSE=True
44
49
 
45
50
  # APILOGICPROJECT_LOG_CONFIG=
46
- """ name of log.yml file (eg, config/logging_prod.yml) """
51
+ # name of log.yml file (eg, config/logging_prod.yml)
47
52
 
48
53
  # APILOGICPROJECT_STOP_OK=FALSE
49
- """ dev only - enable stop url: http://localhost:5656/stop?msg=reason """
54
+ # dev only - enable stop url: http://localhost:5656/stop?msg=reason
50
55
 
@@ -1,6 +1,6 @@
1
1
  """
2
2
 
3
- Version 1.1
3
+ Version 2.1
4
4
 
5
5
  Invoked at server start (api_logic_server_run.py -> config/setup.py)
6
6
 
@@ -47,8 +47,23 @@ def kafka_producer():
47
47
  producer = Producer(conf)
48
48
  logger.debug(f'\nKafka producer connected')
49
49
 
50
+ from sqlalchemy.inspection import inspect
50
51
 
51
- def send_kafka_message(kafka_topic: str, kafka_key: str, msg: str="", json_root_name: str = "",
52
+ def get_primary_key(logic_row: LogicRow):
53
+ """ Return primary key for row, if it exists
54
+
55
+ Args:
56
+ logic_row (LogicRow): The SQLAlchemy row object
57
+
58
+ Returns:
59
+ dict: A dictionary with primary key column names and their values
60
+ """
61
+ primary_key_columns = inspect(logic_row.row).mapper.primary_key
62
+ primary_key = {column.name: getattr(logic_row.row, column.name) for column in primary_key_columns}
63
+ return primary_key
64
+
65
+
66
+ def send_kafka_message(kafka_topic: str, kafka_key: str = None, msg: str="", json_root_name: str = "",
52
67
  logic_row: LogicRow = None, row_dict_mapper: RowDictMapper = None, payload: dict = None):
53
68
  """ Send Kafka message regarding logic_row, mapped by row_dict_mapper
54
69
 
@@ -80,18 +95,26 @@ def send_kafka_message(kafka_topic: str, kafka_key: str, msg: str="", json_root_
80
95
  else:
81
96
  root_name = logic_row.name
82
97
 
98
+ if kafka_key is None:
99
+ kafka_key = get_primary_key(logic_row)
100
+
101
+ log_msg = msg if msg != "" else f"Sending {root_name} to Kafka topic '{kafka_topic}'"
102
+
83
103
  json_string = jsonify({f'{root_name}': row_obj_dict}).data.decode('utf-8')
84
- log_msg = msg + ' sends:'
104
+ log_msg = log_msg
85
105
  if producer: # enabled in config/config.py?
86
106
  try:
87
107
  producer.produce(value=json_string, topic="order_shipping", key=kafka_key)
88
108
  if logic_row:
89
- logic_row.log(msg)
109
+ logic_row.log(log_msg)
90
110
  except KafkaException as ke:
91
111
  logger.error("kafka_producer#send_kafka_message error: {ke}")
92
112
  else:
93
- log_msg += " [Note: **Kafka not enabled** ]"
113
+ log_msg += " [Note: **Kafka not enabled** ]"
94
114
  if logic_row is not None:
95
- logic_row.log(f'\n\n{log_msg}\n{json_string}')
96
- else:
97
- logger.debug(f'\n\n{log_msg}\n{json_string}')
115
+ logic_row.log(f'{log_msg}')
116
+ logger.debug(f'\n\n{log_msg}\n{json_string}')
117
+
118
+
119
+ def send_row_to_kafka(row: object, old_row: object, logic_row: LogicRow, with_args: dict):
120
+ send_kafka_message(logic_row=logic_row, kafka_topic=with_args["topic"])
@@ -1,7 +1,7 @@
1
1
  from database import models
2
2
  from flask import request, jsonify
3
3
  import sqlalchemy as sqlalchemy
4
- from sqlalchemy import Column
4
+ from sqlalchemy import Column, inspect
5
5
  from sqlalchemy.ext.declarative import declarative_base
6
6
  from flask_sqlalchemy.model import DefaultMeta
7
7
  from sqlalchemy.ext.hybrid import hybrid_property
@@ -276,8 +276,7 @@ class RowDictMapper():
276
276
  parent_lookup_list.append(self.parent_lookups)
277
277
  for each_parent_lookup in parent_lookup_list:
278
278
  self._parent_lookup_from_child(child_row_dict = row_dict,
279
- lookup_fields = each_parent_lookup[1],
280
- parent_class = each_parent_lookup[0],
279
+ parent_lookup = each_parent_lookup,
281
280
  child_row = sql_alchemy_row,
282
281
  session = session)
283
282
 
@@ -354,27 +353,31 @@ class RowDictMapper():
354
353
 
355
354
  def _parent_lookup_from_child(self, child_row_dict: dict, child_row: object,
356
355
  session: object,
357
- parent_class: DefaultMeta,
358
- lookup_fields: list[object]):
359
- """ Used from child -- parent_lookups
356
+ parent_lookup: tuple[DefaultMeta, list[tuple[Column, str]]]):
357
+ """ Used from child -- parent_lookups (e,g, B2B Product)
360
358
 
361
359
  Args:
362
- child_row_dict (dict): _description_
363
- child_row (object): _description_
364
- session (object): _description_
365
- lookup_parent_endpoint (RowDictMapper, optional): _description_. Defaults to None.
360
+ child_row_dict (dict): the incoming payload
361
+ child_row (object): row
362
+ parent_lookup (tuple[DefaultMeta, list[tuple[Column, str]]]): parent class, list of attrs/json keys
363
+ session (object): SqlAlchemy session
364
+
365
+ Example lookup_fields (genai_demo/OrderB2B.py):
366
+ parent_lookup = ( models.Customer, [(models.Customer.name, 'Account')] )
367
+ parent_class: parent_lookups[0] Customer)
368
+ lookup_fields: parent_lookups[1] [(models.Customer.name, 'Account')]
366
369
 
367
370
  Raises:
368
- ValueError: _description_
369
- ValueError: _description_
371
+ ValueError: eg, missing parent
370
372
  """
371
-
373
+ parent_class = parent_lookup[0]
374
+ lookup_fields = parent_lookup[1]
372
375
  query = session.query(parent_class)
373
376
 
374
377
  if parent_class.__name__ in ['Product', 'Customer']:
375
378
  logging.debug(f'_parent_lookup_from_child {parent_class.__name__}' )
376
- for each_lookup_param_field in lookup_fields:
377
- attr_name = each_lookup_param_field
379
+ for each_lookup_param_field in lookup_fields: # e.g, (models.Customer.name, 'Account')
380
+ attr_name = each_lookup_param_field # <col_def> <filter-val>
378
381
  if isinstance(each_lookup_param_field, tuple):
379
382
  col_def = each_lookup_param_field[0]
380
383
  attr_name = each_lookup_param_field[1]
@@ -394,6 +397,20 @@ class RowDictMapper():
394
397
  raise ValueError('Lookup failed: missing parent', child_row, parent_class.__name__, str(child_row_dict))
395
398
 
396
399
  parent_row = parent_rows[0]
397
- setattr(child_row, parent_class.__name__, parent_row)
400
+
401
+ # find parent accessor - usually parent_class.__name__, unless fk is lower case (B2bOrders)
402
+ mapper = inspect(child_row).mapper
403
+ parent_accessor = None
404
+ for each_attribute in mapper.attrs: # find parent accessors
405
+ if isinstance(each_attribute, sqlalchemy.orm.relationships.RelationshipProperty):
406
+ if each_attribute.argument == parent_class.__name__:
407
+ if parent_accessor is None:
408
+ parent_accessor = each_attribute.key
409
+ else:
410
+ raise ValueError(f'Parent accessor not unique: {parent_accessor}') # TODO - multiple parents
411
+ if parent_accessor is None:
412
+ raise ValueError(f'Parent accessor not found: {parent_class.__name__}')
413
+
414
+ setattr(child_row, parent_accessor, parent_row)
398
415
 
399
416
  return
@@ -8,6 +8,7 @@ import database.models as models
8
8
  import api.system.opt_locking.opt_locking as opt_locking
9
9
  from security.system.authorization import Grant, Security
10
10
  from logic.load_verify_rules import load_verify_rules
11
+ import integration.kafka.kafka_producer as kafka_producer
11
12
  import logging
12
13
 
13
14
  app_logger = logging.getLogger(__name__)
@@ -26,6 +26,7 @@ declare_logic_message = "ALERT: *** No Rules Yet ***" # printed in api_logic_s
26
26
  rule_import_template = """
27
27
  from logic_bank.logic_bank import Rule
28
28
  from database.models import *
29
+ import integration.kafka.kafka_producer as kafka_producer
29
30
 
30
31
  def init_rule():
31
32
  {rule_code}
@@ -213,4 +214,4 @@ def load_verify_rules():
213
214
  app_logger.warning(f"{Fore.RED}Failed to load active exported rules: {exc}{Style.RESET_ALL}")
214
215
 
215
216
  root_logger.removeHandler(file_handler)
216
-
217
+
@@ -5,8 +5,8 @@ from flask import request, jsonify
5
5
  from safrs import jsonapi_rpc
6
6
  from database import models
7
7
  import integration.system.RowDictMapper as row_dict_mapper
8
- from integration.row_dict_maps.OrderShipping import OrderShipping
9
- from integration.row_dict_maps.OrderB2B import OrderB2B
8
+ # from integration.row_dict_maps.OrderShipping import OrderShipping
9
+ from integration.row_dict_maps.OrderB2B import OrderB2B # TODO - how to drive; B2B...
10
10
 
11
11
  # called by api_logic_server_run.py, to customize api (new end points, services).
12
12
  # separate from expose_api_models.py, to simplify merge if project recreated
@@ -140,15 +140,13 @@ class ServicesEndPoint(safrs.JABase):
140
140
  """ # yaml creates Swagger description
141
141
  args :
142
142
  order:
143
- Account: "Jane Smith"
143
+ Account: "Alice"
144
144
  Notes: "Please Rush"
145
145
  Items :
146
- - ProductName: "Widget A"
146
+ - ProductName: "Product 1"
147
147
  QuantityOrdered: 1
148
- - ProductName: "Gadget B"
148
+ - ProductName: "Product 2"
149
149
  QuantityOrdered: 2
150
- - ProductName: "Green Apples"
151
- QuantityOrdered: 3
152
150
  ---
153
151
 
154
152
  Note attribute alias, Lookup automation in OrderB2B
@@ -159,19 +157,19 @@ class ServicesEndPoint(safrs.JABase):
159
157
  $(venv) ApiLogicServer login --user=admin --password=p
160
158
  $(venv) ApiLogicServer curl "'POST' 'http://localhost:5656/api/ServicesEndPoint/OrderB2B'" --data '
161
159
  {"meta": {"args": {"order": {
162
- "Account": "Jane Smith",
160
+ "Account": "Alice",
163
161
  "Notes": "Please Rush",
164
162
  "Items": [
165
163
  {
166
- "ProductName": "Widget A",
164
+ "ProductName": "Product 1",
167
165
  "QuantityOrdered": 1
168
166
  },
169
167
  {
170
- "ProductName": "Gadget B",
168
+ "ProductName": "Product 2",
171
169
  "QuantityOrdered": 2
172
170
  },
173
171
  {
174
- "ProductName": "Gadget B",
172
+ "ProductName": "Green Apples",
175
173
  "QuantityOrdered": 2
176
174
  }
177
175
  ]
@@ -1,5 +1,6 @@
1
1
  # coding: utf-8
2
- from sqlalchemy import Boolean, Column, DECIMAL, DateTime, ForeignKey, Integer, Text, text
2
+ from sqlalchemy import DECIMAL, DateTime # API Logic Server GenAI assist
3
+ from sqlalchemy import Column, Date, ForeignKey, Integer, Numeric, String, Boolean
3
4
  from sqlalchemy.orm import relationship
4
5
  from sqlalchemy.ext.declarative import declarative_base
5
6
 
@@ -9,13 +10,13 @@ from sqlalchemy.ext.declarative import declarative_base
9
10
  # Alter this file per your database maintenance policy
10
11
  # See https://apilogicserver.github.io/Docs/Project-Rebuild/#rebuilding
11
12
  #
12
- # Created: May 03, 2024 22:23:08
13
- # Database: sqlite:////Users/val/dev/ApiLogicServer/ApiLogicServer-dev/clean/ApiLogicServer/genai_demo/database/db.sqlite
13
+ # Created: January 31, 2025 17:52:29
14
+ # Database: sqlite:////Users/val/dev/ApiLogicServer/ApiLogicServer-dev/build_and_test/ApiLogicServer/genai_demo/database/db.sqlite
14
15
  # Dialect: sqlite
15
16
  #
16
17
  # mypy: ignore-errors
17
18
  ########################################################################################################################
18
-
19
+
19
20
  from database.system.SAFRSBaseX import SAFRSBaseX, TestBase
20
21
  from flask_login import UserMixin
21
22
  import safrs, flask_sqlalchemy, os
@@ -42,72 +43,81 @@ else:
42
43
  print('*** Models.py Using TestBase ***')
43
44
 
44
45
 
45
- class Customer(Base):
46
- __tablename__ = 'Customers'
46
+
47
+ class Customer(Base): # type: ignore
48
+ """
49
+ description: Customer table with unique name, balance and credit_limit.
50
+ """
51
+ __tablename__ = 'customer'
47
52
  _s_collection_name = 'Customer' # type: ignore
48
53
 
49
- CustomerID = Column(Integer, primary_key=True)
50
- CustomerName = Column(Text, nullable=False)
51
- Address = Column(Text)
52
- Phone = Column(Text)
53
- Balance : DECIMAL = Column(DECIMAL(10, 2))
54
- CreditLimit : DECIMAL = Column(DECIMAL(10, 2), nullable=False)
54
+ id = Column(Integer, primary_key=True)
55
+ name = Column(String(255), unique=True)
56
+ balance = Column(Numeric)
57
+ credit_limit = Column(Numeric)
55
58
 
56
59
  # parent relationships (access parent)
57
60
 
58
61
  # child relationships (access children)
59
- OrderList : Mapped[List["Order"]] = relationship(back_populates="Customer")
62
+ OrderList : Mapped[List["Order"]] = relationship(back_populates="customer")
60
63
 
61
64
 
62
- class Product(Base):
63
- __tablename__ = 'Products'
64
- _s_collection_name = 'Product' # type: ignore
65
65
 
66
- ProductID = Column(Integer, primary_key=True)
67
- ProductName = Column(Text, nullable=False)
68
- UnitPrice : DECIMAL = Column(DECIMAL(10, 2), nullable=False)
69
- CarbonNeutral = Column(Boolean)
66
+ class Product(Base): # type: ignore
67
+ """
68
+ description: Product table with unit_price used for copying to Item.
69
+ """
70
+ __tablename__ = 'product'
71
+ _s_collection_name = 'Product' # type: ignore
70
72
 
73
+ id = Column(Integer, primary_key=True)
74
+ name = Column(String(255))
75
+ unit_price = Column(Numeric)
76
+ carbon_neutral = Column(Boolean)
71
77
  # parent relationships (access parent)
72
78
 
73
79
  # child relationships (access children)
74
- ItemList : Mapped[List["Item"]] = relationship(back_populates="Product")
80
+ ItemList : Mapped[List["Item"]] = relationship(back_populates="product")
75
81
 
76
82
 
77
83
 
78
- class Order(Base):
79
- __tablename__ = 'Orders'
84
+ class Order(Base): # type: ignore
85
+ """
86
+ description: Order table with a foreign key to Customer, a notes field, date_shipped and derived amount_total.
87
+ """
88
+ __tablename__ = 'order'
80
89
  _s_collection_name = 'Order' # type: ignore
81
90
 
82
- OrderID = Column(Integer, primary_key=True)
83
- CustomerID = Column(ForeignKey('Customers.CustomerID'))
84
- OrderDate = Column(DateTime, server_default=text("CURRENT_TIMESTAMP"))
85
- Notes = Column(Text)
86
- ShipDate = Column(DateTime)
87
- AmountTotal : DECIMAL = Column(DECIMAL(10, 2))
91
+ id = Column(Integer, primary_key=True)
92
+ customer_id = Column(ForeignKey('customer.id'))
93
+ date_shipped = Column(Date)
94
+ notes = Column(String(255))
95
+ amount_total = Column(Numeric)
88
96
 
89
97
  # parent relationships (access parent)
90
- Customer : Mapped["Customer"] = relationship(back_populates=("OrderList"))
98
+ customer : Mapped["Customer"] = relationship(back_populates=("OrderList"))
91
99
 
92
100
  # child relationships (access children)
93
- ItemList : Mapped[List["Item"]] = relationship(back_populates="Order")
101
+ ItemList : Mapped[List["Item"]] = relationship(back_populates="order")
94
102
 
95
103
 
96
104
 
97
- class Item(Base):
98
- __tablename__ = 'Items'
105
+ class Item(Base): # type: ignore
106
+ """
107
+ description: Item table with non-null quantity, derived amount and unit_price copied from Product.
108
+ """
109
+ __tablename__ = 'item'
99
110
  _s_collection_name = 'Item' # type: ignore
100
111
 
101
- ItemID = Column(Integer, primary_key=True)
102
- OrderID = Column(ForeignKey('Orders.OrderID'))
103
- ProductID = Column(ForeignKey('Products.ProductID'))
104
- Quantity = Column(Integer, nullable=False)
105
- UnitPrice : DECIMAL = Column(DECIMAL(10, 2))
106
- Amount : DECIMAL = Column(DECIMAL(10, 2))
112
+ id = Column(Integer, primary_key=True)
113
+ order_id = Column(ForeignKey('order.id'))
114
+ product_id = Column(ForeignKey('product.id'))
115
+ quantity = Column(Integer, nullable=False)
116
+ unit_price = Column(Numeric)
117
+ amount = Column(Numeric)
107
118
 
108
119
  # parent relationships (access parent)
109
- Order : Mapped["Order"] = relationship(back_populates=("ItemList"))
110
- Product : Mapped["Product"] = relationship(back_populates=("ItemList"))
120
+ order : Mapped["Order"] = relationship(back_populates=("ItemList"))
121
+ product : Mapped["Product"] = relationship(back_populates=("ItemList"))
111
122
 
112
123
  # child relationships (access children)
113
-
@@ -19,15 +19,13 @@ class OrderB2B(RowDictMapper):
19
19
  order = super(OrderB2B, self).__init__(
20
20
  model_class=models.Order
21
21
  , alias = "order"
22
- , fields = [(models.Order.Notes)]
23
- , parent_lookups = [( models.Customer,
24
- [(models.Customer.CustomerName, 'Account')]
25
- )]
22
+ , fields = [(models.Order.notes, "Notes")]
23
+ , parent_lookups = [( models.Customer, [(models.Customer.name, 'Account')] )]
26
24
  , related = [
27
25
  (RowDictMapper(model_class=models.Item
28
26
  , alias="Items"
29
- , fields = [(models.Item.Quantity, "QuantityOrdered")]
30
- , parent_lookups = [( models.Product, [models.Product.ProductName] )]
27
+ , fields = [(models.Item.quantity, "QuantityOrdered")]
28
+ , parent_lookups = [( models.Product, [(models.Product.name, 'ProductName')] )]
31
29
  )
32
30
  )
33
31
  ]
@@ -0,0 +1,3 @@
1
+ Create definitions here to configure mappings for row <-> dicts, typically for application integration.
2
+
3
+ To see a Sample Integration, [click here](https://apilogicserver.github.io/Docs/Sample-Integration/).