pyxecm 2.0.3__py3-none-any.whl → 3.0.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.

Potentially problematic release.


This version of pyxecm might be problematic. Click here for more details.

Files changed (94) hide show
  1. pyxecm/coreshare.py +76 -8
  2. pyxecm/helper/data.py +16 -24
  3. pyxecm/helper/otel_config.py +26 -0
  4. pyxecm/helper/web.py +1 -2
  5. pyxecm/otca.py +1356 -16
  6. pyxecm/otcs.py +4238 -758
  7. pyxecm/otds.py +4 -12
  8. pyxecm/otmm.py +4 -5
  9. pyxecm/py.typed +0 -0
  10. pyxecm-3.0.0.dist-info/METADATA +48 -0
  11. pyxecm-3.0.0.dist-info/RECORD +96 -0
  12. {pyxecm-2.0.3.dist-info → pyxecm-3.0.0.dist-info}/WHEEL +1 -2
  13. pyxecm-3.0.0.dist-info/entry_points.txt +4 -0
  14. {pyxecm/customizer/api → pyxecm_api}/__main__.py +1 -1
  15. pyxecm_api/agents/__init__.py +7 -0
  16. pyxecm_api/agents/app.py +13 -0
  17. pyxecm_api/agents/functions.py +119 -0
  18. pyxecm_api/agents/models.py +10 -0
  19. pyxecm_api/agents/otcm_knowledgegraph/functions.py +85 -0
  20. pyxecm_api/agents/otcm_knowledgegraph/models.py +61 -0
  21. pyxecm_api/agents/otcm_knowledgegraph/router.py +74 -0
  22. pyxecm_api/agents/otcm_user_agent/models.py +20 -0
  23. pyxecm_api/agents/otcm_user_agent/router.py +65 -0
  24. pyxecm_api/agents/otcm_workspace_agent/models.py +40 -0
  25. pyxecm_api/agents/otcm_workspace_agent/router.py +200 -0
  26. pyxecm_api/app.py +221 -0
  27. {pyxecm/customizer/api → pyxecm_api}/auth/functions.py +10 -2
  28. {pyxecm/customizer/api → pyxecm_api}/auth/router.py +4 -3
  29. {pyxecm/customizer/api → pyxecm_api}/common/functions.py +39 -9
  30. {pyxecm/customizer/api → pyxecm_api}/common/metrics.py +1 -2
  31. {pyxecm/customizer/api → pyxecm_api}/common/router.py +12 -11
  32. {pyxecm/customizer/api → pyxecm_api}/settings.py +30 -6
  33. {pyxecm/customizer/api → pyxecm_api}/terminal/router.py +1 -1
  34. {pyxecm/customizer/api → pyxecm_api}/v1_csai/router.py +39 -10
  35. pyxecm_api/v1_csai/statics/bindings/utils.js +189 -0
  36. pyxecm_api/v1_csai/statics/tom-select/tom-select.complete.min.js +356 -0
  37. pyxecm_api/v1_csai/statics/tom-select/tom-select.css +334 -0
  38. pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.css +1 -0
  39. pyxecm_api/v1_csai/statics/vis-9.1.2/vis-network.min.js +27 -0
  40. pyxecm_api/v1_maintenance/__init__.py +1 -0
  41. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/functions.py +3 -3
  42. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/router.py +8 -8
  43. pyxecm_api/v1_otcs/__init__.py +1 -0
  44. {pyxecm/customizer/api → pyxecm_api}/v1_otcs/functions.py +7 -5
  45. {pyxecm/customizer/api → pyxecm_api}/v1_otcs/router.py +24 -13
  46. pyxecm_api/v1_payload/__init__.py +1 -0
  47. {pyxecm/customizer/api → pyxecm_api}/v1_payload/functions.py +10 -7
  48. {pyxecm/customizer/api → pyxecm_api}/v1_payload/router.py +11 -10
  49. {pyxecm/customizer → pyxecm_customizer}/__init__.py +8 -0
  50. {pyxecm/customizer → pyxecm_customizer}/__main__.py +15 -21
  51. {pyxecm/customizer → pyxecm_customizer}/browser_automation.py +414 -103
  52. {pyxecm/customizer → pyxecm_customizer}/customizer.py +178 -116
  53. {pyxecm/customizer → pyxecm_customizer}/guidewire.py +60 -20
  54. {pyxecm/customizer → pyxecm_customizer}/k8s.py +4 -4
  55. pyxecm_customizer/knowledge_graph.py +719 -0
  56. pyxecm_customizer/log.py +35 -0
  57. {pyxecm/customizer → pyxecm_customizer}/m365.py +41 -33
  58. {pyxecm/customizer → pyxecm_customizer}/payload.py +2359 -1991
  59. {pyxecm/customizer/api/common → pyxecm_customizer}/payload_list.py +57 -65
  60. {pyxecm/customizer → pyxecm_customizer}/salesforce.py +1 -1
  61. {pyxecm/customizer → pyxecm_customizer}/sap.py +6 -2
  62. {pyxecm/customizer → pyxecm_customizer}/servicenow.py +2 -4
  63. {pyxecm/customizer → pyxecm_customizer}/settings.py +7 -6
  64. {pyxecm/customizer → pyxecm_customizer}/successfactors.py +40 -28
  65. {pyxecm/customizer → pyxecm_customizer}/translate.py +14 -10
  66. {pyxecm/maintenance_page → pyxecm_maintenance_page}/__main__.py +1 -1
  67. {pyxecm/maintenance_page → pyxecm_maintenance_page}/app.py +16 -6
  68. pyxecm/customizer/api/app.py +0 -163
  69. pyxecm/customizer/log.py +0 -107
  70. pyxecm/customizer/nhc.py +0 -1169
  71. pyxecm/customizer/openapi.py +0 -258
  72. pyxecm/customizer/pht.py +0 -1357
  73. pyxecm-2.0.3.dist-info/METADATA +0 -119
  74. pyxecm-2.0.3.dist-info/RECORD +0 -78
  75. pyxecm-2.0.3.dist-info/licenses/LICENSE +0 -202
  76. pyxecm-2.0.3.dist-info/top_level.txt +0 -1
  77. {pyxecm/customizer/api → pyxecm_api}/__init__.py +0 -0
  78. {pyxecm/customizer/api/auth → pyxecm_api/agents/otcm_knowledgegraph}/__init__.py +0 -0
  79. {pyxecm/customizer/api/common → pyxecm_api/agents/otcm_user_agent}/__init__.py +0 -0
  80. {pyxecm/customizer/api/v1_csai → pyxecm_api/agents/otcm_workspace_agent}/__init__.py +0 -0
  81. {pyxecm/customizer/api/v1_maintenance → pyxecm_api/auth}/__init__.py +0 -0
  82. {pyxecm/customizer/api → pyxecm_api}/auth/models.py +0 -0
  83. {pyxecm/customizer/api/v1_otcs → pyxecm_api/common}/__init__.py +0 -0
  84. {pyxecm/customizer/api → pyxecm_api}/common/models.py +0 -0
  85. {pyxecm/customizer/api → pyxecm_api}/terminal/__init__.py +0 -0
  86. {pyxecm/customizer/api/v1_payload → pyxecm_api/v1_csai}/__init__.py +0 -0
  87. {pyxecm/customizer/api → pyxecm_api}/v1_csai/models.py +0 -0
  88. {pyxecm/customizer/api → pyxecm_api}/v1_maintenance/models.py +0 -0
  89. {pyxecm/customizer/api → pyxecm_api}/v1_payload/models.py +0 -0
  90. {pyxecm/customizer → pyxecm_customizer}/exceptions.py +0 -0
  91. {pyxecm/maintenance_page → pyxecm_maintenance_page}/__init__.py +0 -0
  92. {pyxecm/maintenance_page → pyxecm_maintenance_page}/settings.py +0 -0
  93. {pyxecm/maintenance_page → pyxecm_maintenance_page}/static/favicon.avif +0 -0
  94. {pyxecm/maintenance_page → pyxecm_maintenance_page}/templates/maintenance.html +0 -0
