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.
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: tagreader
3
- Version: 6.0.1
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.7.4,<2025.0.0)
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.0,<4.0.0) ; extra == "notebooks"
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.0,<8.0.0) ; extra == "notebooks"
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.1"
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.7.4"
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.0", optional = true }
38
- matplotlib = { version = "^3.7.0", optional = true }
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
- if self.cache is not None:
446
- r = self.cache.get_metadata(key=tag, properties="unit")
447
- if r is not None and "unit" in r:
448
- units[tag] = r["unit"]
449
- if tag not in units:
450
- unit = self.handler._get_tag_unit(tag)
451
- if self.cache is not None and unit is not None:
452
- self.cache.put_metadata(key=tag, value={"unit": unit})
453
- units[tag] = unit
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
- if self.cache is not None:
462
- r = self.cache.get_metadata(key=tag, properties="description")
463
- if r is not None and "description" in r:
464
- descriptions[tag] = r["description"]
465
- if tag not in descriptions:
466
- desc = self.handler._get_tag_description(tag)
467
- if self.cache is not None and desc is not None:
468
- self.cache.put_metadata(key=tag, value={"description": desc})
469
- descriptions[tag] = desc
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
- description = self._get_tag_description(tagname)
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
- attr_data = data["data"]["tags"][0]["attrData"]
528
- except KeyError as e:
529
- logger.error(f"Error. I got this: {data}")
530
- raise e
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
- desc = ""
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
- res_text = self.fetch_text(url, params=params)
707
- # For now just return result as text regardless of value of parse
707
+
708
+ res = self.session.get(url, params=params)
709
+ res.raise_for_status()
710
+
708
711
  if parse:
709
- raise NotImplementedError(
710
- "Use parse=False to receive and handle text result instead"
711
- )
712
- return res_text
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