From ddc57a8a69ad1f2417f039c73bffa001b16592a2 Mon Sep 17 00:00:00 2001 From: Alex Bourret Date: Mon, 26 Jan 2026 10:48:50 +0100 Subject: [PATCH 01/12] merging template and category search on tree explorer --- js/pi-system_treecontroller.js | 21 ++++++++- python-lib/osisoft_client.py | 37 +++++++++++++++ resource/browse_af_tree.py | 71 +++++++++++++++++++++++++---- resource/pi-system_af-explorer.html | 16 +++++++ 4 files changed, 135 insertions(+), 10 deletions(-) diff --git a/js/pi-system_treecontroller.js b/js/pi-system_treecontroller.js index 09c97d8..aa3b16d 100644 --- a/js/pi-system_treecontroller.js +++ b/js/pi-system_treecontroller.js @@ -89,8 +89,24 @@ app.controller('AfExplorerFormCtrl', [ return item; }); } - - + + $scope.getTemplatesFromDB = function() { + $scope.callPythonDo({method: "get_templates_from_db"}).then(function(data){ + $scope.config.templates = data.choices; + }); + } + + $scope.getCategoriesFromDB = function(){ + $scope.config.attribute_categories = []; + $scope.config.element_categories = []; + $scope.callPythonDo({method: "get_attribute_categories_from_db"}).then(function(data){ + $scope.config.attribute_categories = data.choices; + }); + $scope.callPythonDo({method: "get_element_categories_from_db"}).then(function(data){ + $scope.config.element_categories = data.choices; + }); + } + // Toggle récursif des checkboxes $scope.toggleChildren = function(node) { console.log("ALX:tc:" + JSON.stringify(node)); @@ -110,6 +126,7 @@ app.controller('AfExplorerFormCtrl', [ function(data){ TreeDataService.setTreeData(data.choices); $scope.config.treeData = TreeDataService.getTreeData(); + console.log("ALX:", JSON.stringify($scope.config.treeData)); $scope.config.attributeList = data.attributes; $scope.config.selectedAttributes = []; } diff --git a/python-lib/osisoft_client.py b/python-lib/osisoft_client.py index 71c39c1..5c853d9 100644 --- a/python-lib/osisoft_client.py +++ b/python-lib/osisoft_client.py @@ -748,6 +748,41 @@ def search_attributes(self, database_webid, **kwargs): else: json_response = None + def search_elements(self, database_webid, name=None, description=None, category=None, template=None, full_search=True): + headers = self.get_requests_headers() + tempo_maxcount = OSIsoftConstants.DEFAULT_MAXCOUNT + params = { + "maxCount": tempo_maxcount, + "associations": "Paths", + } + # # https://dku-qa-osi.francecentral.cloudapp.azure.com/piwebapi/assetdatabases/F1RD3VEt1yTvt0ip6-/elements?TemplateName=Substation transformer&categoryName=Westinghouse&nameFilter=TX26*&searchFullHierarchy=true + url = self.endpoint.get_base_url() + "/assetdatabases/{}/elements".format(database_webid) + if name: + params["nameFilter"] = name + if description: + params["descriptionFilter"] = description + if category: + params["categoryName"] = category + if template: + params["templateName"] = template + if full_search: + params["searchFullHierarchy"] = True + json_response = self.get(url=url, headers=headers, params=params) + if OSIsoftConstants.DKU_ERROR_KEY in json_response: + yield json_response + start_index = 0 + while json_response: + items = json_response.get(OSIsoftConstants.API_ITEM_KEY, []) + for item in items: + yield item + if len(items) < tempo_maxcount: + logger.info("No more result items") + return + start_index += tempo_maxcount + logger.info("Trying again with startIndex={}".format(start_index)) + params["startIndex"] = start_index + json_response = self.get(url=url, headers=headers, params=params) + def build_element_query(self, **kwargs): element_query_keys = { "element_name": "Name:'{}'", @@ -866,6 +901,8 @@ def traverse_and_cache(self, path_elements, path_attributes, tree): json_response = self.get(url=next_url, headers=headers, params={}, error_source="traverse_and_cache") else: break + if not before_last_json: + return None items = before_last_json.get(OSIsoftConstants.API_ITEM_KEY, []) for item in items: item_details = get_item_details(item) diff --git a/resource/browse_af_tree.py b/resource/browse_af_tree.py index 91b3f6d..dbf6984 100644 --- a/resource/browse_af_tree.py +++ b/resource/browse_af_tree.py @@ -51,7 +51,28 @@ def do(payload, config, plugin_config, inputs): database_name = config.get("database_name") parent = payload.get("parent", {}) return get_children_from_db(client, parent, database_name=database_name) + if method == "get_templates_from_db": + database_name = config.get("database_name") + parent = payload.get("parent", {}) + ret = get_items_from_db(client, parent, "ElementTemplates", database_name=database_name) + return ret + if method == "get_attribute_categories_from_db": + database_name = config.get("database_name") + parent = payload.get("parent", {}) + ret = get_items_from_db(client, parent, "AttributeCategories", database_name=database_name) + return ret + if method == "get_element_categories_from_db": + database_name = config.get("database_name") + parent = payload.get("parent", {}) + ret = get_items_from_db(client, parent, "ElementCategories", database_name=database_name) + return ret if method == "do_search": + template_name = config.get("template", None) + category_name = config.get("element_category", None) + if template_name == "-- Any --": + template_name = None + if category_name == "-- Any --": + category_name = None database_name = config.get("database_name") element_name = config.get("element_name") attribute_name = config.get("attribute_name") @@ -73,14 +94,29 @@ def do(payload, config, plugin_config, inputs): # "attribute_category": "CategoryName:'{}'", # "attribute_value_type": "Type:'{}'" # } - for attribute in client.search_attributes( - database_webid, - attribute_name=attribute_name, - element_name=element_name, - search_associations="Paths" - ): - attribute["checked"] = False - attributes.append(attribute) + # for attribute in client.search_attributes( + # database_webid, + # attribute_name=attribute_name, + # element_name=element_name, + # search_associations="Paths" + # ): + # attribute["checked"] = False + # attributes.append(attribute) + attributes = [] + if template_name or category_name: + for attribute in client.search_elements(database_webid, name=element_name, template=template_name, category=category_name, full_search=True): + attribute["checked"] = True + attributes.append(attribute) + else: + for attribute in client.search_attributes( + database_webid, + attribute_name=attribute_name, + element_name=element_name, + search_associations="Paths" + ): + attribute["checked"] = True + attributes.append(attribute) + attributes = duplicate_linked_attributes(attributes) items = [] for attribute in attributes: @@ -123,6 +159,25 @@ def get_query_catalogs(cnx, config): return {"choices": [user, password]} +def get_items_from_db(client, parent_node, link_key, database_name=None): + default_choice = {"title": "-- Any --"} + if isinstance(parent_node, dict): + url = parent_node.get("url", database_name) + else: + url = parent_node + this_node = next(client.get_next_item_from_url(url)) + links = this_node.get("Links", {}) + items_url = links.get(link_key) + items = [] + items.append(default_choice) + if items_url: + for item in client.get_next_item_from_url(items_url): + item = get_item_details(item) + item["type"] = link_key + items.append(item) + return {"choices": items} + + def get_children_from_db(client, parent_node, database_name=None): if isinstance(parent_node, dict): url = parent_node.get("url", database_name) diff --git a/resource/pi-system_af-explorer.html b/resource/pi-system_af-explorer.html index d331c88..4330f56 100644 --- a/resource/pi-system_af-explorer.html +++ b/resource/pi-system_af-explorer.html @@ -56,9 +56,20 @@
+ +
+