@@ -8,28 +8,29 @@ import tempfile
8
8
  from http import HTTPStatus
9
9
  from typing import Annotated
10
10
 
11
- from fastapi import APIRouter, Depends, HTTPException, Query, Response
11
+ from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
12
12
  from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
13
13
 
14
- from pyxecm.customizer.api.auth.functions import get_authorized_user
15
- from pyxecm.customizer.api.auth.models import User
16
- from pyxecm.customizer.api.common.functions import PAYLOAD_LIST, list_files_in_directory
17
- from pyxecm.customizer.api.common.models import CustomizerStatus
14
+ from pyxecm_api.auth.functions import get_authorized_user
15
+ from pyxecm_api.auth.models import User
18
16
 
19
- router = APIRouter(tags=["default"])
17
+ from .functions import PAYLOAD_LIST, list_files_in_directory
18
+ from .models import CustomizerStatus
20
19
 
21
- logger = logging.getLogger("pyxecm.customizer.api.common")
20
+ router = APIRouter()
21
+
22
+ logger = logging.getLogger("pyxecm_api.common")
22
23
 
23
24
 
24
25
  @router.get("/", include_in_schema=False)
25
- async def redirect_to_api() -> RedirectResponse:
26
+ async def redirect_to_api(request: Request) -> RedirectResponse:
26
27
  """Redirect from / to /api.
27
28
 
28
29
  Returns:
29
30
  None
30
31
 
31
32
  """
