tagreader 6.0.1__tar.gz → 6.0.2__tar.gz
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.
- {tagreader-6.0.1 → tagreader-6.0.2}/PKG-INFO +9 -8
- {tagreader-6.0.1 → tagreader-6.0.2}/README.md +2 -2
- {tagreader-6.0.1 → tagreader-6.0.2}/pyproject.toml +4 -4
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/clients.py +28 -18
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/web_handlers.py +40 -25
- {tagreader-6.0.1 → tagreader-6.0.2}/LICENSE +0 -0
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/__init__.py +0 -0
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/__version__.py +0 -0
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/cache.py +0 -0
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/logger.py +0 -0
- {tagreader-6.0.1 → tagreader-6.0.2}/tagreader/utils.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: tagreader
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.2
|
|
4
4
|
Summary: Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems.
|
|
5
|
-
Home-page: https://github.com/equinor/tagreader-python
|
|
6
5
|
License: MIT
|
|
7
6
|
Keywords: Aspen InfoPlus.21,OSIsoft PI
|
|
8
7
|
Author: Einar S. Idsø
|
|
@@ -22,20 +21,22 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
25
|
Classifier: Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
|
|
26
26
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
27
|
Classifier: Topic :: Utilities
|
|
28
28
|
Provides-Extra: notebooks
|
|
29
|
-
Requires-Dist: certifi (>=2024.
|
|
29
|
+
Requires-Dist: certifi (>=2024.12.14,<2025.0.0)
|
|
30
30
|
Requires-Dist: diskcache (>=5.6.1,<6.0.0)
|
|
31
|
-
Requires-Dist: matplotlib (>=3.7.
|
|
31
|
+
Requires-Dist: matplotlib (>=3.7.5,<4.0.0) ; extra == "notebooks"
|
|
32
32
|
Requires-Dist: msal-bearer (>=0.2.1,<1.2.0)
|
|
33
|
-
Requires-Dist: notebook (>=7.2.
|
|
33
|
+
Requires-Dist: notebook (>=7.2.2,<8.0.0) ; extra == "notebooks"
|
|
34
34
|
Requires-Dist: pandas (>=1)
|
|
35
35
|
Requires-Dist: pycryptodome (>=3.20.0,<4.0.0)
|
|
36
36
|
Requires-Dist: requests (>=2,<3)
|
|
37
37
|
Requires-Dist: requests-kerberos (>=0,<1)
|
|
38
38
|
Requires-Dist: requests-ntlm (>=1.1,<=2.0)
|
|
39
|
+
Project-URL: Homepage, https://github.com/equinor/tagreader-python
|
|
39
40
|
Project-URL: Repository, https://github.com/equinor/tagreader-python
|
|
40
41
|
Description-Content-Type: text/markdown
|
|
41
42
|
|
|
@@ -54,7 +55,7 @@ as possible to the backend historians.
|
|
|
54
55
|
You can install tagreader directly into your project from pypi by using pip
|
|
55
56
|
or another package manager. The only requirement is Python version 3.8 or above.
|
|
56
57
|
|
|
57
|
-
```shell
|
|
58
|
+
```shell
|
|
58
59
|
pip install tagreader
|
|
59
60
|
```
|
|
60
61
|
|
|
@@ -67,7 +68,7 @@ The following are required and will be installed:
|
|
|
67
68
|
* diskcache
|
|
68
69
|
|
|
69
70
|
## Usage
|
|
70
|
-
Tagreader easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
71
|
+
Tagreader is easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
71
72
|
you simply need to provide the corresponding IMS service URLs and IMSType.
|
|
72
73
|
See [data source](https://equinor.github.io/tagreader-python/docs/about/usage/data-source) for details.
|
|
73
74
|
|
|
@@ -13,7 +13,7 @@ as possible to the backend historians.
|
|
|
13
13
|
You can install tagreader directly into your project from pypi by using pip
|
|
14
14
|
or another package manager. The only requirement is Python version 3.8 or above.
|
|
15
15
|
|
|
16
|
-
```shell
|
|
16
|
+
```shell
|
|
17
17
|
pip install tagreader
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -26,7 +26,7 @@ The following are required and will be installed:
|
|
|
26
26
|
* diskcache
|
|
27
27
|
|
|
28
28
|
## Usage
|
|
29
|
-
Tagreader easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
29
|
+
Tagreader is easy to use for both Equinor internal IMS services, and non-internal usage. For non-internal usage
|
|
30
30
|
you simply need to provide the corresponding IMS service URLs and IMSType.
|
|
31
31
|
See [data source](https://equinor.github.io/tagreader-python/docs/about/usage/data-source) for details.
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "tagreader"
|
|
3
|
-
version = "6.0.
|
|
3
|
+
version = "6.0.2"
|
|
4
4
|
description = "Tagreader is a Python package for reading trend data from the OSIsoft PI and Aspen Infoplus.21 IMS systems."
|
|
5
5
|
authors = ["Einar S. Idsø <eiids@equinor.com>", "Morten Dæhli Aslesen <masl@equinor.com"]
|
|
6
6
|
license = "MIT"
|
|
@@ -30,12 +30,12 @@ classifiers=[
|
|
|
30
30
|
[tool.poetry.dependencies]
|
|
31
31
|
python = "^3.8"
|
|
32
32
|
pandas = ">=1"
|
|
33
|
-
certifi = "^2024.
|
|
33
|
+
certifi = "^2024.12.14"
|
|
34
34
|
requests = "^2"
|
|
35
35
|
requests-kerberos = "^0"
|
|
36
36
|
msal-bearer = ">=0.2.1,<1.2.0"
|
|
37
|
-
notebook = { version = "^7.2.
|
|
38
|
-
matplotlib = { version = "^3.7.
|
|
37
|
+
notebook = { version = "^7.2.2", optional = true }
|
|
38
|
+
matplotlib = { version = "^3.7.5", optional = true }
|
|
39
39
|
diskcache = "^5.6.1"
|
|
40
40
|
pycryptodome = "^3.20.0"
|
|
41
41
|
requests-ntlm = ">=1.1,<=2.0"
|
|
@@ -442,15 +442,20 @@ class IMSClient:
|
|
|
442
442
|
tags = [tags]
|
|
443
443
|
units = {}
|
|
444
444
|
for tag in tags:
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
self.cache
|
|
453
|
-
|
|
445
|
+
try:
|
|
446
|
+
if self.cache is not None:
|
|
447
|
+
r = self.cache.get_metadata(key=tag, properties="unit")
|
|
448
|
+
if r is not None and "unit" in r:
|
|
449
|
+
units[tag] = r["unit"]
|
|
450
|
+
if tag not in units:
|
|
451
|
+
unit = self.handler._get_tag_unit(tag)
|
|
452
|
+
if self.cache is not None and unit is not None:
|
|
453
|
+
self.cache.put_metadata(key=tag, value={"unit": unit})
|
|
454
|
+
units[tag] = unit
|
|
455
|
+
except Exception:
|
|
456
|
+
if self.search(tag) == []: # check for nonexisting string
|
|
457
|
+
logger.warning(f"Tag not found: {tag}")
|
|
458
|
+
continue
|
|
454
459
|
return units
|
|
455
460
|
|
|
456
461
|
def get_descriptions(self, tags: Union[str, List[str]]) -> Dict[str, str]:
|
|
@@ -458,15 +463,20 @@ class IMSClient:
|
|
|
458
463
|
tags = [tags]
|
|
459
464
|
descriptions = {}
|
|
460
465
|
for tag in tags:
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
self.cache
|
|
469
|
-
|
|
466
|
+
try:
|
|
467
|
+
if self.cache is not None:
|
|
468
|
+
r = self.cache.get_metadata(key=tag, properties="description")
|
|
469
|
+
if r is not None and "description" in r:
|
|
470
|
+
descriptions[tag] = r["description"]
|
|
471
|
+
if tag not in descriptions:
|
|
472
|
+
desc = self.handler._get_tag_description(tag)
|
|
473
|
+
if self.cache is not None and desc is not None:
|
|
474
|
+
self.cache.put_metadata(key=tag, value={"description": desc})
|
|
475
|
+
descriptions[tag] = desc
|
|
476
|
+
except Exception:
|
|
477
|
+
if self.search(tag) == []: # check for nonexisting string
|
|
478
|
+
logger.warning(f"Tag not found: {tag}")
|
|
479
|
+
continue
|
|
470
480
|
return descriptions
|
|
471
481
|
|
|
472
482
|
def read_tags(
|
|
@@ -187,6 +187,7 @@ class BaseHandlerWeb(ABC):
|
|
|
187
187
|
self.datasource = datasource
|
|
188
188
|
self.base_url = url
|
|
189
189
|
self.session = requests.Session()
|
|
190
|
+
self.auth = auth
|
|
190
191
|
self.session.auth = auth if auth is not None else get_auth_aspen()
|
|
191
192
|
if verify_ssl is False:
|
|
192
193
|
urllib3.disable_warnings(InsecureRequestWarning)
|
|
@@ -222,14 +223,6 @@ class BaseHandlerWeb(ABC):
|
|
|
222
223
|
txt = res.text.replace('"v":nan', '"v":NaN').replace('"v":-nan', '"v":NaN')
|
|
223
224
|
return json.loads(txt)
|
|
224
225
|
|
|
225
|
-
def fetch_text(
|
|
226
|
-
self, url, params: Optional[Union[str, Dict[str, str]]] = None
|
|
227
|
-
) -> str:
|
|
228
|
-
res = self.session.get(url, params=params)
|
|
229
|
-
res.raise_for_status()
|
|
230
|
-
|
|
231
|
-
return res.text
|
|
232
|
-
|
|
233
226
|
def connect(self):
|
|
234
227
|
try:
|
|
235
228
|
self.verify_connection(self.datasource)
|
|
@@ -503,7 +496,10 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
503
496
|
if not desc and not return_desc:
|
|
504
497
|
ret.append(tagname)
|
|
505
498
|
else:
|
|
506
|
-
|
|
499
|
+
try:
|
|
500
|
+
description = self._get_tag_description(tagname)
|
|
501
|
+
except KeyError:
|
|
502
|
+
description = ""
|
|
507
503
|
ret.append((tagname, description))
|
|
508
504
|
|
|
509
505
|
if not desc:
|
|
@@ -523,11 +519,11 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
523
519
|
query = self.generate_get_unit_query(tag)
|
|
524
520
|
url = urljoin(self.base_url, "TagInfo")
|
|
525
521
|
data = self.fetch(url, params=query)
|
|
526
|
-
try:
|
|
527
|
-
|
|
528
|
-
except KeyError as e:
|
|
529
|
-
|
|
530
|
-
|
|
522
|
+
# try:
|
|
523
|
+
attr_data = data["data"]["tags"][0]["attrData"]
|
|
524
|
+
# except KeyError as e:
|
|
525
|
+
# logger.error(f"Error. I got this: {data}")
|
|
526
|
+
# raise e
|
|
531
527
|
unit = ""
|
|
532
528
|
for a in attr_data:
|
|
533
529
|
if a["g"] == "Units":
|
|
@@ -559,8 +555,8 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
559
555
|
try:
|
|
560
556
|
data = self.fetch(url, params=query)
|
|
561
557
|
desc = data["data"]["tags"][0]["attrData"][0]["samples"][0]["v"]
|
|
562
|
-
except KeyError:
|
|
563
|
-
|
|
558
|
+
# except KeyError:
|
|
559
|
+
# desc = ""
|
|
564
560
|
except JSONDecodeError:
|
|
565
561
|
desc = ""
|
|
566
562
|
return desc
|
|
@@ -664,6 +660,11 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
664
660
|
'dso="CHARINT=N;CHARFLOAT=N;CHARTIME=N;CONVERTERRORS=N" '
|
|
665
661
|
f'm="{max_rows}" to="30" s="1">'
|
|
666
662
|
)
|
|
663
|
+
query = query.replace("\t", " ").replace(
|
|
664
|
+
"\n", " "
|
|
665
|
+
) # Replace new lines and tabs that are typical in formatted SQL queries with spaces.
|
|
666
|
+
|
|
667
|
+
# Need a solution to LIKE comments. These have a % symbol in the query that does not seem to pass through the request
|
|
667
668
|
|
|
668
669
|
connection_string += f"<![CDATA[{query}]]></SQL>"
|
|
669
670
|
return connection_string
|
|
@@ -703,13 +704,26 @@ class AspenHandlerWeb(BaseHandlerWeb):
|
|
|
703
704
|
max_rows=self._max_rows,
|
|
704
705
|
datasource=None,
|
|
705
706
|
)
|
|
706
|
-
|
|
707
|
-
|
|
707
|
+
|
|
708
|
+
res = self.session.get(url, params=params)
|
|
709
|
+
res.raise_for_status()
|
|
710
|
+
|
|
708
711
|
if parse:
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
712
|
+
parsed_dict = res.json()["data"][0]
|
|
713
|
+
|
|
714
|
+
cols = []
|
|
715
|
+
for i in parsed_dict["cols"]:
|
|
716
|
+
cols.append(i["n"])
|
|
717
|
+
|
|
718
|
+
rows = []
|
|
719
|
+
|
|
720
|
+
for i in parsed_dict["rows"]:
|
|
721
|
+
element = []
|
|
722
|
+
for j in i["fld"]:
|
|
723
|
+
element.append(j["v"])
|
|
724
|
+
rows.append(element)
|
|
725
|
+
return pd.DataFrame(data=rows, columns=cols)
|
|
726
|
+
return res.text
|
|
713
727
|
|
|
714
728
|
|
|
715
729
|
class PIHandlerWeb(BaseHandlerWeb):
|
|
@@ -777,6 +791,7 @@ class PIHandlerWeb(BaseHandlerWeb):
|
|
|
777
791
|
tag: Optional[str],
|
|
778
792
|
desc: Optional[str],
|
|
779
793
|
datasource: Optional[str],
|
|
794
|
+
auth: Optional[Any] = None,
|
|
780
795
|
) -> Dict[str, str]:
|
|
781
796
|
q = []
|
|
782
797
|
if tag is not None:
|
|
@@ -788,7 +803,7 @@ class PIHandlerWeb(BaseHandlerWeb):
|
|
|
788
803
|
|
|
789
804
|
if datasource is not None:
|
|
790
805
|
params["dataserverwebid"] = (
|
|
791
|
-
f"{get_piwebapi_source_to_webid_dict()[datasource]}"
|
|
806
|
+
f"{get_piwebapi_source_to_webid_dict(auth=auth)[datasource]}"
|
|
792
807
|
)
|
|
793
808
|
|
|
794
809
|
return params
|
|
@@ -900,7 +915,7 @@ class PIHandlerWeb(BaseHandlerWeb):
|
|
|
900
915
|
return_desc: bool = True,
|
|
901
916
|
) -> Union[List[Tuple[str, str]], List[str]]:
|
|
902
917
|
params = self.generate_search_params(
|
|
903
|
-
tag=tag, desc=desc, datasource=self.datasource
|
|
918
|
+
tag=tag, desc=desc, datasource=self.datasource, auth=self.auth
|
|
904
919
|
)
|
|
905
920
|
url = urljoin(self.base_url, "points", "search")
|
|
906
921
|
done = False
|
|
@@ -956,7 +971,7 @@ class PIHandlerWeb(BaseHandlerWeb):
|
|
|
956
971
|
return self.web_id_cache[tag]
|
|
957
972
|
|
|
958
973
|
params = self.generate_search_params(
|
|
959
|
-
tag=tag, datasource=self.datasource, desc=None
|
|
974
|
+
tag=tag, datasource=self.datasource, desc=None, auth=self.auth
|
|
960
975
|
)
|
|
961
976
|
url = urljoin(self.base_url, "points", "search")
|
|
962
977
|
data = self.fetch(url, params=params)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|