stidapi 1.4.2__tar.gz → 1.4.3__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,24 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stidapi
3
- Version: 1.4.2
3
+ Version: 1.4.3
4
4
  Summary: Python client for connecting to Equinor STIDapi
5
- License: Proprietary
6
5
  Author: Åsmund Våge Fannemel
7
- Author-email: asmf@equinor.com
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: License :: Other/Proprietary License
6
+ Author-email: Åsmund Våge Fannemel <asmf@equinor.com>
7
+ License-Expression: LicenseRef-Proprietary
10
8
  Classifier: Programming Language :: Python :: 3
11
9
  Classifier: Programming Language :: Python :: 3.10
12
10
  Classifier: Programming Language :: Python :: 3.11
13
11
  Classifier: Programming Language :: Python :: 3.12
14
12
  Classifier: Programming Language :: Python :: 3.13
15
13
  Classifier: Programming Language :: Python :: 3.14
16
- Requires-Dist: eq-api-connector (>=1.1.0,<2.0.0)
14
+ Requires-Dist: eq-api-connector>=1.1.0,<2
15
+ Requires-Python: >=3.10, <4
17
16
  Project-URL: repository, https://github.com/equinor/STIDapi-python
18
17
  Description-Content-Type: text/markdown
19
18
 
20
- # STIDapi-python [![SNYK dependency check](https://github.com/equinor/STIDapi-python/actions/workflows/snyk.yml/badge.svg)](https://github.com/equinor/STIDapi-python/actions/workflows/snyk.yml)
21
-
19
+ # STIDapi-python
22
20
  A simple wrapper package to interface Equinor [STIDapi](https://stidapi.equinor.com/) using python and get plant, system, tag and doc data.
23
21
 
24
22
 
@@ -46,14 +44,13 @@ Install package from pypi using `pip install stidapi`
46
44
 
47
45
  ## Developing / testing
48
46
 
49
- Poetry is preferred for developers. Install with required packages for testing and coverage:
50
- `poetry install`
47
+ Dependency manager uv is preferred for developers. Install with required packages for testing and coverage:
48
+ `uv sync`
51
49
 
52
- Call `poetry run pytest` to run tests.
50
+ Call `uv run pytest` to run tests.
53
51
 
54
- To generate coverage report run `poetry run pytest --cov=stidapi tests/ --cov-report=xml`
52
+ To generate coverage report run `uv run pytest --cov=stidapi tests/ --cov-report=xml`
55
53
 
56
54
  To compare coverage with master branch run
57
- `poetry run diff-cover coverage.xml --compare-branch master`
58
-
55
+ `uv run diff-cover coverage.xml --compare-branch master`
59
56
 
@@ -1,5 +1,4 @@
1
- # STIDapi-python [![SNYK dependency check](https://github.com/equinor/STIDapi-python/actions/workflows/snyk.yml/badge.svg)](https://github.com/equinor/STIDapi-python/actions/workflows/snyk.yml)
2
-
1
+ # STIDapi-python
3
2
  A simple wrapper package to interface Equinor [STIDapi](https://stidapi.equinor.com/) using python and get plant, system, tag and doc data.
4
3
 
5
4
 
@@ -27,13 +26,13 @@ Install package from pypi using `pip install stidapi`
27
26
 
28
27
  ## Developing / testing
29
28
 
30
- Poetry is preferred for developers. Install with required packages for testing and coverage:
31
- `poetry install`
29
+ Dependency manager uv is preferred for developers. Install with required packages for testing and coverage:
30
+ `uv sync`
32
31
 
33
- Call `poetry run pytest` to run tests.
32
+ Call `uv run pytest` to run tests.
34
33
 
35
- To generate coverage report run `poetry run pytest --cov=stidapi tests/ --cov-report=xml`
34
+ To generate coverage report run `uv run pytest --cov=stidapi tests/ --cov-report=xml`
36
35
 
37
36
  To compare coverage with master branch run
38
- `poetry run diff-cover coverage.xml --compare-branch master`
37
+ `uv run diff-cover coverage.xml --compare-branch master`
39
38
 
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "stidapi"
3
+ version = "1.4.3"
4
+ description = "Python client for connecting to Equinor STIDapi"
5
+ requires-python = ">=3.10,<4"
6
+ readme = "README.md"
7
+ license = "LicenseRef-Proprietary"
8
+ classifiers = [
9
+ "Programming Language :: Python :: 3",
10
+ "Programming Language :: Python :: 3.10",
11
+ "Programming Language :: Python :: 3.11",
12
+ "Programming Language :: Python :: 3.12",
13
+ "Programming Language :: Python :: 3.13",
14
+ "Programming Language :: Python :: 3.14",
15
+ ]
16
+ dependencies = ["eq-api-connector>=1.1.0,<2"]
17
+
18
+ [[project.authors]]
19
+ name = "Åsmund Våge Fannemel"
20
+ email = "asmf@equinor.com"
21
+
22
+ [project.urls]
23
+ repository = "https://github.com/equinor/STIDapi-python"
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "pytest>=9.0.0,<10",
28
+ "pytest-cov>=7.0.0,<8",
29
+ "black>=26.3.0,<27",
30
+ "diff-cover>=10.2.0,<11",
31
+ ]
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.11.16,<0.13.0"]
35
+ build-backend = "uv_build"
@@ -0,0 +1,32 @@
1
+ [project]
2
+ name = "stidapi"
3
+ version = "1.4.3"
4
+ description = "Python client for connecting to Equinor STIDapi"
5
+ authors = [{ name = "Åsmund Våge Fannemel", email = "asmf@equinor.com" }]
6
+ requires-python = ">=3.10,<4"
7
+ readme = "README.md"
8
+ license = "LicenseRef-Proprietary"
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.10",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
16
+ ]
17
+ dependencies = ["eq-api-connector>=1.1.0,<2"]
18
+
19
+ [project.urls]
20
+ repository = "https://github.com/equinor/STIDapi-python"
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "pytest>=9.0.0,<10",
25
+ "pytest-cov>=7.0.0,<8",
26
+ "black>=26.3.0,<27",
27
+ "diff-cover>=10.2.0,<11",
28
+ ]
29
+
30
+ [build-system]
31
+ requires = ["uv_build>=0.11.16,<0.13.0"]
32
+ build-backend = "uv_build"
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from typing import Any, Dict, List, Optional
2
3
  from urllib.parse import urljoin
