From acf08dbe9ea9359d789e3269daeeb83c269065a0 Mon Sep 17 00:00:00 2001 From: rosalinep Date: Mon, 18 Dec 2023 12:37:59 -0800 Subject: [PATCH 1/9] Implement --- CHANGE.txt | 6 ++++++ labkey/query.py | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGE.txt b/CHANGE.txt index 7586271..710dbd9 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -2,6 +2,12 @@ LabKey Python Client API News +++++++++++ +What's New in the LabKey 2.7.0 package +============================== + +*Release date: TBD* +- Query API - add options parameter to insert_rows, select_rows, and delete_rows + What's New in the LabKey 2.6.0 package ============================== diff --git a/labkey/query.py b/labkey/query.py index ba76b6e..ff1a43c 100644 --- a/labkey/query.py +++ b/labkey/query.py @@ -170,6 +170,7 @@ def delete_rows( query_name: str, rows: any, container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): """ @@ -179,12 +180,16 @@ def delete_rows( :param query_name: table name to delete from :param rows: Set of rows to delete :param container_path: labkey container path if not already set in context + :param options: Additional parameters :param timeout: timeout of request in seconds (defaults to 30s) :return: """ url = server_context.build_url("query", "deleteRows.api", container_path=container_path) payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} + if options is not None: + payload = payload | options + return server_context.make_request( url, json=payload, @@ -285,6 +290,7 @@ def insert_rows( query_name: str, rows: List[any], container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): """ @@ -294,6 +300,7 @@ def insert_rows( :param query_name: table name to insert into :param rows: set of rows to insert :param container_path: labkey container path if not already set in context + :param options: Additional parameters :param timeout: timeout of request in seconds (defaults to 30s) :return: """ @@ -301,6 +308,9 @@ def insert_rows( payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} + if options is not None: + payload = payload | options + return server_context.make_request( url, json=payload, @@ -419,6 +429,7 @@ def update_rows( query_name: str, rows: List[any], container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): """ @@ -429,6 +440,7 @@ def update_rows( :param query_name: table name to update :param rows: Set of rows to update :param container_path: labkey container path if not already set in context + :param options: Additional parameters :param timeout: timeout of request in seconds (defaults to 30s) :return: """ @@ -436,6 +448,9 @@ def update_rows( payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} + if options is not None: + payload = payload | options + return server_context.make_request( url, json=payload, @@ -458,10 +473,11 @@ def delete_rows( query_name: str, rows: any, container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): return delete_rows( - self.server_context, schema_name, query_name, rows, container_path, timeout + self.server_context, schema_name, query_name, rows, container_path, options, timeout ) @functools.wraps(truncate_table) @@ -507,10 +523,11 @@ def insert_rows( query_name: str, rows: List[any], container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): return insert_rows( - self.server_context, schema_name, query_name, rows, container_path, timeout + self.server_context, schema_name, query_name, rows, container_path, options, timeout ) @functools.wraps(select_rows) @@ -566,8 +583,9 @@ def update_rows( query_name: str, rows: List[any], container_path: str = None, + options: dict = None, timeout: int = _default_timeout, ): return update_rows( - self.server_context, schema_name, query_name, rows, container_path, timeout + self.server_context, schema_name, query_name, rows, container_path, options, timeout ) From 312a8bf549c46ede2f03d184b53c84d3e2d70a9e Mon Sep 17 00:00:00 2001 From: RosalineP Date: Wed, 20 Dec 2023 13:55:28 -0800 Subject: [PATCH 2/9] Use Python 3.7 syntax Co-authored-by: Alan Vezina --- labkey/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labkey/query.py b/labkey/query.py index ff1a43c..e0e5aea 100644 --- a/labkey/query.py +++ b/labkey/query.py @@ -309,7 +309,7 @@ def insert_rows( payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} if options is not None: - payload = payload | options + payload = {**payload, **options} return server_context.make_request( url, From b604aa24ddfadc96fc98bc074e4c9c261b876b1f Mon Sep 17 00:00:00 2001 From: rosalinep Date: Wed, 20 Dec 2023 15:25:56 -0800 Subject: [PATCH 3/9] Apply feedback --- labkey/query.py | 117 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 93 insertions(+), 24 deletions(-) diff --git a/labkey/query.py b/labkey/query.py index e0e5aea..f32228f 100644 --- a/labkey/query.py +++ b/labkey/query.py @@ -164,13 +164,25 @@ def __repr__(self): return "".format(self.column_name, self.filter_type, self.value) +class AuditBehavior: + """ + Enum of different auditing levels + """ + + DETAILED = "DETAILED" + NONE = "NONE" + SUMMARY = "SUMMARY" + + def delete_rows( server_context: ServerContext, schema_name: str, query_name: str, rows: any, container_path: str = None, - options: dict = None, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): """ @@ -180,15 +192,21 @@ def delete_rows( :param query_name: table name to delete from :param rows: Set of rows to delete :param container_path: labkey container path if not already set in context - :param options: Additional parameters + :param transacted: whether all of the updates should be done in a single transaction + :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior + :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records :param timeout: timeout of request in seconds (defaults to 30s) :return: """ url = server_context.build_url("query", "deleteRows.api", container_path=container_path) - payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} - - if options is not None: - payload = payload | options + payload = { + "schemaName": schema_name, + "queryName": query_name, + "rows": rows, + "transacted": transacted, + "auditBehavior": audit_behavior, + "auditUserComment": audit_user_comment + } return server_context.make_request( url, @@ -290,7 +308,10 @@ def insert_rows( query_name: str, rows: List[any], container_path: str = None, - options: dict = None, + skip_reselect_rows: bool = False, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): """ @@ -300,16 +321,24 @@ def insert_rows( :param query_name: table name to insert into :param rows: set of rows to insert :param container_path: labkey container path if not already set in context - :param options: Additional parameters + :param skip_reselect_rows: whether the full detailed response for the insert can be skipped + :param transacted: whether all of the updates should be done in a single transaction + :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior + :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records :param timeout: timeout of request in seconds (defaults to 30s) :return: """ url = server_context.build_url("query", "insertRows.api", container_path=container_path) - payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} - - if options is not None: - payload = {**payload, **options} + payload = { + "schemaName": schema_name, + "queryName": query_name, + "rows": rows, + "skipReselectRows": skip_reselect_rows, + "transacted": transacted, + "auditBehavior": audit_behavior, + "auditUserComment": audit_user_comment + } return server_context.make_request( url, @@ -429,7 +458,9 @@ def update_rows( query_name: str, rows: List[any], container_path: str = None, - options: dict = None, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): """ @@ -440,16 +471,22 @@ def update_rows( :param query_name: table name to update :param rows: Set of rows to update :param container_path: labkey container path if not already set in context - :param options: Additional parameters + :param transacted: whether all of the updates should be done in a single transaction + :param audit_behavior: used to override the audit behavior for the update. See class query.AuditBehavior + :param audit_user_comment: used to provide a comment that will be attached to certain detailed audit log records :param timeout: timeout of request in seconds (defaults to 30s) :return: """ url = server_context.build_url("query", "updateRows.api", container_path=container_path) - payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} - - if options is not None: - payload = payload | options + payload = { + "schemaName": schema_name, + "queryName": query_name, + "rows": rows, + "transacted": transacted, + "auditBehavior": audit_behavior, + "auditUserComment": audit_user_comment + } return server_context.make_request( url, @@ -473,11 +510,21 @@ def delete_rows( query_name: str, rows: any, container_path: str = None, - options: dict = None, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): return delete_rows( - self.server_context, schema_name, query_name, rows, container_path, options, timeout + self.server_context, + schema_name, + query_name, + rows, + container_path, + transacted, + audit_behavior, + audit_user_comment, + timeout ) @functools.wraps(truncate_table) @@ -523,11 +570,23 @@ def insert_rows( query_name: str, rows: List[any], container_path: str = None, - options: dict = None, + skip_reselect_rows: bool = False, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): return insert_rows( - self.server_context, schema_name, query_name, rows, container_path, options, timeout + self.server_context, + schema_name, + query_name, + rows, + container_path, + skip_reselect_rows, + transacted, + audit_behavior, + audit_user_comment, + timeout ) @functools.wraps(select_rows) @@ -583,9 +642,19 @@ def update_rows( query_name: str, rows: List[any], container_path: str = None, - options: dict = None, + transacted: bool = True, + audit_behavior: AuditBehavior = None, + audit_user_comment: str = None, timeout: int = _default_timeout, ): return update_rows( - self.server_context, schema_name, query_name, rows, container_path, options, timeout + self.server_context, + schema_name, + query_name, + rows, + container_path, + transacted, + audit_behavior, + audit_user_comment, + timeout ) From c56974559d0ff8781f057c812a626b391cb7ff94 Mon Sep 17 00:00:00 2001 From: rosalinep Date: Wed, 20 Dec 2023 15:28:40 -0800 Subject: [PATCH 4/9] Change log --- CHANGE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGE.txt b/CHANGE.txt index 710dbd9..53c95ae 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -6,7 +6,7 @@ What's New in the LabKey 2.7.0 package ============================== *Release date: TBD* -- Query API - add options parameter to insert_rows, select_rows, and delete_rows +- Query API - add optional parameters to insert_rows, select_rows, and delete_rows What's New in the LabKey 2.6.0 package ============================== From e76fdf46a3212ed31707be99ce04b341264590df Mon Sep 17 00:00:00 2001 From: rosalinep Date: Thu, 21 Dec 2023 13:13:47 -0800 Subject: [PATCH 5/9] Wording --- CHANGE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGE.txt b/CHANGE.txt index 53c95ae..2c7a979 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -6,7 +6,7 @@ What's New in the LabKey 2.7.0 package ============================== *Release date: TBD* -- Query API - add optional parameters to insert_rows, select_rows, and delete_rows +- Query API - add optional parameters to insert_rows, select_rows, update_rows, and delete_rows What's New in the LabKey 2.6.0 package ============================== From 4210f0f46a97baa830dbf1fb7e049d8e9a1580f5 Mon Sep 17 00:00:00 2001 From: rosalinep Date: Thu, 21 Dec 2023 13:34:47 -0800 Subject: [PATCH 6/9] Wording --- CHANGE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGE.txt b/CHANGE.txt index 26eb1e5..cb6f443 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -6,7 +6,7 @@ What's New in the LabKey 3.1.0 package ============================== *Release date: TBD* -- Query API - add optional parameters to insert_rows, select_rows, update_rows, and delete_rows +- Query API - add optional parameters to insert_rows, update_rows, and delete_rows What's New in the LabKey 3.0.0 package ============================== From a9201ef039dde81c8f737e8313068d9d3ab29e5f Mon Sep 17 00:00:00 2001 From: rosalinep Date: Thu, 21 Dec 2023 13:44:00 -0800 Subject: [PATCH 7/9] Oops --- CHANGE.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGE.txt b/CHANGE.txt index cb6f443..08e2517 100644 --- a/CHANGE.txt +++ b/CHANGE.txt @@ -2,12 +2,6 @@ LabKey Python Client API News +++++++++++ -What's New in the LabKey 3.1.0 package -============================== - -*Release date: TBD* -- Query API - add optional parameters to insert_rows, update_rows, and delete_rows - What's New in the LabKey 3.0.0 package ============================== @@ -15,6 +9,7 @@ What's New in the LabKey 3.0.0 package - Query API - WAF encode "sql" parameter for execute_sql - WAF encoding of parameters is initially supported with LabKey Server v23.09 - WAF encoding can be opted out of on execute_sql calls by specifying waf_encode_sql=False +- Query API - add optional parameters to insert_rows, update_rows, and delete_rows What's New in the LabKey 2.6.1 package ============================== From da6bf96a358e618efeb41c5d0e00310e9e18cbf0 Mon Sep 17 00:00:00 2001 From: rosalinep Date: Wed, 27 Dec 2023 12:59:28 -0800 Subject: [PATCH 8/9] Change approach --- labkey/query.py | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/labkey/query.py b/labkey/query.py index e553b5e..0d9ebf7 100644 --- a/labkey/query.py +++ b/labkey/query.py @@ -204,11 +204,17 @@ def delete_rows( "schemaName": schema_name, "queryName": query_name, "rows": rows, - "transacted": transacted, - "auditBehavior": audit_behavior, - "auditUserComment": audit_user_comment } + if transacted is False: + payload["transacted"] = transacted + + if audit_behavior is not None: + payload["auditBehavior"] = audit_behavior + + if audit_user_comment is not None: + payload["auditUserComment"] = audit_user_comment + return server_context.make_request( url, json=payload, @@ -343,6 +349,18 @@ def insert_rows( "auditUserComment": audit_user_comment } + if skip_reselect_rows is True: + payload["skipReselectRows"] = skip_reselect_rows + + if transacted is False: + payload["transacted"] = transacted + + if audit_behavior is not None: + payload["auditBehavior"] = audit_behavior + + if audit_user_comment is not None: + payload["auditUserComment"] = audit_user_comment + return server_context.make_request( url, json=payload, @@ -486,11 +504,17 @@ def update_rows( "schemaName": schema_name, "queryName": query_name, "rows": rows, - "transacted": transacted, - "auditBehavior": audit_behavior, - "auditUserComment": audit_user_comment } + if transacted is False: + payload["transacted"] = transacted + + if audit_behavior is not None: + payload["auditBehavior"] = audit_behavior + + if audit_user_comment is not None: + payload["auditUserComment"] = audit_user_comment + return server_context.make_request( url, json=payload, From 256e2d61fe47e9ba9523cdacf9d656faf5671b6e Mon Sep 17 00:00:00 2001 From: rosalinep Date: Wed, 27 Dec 2023 15:56:12 -0800 Subject: [PATCH 9/9] Fix oversight --- labkey/query.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/labkey/query.py b/labkey/query.py index 0d9ebf7..d784490 100644 --- a/labkey/query.py +++ b/labkey/query.py @@ -200,11 +200,8 @@ def delete_rows( :return: """ url = server_context.build_url("query", "deleteRows.api", container_path=container_path) - payload = { - "schemaName": schema_name, - "queryName": query_name, - "rows": rows, - } + + payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} if transacted is False: payload["transacted"] = transacted @@ -339,15 +336,7 @@ def insert_rows( """ url = server_context.build_url("query", "insertRows.api", container_path=container_path) - payload = { - "schemaName": schema_name, - "queryName": query_name, - "rows": rows, - "skipReselectRows": skip_reselect_rows, - "transacted": transacted, - "auditBehavior": audit_behavior, - "auditUserComment": audit_user_comment - } + payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} if skip_reselect_rows is True: payload["skipReselectRows"] = skip_reselect_rows @@ -500,11 +489,7 @@ def update_rows( """ url = server_context.build_url("query", "updateRows.api", container_path=container_path) - payload = { - "schemaName": schema_name, - "queryName": query_name, - "rows": rows, - } + payload = {"schemaName": schema_name, "queryName": query_name, "rows": rows} if transacted is False: payload["transacted"] = transacted