ApiLogicServer 14.3.25__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.
- api_logic_server_cli/add_cust/add_cust.py +283 -0
- api_logic_server_cli/api_logic_server.py +18 -250
- api_logic_server_cli/api_logic_server_info.yaml +3 -3
- api_logic_server_cli/cli.py +54 -35
- api_logic_server_cli/create_from_model/__pycache__/api_logic_server_utils.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/create_db_from_model.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/dbml.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_build.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/__pycache__/ont_create.cpython-312.pyc +0 -0
- api_logic_server_cli/create_from_model/api_logic_server_utils.py +47 -0
- api_logic_server_cli/create_from_model/create_db_from_model.py +2 -0
- api_logic_server_cli/create_from_model/dbml.py +113 -58
- api_logic_server_cli/create_from_model/ont_build.py +102 -74
- api_logic_server_cli/create_from_model/ont_create.py +7 -6
- api_logic_server_cli/create_from_model/safrs-react-admin-npm-build/static/.DS_Store +0 -0
- api_logic_server_cli/database/basic_demo.sqlite +0 -0
- api_logic_server_cli/database/basic_demo.txt +1 -0
- api_logic_server_cli/database/basic_demo_wg.sqlite +0 -0
- api_logic_server_cli/database/nw-gold-fix.sql +62 -0
- api_logic_server_cli/database/nw-gold.sqlite +0 -0
- api_logic_server_cli/{prototypes/manager/webgenai → fragments}/docker-compose.yml +1 -1
- api_logic_server_cli/genai/genai.py +42 -11
- api_logic_server_cli/genai/genai_graphics.py +252 -38
- api_logic_server_cli/genai/genai_svcs.py +20 -12
- api_logic_server_cli/manager.py +22 -12
- api_logic_server_cli/prototypes/.DS_Store +0 -0
- api_logic_server_cli/prototypes/base/.DS_Store +0 -0
- api_logic_server_cli/prototypes/base/.vscode/launch.json +22 -2
- api_logic_server_cli/prototypes/base/api/expose_api_models.py +3 -1
- api_logic_server_cli/prototypes/base/api_logic_server_run.py +5 -2
- api_logic_server_cli/prototypes/base/config/activate_logicbank.py +1 -0
- api_logic_server_cli/prototypes/base/config/config.py +123 -25
- api_logic_server_cli/prototypes/base/config/default.env +7 -1
- api_logic_server_cli/prototypes/base/config/logging.yml +1 -0
- api_logic_server_cli/prototypes/base/config/server_setup.py +33 -1
- api_logic_server_cli/prototypes/base/database/test_data/readme.md +5 -2
- api_logic_server_cli/prototypes/base/devops/docker-standard-image/docker-compose-standard-image.yml +7 -2
- api_logic_server_cli/prototypes/base/docs/training/logic_bank_api.prompt +314 -0
- api_logic_server_cli/prototypes/base/docs/training/logic_example.py +41 -0
- api_logic_server_cli/prototypes/base/integration/kafka/kafka_producer.py +12 -5
- api_logic_server_cli/prototypes/base/integration/n8n/n8n_producer.py +68 -21
- api_logic_server_cli/prototypes/base/integration/n8n/n8n_readme.md +19 -0
- api_logic_server_cli/prototypes/base/integration/system/FlaskKafka.py +5 -1
- api_logic_server_cli/prototypes/base/test/basic/server_test.py +1 -1
- api_logic_server_cli/prototypes/base/ui/templates/bar_chart.jinja +64 -0
- api_logic_server_cli/prototypes/basic_demo/README.md +29 -52
- api_logic_server_cli/prototypes/basic_demo/customizations/api/.DS_Store +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/mcp_server_executor.py +138 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/openapi.py +92 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/api/api_discovery/proper_update_def.json +71 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/config/default.env +13 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/database/db.sqlite +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/database/models.py +131 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/.DS_Store +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/.DS_Store +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/1_langchain_loader.py +71 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/2_gpt_mcp_prompt.txt +19 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/README_mcp.md +13 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_client_executor.py +295 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_schema.txt +47 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/mcp_server_discovery.json +9 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_flow.png +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/multi_mcp_orchestration.yaml +49 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/multi_mcp_flow/wny mcp flows.png +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/natlang_to_api.py +73 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/curl.txt +5 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP Overview.png +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Arch.png +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/images/MCP_Overview_Executor.png +0 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/1 - prompt_messages_array.json +10 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/invoke_llm/2 - completion_tool_context.json +12 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/llm_schema.txt +38 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_2.yaml +17393 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_3.yaml +16660 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/nw_swagger_3_relaxed.yaml +109 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_server.py +51 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/proxy_serverZ.py +72 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/resources/validate_jsonapi.py +64 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/run_executor.py +23 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/swagger_converter.py +65 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/mcp/z_old/3_executor_test_agent.py +52 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/3_executor_test_agent.py +52 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/README_functon.md +201 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/ai_plugin.json +17 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/nw-swagger_3.json +1731 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/snippets.txt +5 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3 genai_demo_with_get.json +1731 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3.json +1782 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo.json +264 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/integration/openai_function/swagger_3_genai_demo_with_update.json +1782 -0
- api_logic_server_cli/prototypes/basic_demo/customizations/logic/declare_logic.py +62 -44
- api_logic_server_cli/prototypes/basic_demo/customizations/security/declare_security.py +11 -12
- api_logic_server_cli/prototypes/basic_demo/customizations/ui/admin/admin.yaml +166 -0
- api_logic_server_cli/prototypes/basic_demo/iteration/api/{customize_api.py → api_discovery/order_b2b.py} +17 -23
- api_logic_server_cli/prototypes/basic_demo/iteration/database/db.sqlite +0 -0
- api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderB2B.py +6 -5
- api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderShipping.py +4 -4
- api_logic_server_cli/prototypes/basic_demo/iteration/logic/declare_logic.py +69 -43
- api_logic_server_cli/prototypes/basic_demo/iteration/ui/admin/admin.yaml +125 -50
- api_logic_server_cli/prototypes/genai_demo/ui/admin/admin.yaml +1 -1
- api_logic_server_cli/prototypes/manager/README.md +30 -4
- api_logic_server_cli/prototypes/manager/README_X.md +663 -0
- api_logic_server_cli/prototypes/manager/system/genai/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/.DS_Store +0 -0
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.prompt +0 -10
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo.response_example +32 -10
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/002_create_db_models.prompt +4 -4
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_docs_logic/docs/003_create_db_models.response +77 -47
- api_logic_server_cli/prototypes/manager/system/genai/examples/genai_demo/genai_demo_informal.prompt +1 -1
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/dashboard_services.jinja +83 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_dashboard_WIP.py +34 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/{graphics_services.py → graphics_services_api_xxx.py} +0 -9
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db.jinja +46 -0
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/graphics_services_db_each_method.jinja +36 -0
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/graphics.prompt +7 -3
- api_logic_server_cli/prototypes/manager/system/genai/prompt_inserts/response_format.prompt +8 -1
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.ps1 +100 -0
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/install-ApiLogicServer-dev.sh +116 -0
- api_logic_server_cli/prototypes/manager/system/install-ApiLogicServer-dev/readme.md +7 -0
- api_logic_server_cli/prototypes/manager/system/style-guide.yaml +2 -2
- api_logic_server_cli/prototypes/manager/webgenai/README.md +6 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/count_orders_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/order_count_by_month.prompt +1 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/request copy.json +892 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/request.json +6 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/response.json +17 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/response.yaml +59 -0
- api_logic_server_cli/prototypes/nw/docs/graphics/sales_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw/ui/admin/home.js +5 -4
- api_logic_server_cli/prototypes/nw/ui/app_model_custom.yaml +851 -1082
- api_logic_server_cli/prototypes/nw_no_cust/Tutorial.md +45 -26
- api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/openapi.py +130 -0
- api_logic_server_cli/prototypes/nw_no_cust/api/api_discovery/proper_update_def.json +71 -0
- api_logic_server_cli/prototypes/nw_no_cust/config/default.env +13 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/count_orders_by_category.prompt +1 -0
- api_logic_server_cli/prototypes/nw_no_cust/docs/graphics/sales_by_employee.prompt +1 -0
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/nginx/nginx.conf +2 -2
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/package-lock.json +9725 -1180
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/package.json +6 -9
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/app.config.ts +2 -1
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/app/shared/app.services.config.ts +1 -1
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/css/app.scss +4 -0
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/en.json +1 -1
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/assets/i18n/es.json +14 -12
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.prod.ts +5 -5
- api_logic_server_cli/prototypes/ont_app/ontimize_seed/src/environments/environment.ts +5 -5
- api_logic_server_cli/prototypes/ont_app/templates/app_config.jinja +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/date_template.html +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/detail_template.html +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/new_template.html +16 -16
- api_logic_server_cli/prototypes/ont_app/templates/textarea_template.html +1 -1
- api_logic_server_cli/prototypes/ont_app/templates/timestamp_template.html +1 -1
- api_logic_server_cli/prototypes/sample_ai/logic/declare_logic.py +30 -13
- apilogicserver-14.5.0.dist-info/METADATA +76 -0
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/RECORD +160 -88
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/WHEEL +1 -1
- api_logic_server_cli/prototypes/basic_demo/apply_customizations.ps1 +0 -17
- api_logic_server_cli/prototypes/basic_demo/apply_customizations.sh +0 -14
- api_logic_server_cli/prototypes/basic_demo/apply_iteration.ps1 +0 -20
- api_logic_server_cli/prototypes/basic_demo/apply_iteration.sh +0 -15
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_jsonapi_rpc.jinja +0 -37
- api_logic_server_cli/prototypes/manager/system/genai/graphics_templates/service_template_unused.jinja +0 -38
- apilogicserver-14.3.25.dist-info/METADATA +0 -167
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/entry_points.txt +0 -0
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/licenses/LICENSE +0 -0
- {apilogicserver-14.3.25.dist-info → apilogicserver-14.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,87 +1,105 @@
|
|
|
1
|
-
|
|
2
|
-
# copy this file over logic/declare_logic.py
|
|
3
|
-
#############################################
|
|
4
|
-
|
|
5
|
-
import datetime
|
|
1
|
+
import datetime, os
|
|
6
2
|
from decimal import Decimal
|
|
7
3
|
from logic_bank.exec_row_logic.logic_row import LogicRow
|
|
8
4
|
from logic_bank.extensions.rule_extensions import RuleExtension
|
|
9
5
|
from logic_bank.logic_bank import Rule
|
|
10
|
-
from
|
|
6
|
+
from logic_bank.logic_bank import DeclareRule
|
|
7
|
+
import database.models as models
|
|
11
8
|
import api.system.opt_locking.opt_locking as opt_locking
|
|
12
|
-
from security.system.authorization import Grant
|
|
13
|
-
import
|
|
9
|
+
from security.system.authorization import Grant, Security
|
|
10
|
+
from logic.load_verify_rules import load_verify_rules
|
|
11
|
+
import integration.kafka.kafka_producer as kafka_producer
|
|
12
|
+
import logging
|
|
14
13
|
|
|
15
14
|
app_logger = logging.getLogger(__name__)
|
|
16
15
|
|
|
17
|
-
declare_logic_message = "
|
|
16
|
+
declare_logic_message = "ALERT: *** No Rules Yet ***" # printed in api_logic_server.py
|
|
18
17
|
|
|
19
18
|
def declare_logic():
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
''' Declarative multi-table derivations and constraints, extensible with Python.
|
|
20
|
+
|
|
23
21
|
Brief background: see readme_declare_logic.md
|
|
22
|
+
|
|
23
|
+
Your Code Goes Here - Use code completion (Rule.) to declare rules
|
|
24
|
+
'''
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if os.environ.get("WG_PROJECT"):
|
|
27
|
+
# Inside WG: Load rules from docs/expprt/export.json
|
|
28
|
+
load_verify_rules()
|
|
29
|
+
else:
|
|
30
|
+
# Outside WG: load declare_logic function
|
|
31
|
+
from logic.logic_discovery.auto_discovery import discover_logic
|
|
32
|
+
discover_logic()
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
# Logic from GenAI: (or, use your IDE w/ code completion)
|
|
35
|
+
from database.models import Product, Order, Item, Customer
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
# Ensure the customer's balance is less than their credit limit
|
|
38
|
+
Rule.constraint(validate=Customer, as_condition=lambda row: row.balance <= row.credit_limit, error_msg="Customer balance ({row.balance}) exceeds credit limit ({row.credit_limit})")
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
# Derive the customer's balance as the sum of order totals where not yet shipped.
|
|
41
|
+
Rule.sum(derive=Customer.balance, as_sum_of=Order.amount_total, where=lambda row: row.date_shipped is None)
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
# Derive the order's total amount from the sum of item amounts.
|
|
44
|
+
Rule.sum(derive=Order.amount_total, as_sum_of=Item.amount)
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
error_msg="balance ({round(row.Balance, 2)}) exceeds credit ({round(row.CreditLimit, 2)})")
|
|
46
|
+
# Calculate item amount based on quantity and unit price.
|
|
47
|
+
Rule.formula(derive=Item.amount, as_expression=lambda row: row.quantity * row.unit_price)
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
where=lambda row: row.ShipDate is None) # adjusts - *not* a sql select sum...
|
|
49
|
+
# Copy unit price from product to item.
|
|
50
|
+
Rule.copy(derive=Item.unit_price, from_parent=Product.unit_price)
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
# Send order details to Kafka if order is shipped.
|
|
53
|
+
Rule.after_flush_row_event(on_class=Order, calling=kafka_producer.send_row_to_kafka, if_condition=lambda row: row.date_shipped is not None, with_args={'topic': 'order_shipping'})
|
|
50
54
|
|
|
51
|
-
|
|
52
|
-
as_expression=lambda row: row.UnitPrice * row.Quantity)
|
|
55
|
+
# End Logic from GenAI
|
|
53
56
|
|
|
54
|
-
Rule.copy(derive=Item.UnitPrice, # get Product Price (e,g., on insert, or ProductId change)
|
|
55
|
-
from_parent=Product.UnitPrice)
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
def handle_all(logic_row: LogicRow): # OPTIMISTIC LOCKING, [TIME / DATE STAMPING]
|
|
58
|
+
def handle_all(logic_row: LogicRow): # #als: TIME / DATE STAMPING, OPTIMISTIC LOCKING
|
|
59
59
|
"""
|
|
60
60
|
This is generic - executed for all classes.
|
|
61
61
|
|
|
62
|
-
Invokes optimistic locking.
|
|
62
|
+
Invokes optimistic locking, and checks Grant permissions.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
Also provides user/date stamping.
|
|
65
65
|
|
|
66
66
|
Args:
|
|
67
67
|
logic_row (LogicRow): from LogicBank - old/new row, state
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
|
-
if
|
|
70
|
+
if os.getenv("APILOGICPROJECT_NO_FLASK") is not None:
|
|
71
|
+
print("\ndeclare_logic.py Using TestBase\n")
|
|
71
72
|
return # enables rules to be used outside of Flask, e.g., test data loading
|
|
72
73
|
|
|
73
74
|
if logic_row.is_updated() and logic_row.old_row is not None and logic_row.nest_level == 0:
|
|
74
75
|
opt_locking.opt_lock_patch(logic_row=logic_row)
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
|
|
77
|
+
Grant.process_updates(logic_row=logic_row)
|
|
78
|
+
|
|
79
|
+
did_stamping = False
|
|
80
|
+
if enable_stamping := False: # #als: DATE / USER STAMPING
|
|
77
81
|
row = logic_row.row
|
|
78
82
|
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedOn"):
|
|
79
83
|
row.CreatedOn = datetime.datetime.now()
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
did_stamping = True
|
|
85
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedBy"):
|
|
86
|
+
row.CreatedBy = Security.current_user().id
|
|
87
|
+
# if Config.SECURITY_ENABLED == True else 'public'
|
|
88
|
+
did_stamping = True
|
|
89
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedOn"):
|
|
90
|
+
row.UpdatedOn = datetime.datetime.now()
|
|
91
|
+
did_stamping = True
|
|
92
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedBy"):
|
|
93
|
+
row.UpdatedBy = Security.current_user().id \
|
|
94
|
+
if Config.SECURITY_ENABLED == True else 'public'
|
|
95
|
+
did_stamping = True
|
|
96
|
+
if did_stamping:
|
|
97
|
+
logic_row.log("early_row_event_all_classes - handle_all did stamping")
|
|
83
98
|
Rule.early_row_event_all_classes(early_row_event_all_classes=handle_all)
|
|
84
99
|
|
|
100
|
+
#als rules report
|
|
101
|
+
from api.system import api_utils
|
|
102
|
+
# api_utils.rules_report()
|
|
85
103
|
|
|
86
104
|
app_logger.debug("..logic/declare_logic.py (logic == rules + code)")
|
|
87
105
|
|
|
@@ -35,26 +35,25 @@ DefaultRolePermission(to_role = Roles.renter, can_read=True, can_delete=False)
|
|
|
35
35
|
DefaultRolePermission(to_role = Roles.manager, can_read=True, can_delete=False)
|
|
36
36
|
DefaultRolePermission(to_role = Roles.sales, can_read=True, can_delete=False)
|
|
37
37
|
|
|
38
|
+
|
|
38
39
|
#############################################
|
|
39
40
|
# Observe: Filters are AND'd, Grants are OR'd
|
|
40
41
|
#############################################
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
|
|
43
|
+
GlobalFilter( global_filter_attribute_name = "name", # sales does not see Bob (yes, silly - see nw & multi-tenant))
|
|
44
|
+
roles_not_filtered = ["sa", "manager", "tenant", "renter"], # ie, just sales
|
|
45
|
+
filter = '{entity_class}.name != \'Bob\' ') # poor Bob
|
|
46
|
+
|
|
47
|
+
|
|
45
48
|
Grant( on_entity = models.Customer,
|
|
46
49
|
to_role = Roles.sales,
|
|
47
|
-
filter = lambda : models.Customer.
|
|
48
|
-
filter_debug = "
|
|
50
|
+
filter = lambda : models.Customer.credit_limit >= 3000,
|
|
51
|
+
filter_debug = "credit_limit > 3000") # this eliminates Charlie, but...
|
|
49
52
|
|
|
50
53
|
Grant( on_entity = models.Customer,
|
|
51
54
|
to_role = Roles.sales,
|
|
52
|
-
filter = lambda : models.Customer.
|
|
53
|
-
filter_debug = "
|
|
54
|
-
|
|
55
|
-
# so user s1 sees the CTWSR customer row, per the resulting where from 2 global filters and 2 Grants:
|
|
56
|
-
# where (Client_id=2 and region="British Isles") and (CreditLimit>300 or ContactName="Mike")
|
|
57
|
-
# <---- Filters AND'd -------------------> <--- Grants OR'd --------------------->
|
|
55
|
+
filter = lambda : models.Customer.balance > 0,
|
|
56
|
+
filter_debug = "balance > 0 (see security/declare_security.py)") # this let's us see Charlie (grants OR'd)
|
|
58
57
|
|
|
59
58
|
|
|
60
59
|
app_logger.debug("Declare Security complete - security/declare_security.py"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
about:
|
|
2
|
+
date: May 13, 2025 20:12:31
|
|
3
|
+
merged:
|
|
4
|
+
at: May 13, 2025 20:15:21
|
|
5
|
+
new_attributes: 'Customer.email Order.CreatedOn '
|
|
6
|
+
new_resources: 'Email '
|
|
7
|
+
new_tab_groups: 'Customer.EmailList '
|
|
8
|
+
recent_changes: works with modified safrs-react-admin
|
|
9
|
+
version: 0.0.0
|
|
10
|
+
api_root: '{http_type}://{swagger_host}:{port}/{api}'
|
|
11
|
+
authentication: '{system-default}'
|
|
12
|
+
info:
|
|
13
|
+
number_relationships: 4
|
|
14
|
+
number_tables: 5
|
|
15
|
+
info_toggle_checked: true
|
|
16
|
+
resources:
|
|
17
|
+
Customer:
|
|
18
|
+
attributes:
|
|
19
|
+
- label: ' name*'
|
|
20
|
+
name: name
|
|
21
|
+
search: true
|
|
22
|
+
sort: true
|
|
23
|
+
- name: balance
|
|
24
|
+
type: DECIMAL
|
|
25
|
+
- name: credit_limit
|
|
26
|
+
type: DECIMAL
|
|
27
|
+
- name: id
|
|
28
|
+
- name: email
|
|
29
|
+
- name: email_opt_out
|
|
30
|
+
type: BOOLEAN
|
|
31
|
+
tab_groups:
|
|
32
|
+
- direction: tomany
|
|
33
|
+
fks:
|
|
34
|
+
- customer_id
|
|
35
|
+
name: OrderList
|
|
36
|
+
resource: Order
|
|
37
|
+
- direction: tomany
|
|
38
|
+
fks:
|
|
39
|
+
- customer_id
|
|
40
|
+
name: EmailList
|
|
41
|
+
resource: Email
|
|
42
|
+
type: Customer
|
|
43
|
+
user_key: name
|
|
44
|
+
Email:
|
|
45
|
+
attributes:
|
|
46
|
+
- label: ' id*'
|
|
47
|
+
name: id
|
|
48
|
+
search: true
|
|
49
|
+
sort: true
|
|
50
|
+
- name: customer_id
|
|
51
|
+
required: true
|
|
52
|
+
- name: message
|
|
53
|
+
- name: CreatedOn
|
|
54
|
+
type: DATE
|
|
55
|
+
tab_groups:
|
|
56
|
+
- direction: toone
|
|
57
|
+
fks:
|
|
58
|
+
- customer_id
|
|
59
|
+
name: customer
|
|
60
|
+
resource: Customer
|
|
61
|
+
type: Email
|
|
62
|
+
user_key: id
|
|
63
|
+
Item:
|
|
64
|
+
attributes:
|
|
65
|
+
- label: ' id*'
|
|
66
|
+
name: id
|
|
67
|
+
search: true
|
|
68
|
+
sort: true
|
|
69
|
+
- name: order_id
|
|
70
|
+
- name: product_id
|
|
71
|
+
required: true
|
|
72
|
+
- name: quantity
|
|
73
|
+
required: true
|
|
74
|
+
- name: amount
|
|
75
|
+
type: DECIMAL
|
|
76
|
+
- name: unit_price
|
|
77
|
+
type: DECIMAL
|
|
78
|
+
tab_groups:
|
|
79
|
+
- direction: toone
|
|
80
|
+
fks:
|
|
81
|
+
- order_id
|
|
82
|
+
name: order
|
|
83
|
+
resource: Order
|
|
84
|
+
- direction: toone
|
|
85
|
+
fks:
|
|
86
|
+
- product_id
|
|
87
|
+
name: product
|
|
88
|
+
resource: Product
|
|
89
|
+
type: Item
|
|
90
|
+
user_key: id
|
|
91
|
+
Order:
|
|
92
|
+
attributes:
|
|
93
|
+
- label: ' id*'
|
|
94
|
+
name: id
|
|
95
|
+
search: true
|
|
96
|
+
sort: true
|
|
97
|
+
- name: customer_id
|
|
98
|
+
required: true
|
|
99
|
+
- name: notes
|
|
100
|
+
- name: amount_total
|
|
101
|
+
type: DECIMAL
|
|
102
|
+
- name: date_shipped
|
|
103
|
+
type: DATE
|
|
104
|
+
- name: CreatedOn
|
|
105
|
+
type: DATE
|
|
106
|
+
tab_groups:
|
|
107
|
+
- direction: tomany
|
|
108
|
+
fks:
|
|
109
|
+
- order_id
|
|
110
|
+
name: ItemList
|
|
111
|
+
resource: Item
|
|
112
|
+
- direction: toone
|
|
113
|
+
fks:
|
|
114
|
+
- customer_id
|
|
115
|
+
name: customer
|
|
116
|
+
resource: Customer
|
|
117
|
+
type: Order
|
|
118
|
+
user_key: id
|
|
119
|
+
Product:
|
|
120
|
+
attributes:
|
|
121
|
+
- label: ' name*'
|
|
122
|
+
name: name
|
|
123
|
+
search: true
|
|
124
|
+
sort: true
|
|
125
|
+
- name: unit_price
|
|
126
|
+
type: DECIMAL
|
|
127
|
+
- name: id
|
|
128
|
+
tab_groups:
|
|
129
|
+
- direction: tomany
|
|
130
|
+
fks:
|
|
131
|
+
- product_id
|
|
132
|
+
name: ItemList
|
|
133
|
+
resource: Item
|
|
134
|
+
type: Product
|
|
135
|
+
user_key: name
|
|
136
|
+
settings:
|
|
137
|
+
HomeJS: /admin-app/home.js
|
|
138
|
+
max_list_columns: 8
|
|
139
|
+
style_guide:
|
|
140
|
+
applicationLocales:
|
|
141
|
+
- en
|
|
142
|
+
- es
|
|
143
|
+
currency_symbol: $
|
|
144
|
+
currency_symbol_position: left
|
|
145
|
+
date_format: LL
|
|
146
|
+
decimal_max: '1000000000'
|
|
147
|
+
decimal_min: '2'
|
|
148
|
+
decimal_separator: .
|
|
149
|
+
detail_mode: tab
|
|
150
|
+
edit_on_mode: dblclick
|
|
151
|
+
exclude_listpicker: false
|
|
152
|
+
include_translation: 'false'
|
|
153
|
+
keycloak_client_id: alsclient
|
|
154
|
+
keycloak_realm: kcals
|
|
155
|
+
keycloak_url: http://localhost:8080
|
|
156
|
+
locale: en
|
|
157
|
+
max_decimal_digits: '4'
|
|
158
|
+
min_decimal_digits: '2'
|
|
159
|
+
new_mode: dialog
|
|
160
|
+
pick_style: list
|
|
161
|
+
row_height: small,
|
|
162
|
+
serviceType: JSONAPI
|
|
163
|
+
startSessionPath: /auth/login
|
|
164
|
+
style: light
|
|
165
|
+
thousand_separator: ','
|
|
166
|
+
use_keycloak: 'false'
|
|
@@ -1,35 +1,29 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import api.system.api_utils as api_utils
|
|
3
|
-
import safrs
|
|
4
1
|
from flask import request, jsonify
|
|
2
|
+
import logging
|
|
3
|
+
|
|
5
4
|
from safrs import jsonapi_rpc
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
from integration.row_dict_maps.OrderShipping import OrderShipping
|
|
5
|
+
import safrs
|
|
6
|
+
|
|
9
7
|
from integration.row_dict_maps.OrderB2B import OrderB2B
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
# separate from expose_api_models.py, to simplify merge if project recreated
|
|
9
|
+
app_logger = logging.getLogger("api_logic_server_app")
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
def add_service(app, api, project_dir, swagger_host: str, PORT: str, method_decorators = []):
|
|
12
|
+
pass
|
|
15
13
|
|
|
16
|
-
def expose_services(app, api, project_dir, swagger_host: str, PORT: str):
|
|
17
|
-
""" Customize API - new end points for services
|
|
18
|
-
|
|
19
|
-
Brief background: see readme_customize_api.md
|
|
20
14
|
|
|
21
|
-
Your Code Goes Here
|
|
22
|
-
|
|
23
15
|
"""
|
|
16
|
+
Illustrates #als: custom end point with swagger, RowDictMapper
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
* Custom service - visible in swagger
|
|
19
|
+
* Services *not* requiring authentication (contrast to CategoriesEndPoint, below)
|
|
20
|
+
* Use OrderB2B (extends RowDictMapper) to map json to rows with aliasing, joins and lookups
|
|
21
|
+
* Recall business logic is not in service, but encapsulated for reuse in logic/declare_logic.py
|
|
22
|
+
"""
|
|
23
|
+
# class B2BSvcs(safrs.JABase):
|
|
29
24
|
|
|
30
25
|
api.expose_object(ServicesEndPoint) # Swagger-visible services
|
|
31
26
|
|
|
32
|
-
|
|
33
27
|
"""
|
|
34
28
|
Illustrates #als: custom end point with swagger, RowDictMapper
|
|
35
29
|
|
|
@@ -47,12 +41,12 @@ class ServicesEndPoint(safrs.JABase):
|
|
|
47
41
|
""" # yaml creates Swagger description
|
|
48
42
|
args :
|
|
49
43
|
order:
|
|
50
|
-
Account: "
|
|
44
|
+
Account: "Alice"
|
|
51
45
|
Notes: "Please Rush"
|
|
52
46
|
Items :
|
|
53
|
-
- Name: "
|
|
47
|
+
- Name: "Gadget"
|
|
54
48
|
QuantityOrdered: 1
|
|
55
|
-
- Name: "
|
|
49
|
+
- Name: "Widget"
|
|
56
50
|
QuantityOrdered: 2
|
|
57
51
|
- Name: "Green"
|
|
58
52
|
QuantityOrdered: 3
|
|
Binary file
|
|
@@ -19,16 +19,17 @@ 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)]
|
|
22
|
+
, fields = [(models.Order.notes, "Notes")]
|
|
23
23
|
, parent_lookups = [( models.Customer,
|
|
24
|
-
[(models.Customer.
|
|
24
|
+
[(models.Customer.name, 'Account')]
|
|
25
25
|
)]
|
|
26
26
|
, related = [
|
|
27
27
|
(RowDictMapper(model_class=models.Item
|
|
28
28
|
, alias="Items"
|
|
29
|
-
, fields = [(models.Item.
|
|
30
|
-
, parent_lookups = [( models.Product,
|
|
31
|
-
|
|
29
|
+
, fields = [(models.Item.quantity, "QuantityOrdered")]
|
|
30
|
+
, parent_lookups = [( models.Product,
|
|
31
|
+
[(models.Product.name, "Name" )]
|
|
32
|
+
)])
|
|
32
33
|
)
|
|
33
34
|
]
|
|
34
35
|
)
|
api_logic_server_cli/prototypes/basic_demo/iteration/integration/row_dict_maps/OrderShipping.py
CHANGED
|
@@ -17,11 +17,11 @@ class OrderShipping(RowDictMapper):
|
|
|
17
17
|
order = super(OrderShipping, self).__init__(
|
|
18
18
|
model_class=models.Order
|
|
19
19
|
, alias = "order"
|
|
20
|
-
, fields = [models.Order.
|
|
20
|
+
, fields = [models.Order.id, (models.Order.amount_total, "Total"), (models.Order.date_shipped, "Order Date"), models.Order.customer_id]
|
|
21
21
|
, related = RowDictMapper(model_class=models.Item, alias="Items"
|
|
22
|
-
, fields = [models.Item.
|
|
23
|
-
, related = RowDictMapper(model_class=models.Product, alias="product"
|
|
24
|
-
, fields=[models.Product.
|
|
22
|
+
, fields = [models.Item.order_id, models.Item.quantity, models.Item.amount]
|
|
23
|
+
, related = RowDictMapper(model_class=models.Product, alias="product", role_name='product'
|
|
24
|
+
, fields=[models.Product.name, models.Product.unit_price]
|
|
25
25
|
, isParent=True
|
|
26
26
|
)
|
|
27
27
|
)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import datetime
|
|
1
|
+
import datetime, os
|
|
2
2
|
from decimal import Decimal
|
|
3
3
|
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
|
-
from
|
|
7
|
-
|
|
6
|
+
from logic_bank.logic_bank import DeclareRule
|
|
7
|
+
import database.models as models
|
|
8
8
|
import api.system.opt_locking.opt_locking as opt_locking
|
|
9
|
-
from security.system.authorization import Grant
|
|
10
|
-
import logging, os
|
|
11
9
|
from integration.row_dict_maps.OrderShipping import OrderShipping
|
|
12
|
-
from
|
|
10
|
+
from security.system.authorization import Grant, Security
|
|
11
|
+
from logic.load_verify_rules import load_verify_rules
|
|
13
12
|
import integration.kafka.kafka_producer as kafka_producer
|
|
13
|
+
import logging
|
|
14
14
|
|
|
15
15
|
app_logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
@@ -22,45 +22,55 @@ def declare_logic():
|
|
|
22
22
|
Brief background: see readme_declare_logic.md
|
|
23
23
|
|
|
24
24
|
Your Code Goes Here - Use code completion (Rule.) to declare rules
|
|
25
|
-
|
|
26
|
-
GenAI: Paste the following into Copilot Chat, and paste the result below.
|
|
27
|
-
|
|
28
|
-
Use Logic Bank to enforce these requirements:
|
|
29
|
-
|
|
30
|
-
Enforce the Check Credit requirement (do not generate check constraints):
|
|
31
|
-
1. Customer.Balance <= CreditLimit
|
|
32
|
-
2. Customer.Balance = Sum(Order.AmountTotal where date shipped is null)
|
|
33
|
-
3. Order.AmountTotal = Sum(Items.Amount)
|
|
34
|
-
4. Items.Amount = Quantity * UnitPrice
|
|
35
|
-
5. Store the Items.UnitPrice as a copy from Product.UnitPrice
|
|
36
25
|
'''
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
if os.environ.get("WG_PROJECT"):
|
|
28
|
+
# Inside WG: Load rules from docs/expprt/export.json
|
|
29
|
+
load_verify_rules()
|
|
30
|
+
else:
|
|
31
|
+
# Outside WG: load declare_logic function
|
|
32
|
+
from logic.logic_discovery.auto_discovery import discover_logic
|
|
33
|
+
discover_logic()
|
|
34
|
+
|
|
35
|
+
# Logic from GenAI: (or, use your IDE w/ code completion)
|
|
36
|
+
from database.models import Product, Order, Item, Customer
|
|
37
|
+
|
|
38
|
+
# Ensure the customer's balance is less than their credit limit
|
|
39
|
+
Rule.constraint(validate=Customer, as_condition=lambda row: row.balance <= row.credit_limit, error_msg="Customer balance ({row.balance}) exceeds credit limit ({row.credit_limit})")
|
|
40
|
+
|
|
41
|
+
# Derive the customer's balance as the sum of order totals where not yet shipped.
|
|
42
|
+
Rule.sum(derive=Customer.balance, as_sum_of=Order.amount_total, where=lambda row: row.date_shipped is None)
|
|
39
43
|
|
|
40
|
-
#
|
|
41
|
-
Rule.
|
|
42
|
-
error_msg="balance exceeds credit limit",
|
|
43
|
-
as_condition=lambda row: row.Balance <= row.CreditLimit)
|
|
44
|
+
# Derive the order's total amount from the sum of item amounts.
|
|
45
|
+
Rule.sum(derive=Order.amount_total, as_sum_of=Item.amount)
|
|
44
46
|
|
|
45
|
-
# 2. Customer.Balance = Sum(Order.AmountTotal where date shipped is null)
|
|
46
|
-
Rule.sum(derive=Customer.Balance, as_sum_of=Order.AmountTotal,
|
|
47
|
-
where=lambda row: row.ShipDate is None)
|
|
48
47
|
|
|
49
|
-
#
|
|
50
|
-
Rule.sum(derive=Order.AmountTotal, as_sum_of=Item.Amount)
|
|
48
|
+
# ************ Python Customization Example ****************
|
|
51
49
|
|
|
50
|
+
# Calculate item amount based on quantity and unit price.
|
|
51
|
+
# Formerly, we had this rule:
|
|
52
|
+
# Rule.formula(derive=Item.amount, as_expression=lambda row: row.quantity * row.unit_price)
|
|
53
|
+
# If the derivation were more complex, we could use a Python function, like this:
|
|
52
54
|
def derive_amount(row: models.Item, old_row: models.Item, logic_row: LogicRow):
|
|
53
|
-
amount = row.
|
|
54
|
-
|
|
55
|
+
amount = row.quantity * row.unit_price
|
|
56
|
+
product = row.product
|
|
57
|
+
if product.carbon_neutral == True and row.quantity >= 10:
|
|
55
58
|
amount = amount * Decimal(0.9) # breakpoint here
|
|
56
59
|
return amount
|
|
57
60
|
|
|
58
61
|
# 4. Items.Amount = Quantity * UnitPrice
|
|
59
|
-
Rule.formula(derive=models.Item.
|
|
62
|
+
Rule.formula(derive=models.Item.amount, calling=derive_amount)
|
|
60
63
|
|
|
61
|
-
#
|
|
62
|
-
Rule.copy(Item.
|
|
64
|
+
# Copy unit price from product to item.
|
|
65
|
+
Rule.copy(derive=Item.unit_price, from_parent=Product.unit_price)
|
|
63
66
|
|
|
67
|
+
|
|
68
|
+
# ************ Python Customization Example ****************
|
|
69
|
+
|
|
70
|
+
# Send order details to Kafka if order is shipped.
|
|
71
|
+
# Formeraly, we had this rule:
|
|
72
|
+
# Rule.after_flush_row_event(on_class=Order, calling=kafka_producer.send_row_to_kafka, if_condition=lambda row: row.date_shipped is not None, with_args={'topic': 'order_shipping'})
|
|
73
|
+
# If the integration were more complex, we could use Python, like this:
|
|
64
74
|
#als: Demonstrate that logic == Rules + Python (for extensibility)
|
|
65
75
|
|
|
66
76
|
def send_order_to_shipping(row: models.Order, old_row: models.Order, logic_row: LogicRow):
|
|
@@ -68,7 +78,7 @@ def declare_logic():
|
|
|
68
78
|
|
|
69
79
|
Format row per shipping requirements, and send (e.g., a message)
|
|
70
80
|
|
|
71
|
-
NB: the after_flush event makes Order.Id avaible.
|
|
81
|
+
NB: the after_flush event makes Order.Id avaible.
|
|
72
82
|
|
|
73
83
|
Args:
|
|
74
84
|
row (models.Order): inserted Order
|
|
@@ -79,38 +89,54 @@ def declare_logic():
|
|
|
79
89
|
kafka_producer.send_kafka_message(logic_row=logic_row,
|
|
80
90
|
row_dict_mapper=OrderShipping,
|
|
81
91
|
kafka_topic="order_shipping",
|
|
82
|
-
kafka_key=str(row.
|
|
92
|
+
kafka_key=str(row.id),
|
|
83
93
|
msg="Sending Order to Shipping")
|
|
84
94
|
|
|
85
95
|
Rule.after_flush_row_event(on_class=models.Order, calling=send_order_to_shipping) # see above
|
|
86
96
|
|
|
97
|
+
# End Logic from GenAI
|
|
87
98
|
|
|
88
99
|
|
|
89
|
-
def handle_all(logic_row: LogicRow): #
|
|
100
|
+
def handle_all(logic_row: LogicRow): # #als: TIME / DATE STAMPING, OPTIMISTIC LOCKING
|
|
90
101
|
"""
|
|
91
102
|
This is generic - executed for all classes.
|
|
92
103
|
|
|
93
|
-
Invokes optimistic locking.
|
|
104
|
+
Invokes optimistic locking, and checks Grant permissions.
|
|
94
105
|
|
|
95
|
-
|
|
106
|
+
Also provides user/date stamping.
|
|
96
107
|
|
|
97
108
|
Args:
|
|
98
109
|
logic_row (LogicRow): from LogicBank - old/new row, state
|
|
99
110
|
"""
|
|
100
111
|
|
|
101
|
-
if
|
|
112
|
+
if os.getenv("APILOGICPROJECT_NO_FLASK") is not None:
|
|
113
|
+
print("\ndeclare_logic.py Using TestBase\n")
|
|
102
114
|
return # enables rules to be used outside of Flask, e.g., test data loading
|
|
103
115
|
|
|
104
116
|
if logic_row.is_updated() and logic_row.old_row is not None and logic_row.nest_level == 0:
|
|
105
117
|
opt_locking.opt_lock_patch(logic_row=logic_row)
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
|
|
119
|
+
Grant.process_updates(logic_row=logic_row)
|
|
120
|
+
|
|
121
|
+
did_stamping = False
|
|
122
|
+
if enable_stamping := False: # #als: DATE / USER STAMPING
|
|
108
123
|
row = logic_row.row
|
|
109
124
|
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedOn"):
|
|
110
125
|
row.CreatedOn = datetime.datetime.now()
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
126
|
+
did_stamping = True
|
|
127
|
+
if logic_row.ins_upd_dlt == "ins" and hasattr(row, "CreatedBy"):
|
|
128
|
+
row.CreatedBy = Security.current_user().id
|
|
129
|
+
# if Config.SECURITY_ENABLED == True else 'public'
|
|
130
|
+
did_stamping = True
|
|
131
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedOn"):
|
|
132
|
+
row.UpdatedOn = datetime.datetime.now()
|
|
133
|
+
did_stamping = True
|
|
134
|
+
if logic_row.ins_upd_dlt == "upd" and hasattr(row, "UpdatedBy"):
|
|
135
|
+
row.UpdatedBy = Security.current_user().id \
|
|
136
|
+
if Config.SECURITY_ENABLED == True else 'public'
|
|
137
|
+
did_stamping = True
|
|
138
|
+
if did_stamping:
|
|
139
|
+
logic_row.log("early_row_event_all_classes - handle_all did stamping")
|
|
114
140
|
Rule.early_row_event_all_classes(early_row_event_all_classes=handle_all)
|
|
115
141
|
|
|
116
142
|
#als rules report
|