32
- return RedirectResponse(url="/api")
33
+ return RedirectResponse(url=f"{request.url.path}api")
33
34
 
34
35
 
35
36
  @router.get(path="/status", name="Get Status")
@@ -74,7 +75,7 @@ def shutdown(user: Annotated[User, Depends(get_authorized_user)]) -> JSONRespons
74
75
  return JSONResponse({"status": "shutdown"}, status_code=HTTPStatus.ACCEPTED)
75
76
 
76
77
 
77
- @router.get(path="/browser_automations/assets")
78
+ @router.get(path="/browser_automations/assets", tags=["payload"])
78
79
  def list_browser_automation_files(
79
80
  user: Annotated[User, Depends(get_authorized_user)], # noqa: ARG001
80
81
  ) -> JSONResponse:
@@ -90,7 +91,7 @@ def list_browser_automation_files(
90
91
  return JSONResponse(result)
91
92
 
92
93
 
93
- @router.get(path="/browser_automations/download")
94
+ @router.get(path="/browser_automations/download", tags=["payload"])
94
95
  def get_browser_automation_file(
95
96
  user: Annotated[User, Depends(get_authorized_user)], # noqa: ARG001
96
97
  file: Annotated[str, Query(description="File name")],
@@ -16,28 +16,38 @@ from pydantic_settings import (
16
16
  class CustomizerAPISettings(BaseSettings):
17
17
  """Settings for the Customizer API."""
18
18
 
19
+ title: str = Field(default="Customizer API", description="Name of the API Service")
20
+ description: str = Field(
21
+ default="API provided by [pyxecm](https://github.com/opentext/pyxecm). The documentation for the payload syntax can be found [here](https://opentext.github.io/pyxecm/payload-syntax/).",
22
+ description="Descriptive text on the SwaggerUI page.",
23
+ )
24
+
19
25
  api_key: str | None = Field(
20
26
  default=None,
21
27
  description="Optional API KEY that can be specified that has access to the Customizer API, bypassing the OTDS authentication.",
22
28
  )
23
29
  bind_address: str = Field(default="0.0.0.0", description="Interface to bind the Customizer API.") # noqa: S104
24
30
  bind_port: int = Field(default=8000, description="Port to bind the Customizer API to")
25
- workers: int = Field(default=1, description="Number of workers to use for the API BackgroundTasks")
26
- root_path: str = Field(default="/", description="Root path for the Customizer API")
31
+ workers: int = Field(default=1, description="Number of workers to use for the API background tasks")
32
+ root_path: str = Field(default="", description="Root path for the Customizer API")
27
33
  openapi_url: str = Field(default="/api/openapi.json", description="OpenAPI URL")
34
+ reload: bool = Field(default=False, description="Enable or disable the autoreload feature")
28
35
 
36
+ concurrent_payloads: int = Field(
37
+ default=3, description="Maximum number of concurrent payloads that are executed at the same time."
38
+ )
29
39
  import_payload: bool = Field(default=False)
30
40
  payload: str = Field(
31
41
  default="/payload/payload.yml.gz.b64",
32
- description="Path to a single Payload file to be loaded.",
42
+ description="Path to a single payload file to be loaded.",
33
43
  )
34
44
  payload_dir: str = Field(
35
45
  default="/payload-external/",
36
- description="Path to a directory of Payload files. All files in this directory will be loaded in alphabetical order and dependencies will be added automatically on the previous object. So all payload in this folder will be processed sequentially in alphabetical oder.",
46
+ description="Path to a directory of payload files. All files in this directory will be loaded in alphabetical order and dependencies will be added automatically on the previous object. So all payload in this folder will be processed sequentially in alphabetical oder.",
37
47
  )
38
48
  payload_dir_optional: str = Field(
39
49
  default="/payload-optional/",
40
- description="Path of Payload files to be loaded. No additional logic for dependencies will be applied, they need to be managed within the payloadSetitings section of each payload. See -> payloadOptions in the Payload Syntax documentation.",
50
+ description="Path of payload files to be loaded. No additional logic for dependencies will be applied, they need to be managed within the payloadSettings section of each payload. See -> payloadOptions in the Payload Syntax documentation.",
41
51
  )
42
52
 
43
53
  temp_dir: str = Field(
@@ -46,6 +56,9 @@ class CustomizerAPISettings(BaseSettings):
46
56
  )
47
57
 
48
58
  loglevel: Literal["INFO", "DEBUG", "WARNING", "ERROR"] = "INFO"
59
+ log_payload_processing: bool = Field(
60
+ default=False, description="Print the customizer payload processing log messages to stdout"
61
+ )
49
62
  logfolder: str = Field(
50
63
  default=os.path.join(tempfile.gettempdir(), "customizer"),
51
64
  description="Logfolder for Customizer logfiles",
@@ -59,6 +72,12 @@ class CustomizerAPISettings(BaseSettings):
59
72
  default="default",
60
73
  description="Namespace to use for otxecm resource lookups",
61
74
  )
75
+
76
+ maintenance_page: bool = Field(
77
+ default=True,
78
+ description="Start the Maintenance Page server.",
79
+ )
80
+
62
81
  maintenance_mode: bool = Field(
63
82
  default=False,
64
83
  description="Automatically enable and disable the maintenance mode during payload deployments.",
@@ -94,7 +113,7 @@ class CustomizerAPISettings(BaseSettings):
94
113
  )
95
114
 
96
115
  csai: bool = Field(
97
- default=True,
116
+ default=False,
98
117
  description="Enable the CSAI integration",
99
118
  )
100
119
 
@@ -103,6 +122,11 @@ class CustomizerAPISettings(BaseSettings):
103
122
  description="Prefix for the CSAI",
104
123
  )
105
124
 
125
+ csai_studio_integration: bool = Field(
126
+ default=False,
127
+ description="Enable the CSAI Studio Integration",
128
+ )
129
+
106
130
  upload_folder: str = Field(default=os.path.join(tempfile.gettempdir(), "upload"), description="Folder for uploads")
107
131
 
108
132
  upload_key: str = Field(default=str(uuid.uuid4()), description="Upload key for the Logs")
@@ -10,7 +10,7 @@ import subprocess
10
10
 
11
11
  from fastapi import APIRouter, HTTPException, Query, WebSocket, WebSocketDisconnect, status
12
12
 
13
- from pyxecm.customizer.api.auth.functions import get_authorized_user, get_current_user
13
+ from pyxecm_api.auth.functions import get_authorized_user, get_current_user
14
14
 
15
15
  router = APIRouter(tags=["terminal"])
16
16
 
@@ -4,20 +4,22 @@ import logging
4
4
  from http import HTTPStatus
5
5
  from typing import Annotated
6
6
 
7
- from fastapi import APIRouter, Body, Depends
8
- from fastapi.responses import JSONResponse
9
-
10
- from pyxecm.customizer.api.auth.functions import get_authorized_user
11
- from pyxecm.customizer.api.auth.models import User
12
- from pyxecm.customizer.api.common.functions import get_k8s_object, get_otcs_object, get_settings
13
- from pyxecm.customizer.api.settings import CustomizerAPISettings
14
- from pyxecm.customizer.api.v1_csai.models import CSAIEmbedMetadata
15
- from pyxecm.customizer.k8s import K8s
7
+ from fastapi import APIRouter, Body, Depends, Query
8
+ from fastapi.responses import HTMLResponse, JSONResponse
16
9
  from pyxecm.otcs import OTCS
10
+ from pyxecm_customizer.k8s import K8s
11
+
12
+ from pyxecm_api.agents.functions import get_otca_object
13
+ from pyxecm_api.auth.functions import get_authorized_user
14
+ from pyxecm_api.auth.models import User
15
+ from pyxecm_api.common.functions import get_k8s_object, get_otcs_object, get_settings
16
+ from pyxecm_api.settings import CustomizerAPISettings
17
+
18
+ from .models import CSAIEmbedMetadata
17
19
 
18
20
  router = APIRouter(prefix="/api/v1/csai", tags=["csai"])
19
21
 
20
- logger = logging.getLogger("pyxecm.customizer.api.v1_csai")
22
+ logger = logging.getLogger("pyxecm_api.v1_csai")
21
23
 
22
24
 
23
25
  @router.post("/metadata")
@@ -110,3 +112,30 @@ def set_csai_config_data(
110
112
  k8s_object.restart_deployment(deployment)
111
113
 
112
114
  return get_csai_config_data(user=user, k8s_object=k8s_object, settings=settings)
115
+
116
+
117
+ @router.get("/graph")
118
+ def get_csai_graph(name: Annotated[str, Query(..., description="Name of the Graph")]) -> HTMLResponse:
119
+ """Display the graph of the given name.
120
+
121
+ Args:
122
+ otca (Annotated[OTCA, Depends): Generic OTCA Objec
123
+ name (str): name of the graph
124
+
125
+ Returns:
126
+ HTMLResponse: _description_
127
+
128
+ """
129
+ otca = get_otca_object(otcs_object=None)
130
+
131
+ graphs = otca.get_graphs()
132
+ graph = [g for g in graphs if g["name"] == name]
133
+
134
+ if graph:
135
+ filename = otca.visualize_graph(graph[0]["id"])
136
+
137
+ with open(filename) as f:
138
+ file_content = f.read()
139
+
140
+ logger.info("name: %s", name)
141
+ return HTMLResponse(status_code=200, content=file_content)
@@ -0,0 +1,189 @@
1
+ function neighbourhoodHighlight(params) {
2
+ // console.log("in nieghbourhoodhighlight");
3
+ allNodes = nodes.get({ returnType: "Object" });
4
+ // originalNodes = JSON.parse(JSON.stringify(allNodes));
5
+ // if something is selected:
6
+ if (params.nodes.length > 0) {
7
+ highlightActive = true;
8
+ var i, j;
9
+ var selectedNode = params.nodes[0];
10
+ var degrees = 2;
11
+
12
+ // mark all nodes as hard to read.
13
+ for (let nodeId in allNodes) {
14
+ // nodeColors[nodeId] = allNodes[nodeId].color;
15
+ allNodes[nodeId].color = "rgba(200,200,200,0.5)";
16
+ if (allNodes[nodeId].hiddenLabel === undefined) {
17
+ allNodes[nodeId].hiddenLabel = allNodes[nodeId].label;
18
+ allNodes[nodeId].label = undefined;
19
+ }
20
+ }
21
+ var connectedNodes = network.getConnectedNodes(selectedNode);
22
+ var allConnectedNodes = [];
23
+
24
+ // get the second degree nodes
25
+ for (i = 1; i < degrees; i++) {
26
+ for (j = 0; j < connectedNodes.length; j++) {
27
+ allConnectedNodes = allConnectedNodes.concat(
28
+ network.getConnectedNodes(connectedNodes[j])
29
+ );
30
+ }
31
+ }
32
+
33
+ // all second degree nodes get a different color and their label back
34
+ for (i = 0; i < allConnectedNodes.length; i++) {
35
+ // allNodes[allConnectedNodes[i]].color = "pink";
36
+ allNodes[allConnectedNodes[i]].color = "rgba(150,150,150,0.75)";
37
+ if (allNodes[allConnectedNodes[i]].hiddenLabel !== undefined) {
38
+ allNodes[allConnectedNodes[i]].label =
39
+ allNodes[allConnectedNodes[i]].hiddenLabel;
40
+ allNodes[allConnectedNodes[i]].hiddenLabel = undefined;
41
+ }
42
+ }
43
+
44
+ // all first degree nodes get their own color and their label back
45
+ for (i = 0; i < connectedNodes.length; i++) {
46
+ // allNodes[connectedNodes[i]].color = undefined;
47
+ allNodes[connectedNodes[i]].color = nodeColors[connectedNodes[i]];
48
+ if (allNodes[connectedNodes[i]].hiddenLabel !== undefined) {
49
+ allNodes[connectedNodes[i]].label =
50
+ allNodes[connectedNodes[i]].hiddenLabel;
51
+ allNodes[connectedNodes[i]].hiddenLabel = undefined;
52
+ }
53
+ }
54
+
55
+ // the main node gets its own color and its label back.
56
+ // allNodes[selectedNode].color = undefined;
57
+ allNodes[selectedNode].color = nodeColors[selectedNode];
58
+ if (allNodes[selectedNode].hiddenLabel !== undefined) {
59
+ allNodes[selectedNode].label = allNodes[selectedNode].hiddenLabel;
60
+ allNodes[selectedNode].hiddenLabel = undefined;
61
+ }
62
+ } else if (highlightActive === true) {
63
+ // console.log("highlightActive was true");
64
+ // reset all nodes
65
+ for (let nodeId in allNodes) {
66
+ // allNodes[nodeId].color = "purple";
67
+ allNodes[nodeId].color = nodeColors[nodeId];
68
+ // delete allNodes[nodeId].color;
69
+ if (allNodes[nodeId].hiddenLabel !== undefined) {
70
+ allNodes[nodeId].label = allNodes[nodeId].hiddenLabel;
71
+ allNodes[nodeId].hiddenLabel = undefined;
72
+ }
73
+ }
74
+ highlightActive = false;
75
+ }
76
+
77
+ // transform the object into an array
78
+ var updateArray = [];
79
+ if (params.nodes.length > 0) {
80
+ for (let nodeId in allNodes) {
81
+ if (allNodes.hasOwnProperty(nodeId)) {
82
+ // console.log(allNodes[nodeId]);
83
+ updateArray.push(allNodes[nodeId]);
84
+ }
85
+ }
86
+ nodes.update(updateArray);
87
+ } else {
88
+ // console.log("Nothing was selected");
89
+ for (let nodeId in allNodes) {
90
+ if (allNodes.hasOwnProperty(nodeId)) {
91
+ // console.log(allNodes[nodeId]);
92
+ // allNodes[nodeId].color = {};
93
+ updateArray.push(allNodes[nodeId]);
94
+ }
95
+ }
96
+ nodes.update(updateArray);
97
+ }
98
+ }
99
+
100
+ function filterHighlight(params) {
101
+ allNodes = nodes.get({ returnType: "Object" });
102
+ // if something is selected:
103
+ if (params.nodes.length > 0) {
104
+ filterActive = true;
105
+ let selectedNodes = params.nodes;
106
+
107
+ // hiding all nodes and saving the label
108
+ for (let nodeId in allNodes) {
109
+ allNodes[nodeId].hidden = true;
110
+ if (allNodes[nodeId].savedLabel === undefined) {
111
+ allNodes[nodeId].savedLabel = allNodes[nodeId].label;
112
+ allNodes[nodeId].label = undefined;
113
+ }
114
+ }
115
+
116
+ for (let i=0; i < selectedNodes.length; i++) {
117
+ allNodes[selectedNodes[i]].hidden = false;
118
+ if (allNodes[selectedNodes[i]].savedLabel !== undefined) {
119
+ allNodes[selectedNodes[i]].label = allNodes[selectedNodes[i]].savedLabel;
120
+ allNodes[selectedNodes[i]].savedLabel = undefined;
121
+ }
122
+ }
123
+
124
+ } else if (filterActive === true) {
125
+ // reset all nodes
126
+ for (let nodeId in allNodes) {
127
+ allNodes[nodeId].hidden = false;
128
+ if (allNodes[nodeId].savedLabel !== undefined) {
129
+ allNodes[nodeId].label = allNodes[nodeId].savedLabel;
130
+ allNodes[nodeId].savedLabel = undefined;
131
+ }
132
+ }
133
+ filterActive = false;
134
+ }
135
+
136
+ // transform the object into an array
137
+ var updateArray = [];
138
+ if (params.nodes.length > 0) {
139
+ for (let nodeId in allNodes) {
140
+ if (allNodes.hasOwnProperty(nodeId)) {
141
+ updateArray.push(allNodes[nodeId]);
142
+ }
143
+ }
144
+ nodes.update(updateArray);
145
+ } else {
146
+ for (let nodeId in allNodes) {
147
+ if (allNodes.hasOwnProperty(nodeId)) {
148
+ updateArray.push(allNodes[nodeId]);
149
+ }
150
+ }
151
+ nodes.update(updateArray);
152
+ }
153
+ }
154
+
155
+ function selectNode(nodes) {
156
+ network.selectNodes(nodes);
157
+ neighbourhoodHighlight({ nodes: nodes });
158
+ return nodes;
159
+ }
160
+
161
+ function selectNodes(nodes) {
162
+ network.selectNodes(nodes);
163
+ filterHighlight({nodes: nodes});
164
+ return nodes;
165
+ }
166
+
167
+ function highlightFilter(filter) {
168
+ let selectedNodes = []
169
+ let selectedProp = filter['property']
170
+ if (filter['item'] === 'node') {
171
+ let allNodes = nodes.get({ returnType: "Object" });
172
+ for (let nodeId in allNodes) {
173
+ if (allNodes[nodeId][selectedProp] && filter['value'].includes((allNodes[nodeId][selectedProp]).toString())) {
174
+ selectedNodes.push(nodeId)
175
+ }
176
+ }
177
+ }
178
+ else if (filter['item'] === 'edge'){
179
+ let allEdges = edges.get({returnType: 'object'});
180
+ // check if the selected property exists for selected edge and select the nodes connected to the edge
181
+ for (let edge in allEdges) {
182
+ if (allEdges[edge][selectedProp] && filter['value'].includes((allEdges[edge][selectedProp]).toString())) {
183
+ selectedNodes.push(allEdges[edge]['from'])
184
+ selectedNodes.push(allEdges[edge]['to'])
185
+ }
186
+ }
187
+ }
188
+ selectNodes(selectedNodes)
189
+ }