3
4
  import stidapi.utils as u
@@ -54,6 +55,40 @@ class File:
54
55
  """Get object type."""
55
56
  return getattr(self, "objectType", "")
56
57
 
58
+ def download_file(
59
+ self, destination: Optional[str] = None, overwrite: bool = False
60
+ ) -> str:
61
+ """Download this file from STID.
62
+
63
+ Args:
64
+ destination: Optional local path for the downloaded file.
65
+ overwrite: Allow an existing destination file to be replaced.
66
+
67
+ Returns:
68
+ Path returned by the API connector after downloading the file.
69
+
70
+ Raises:
71
+ ValueError: If no download URL or file name is available.
72
+ """
73
+ download_url = getattr(self, "url", "")
74
+ if not download_url:
75
+ file_id = getattr(self, "id", None)
76
+ if file_id is None or not self.inst_code:
77
+ raise ValueError(
78
+ f"No download endpoint found for file {self.file_name}"
79
+ )
80
+ download_url = urljoin(u.get_api_url(), f"{self.inst_code}/file/{file_id}")
81
+
82
+ output_name = destination or self.file_name
83
+ if destination and os.path.isdir(destination):
84
+ output_name = os.path.join(destination, self.file_name)
85
+ if not output_name:
86
+ raise ValueError("A destination is required when the file has no name")
87
+ if os.path.exists(output_name) and not overwrite:
88
+ raise FileExistsError(f"Destination already exists: {output_name}")
89
+
90
+ return u.get_api_client().get_file(download_url, output_name)
91
+
57
92
  def __str__(self) -> str:
58
93
  return f"InstCode: {self.inst_code}, FileName: {self.file_name}, Description: {self.description}, ObjectType: {self.object_type}"
59
94
 
