Skip to content

Commit c8352c7

Browse files
committed
init add Complex Tag functionality
1 parent f74e434 commit c8352c7

File tree

3 files changed

+191
-1
lines changed

3 files changed

+191
-1
lines changed

kepconfig/adv_tags/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
specific objects within the Kepware Configuration API
99
"""
1010

11-
from . import adv_tag_group, average_tags, derived_tags
11+
from . import adv_tag_group, average_tags, derived_tags, complex_tags
1212
ADV_TAGS_ROOT = '/project/_advancedtags'
1313

1414
def adv_tag_path_split(path: str, *, isItem=False) -> dict:

kepconfig/adv_tags/complex_tags.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) PTC Inc. and/or all its affiliates. All rights reserved.
3+
# See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
r"""`complex_tags` exposes an API to allow modifications (add, delete, modify) to
8+
complex tag objects within the Kepware Configuration API
9+
"""
10+
11+
from ..connection import server
12+
from ..error import KepError, KepHTTPError
13+
from ..utils import _url_parse_object
14+
from typing import Union
15+
from .. import adv_tags
16+
17+
COMPLEX_TAGS_ROOT = '/complex_tags'
18+
19+
def _get_complex_tags_url(tag: str = None) -> str:
20+
'''Creates url object for the "complex_tags" branch of Kepware's project tree.
21+
22+
Returns the complex tag specific url when a value is passed as the tag name.
23+
'''
24+
if tag is None:
25+
return COMPLEX_TAGS_ROOT
26+
else:
27+
return f'{COMPLEX_TAGS_ROOT}/{_url_parse_object(tag)}'
28+
29+
def add_complex_tag(server: server, adv_tag_group_path: str, DATA: Union[dict, list]) -> Union[bool, list]:
30+
'''Add `"complex_tag"` or multiple `"complex_tag"` objects to a specific path in Kepware.
31+
Can be used to pass a list of complex tags to be added at one path location.
32+
33+
:param server: instance of the `server` class
34+
:param adv_tag_group_path: path identifying where to add complex tag(s). Standard Kepware address decimal
35+
notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
36+
:param DATA: Dict or List of Dicts of the complex tag(s) to add
37+
38+
:return: True - If a "HTTP 201 - Created" is received from Kepware server
39+
:return: If a "HTTP 207 - Multi-Status" is received from Kepware with a list of dict error responses for all
40+
complex tags added that failed.
41+
42+
:raises KepHTTPError: If urllib provides an HTTPError
43+
:raises KepURLError: If urllib provides an URLError
44+
'''
45+
path_obj = adv_tags.adv_tag_path_split(adv_tag_group_path, isItem=False)
46+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url()
47+
48+
r = server._config_add(url, DATA)
49+
if r.code == 201:
50+
return True
51+
elif r.code == 207:
52+
errors = [item for item in r.payload if item['code'] != 201]
53+
return errors
54+
else:
55+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
56+
57+
def modify_complex_tag(server: server, complex_tag_path: str, DATA: dict, force: bool = False) -> bool:
58+
'''Modify a `"complex_tag"` object and its properties in Kepware.
59+
60+
:param server: instance of the `server` class
61+
:param complex_tag_path: path identifying location and complex tag to modify. Standard Kepware address decimal
62+
notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
63+
:param DATA: Dict of the `complex_tag` properties to be modified
64+
:param force: *(optional)* if True, will force the configuration update to the Kepware server
65+
66+
:return: True - If a "HTTP 200 - OK" is received from Kepware server
67+
68+
:raises KepHTTPError: If urllib provides an HTTPError
69+
:raises KepURLError: If urllib provides an URLError
70+
'''
71+
complex_tag_data = server._force_update_check(force, DATA)
72+
path_obj = adv_tags.adv_tag_path_split(complex_tag_path, isItem=True)
73+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
74+
75+
r = server._config_update(url, complex_tag_data)
76+
if r.code == 200:
77+
return True
78+
else:
79+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
80+
81+
def del_complex_tag(server: server, complex_tag_path: str) -> bool:
82+
'''Delete `"complex_tag"` object at a specific path in Kepware.
83+
84+
:param server: instance of the `server` class
85+
:param complex_tag_path: path identifying location and complex tag to delete. Standard Kepware address decimal
86+
notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
87+
88+
:return: True - If a "HTTP 200 - OK" is received from Kepware server
89+
90+
:raises KepHTTPError: If urllib provides an HTTPError
91+
:raises KepURLError: If urllib provides an URLError
92+
'''
93+
path_obj = adv_tags.adv_tag_path_split(complex_tag_path, isItem=True)
94+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
95+
96+
r = server._config_del(url)
97+
if r.code == 200:
98+
return True
99+
else:
100+
raise KepHTTPError(r.url, r.code, r.msg, r.hdrs, r.payload)
101+
102+
def get_complex_tag(server: server, complex_tag_path: str) -> dict:
103+
'''Returns the properties of the `"complex_tag"` object at a specific path in Kepware.
104+
105+
:param server: instance of the `server` class
106+
:param complex_tag_path: path identifying location and complex tag to retrieve. Standard Kepware address decimal
107+
notation string including the complex tag such as "_advancedtags.AdvTagGroup1.ComplexTag1"
108+
109+
:return: Dict of data for the complex tag requested
110+
111+
:raises KepHTTPError: If urllib provides an HTTPError
112+
:raises KepURLError: If urllib provides an URLError
113+
'''
114+
path_obj = adv_tags.adv_tag_path_split(complex_tag_path, isItem=True)
115+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url(path_obj['item'])
116+
117+
r = server._config_get(url)
118+
return r.payload
119+
120+
def get_all_complex_tags(server: server, adv_tag_group_path: str, *, options: dict = None) -> list:
121+
'''Returns the properties of all `"complex_tag"` objects at a specific path in Kepware.
122+
123+
:param server: instance of the `server` class
124+
:param adv_tag_group_path: path identifying location to retrieve complex tag list. Standard Kepware address decimal
125+
notation string such as "_advancedtags.AdvTagGroup1" or "_advancedtags.AdvTagGroup1.AdvTagGroupChild"
126+
:param options: *(optional)* Dict of parameters to filter, sort or paginate the list of complex tags. Options are `filter`,
127+
`sortOrder`, `sortProperty`, `pageNumber`, and `pageSize`
128+
129+
:return: List of data for all complex tags
130+
131+
:raises KepHTTPError: If urllib provides an HTTPError
132+
:raises KepURLError: If urllib provides an URLError
133+
'''
134+
path_obj = adv_tags.adv_tag_path_split(adv_tag_group_path, isItem=False)
135+
url = adv_tags._create_adv_tags_base_url(server.url, path_obj) + _get_complex_tags_url()
136+
137+
r = server._config_get(url, params=options)
138+
return r.payload

tests/adv_tags_test.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@
4242
}
4343
]
4444

45+
complex_tag_name = 'ComplexTag1'
46+
complex_tag_data = [
47+
{
48+
"common.ALLTYPES_NAME": complex_tag_name,
49+
"common.ALLTYPES_DESCRIPTION": "",
50+
"advanced_tags.ENABLED": True,
51+
}
52+
]
53+
4554
def HTTPErrorHandler(err):
4655
if err.__class__ is error.KepHTTPError:
4756
print(err.code)
@@ -73,6 +82,10 @@ def complete(server: connection.server):
7382
for obj in obj_list:
7483
derived_tag_path = f'_advancedtags.{obj["name"]}'
7584
adv_tags.derived_tags.del_derived_tag(server, derived_tag_path)
85+
elif key == 'complex_tags':
86+
for obj in obj_list:
87+
complex_tag_path = f'_advancedtags.{obj["name"]}'
88+
adv_tags.complex_tags.del_complex_tag(server, complex_tag_path)
7689
pass
7790

7891
@pytest.fixture(scope="module")
@@ -205,6 +218,45 @@ def test_derived_tag_del(server):
205218
derived_tag_path = f'_advancedtags.{adv_tag_group_name}.{derived_tag_name}'
206219
assert adv_tags.derived_tags.del_derived_tag(server, derived_tag_path)
207220

221+
def test_complex_tag_add(server):
222+
# Add a complex tag to the root advanced tag plug-in
223+
assert adv_tags.complex_tags.add_complex_tag(server, f'_advancedtags', complex_tag_data)
224+
225+
testTag = {
226+
"common.ALLTYPES_NAME": "newComplexTag",
227+
"common.ALLTYPES_DESCRIPTION": "",
228+
"advanced_tags.ENABLED": True,
229+
}
230+
complex_tag_data.append(testTag)
231+
# Add a complex tag to the advanced tag group
232+
assert adv_tags.complex_tags.add_complex_tag(server, f'_advancedtags.{adv_tag_group_name}', complex_tag_data)
233+
234+
def test_complex_tag_get(server):
235+
# Get the complex tag
236+
complex_tag_path = f'_advancedtags.{adv_tag_group_name}.{complex_tag_name}'
237+
result = adv_tags.complex_tags.get_complex_tag(server, complex_tag_path)
238+
assert type(result) == dict
239+
assert result.get("common.ALLTYPES_NAME") == complex_tag_name
240+
241+
def test_complex_tag_modify(server):
242+
# Modify the complex tag
243+
complex_tag_path = f'_advancedtags.{adv_tag_group_name}.{complex_tag_name}'
244+
tag_data = {
245+
"common.ALLTYPES_DESCRIPTION": "Modified complex tag"
246+
}
247+
assert adv_tags.complex_tags.modify_complex_tag(server, complex_tag_path, tag_data, force=True)
248+
249+
def test_complex_tag_get_all(server):
250+
# Get all complex tags under the group
251+
result = adv_tags.complex_tags.get_all_complex_tags(server, f'_advancedtags.{adv_tag_group_name}')
252+
assert type(result) == list
253+
assert any(tag.get("common.ALLTYPES_NAME") == complex_tag_name for tag in result)
254+
255+
def test_complex_tag_del(server):
256+
# Delete the complex tag
257+
complex_tag_path = f'_advancedtags.{adv_tag_group_name}.{complex_tag_name}'
258+
assert adv_tags.complex_tags.del_complex_tag(server, complex_tag_path)
259+
208260

209261
def test_adv_tag_group_del(server):
210262
# Delete parent advanced tag group

0 commit comments

Comments
 (0)