stidapi 1.0.1__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.
stidapi-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.1
2
+ Name: stidapi
3
+ Version: 1.0.1
4
+ Summary: Python client for connecting to Equinor STIDapi
5
+ License: Proprietary
6
+ Author: Åsmund Våge Fannemel
7
+ Author-email: asmf@equinor.com
8
+ Requires-Python: >=3.9,<4.0
9
+ Classifier: License :: Other/Proprietary License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Dist: certifi (>=2023.11.17,<2024.0.0)
16
+ Requires-Dist: msal-bearer (>=0.2.1,<1.1.0)
17
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
18
+ Project-URL: repository, https://github.com/equinor/STIDapi-python
19
+ Description-Content-Type: text/markdown
20
+
21
+ # STIDapi-python
22
+
23
+ A simple wrapper package to interface Equinor [STIDapi](https://stidapi.equinor.com/).
24
+ Used to get plant, systen and tag and (future) doc data from STID rest api.
25
+
26
+
27
+ ## Use
28
+
29
+ Try it out by running the [demo](examples/demo.py).
30
+
31
+ ## Installing
32
+
33
+ To **install** call the following from your project environment:
34
+ `pip install git+https://github.com/equinor/STIDapi-python.git`
35
+
36
+ To **upgrade** call the following from your project environment:
37
+ `pip install --upgrade git+https://github.com/equinor/STIDapi-python.git`
38
+
39
+ To include in your project, add the following to your requirements file:
40
+ `git+https://github.com/equinor/STIDapi-python.git`
41
+
42
+ or in requirements setup.py:
43
+ `'stidapi @ git+https://github.com/equinor/STIDapi-python'`
44
+
45
+ or in pyproject.toml:
46
+ `stidapi = { git = "https://github.com/Equinor/stidapi-python" }`
47
+
48
+
49
+ ## Developing / testing
50
+
51
+ Poetry is preferred for developers. Install with required packages for testing and coverage:
52
+ `poetry install`
53
+
54
+ Call `poetry run pytest` to run tests.
55
+
56
+ To generate coverage report in html run `poetry run pytest --cov=stidapi tests/ --cov-report html`
57
+
58
+
@@ -0,0 +1,37 @@
1
+ # STIDapi-python
2
+
3
+ A simple wrapper package to interface Equinor [STIDapi](https://stidapi.equinor.com/).
4
+ Used to get plant, systen and tag and (future) doc data from STID rest api.
5
+
6
+
7
+ ## Use
8
+
9
+ Try it out by running the [demo](examples/demo.py).
10
+
11
+ ## Installing
12
+
13
+ To **install** call the following from your project environment:
14
+ `pip install git+https://github.com/equinor/STIDapi-python.git`
15
+
16
+ To **upgrade** call the following from your project environment:
17
+ `pip install --upgrade git+https://github.com/equinor/STIDapi-python.git`
18
+
19
+ To include in your project, add the following to your requirements file:
20
+ `git+https://github.com/equinor/STIDapi-python.git`
21
+
22
+ or in requirements setup.py:
23
+ `'stidapi @ git+https://github.com/equinor/STIDapi-python'`
24
+
25
+ or in pyproject.toml:
26
+ `stidapi = { git = "https://github.com/Equinor/stidapi-python" }`
27
+
28
+
29
+ ## Developing / testing
30
+
31
+ Poetry is preferred for developers. Install with required packages for testing and coverage:
32
+ `poetry install`
33
+
34
+ Call `poetry run pytest` to run tests.
35
+
36
+ To generate coverage report in html run `poetry run pytest --cov=stidapi tests/ --cov-report html`
37
+
@@ -0,0 +1,24 @@
1
+ [tool.poetry]
2
+ name = "stidapi"
3
+ version = "1.0.1"
4
+ description = "Python client for connecting to Equinor STIDapi"
5
+ authors = ["Åsmund Våge Fannemel <asmf@equinor.com>"]
6
+ license = "Proprietary"
7
+ readme = "README.md"
8
+
9
+ [tool.poetry.urls]
10
+ "repository" = "https://github.com/equinor/STIDapi-python"
11
+
12
+ [tool.poetry.dependencies]
13
+ python = "^3.9"
14
+ certifi = "^2023.11.17"
15
+ msal-bearer = ">=0.2.1,<1.1.0"
16
+ requests = "^2.31.0"
17
+
18
+ [tool.poetry.group.test.dependencies]
19
+ pytest = "^7.4.4"
20
+ pytest-cov = "^4.1.0"
21
+
22
+ [build-system]
23
+ requires = ["poetry-core"]
24
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,152 @@
1
+ from typing import List
2
+ from urllib.parse import urljoin
3
+ import stidapi.utils as u
4
+
5
+
6
+ class File:
7
+ """Class for internal use. Copy of File as returned by document endpoint."""
8
+
9
+ # "id": 0,
10
+ # "instCode": "string",
11
+ # "fileName": "string",
12
+ # "objectType": "string",
13
+ # "description": "string",
14
+ # "fileOrder": 0,
15
+ # "prodViewCode": "string",
16
+ # "insertedDate": "2024-04-09T11:46:07.501Z",
17
+ # "thumbnail": "string",
18
+ # "fileSize": 0,
19
+ # "blobId": "string"
20
+
21
+ def __init__(self, data: dict):
22
+ for prop in data.keys():
23
+ self.__setattr__(prop, data[prop])
24
+
25
+ def __str__(self):
26
+ return f"InstCode: {self.instCode}, FileName: {self.fileName}, Description: {self.description}, ObjectType: {self.objectType}"
27
+
28
+
29
+ class Revision:
30
+ """Class for internal use. Copy of Revision as returned by document endpoint."""
31
+
32
+ # "revNo": "string",
33
+ # "revStatus": "string",
34
+ # "revStatusDescription": "string",
35
+ # "revDate": "2024-04-09T11:46:07.501Z",
36
+ # "isCurrent": true,
37
+ # "projectCode": "string",
38
+ # "projectCodeDescription": "string",
39
+ # "reasonForIssue": "string",
40
+ # "acceptanceCode": 0,
41
+ # "acceptanceCodeDescription": "string",
42
+ # "files": [File objects]
43
+
44
+ def __init__(self, data: dict):
45
+ self._data = data
46
+ for prop in data.keys():
47
+ if prop == "files":
48
+ continue
49
+ self.__setattr__(prop, data[prop])
50
+
51
+ def get_files(self):
52
+ return [File(x) for x in self._data["files"]]
53
+
54
+ def __str__(self):
55
+ return (
56
+ f"RevNo: {self.revNo}, RevStatus: {self.revStatus}, RevDate: {self.revDate}"
57
+ )
58
+
59
+
60
+ class Doc:
61
+ def __init__(self, inst_code: str, doc_no: str):
62
+ """
63
+ Object initializer for Doc
64
+ :param inst_code: STID installation code
65
+ :param doc_no: Doc number as found in STID
66
+
67
+ Initializes object and gets data from STID if available.
68
+ """
69
+ self.inst_code = ""
70
+ self.no = ""
71
+ self.title = ""
72
+
73
+ self.category = ""
74
+ self.discipline = ""
75
+ self.type = ""
76
+
77
+ self._data = Doc.get(inst_code=inst_code, doc_no=doc_no)
78
+
79
+ common_prop = [
80
+ ("instCode", "inst_code"),
81
+ ("docNo", "no"),
82
+ ("docTitle", "title"),
83
+ ("docCategoryDescription", "category"),
84
+ ("disciplineCodeDescription", "discipline"),
85
+ ("docTypeDescription", "type"),
86
+ ]
87
+ for prop in common_prop:
88
+ if prop[0] in self._data:
89
+ self.__setattr__(prop[1], self._data[prop[0]])
90
+ else:
91
+ self.__setattr__(prop[1], "")
92
+
93
+ self.docNo = self.no
94
+ self.description = self.title
95
+
96
+ optional_prop = []
97
+ for prop in optional_prop:
98
+ if prop[0] in self._data and self._data[prop[0]] is not None:
99
+ self.__setattr__(prop[1], self._data[prop[0]])
100
+
101
+ def get_current_revision(self) -> Revision:
102
+ return Revision(self._data["currentRevision"])
103
+
104
+ def get_files(self) -> List[File]:
105
+ return self.get_current_revision().get_files()
106
+
107
+ def __str__(self):
108
+ return f"InstCode: {self.inst_code}, No: {self.no}, Title: {self.title}"
109
+
110
+ # "instCode": "string", "instCodeDescription": "string",
111
+ # "docNo": "string", "docTitle": "string",
112
+ # "docCategory": "string", "docCategoryDescription": "string",
113
+ # "docType": "string", "docTypeDescription": "string",
114
+ # "projectCode": "string", "projectCodeDescription": "string",
115
+ # "contrCode": "string", "contrCodeDescription": "string",
116
+ # "disciplineCode": "string","disciplineCodeDescription": "string",
117
+ # "locationCode": "string","locationCodeDescription": "string",
118
+ # "docClass": "string","docClassName": "string", "docClassDescription": "string",
119
+ # "poNo": "string","poNoDescription": "string",
120
+ # "system": "string","systemDescription": "string",
121
+ # "remark": "string",
122
+ # "supplDocNo": "string",
123
+ # "companyCode": "string",
124
+ # "source": "string",
125
+ # "size": "string",
126
+ # "priority": "string", "priorityDescription": "string",
127
+ # "weightBearing": "string",
128
+ # "productCode": "string",
129
+ # "insulationClass": "string", "insulationClassDescription": "string",
130
+ # "tagRefCount": 0, "docRefCount": 0,
131
+ # "currentRevision": { Revision object },
132
+ # "projectRevisions": [ { Revision object }],
133
+ # "additionalFields": [{"type": "string","typeDescription": "string","value": "string","description": "string"}],
134
+ # "projects": [{"instCode": "string","projectCode": "string","description": "string","stidDeliveryCode": "string",
135
+ # "insertedDate": "2024-04-09T11:46:07.501Z","isPrimary": true,"isValid": true,}],
136
+ # "purchaseOrders": [
137
+ # {"instCode": "string","poNo": "string","description": "string","insertedDate": "2024-04-09T11:46:07.501Z","isPrimary": true,"isValid": true}],
138
+ # "apiResponseTime": "string",
139
+
140
+ @staticmethod
141
+ def get(inst_code: str, doc_no: str):
142
+ """
143
+ Get tag data from STID for a single doc.
144
+ :param inst_code: STID installation code
145
+ :param doc_no: STID doc number
146
+ :return: Dictionary of data from STID for doc.
147
+ """
148
+
149
+ url = urljoin(
150
+ u.get_api_url(), str(inst_code) + "/document?docNo=" + str(doc_no)
151
+ )
152
+ return u.get_json(url)
@@ -0,0 +1,113 @@
1
+ from typing import List, Union
2
+ from urllib.parse import urljoin
3
+
4
+ import stidapi.utils as u
5
+
6
+
7
+ class Plant:
8
+ _plant_list = []
9
+
10
+ def __init__(self, code: Union[str, int] = "", data=None):
11
+ self.inst_code = ""
12
+ self.sap_id = ""
13
+ self.description = ""
14
+ self.business_area = ""
15
+ self.ims = ""
16
+
17
+ if isinstance(code, int):
18
+ code = str(code)
19
+
20
+ if isinstance(code, str) and len(code) > 0:
21
+ self.inst_code = code
22
+ if data is None:
23
+ data = Plant._get_data(code)
24
+
25
+ if isinstance(data, dict) and len(data) > 0:
26
+ self._data = data
27
+ self.inst_code = data["instCode"]
28
+ self.sap_id = data["sapPlantId"]
29
+ self.description = data["description"]
30
+ self.business_area = data["businessArea"]
31
+ self.ims = data["imsPlant"]
32
+ else:
33
+ self._data = {}
34
+
35
+ self.plant_code = self.inst_code
36
+
37
+ def __str__(self):
38
+ if not isinstance(self.inst_code, str) or len(self.inst_code) == 0:
39
+ return "Empty plant object"
40
+
41
+ if len(self._data) > 0:
42
+ return (
43
+ "Plant "
44
+ + str(self.inst_code)
45
+ + " - "
46
+ + str(self.description)
47
+ + " with data"
48
+ )
49
+ else:
50
+ return (
51
+ "Plant "
52
+ + str(self.inst_code)
53
+ + " - "
54
+ + str(self.description)
55
+ + " with no data"
56
+ )
57
+
58
+ @classmethod
59
+ def get_all_inst_code(cls) -> List[str]:
60
+ return [x["instCode"] for x in Plant.get_all_data()]
61
+
62
+ @classmethod
63
+ def get_all_data(cls):
64
+ if len(cls._plant_list) == 0:
65
+ url = urljoin(u.get_api_url(), "plants")
66
+ parsed_json = u.get_json(url)
67
+
68
+ if isinstance(parsed_json, list):
69
+ cls._plant_list = [x for x in parsed_json if "instCode" in x]
70
+
71
+ return cls._plant_list
72
+
73
+ @classmethod
74
+ def _get_data(cls, code: Union[str, int]):
75
+ if (
76
+ code is None
77
+ or (isinstance(code, str) and len(code) == 0)
78
+ or (isinstance(code, int) and code < 1000)
79
+ ):
80
+ return None
81
+
82
+ plant_data_list = Plant.get_all_data()
83
+
84
+ if len(plant_data_list) == 0:
85
+ return None
86
+
87
+ if isinstance(code, str):
88
+ plant_data_list = [
89
+ x
90
+ for x in plant_data_list
91
+ if x["instCode"].lower() == code.lower() or x["sapPlantId"] == code
92
+ ]
93
+ elif isinstance(code, int):
94
+ plant_data_list = [x for x in plant_data_list if x["sapPlantId"] == code]
95
+ else:
96
+ return ValueError("Input code must be string or int.")
97
+
98
+ if len(plant_data_list) > 0:
99
+ return plant_data_list[0]
100
+ else:
101
+ print("Warning: did not get data for Plant " + str(code))
102
+ return None
103
+
104
+ @classmethod
105
+ def get_plant(cls, inst_code: Union[str, int]) -> "Plant":
106
+ plant_data = Plant._get_data(inst_code)
107
+ return Plant(inst_code, plant_data)
108
+
109
+ @classmethod
110
+ def get_plants(cls) -> List["Plant"]:
111
+ plant_data_list = Plant.get_all_data()
112
+
113
+ return [Plant(x["instCode"], x) for x in plant_data_list]
@@ -0,0 +1,40 @@
1
+ from urllib.parse import urljoin
2
+
3
+ import stidapi.utils as u
4
+
5
+
6
+ class System:
7
+ def __init__(self, inst_code: str = "", data=None):
8
+ if isinstance(inst_code, str) and len(inst_code) > 0:
9
+ self.inst_code = inst_code
10
+
11
+ self._data = {}
12
+ self.no = ""
13
+ self.description = ""
14
+ self.inst_code = ""
15
+ self.valid = None
16
+
17
+ if isinstance(data, dict) and len(data) > 0:
18
+ self._data = data
19
+ self.inst_code = data["instCode"]
20
+ self.no = data["system"]
21
+ self.description = data["description"]
22
+ self.valid = data["validFlg"]
23
+
24
+ @staticmethod
25
+ def get_all_data(inst_code, is_valid: bool = False):
26
+ """
27
+
28
+ :param get_statistics:
29
+ :return:
30
+ """
31
+
32
+ url = urljoin(u.get_api_url(), f"{inst_code}/system?isValid={is_valid}")
33
+ parsed_json = u.get_json(url)
34
+
35
+ return parsed_json
36
+
37
+ @staticmethod
38
+ def get_system(inst_code: str, get_statistics: bool = False):
39
+ plant_data = System.get_all_data(inst_code, get_statistics)
40
+ return [System(inst_code, x) for x in plant_data]
@@ -0,0 +1,262 @@
1
+ from typing import List, Union
2
+ from urllib.parse import urljoin
3
+
4
+ from stidapi.Doc import Doc
5
+ from stidapi.Plant import Plant
6
+ import stidapi.utils as u
7
+
8
+
9
+ class Tag:
10
+ def __init__(self, inst_code: str, tag_no: str):
11
+ """
12
+ Object initializer for Tag
13
+ :param inst_code: STID installation code
14
+ :param tag_no: Tag number as found in STID
15
+
16
+ Initializes object and gets data from STID if available.
17
+ """
18
+
19
+ self.inst_code = ""
20
+ self.no = ""
21
+ self.description = ""
22
+ self.category = ""
23
+ self.discipline = ""
24
+ self.is_function = False
25
+ self.is_signal = False
26
+
27
+ self._data = Tag.get(inst_code=inst_code, tag_no=tag_no)
28
+
29
+ common_prop = [
30
+ ("instCode", "inst_code"),
31
+ ("tagNo", "no"),
32
+ ("description", "description"),
33
+ ("tagCategoryDescription", "category"),
34
+ ("disciplineCodeDescription", "discipline"),
35
+ ]
36
+ for prop in common_prop:
37
+ if prop[0] in self._data:
38
+ self.__setattr__(prop[1], self._data[prop[0]])
39
+ else:
40
+ self.__setattr__(prop[1], "")
41
+
42
+ self.tag_no = self.no
43
+
44
+ if "isFunction" in self._data and self._data["isFunction"]:
45
+ self.is_function = True
46
+ function_prop = [("functionBlock", "FB")]
47
+ for prop in function_prop:
48
+ if prop[0] in self._data:
49
+ self.__setattr__(prop[1], self._data[prop[0]])
50
+
51
+ if "isSignal" in self._data and self._data["isSignal"]:
52
+ self.is_signal = True
53
+ signal_prop = [("signalType", "signalType")]
54
+ for prop in signal_prop:
55
+ if prop[0] in self._data:
56
+ self.__setattr__(prop[1], self._data[prop[0]])
57
+
58
+ optional_prop = [
59
+ ("setpointL", "L"),
60
+ ("setpointLl", "LL"),
61
+ ("setpointH", "H"),
62
+ ("setpointHh", "HH"),
63
+ ]
64
+ for prop in optional_prop:
65
+ if prop[0] in self._data and self._data[prop[0]] is not None:
66
+ self.__setattr__(prop[1], self._data[prop[0]])
67
+
68
+ def get_addition_field(self, field: str = "") -> List[dict]:
69
+ """Get list of additional fields. Optionally filter by type.
70
+
71
+ Args:
72
+ field (str, optional): Filter by field type. Defaults to "" which returns all.
73
+
74
+ Returns:
75
+ List[dict]: List of additional fields.
76
+ """
77
+ if field is None or len(field) == 0:
78
+ return self._data["additionalFields"]
79
+
80
+ return [x for x in self._data["additionalFields"] if x["type"] == field]
81
+
82
+ def get_doc(self) -> List[Doc]:
83
+ """Get list of documents that tag refers to.
84
+
85
+ Returns:
86
+ List[Doc]: List of referred Documents
87
+ """
88
+
89
+ return [Doc(x["instCode"], x["docNo"]) for x in self.get_doc_references()]
90
+
91
+ def get_doc_references(self) -> List[dict]:
92
+ """Get doc references of tag from stidapi as dicts.
93
+
94
+ Returns:
95
+ List[dict]: List of dicts describing document references.
96
+ """
97
+ url = urljoin(
98
+ u.get_api_url(),
99
+ f"{str(self.inst_code)}/tag/document-refs?tagNo={str(self.tag_no)}&noContentAs200=true",
100
+ )
101
+ json = u.get_json(url)
102
+ return json
103
+
104
+ def get_functional_location(self) -> str:
105
+ """Get functional location of Tagged item or empty string if it does not have a functional location.
106
+
107
+ Returns:
108
+ str: Functional location or empty string if tagged item does not have a functional location.
109
+ """
110
+
111
+ url = urljoin(
112
+ u.get_api_url(),
113
+ f"portal/{self.inst_code}/tag/sap-tag?tagNo={self.tag_no}",
114
+ )
115
+ json = u.get_json(url)
116
+
117
+ if isinstance(json, dict) and "functionalLocation" in json.keys():
118
+ return json["functionalLocation"]
119
+ else:
120
+ return ""
121
+
122
+ def __str__(self):
123
+ """
124
+ Overload string representation of object.
125
+ :return: Pretty-print string describing Tag object
126
+ """
127
+ if isinstance(self._data, dict) and len(self._data) > 0:
128
+ return str(self.tag_no) + "@" + str(self.inst_code) + " with data"
129
+ else:
130
+ return str(self.tag_no) + "@" + str(self.inst_code) + " with no data"
131
+
132
+ def prune_empty_data(self):
133
+ """
134
+ Remove all empty, i.e., containing None, entries in data dictionary.
135
+ """
136
+ if isinstance(self._data, dict) and len(self._data) > 0:
137
+ self._data = {k: v for k, v in self._data.items() if v is not None}
138
+ else:
139
+ print("Tag has no data to prune")
140
+
141
+ @staticmethod
142
+ def get(inst_code: str, tag_no: str):
143
+ """
144
+ Get tag data from STID for a single tag.
145
+ :param inst_code: STID installation code
146
+ :param tag_no: STID tag number
147
+ :return: Dictionary of data from STID for tag.
148
+ """
149
+
150
+ url = urljoin(u.get_api_url(), str(inst_code) + "/tag?tagNo=" + str(tag_no))
151
+ return u.get_json(url)
152
+
153
+ @staticmethod
154
+ def search(
155
+ inst_code: str,
156
+ tag_no: str = "",
157
+ description: str = "",
158
+ tag_status: str = "",
159
+ tag_category: int = -1,
160
+ tag_type: str = "",
161
+ system: str = "",
162
+ discipline_code: str = "",
163
+ skip: int = 0,
164
+ take: int = 50,
165
+ ):
166
+ # string subSystem, string mainSystem, string projectCode, string poNo,
167
+ # string contrCode, string locationCode, string plantId,
168
+ if len(tag_no) > 0:
169
+ tag_no = "tagNo=" + tag_no + "&"
170
+
171
+ if len(description) > 0:
172
+ description = "description=" + description + "&"
173
+
174
+ if len(tag_status) > 0:
175
+ tag_status = "tagStatus=" + tag_status + "&"
176
+
177
+ if tag_category > 0:
178
+ tag_category_string = "tagCategory=" + str(tag_category) + "&"
179
+ else:
180
+ tag_category_string = ""
181
+
182
+ if len(tag_type) > 0:
183
+ tag_type = "tagType=" + tag_type + "&"
184
+
185
+ if len(system) > 0:
186
+ system = "system=" + system + "&"
187
+
188
+ if len(discipline_code) > 0:
189
+ discipline_code = "disciplineCode=" + discipline_code + "&"
190
+
191
+ skip_string = "skip=" + str(skip) + "&"
192
+
193
+ # take shall be last, thus no trailing &
194
+ take_string = "take=" + str(take)
195
+
196
+ url = urljoin(
197
+ u.get_api_url(),
198
+ f"/{inst_code}/tags"
199
+ + "?"
200
+ + str(tag_no)
201
+ + str(description)
202
+ + str(tag_status)
203
+ + str(tag_category_string)
204
+ + str(tag_type)
205
+ + str(system)
206
+ + str(skip_string)
207
+ + str(take_string),
208
+ )
209
+
210
+ return u.get_json(url)
211
+
212
+ @staticmethod
213
+ def search_additional_field(
214
+ inst_code: str, field_name: str, value: str
215
+ ) -> List[dict]:
216
+ url = urljoin(
217
+ u.get_api_url(),
218
+ f"/{inst_code}/tag-additional-field/{field_name}?searchValue={value}&noContentAs200=true",
219
+ )
220
+ return u.get_json(url)
221
+
222
+ @staticmethod
223
+ def get_from_additional_field(
224
+ field_name: str,
225
+ value: str,
226
+ inst_code: Union[str, List[str]] = None,
227
+ stop_first=True,
228
+ ) -> List["Tag"]:
229
+ """Get List of Tag objects by searching for additional fields
230
+ field_name: Name of additional field
231
+ value: Value of additional field
232
+ inst_code: STID plant code, or list of codes, to limit search to.
233
+ Defaults to empty which will search all.
234
+ stop_first: Set to False to get matches from all provided inst_code,
235
+ else will stop at first match. Defaults to False.
236
+
237
+ Returns:
238
+ (List["Tag"]): List of Tag objects for matches.
239
+ """
240
+
241
+ if inst_code is None or (isinstance(inst_code, str) and len(inst_code) == 0):
242
+ inst_code = Plant.get_all_inst_code()
243
+
244
+ if isinstance(inst_code, str):
245
+ inst_code = [inst_code]
246
+
247
+ res = []
248
+
249
+ for code in inst_code:
250
+ data = [
251
+ Tag(inst_code=x["instCode"], tag_no=x["tagNo"])
252
+ for x in Tag.search_additional_field(
253
+ inst_code=code, field_name=field_name, value=value
254
+ )
255
+ ]
256
+ if len(data) > 0:
257
+ res.extend(data)
258
+
259
+ if stop_first:
260
+ break
261
+
262
+ return res
@@ -0,0 +1,48 @@
1
+ import json
2
+ from types import SimpleNamespace
3
+
4
+ import requests
5
+
6
+
7
+ def get_api_url():
8
+ return "https://stidapi.equinor.com/"
9
+
10
+
11
+ def get_auth():
12
+ from msal_bearer.BearerAuth import BearerAuth, get_login_name
13
+
14
+ tenantID = "3aa4a235-b6e2-48d5-9195-7fcf05b459b0"
15
+ clientID = "1a35a8df-b48d-40df-987b-267968b1b198" # stidapi-python
16
+ scopes = ["1734406c-3449-4192-a50d-7c3a63d3f57d/user_impersonation"]
17
+ auth = BearerAuth.get_auth(
18
+ tenantID=tenantID,
19
+ clientID=clientID,
20
+ scopes=scopes,
21
+ username=f"{get_login_name()}@equinor.com",
22
+ )
23
+
24
+ return auth
25
+
26
+
27
+ def get_object_from_json(text: str):
28
+ if isinstance(text, list):
29
+ obj = [json.loads(x, object_hook=lambda d: SimpleNamespace(**d)) for x in text]
30
+ else:
31
+ obj = json.loads(text, object_hook=lambda d: SimpleNamespace(**d))
32
+ return obj
33
+
34
+
35
+ def get_json(url: str):
36
+ response = requests.get(url, auth=get_auth())
37
+ # response.raise_for_status()
38
+ if response.status_code == 200:
39
+ try:
40
+ return response.json()
41
+ except json.JSONDecodeError:
42
+ print(
43
+ f"Warning: {str(url)} returned successfully, but not with a valid json response"
44
+ )
45
+ else:
46
+ print(f"Warning: {str(url)} returned status code {response.status_code}")
47
+
48
+ return []