lr-gladiator 0.6.0__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
Potentially problematic release.
This version of lr-gladiator might be problematic. Click here for more details.
- gladiator/arena.py +81 -13
- gladiator/cli.py +19 -2
- {lr_gladiator-0.6.0.dist-info → lr_gladiator-0.7.0.dist-info}/METADATA +1 -1
- lr_gladiator-0.7.0.dist-info/RECORD +10 -0
- lr_gladiator-0.6.0.dist-info/RECORD +0 -10
- {lr_gladiator-0.6.0.dist-info → lr_gladiator-0.7.0.dist-info}/WHEEL +0 -0
- {lr_gladiator-0.6.0.dist-info → lr_gladiator-0.7.0.dist-info}/entry_points.txt +0 -0
- {lr_gladiator-0.6.0.dist-info → lr_gladiator-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {lr_gladiator-0.6.0.dist-info → lr_gladiator-0.7.0.dist-info}/top_level.txt +0 -0
gladiator/arena.py
CHANGED
|
@@ -156,21 +156,88 @@ class ArenaClient:
|
|
|
156
156
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
157
157
|
downloaded: List[Path] = []
|
|
158
158
|
for f in files:
|
|
159
|
+
# Skip associations with no blob
|
|
160
|
+
if not f.get("haveContent", True):
|
|
161
|
+
self._log(
|
|
162
|
+
f"Skip {item_number}: file {f.get('filename')} has no content"
|
|
163
|
+
)
|
|
164
|
+
continue
|
|
165
|
+
|
|
159
166
|
url = f.get("downloadUrl") or f.get("url")
|
|
160
167
|
filename = f.get("filename") or f.get("name")
|
|
161
168
|
if not url or not filename:
|
|
162
169
|
continue
|
|
170
|
+
|
|
163
171
|
p = out_dir / filename
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
r
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
try:
|
|
173
|
+
with self.session.get(
|
|
174
|
+
url,
|
|
175
|
+
stream=True,
|
|
176
|
+
headers={"arena_session_id": self.cfg.arena_session_id or ""},
|
|
177
|
+
) as r:
|
|
178
|
+
# If the blob is missing/forbidden, don’t abort the whole command
|
|
179
|
+
if r.status_code in (400, 403, 404):
|
|
180
|
+
self._log(
|
|
181
|
+
f"Skip {item_number}: {filename} content unavailable "
|
|
182
|
+
f"(HTTP {r.status_code})"
|
|
183
|
+
)
|
|
184
|
+
continue
|
|
185
|
+
r.raise_for_status()
|
|
186
|
+
with open(p, "wb") as fh:
|
|
187
|
+
for chunk in r.iter_content(128 * 1024):
|
|
188
|
+
fh.write(chunk)
|
|
189
|
+
downloaded.append(p)
|
|
190
|
+
except requests.HTTPError as e:
|
|
191
|
+
# Be resilient: log and continue
|
|
192
|
+
self._log(f"Download failed for {filename}: {e}")
|
|
193
|
+
continue
|
|
194
|
+
return downloaded
|
|
195
|
+
|
|
196
|
+
def download_files_recursive(
|
|
197
|
+
self,
|
|
198
|
+
item_number: str,
|
|
199
|
+
revision: Optional[str] = None,
|
|
200
|
+
out_dir: Path = Path("."),
|
|
201
|
+
*,
|
|
202
|
+
max_depth: Optional[int] = None,
|
|
203
|
+
) -> List[Path]:
|
|
204
|
+
"""
|
|
205
|
+
Download files for `item_number` AND, recursively, for all subassemblies
|
|
206
|
+
discovered via the BOM. Each child item is placed under a subdirectory:
|
|
207
|
+
<out_dir>/<child_item_number>/
|
|
208
|
+
Root files go directly in <out_dir>/.
|
|
209
|
+
|
|
210
|
+
Depth semantics match `get_bom(..., recursive=True, max_depth=...)`.
|
|
211
|
+
"""
|
|
212
|
+
# Ensure the root directory exists
|
|
213
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
214
|
+
|
|
215
|
+
downloaded: List[Path] = []
|
|
216
|
+
# 1) Download files for the root item into out_dir
|
|
217
|
+
downloaded.extend(self.download_files(item_number, revision, out_dir=out_dir))
|
|
218
|
+
|
|
219
|
+
# 2) Expand BOM recursively and collect unique child item numbers
|
|
220
|
+
lines = self.get_bom(
|
|
221
|
+
item_number,
|
|
222
|
+
revision,
|
|
223
|
+
recursive=True,
|
|
224
|
+
max_depth=max_depth,
|
|
225
|
+
)
|
|
226
|
+
seen_children = set()
|
|
227
|
+
for ln in lines:
|
|
228
|
+
child_num = (ln or {}).get("itemNumber")
|
|
229
|
+
if not child_num or child_num == item_number:
|
|
230
|
+
continue
|
|
231
|
+
if child_num in seen_children:
|
|
232
|
+
continue
|
|
233
|
+
seen_children.add(child_num)
|
|
234
|
+
|
|
235
|
+
# Place each child's files under <out_dir>/<child_num>/
|
|
236
|
+
child_dir = out_dir / child_num
|
|
237
|
+
downloaded.extend(
|
|
238
|
+
self.download_files(child_num, revision, out_dir=child_dir)
|
|
239
|
+
)
|
|
240
|
+
|
|
174
241
|
return downloaded
|
|
175
242
|
|
|
176
243
|
def upload_file_to_working(
|
|
@@ -406,18 +473,18 @@ class ArenaClient:
|
|
|
406
473
|
file_guid = f.get("guid") or f.get("id")
|
|
407
474
|
norm.append(
|
|
408
475
|
{
|
|
409
|
-
"id": row.get("guid") or row.get("id"),
|
|
410
|
-
"fileGuid": file_guid,
|
|
476
|
+
"id": row.get("guid") or row.get("id"),
|
|
477
|
+
"fileGuid": file_guid,
|
|
411
478
|
"name": f.get("name") or f.get("title"),
|
|
412
479
|
"filename": f.get("name") or f.get("title"),
|
|
413
480
|
"size": f.get("size"),
|
|
414
481
|
"checksum": f.get("checksum") or f.get("md5"),
|
|
482
|
+
"haveContent": f.get("haveContent", True),
|
|
415
483
|
"downloadUrl": (
|
|
416
484
|
f"{self._api_base()}/files/{file_guid}/content"
|
|
417
485
|
if file_guid
|
|
418
486
|
else None
|
|
419
487
|
),
|
|
420
|
-
# for “pick latest” helper:
|
|
421
488
|
"version": f.get("version") or f.get("edition"),
|
|
422
489
|
"updatedAt": f.get("lastModifiedDateTime")
|
|
423
490
|
or f.get("lastModifiedDate")
|
|
@@ -505,6 +572,7 @@ class ArenaClient:
|
|
|
505
572
|
"filename": f.get("name") or f.get("title"),
|
|
506
573
|
"size": f.get("size"),
|
|
507
574
|
"checksum": f.get("checksum") or f.get("md5"),
|
|
575
|
+
"haveContent": f.get("haveContent", True),
|
|
508
576
|
"downloadUrl": (
|
|
509
577
|
f"{self._api_base()}/files/{file_guid}/content"
|
|
510
578
|
if file_guid
|
gladiator/cli.py
CHANGED
|
@@ -221,10 +221,27 @@ def get_files(
|
|
|
221
221
|
"--out",
|
|
222
222
|
help="Output directory (default: a folder named after the item number)",
|
|
223
223
|
),
|
|
224
|
+
recursive: bool = typer.Option(
|
|
225
|
+
False,
|
|
226
|
+
"--recursive/--no-recursive",
|
|
227
|
+
help="Recursively download files from subassemblies",
|
|
228
|
+
),
|
|
229
|
+
max_depth: Optional[int] = typer.Option(
|
|
230
|
+
None,
|
|
231
|
+
"--max-depth",
|
|
232
|
+
min=1,
|
|
233
|
+
help="Maximum recursion depth for --recursive (1 = only direct children).",
|
|
234
|
+
),
|
|
224
235
|
):
|
|
225
236
|
try:
|
|
226
|
-
out_dir = out or Path(item)
|
|
227
|
-
|
|
237
|
+
out_dir = out or Path(item) # article numbers are never files; safe as dir name
|
|
238
|
+
if recursive:
|
|
239
|
+
paths = _client().download_files_recursive(
|
|
240
|
+
item, revision, out_dir=out_dir, max_depth=max_depth
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
paths = _client().download_files(item, revision, out_dir=out_dir)
|
|
244
|
+
|
|
228
245
|
for p in paths:
|
|
229
246
|
print(str(p))
|
|
230
247
|
except requests.HTTPError as e:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lr-gladiator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: CLI and Python client for Arena PLM (app.bom.com): login, get revisions, list/download attachments, and upload to working revisions.
|
|
5
5
|
Author-email: Jonas Estberger <jonas.estberger@lumenradio.com>
|
|
6
6
|
License: MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
gladiator/__init__.py,sha256=ZeHpVdzARFyIp9QbdTkX0jNqnbRFX5nFQ5RkEFzSRL0,208
|
|
2
|
+
gladiator/arena.py,sha256=x2dg-JImb9Lw1fIj0ptm48GF1m4zIj2pmg4GqmWBPbA,30285
|
|
3
|
+
gladiator/cli.py,sha256=Pi1yjRZDr2s4izq51xQgpUiKN-Z7wl7yQjHiht9J9BU,10295
|
|
4
|
+
gladiator/config.py,sha256=oe2UpFv1HcrP1-lVWs_nnex444Igq18BW3nTs9wL__k,1760
|
|
5
|
+
lr_gladiator-0.7.0.dist-info/licenses/LICENSE,sha256=2CEtbEagerjoU3EDSk-eTM5LKgI_RpiVIOh3_CV4kms,1069
|
|
6
|
+
lr_gladiator-0.7.0.dist-info/METADATA,sha256=RckucPmR2KG5YwcP9gq3yNf-21JYDhEtT_uAdnl32BM,1912
|
|
7
|
+
lr_gladiator-0.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
lr_gladiator-0.7.0.dist-info/entry_points.txt,sha256=SLka4w7iGS2B8HrbeZyNk5mxaIC6QKcv93us1OaWNwQ,48
|
|
9
|
+
lr_gladiator-0.7.0.dist-info/top_level.txt,sha256=tfrcAmK7_7Lf63w7kWy0wv_Qg9RrcFWGoins1-jGUF4,10
|
|
10
|
+
lr_gladiator-0.7.0.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
gladiator/__init__.py,sha256=ZeHpVdzARFyIp9QbdTkX0jNqnbRFX5nFQ5RkEFzSRL0,208
|
|
2
|
-
gladiator/arena.py,sha256=fy26XTNbJnC8XdeALZYNrzUhfbncQGUAU_hNvB3onDs,27747
|
|
3
|
-
gladiator/cli.py,sha256=B8zHulDQUldC-WQ37jhkCfyxEPV1KvY3FEsF-Af25Wk,9716
|
|
4
|
-
gladiator/config.py,sha256=oe2UpFv1HcrP1-lVWs_nnex444Igq18BW3nTs9wL__k,1760
|
|
5
|
-
lr_gladiator-0.6.0.dist-info/licenses/LICENSE,sha256=2CEtbEagerjoU3EDSk-eTM5LKgI_RpiVIOh3_CV4kms,1069
|
|
6
|
-
lr_gladiator-0.6.0.dist-info/METADATA,sha256=ohbCWnqTS1baU_cZiMtyBiYiqOtDj8AlkclIPzzl0U0,1912
|
|
7
|
-
lr_gladiator-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
lr_gladiator-0.6.0.dist-info/entry_points.txt,sha256=SLka4w7iGS2B8HrbeZyNk5mxaIC6QKcv93us1OaWNwQ,48
|
|
9
|
-
lr_gladiator-0.6.0.dist-info/top_level.txt,sha256=tfrcAmK7_7Lf63w7kWy0wv_Qg9RrcFWGoins1-jGUF4,10
|
|
10
|
-
lr_gladiator-0.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|