@@ -108,6 +143,27 @@ class Revision:
108
143
  """
109
144
  return [File(x) for x in self._data.get("files", [])]
110
145
 
146
+ def download_files(
147
+ self, destination: Optional[str] = None, overwrite: bool = False
148
+ ) -> List[str]:
149
+ """Download all files in this revision.
150
+
151
+ Args:
152
+ destination: Optional destination directory for the downloaded files.
153
+ overwrite: Allow existing destination files to be replaced.
154
+
155
+ Returns:
156
+ Paths returned by the API connector for each downloaded file.
157
+ """
158
+ if destination is None:
159
+ destination = os.path.join(os.getcwd(), self.rev_no)
160
+ os.makedirs(destination, exist_ok=True)
161
+
162
+ return [
163
+ file.download_file(destination, overwrite=overwrite)
164
+ for file in self.get_files()
165
+ ]
166
+
111
167
  def __str__(self) -> str:
112
168
  return f"RevNo: {self.rev_no}, RevStatus: {self.rev_status}, RevDate: {self.rev_date}"
113
169
 
@@ -164,6 +220,7 @@ class Doc:
164
220
  raise ValueError("doc_no must be a non-empty string")
165
221
 
166
222
  self._data: Dict[str, Any] = {}
223
+ self._full_data_loaded = data is None or "currentRevision" in data
167
224
 
168
225
  if data is not None:
169
226
  self._data = data
@@ -187,19 +244,32 @@ class Doc:
187
244
  """Get document number."""
188
245
  return self._data.get("docNo", "")
189
246
 
247
+ def _ensure_full_data(self) -> None:
248
+ """Reload document data once when initialized from a partial reference."""
249
+ if self._full_data_loaded:
250
+ return
251
+
252
+ fetched_data = Doc.get(self.inst_code, self.no)
253
+ if isinstance(fetched_data, dict):
254
+ self._data = fetched_data
255
+ self._full_data_loaded = True
256
+
190
257
  @property
191
258
  def category(self) -> str:
192
259
  """Get document category description."""
260
+ self._ensure_full_data()
193
261
  return self._data.get("docCategoryDescription", "")
194
262
 
195
263
  @property
196
264
  def discipline(self) -> str:
197
265
  """Get discipline code description."""
266
+ self._ensure_full_data()
198
267
  return self._data.get("disciplineCodeDescription", "")
199
268
 
200
269
  @property
201
270
  def type(self) -> str:
202
271
  """Get document type description."""
272
+ self._ensure_full_data()
203
273
  return self._data.get("docTypeDescription", "")
204
274
 
205
275
  @property
@@ -207,13 +277,35 @@ class Doc:
207
277
  """Get document title."""
208
278
  return self._data.get("docTitle", "")
209
279
 
210
- def get_current_revision(self) -> Revision:
280
+ @property
281
+ def current_revision(self) -> Revision:
211
282
  """Get the current revision of this document.
212
283
 
213
284
  Returns:
214
285
  Revision object for the current revision.
215
286
  """
216
- return Revision(self._data.get("currentRevision", {}))
287
+ self._ensure_full_data()
288
+ current_revision = self._data.get("currentRevision")
289
+
290
+ if isinstance(current_revision, dict) and current_revision:
291
+ return Revision(current_revision)
292
+
293
+ project_revisions = [
294
+ revision
295
+ for revision in self._data.get("projectRevisions", [])
296
+ if isinstance(revision, dict)
297
+ ]
298
+ marked_current = next(
299
+ (revision for revision in project_revisions if revision.get("isCurrent")),
300
+ None,
301
+ )
302
+ return Revision(
303
+ marked_current or (project_revisions[0] if project_revisions else {})
304
+ )
305
+
306
+ def get_current_revision(self) -> Revision:
307
+ """Backward-compatible alias for current_revision."""
308
+ return self.current_revision
217
309
 
218
310
  def get_files(self) -> List[File]:
219
311
  """Get files from the current revision.
@@ -221,7 +313,29 @@ class Doc:
221
313
  Returns:
222
314
  List of File objects from the current revision.
223
315
  """
224
- return self.get_current_revision().get_files()
316
+ return self.current_revision.get_files()
317
+
318
+ def download_files(
319
+ self,
320
+ revision: Revision,
321
+ destination: Optional[str] = None,
322
+ overwrite: bool = False,
323
+ ) -> List[str]:
324
+ """Download all files from a specific revision.
325
+
326
+ Args:
327
+ revision: Revision whose files should be downloaded.
328
+ destination: Optional destination directory. Defaults to a
329
+ document/revision directory in the current working directory.
330
+ overwrite: Allow existing destination files to be replaced.
331
+
332
+ Returns:
333
+ Paths returned by the API connector for each downloaded file.
334
+ """
335
+ if destination is None:
336
+ destination = os.path.join(os.getcwd(), self.no, revision.rev_no)
337
+ os.makedirs(destination, exist_ok=True)
338
+ return revision.download_files(destination, overwrite=overwrite)
225
339
 
226
340
  def __str__(self) -> str:
227
341
  return f"InstCode: {self.inst_code}, No: {self.no}, Title: {self.title}"
@@ -1,24 +0,0 @@
1
- [tool.poetry]
2
- name = "stidapi"
3
- version = "1.4.2"
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.10"
14
- eq-api-connector = "^1.1.0"
15
-
16
- [tool.poetry.group.dev.dependencies]
17
- pytest = "^9.0.0"
18
- pytest-cov = "^7.0.0"
19
- black = "^25.10.0"
20
- diff-cover = "^10.2.0"
21
-
22
- [build-system]
23
- requires = ["poetry-core"]
24
- build-backend = "poetry.core.masonry.api"
File without changes
File without changes
File without changes
File without changes
File without changes