pdmv-http-client 2.1.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.
- pdmv_http_client-2.1.0.dist-info/METADATA +68 -0
- pdmv_http_client-2.1.0.dist-info/RECORD +26 -0
- pdmv_http_client-2.1.0.dist-info/WHEEL +5 -0
- pdmv_http_client-2.1.0.dist-info/licenses/LICENSE +21 -0
- pdmv_http_client-2.1.0.dist-info/top_level.txt +1 -0
- rest/__init__.py +10 -0
- rest/_version.py +34 -0
- rest/applications/__init__.py +0 -0
- rest/applications/base.py +238 -0
- rest/applications/mcm/__init__.py +0 -0
- rest/applications/mcm/core.py +277 -0
- rest/applications/mcm/invalidate_request.py +399 -0
- rest/applications/mcm/resubmission.py +431 -0
- rest/applications/rereco/core.py +78 -0
- rest/applications/stats/core.py +95 -0
- rest/client/__init__.py +0 -0
- rest/client/auth/__init__.py +0 -0
- rest/client/auth/auth_interface.py +102 -0
- rest/client/auth/handlers/__init__.py +0 -0
- rest/client/auth/handlers/oauth2_tokens.py +307 -0
- rest/client/auth/handlers/session_cookies.py +110 -0
- rest/client/session.py +99 -0
- rest/utils/__init__.py +0 -0
- rest/utils/logger.py +36 -0
- rest/utils/miscellaneous.py +47 -0
- rest/utils/shell.py +57 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from rest import McM
|
|
5
|
+
from rest.applications.mcm.invalidate_request import InvalidateDeleteRequests
|
|
6
|
+
from rest.utils.miscellaneous import pformat
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RootRequestReset(Enum):
|
|
10
|
+
"""
|
|
11
|
+
Describes how a root request was reset so that
|
|
12
|
+
it is reusable in later steps to recreate its
|
|
13
|
+
chained requests.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# Root request was soft reset.
|
|
17
|
+
SOFT_RESET = 1
|
|
18
|
+
|
|
19
|
+
# Root request was fully reset.
|
|
20
|
+
# This request was validated before, so
|
|
21
|
+
# instead of repeating the validation,
|
|
22
|
+
# old results are reincluded again.
|
|
23
|
+
FULL_RESET = 2
|
|
24
|
+
|
|
25
|
+
# Root request keeps output
|
|
26
|
+
# (it created a big "RAW" dataset being used in other steps, chains, ...)
|
|
27
|
+
# Handle it so that it is not required to recreate it
|
|
28
|
+
# again.
|
|
29
|
+
KEEPS_OUTPUT = 3
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ChainRequestResubmitter:
|
|
33
|
+
"""
|
|
34
|
+
Rewinds a chained request to its root
|
|
35
|
+
so that you can recreate the chain and attempt to
|
|
36
|
+
inject it again.
|
|
37
|
+
|
|
38
|
+
The scenario to use this is the following: Some configuration
|
|
39
|
+
applied in a campaign or chained campaign was wrong so that, after
|
|
40
|
+
applying a patch on these elements (that work as a template) you
|
|
41
|
+
need to recreate all the requests using this new change and reinject them.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
mcm: McM client instance
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, mcm: McM) -> None:
|
|
48
|
+
self._mcm = mcm
|
|
49
|
+
self._invalidator = InvalidateDeleteRequests(mcm=self._mcm)
|
|
50
|
+
self.logger = self._mcm.logger
|
|
51
|
+
|
|
52
|
+
def _root_request_to_approve(self, root_prepid: str) -> RootRequestReset:
|
|
53
|
+
"""
|
|
54
|
+
This is a customization for resetting a root request so
|
|
55
|
+
that it ends up in an 'approve/approved' status and could
|
|
56
|
+
be easily reused to recreate a chained request and inject it
|
|
57
|
+
again.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
root_prepid: Root request's prepid
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
An attribute describing how the root request was processed.
|
|
64
|
+
"""
|
|
65
|
+
data: Union[dict, None] = self._mcm.get(
|
|
66
|
+
object_type="requests", object_id=root_prepid
|
|
67
|
+
)
|
|
68
|
+
if not data:
|
|
69
|
+
raise ValueError(f"Root request {root_prepid} not found")
|
|
70
|
+
|
|
71
|
+
approval = data.get("approval")
|
|
72
|
+
status = data.get("status")
|
|
73
|
+
if approval == "submit" and status == "done":
|
|
74
|
+
keep_output: Union[list[bool], None] = data.get("keep_output")
|
|
75
|
+
if not keep_output:
|
|
76
|
+
raise ValueError("Keep output is not defined")
|
|
77
|
+
|
|
78
|
+
if all(keep_output):
|
|
79
|
+
# Avoid resetting this kind of root request
|
|
80
|
+
# Just reuse it as it is
|
|
81
|
+
return RootRequestReset.KEEPS_OUTPUT
|
|
82
|
+
else:
|
|
83
|
+
self.logger.warning(
|
|
84
|
+
(
|
|
85
|
+
"Root request (%s) is already done, resetting it, "
|
|
86
|
+
"announcing the invalidation, and setting it to approve/approved"
|
|
87
|
+
),
|
|
88
|
+
root_prepid,
|
|
89
|
+
)
|
|
90
|
+
validation: Union[dict, None] = data.get("validation")
|
|
91
|
+
self.logger.debug("Validation results: %s", validation)
|
|
92
|
+
if not validation:
|
|
93
|
+
raise ValueError("Validation results not available")
|
|
94
|
+
|
|
95
|
+
# Reset the request.
|
|
96
|
+
reset_result = self._mcm.reset(prepid=root_prepid)
|
|
97
|
+
if not reset_result:
|
|
98
|
+
msg = f"Unable to reset the root request: {root_prepid}"
|
|
99
|
+
self.logger.error(msg)
|
|
100
|
+
raise RuntimeError(msg)
|
|
101
|
+
|
|
102
|
+
# Announce the invalidation.
|
|
103
|
+
self._invalidator._process_invalidation(request_prepids=[root_prepid])
|
|
104
|
+
|
|
105
|
+
# Force its status to approve/approved, re-including the validation.
|
|
106
|
+
data: dict = self._mcm.get(
|
|
107
|
+
object_type="requests", object_id=root_prepid
|
|
108
|
+
)
|
|
109
|
+
assert isinstance(data, dict)
|
|
110
|
+
|
|
111
|
+
data["validation"] = validation
|
|
112
|
+
data["approval"] = "approve"
|
|
113
|
+
data["status"] = "approved"
|
|
114
|
+
|
|
115
|
+
updated_result = self._mcm.update(
|
|
116
|
+
object_type="requests", object_data=data
|
|
117
|
+
)
|
|
118
|
+
if not updated_result or not updated_result.get("results"):
|
|
119
|
+
msg = f"Error forcing the root request status: {root_prepid}"
|
|
120
|
+
self.logger.error(msg)
|
|
121
|
+
raise RuntimeError(msg)
|
|
122
|
+
|
|
123
|
+
return RootRequestReset.FULL_RESET
|
|
124
|
+
else:
|
|
125
|
+
# Soft reset request
|
|
126
|
+
soft_reset_result = self._mcm.soft_reset(prepid=root_prepid)
|
|
127
|
+
self.logger.info("Soft reset result: %s", soft_reset_result)
|
|
128
|
+
if not soft_reset_result:
|
|
129
|
+
msg = (
|
|
130
|
+
f"Unable to soft reset the request ({root_prepid}) "
|
|
131
|
+
"and set its status to approve/approved"
|
|
132
|
+
)
|
|
133
|
+
self.logger.error(msg)
|
|
134
|
+
raise RuntimeError(msg)
|
|
135
|
+
|
|
136
|
+
return RootRequestReset.SOFT_RESET
|
|
137
|
+
|
|
138
|
+
def _include_tag(self, request_prepid: str, tag: str) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Includes a new tag for the `request`
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
request_prepid (str): Request identifier
|
|
144
|
+
tag (str): New tag to include
|
|
145
|
+
"""
|
|
146
|
+
request_data: dict = self._mcm.get("requests", object_id=request_prepid)
|
|
147
|
+
assert isinstance(request_data, dict)
|
|
148
|
+
|
|
149
|
+
request_data["tags"] += [tag]
|
|
150
|
+
tag_result = self._mcm.update("requests", request_data)
|
|
151
|
+
self.logger.debug("Include `tag` result: %s", tag_result)
|
|
152
|
+
if not tag_result or not tag_result.get("results"):
|
|
153
|
+
msg = f"Unable to include a tracking tag for the root request: {request_prepid}"
|
|
154
|
+
self.logger.error(msg)
|
|
155
|
+
raise RuntimeError(msg)
|
|
156
|
+
|
|
157
|
+
def _pick_target_campaign(
|
|
158
|
+
self, chained_campaign_prepid: str, datatier: str, soft_datatier: bool
|
|
159
|
+
) -> Union[str, None]:
|
|
160
|
+
"""
|
|
161
|
+
Retrieves the campaign related to the desired data tier for a given chained campaign
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
chained_campaign_prepid: Chained campaign identifier.
|
|
165
|
+
datatier: Data tier related to the campaign to retrieve.
|
|
166
|
+
soft_datatier: Force using the latest campaign found in the chained request
|
|
167
|
+
if there is none related to the requested data tier.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Campaign related to the data tier, None if not found
|
|
171
|
+
"""
|
|
172
|
+
chained_campaign: Union[dict, None] = self._mcm.get(
|
|
173
|
+
object_type="chained_campaigns", object_id=chained_campaign_prepid
|
|
174
|
+
)
|
|
175
|
+
if not chained_campaign:
|
|
176
|
+
raise ValueError(f"Chained campaign not found: {chained_campaign_prepid}")
|
|
177
|
+
|
|
178
|
+
campaigns: list[list[str]] = chained_campaign.get("campaigns", [])
|
|
179
|
+
for campaign_range in campaigns:
|
|
180
|
+
for c in campaign_range:
|
|
181
|
+
c_str = c or ""
|
|
182
|
+
if c_str.startswith("Run") and datatier.lower() in c_str.lower():
|
|
183
|
+
return c_str
|
|
184
|
+
|
|
185
|
+
self.logger.debug(
|
|
186
|
+
"There's no campaign related to the data tier %s for the chained campaign %s: %s",
|
|
187
|
+
datatier,
|
|
188
|
+
chained_campaign_prepid,
|
|
189
|
+
campaigns,
|
|
190
|
+
)
|
|
191
|
+
if soft_datatier:
|
|
192
|
+
latest_campaign_range = campaigns[-1]
|
|
193
|
+
for c in latest_campaign_range:
|
|
194
|
+
c_str = c or ""
|
|
195
|
+
if c_str.startswith("Run"):
|
|
196
|
+
self.logger.debug("Using the latest available campaign: %s", c_str)
|
|
197
|
+
return c_str
|
|
198
|
+
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
def _reserve_chain_request(
|
|
202
|
+
self, chain_request_prepid: str, datatier: str, soft_datatier: bool
|
|
203
|
+
) -> None:
|
|
204
|
+
"""
|
|
205
|
+
Reserve a chained request up to a desired data tier. This creates the
|
|
206
|
+
remaining requests in the chained request based on a chained campaign
|
|
207
|
+
definition (template).
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
chain_request_prepid: Chained request identifier.
|
|
211
|
+
datatier: Limits the requests created up to this data tier.
|
|
212
|
+
soft_datatier: Force using the latest campaign found in the chained request
|
|
213
|
+
if there is none related to the requested data tier.
|
|
214
|
+
"""
|
|
215
|
+
chained_request: Union[dict, None] = self._mcm.get(
|
|
216
|
+
object_type="chained_requests", object_id=chain_request_prepid
|
|
217
|
+
)
|
|
218
|
+
if not chained_request:
|
|
219
|
+
raise ValueError(f"Chain request not found: {chain_request_prepid}")
|
|
220
|
+
|
|
221
|
+
# Pick the `member_of_campaign` attribute and look
|
|
222
|
+
# for the chained campaign. This has the info for the correct
|
|
223
|
+
# campaign modifications (campaigns and flows, customizations)
|
|
224
|
+
member_of_campaign = chained_request.get("member_of_campaign")
|
|
225
|
+
if not member_of_campaign:
|
|
226
|
+
raise ValueError(
|
|
227
|
+
f"Member of campaign is not set for {chain_request_prepid}"
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
target_campaign = self._pick_target_campaign(
|
|
231
|
+
chained_campaign_prepid=member_of_campaign,
|
|
232
|
+
datatier=datatier,
|
|
233
|
+
soft_datatier=soft_datatier,
|
|
234
|
+
)
|
|
235
|
+
if not target_campaign:
|
|
236
|
+
msg = (
|
|
237
|
+
f"There's no campaign related to the desired data tier ({datatier}) in "
|
|
238
|
+
f"the linked chained campaign ({member_of_campaign}) that could "
|
|
239
|
+
"be used to reserve the chained request"
|
|
240
|
+
)
|
|
241
|
+
raise ValueError(msg)
|
|
242
|
+
|
|
243
|
+
# Reserve the chain
|
|
244
|
+
reserve_endpoint = f"restapi/chained_requests/flow/{chain_request_prepid}/reserve/{target_campaign}"
|
|
245
|
+
reserve_result = self._mcm._get(url=reserve_endpoint)
|
|
246
|
+
if not reserve_result or not reserve_result.get("results", False):
|
|
247
|
+
raise RuntimeError(
|
|
248
|
+
f"Unable to reserve the chained request ({chain_request_prepid}). Details: {reserve_result}"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def _approve_request_until(
|
|
252
|
+
self, request_prepid: str, approval: str, status: str
|
|
253
|
+
) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Approves one request until a desired state.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
request_prepid: Request identifier.
|
|
259
|
+
approval: Desired approval for the request.
|
|
260
|
+
status: Desired status for the request.
|
|
261
|
+
"""
|
|
262
|
+
request: Union[dict, None] = self._mcm.get(
|
|
263
|
+
object_type="requests", object_id=request_prepid
|
|
264
|
+
)
|
|
265
|
+
if not request:
|
|
266
|
+
raise ValueError(f"Request not found: {request_prepid}")
|
|
267
|
+
|
|
268
|
+
is_root: bool = self._mcm.is_root_request(request=request)
|
|
269
|
+
attempts = 10
|
|
270
|
+
for _ in range(attempts):
|
|
271
|
+
request = self._mcm.get(object_type="requests", object_id=request_prepid)
|
|
272
|
+
req_approval = request.get("approval")
|
|
273
|
+
req_status = request.get("status")
|
|
274
|
+
|
|
275
|
+
if approval == req_approval and status == req_status:
|
|
276
|
+
return
|
|
277
|
+
|
|
278
|
+
approve_result = self._mcm.approve(
|
|
279
|
+
object_type="requests", object_id=request_prepid
|
|
280
|
+
)
|
|
281
|
+
if not approve_result or not approve_result.get("results"):
|
|
282
|
+
if approve_result and is_root:
|
|
283
|
+
message = approve_result.get("message", "")
|
|
284
|
+
if "Illegal Approval Step: 5" in message:
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
msg = (
|
|
288
|
+
"Unable to approve the request to the next status - "
|
|
289
|
+
f"Request PrepID: {request_prepid} - "
|
|
290
|
+
f"Current approval/status: {req_approval}/{req_status} - "
|
|
291
|
+
f"Details: {pformat(approve_result)}"
|
|
292
|
+
)
|
|
293
|
+
self.logger.error(msg)
|
|
294
|
+
raise RuntimeError(msg)
|
|
295
|
+
|
|
296
|
+
raise RuntimeError("Unable to get the desired approval status")
|
|
297
|
+
|
|
298
|
+
def _perform_chain_request_injection(
|
|
299
|
+
self, chain_request_prepid: str, root_request_processing: RootRequestReset
|
|
300
|
+
) -> None:
|
|
301
|
+
"""
|
|
302
|
+
Injects a chained request based on how its root request was reset.
|
|
303
|
+
This MUST BE executed only after rewinding a chained request to root and reserving it again.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
chain_request_prepid: Chained request identifier.
|
|
307
|
+
root_request_processing: Describes how the root request was reset.
|
|
308
|
+
"""
|
|
309
|
+
chained_request: Union[dict, None] = self._mcm.get(
|
|
310
|
+
object_type="chained_requests", object_id=chain_request_prepid
|
|
311
|
+
)
|
|
312
|
+
if not chained_request:
|
|
313
|
+
raise ValueError(f"Chain request not found: {chain_request_prepid}")
|
|
314
|
+
|
|
315
|
+
if root_request_processing == RootRequestReset.KEEPS_OUTPUT:
|
|
316
|
+
# Just flow the chained request
|
|
317
|
+
flow_result = self._mcm.flow(chained_request_prepid=chain_request_prepid)
|
|
318
|
+
if not flow_result:
|
|
319
|
+
msg = f"Unable to flow the following chained request: {chain_request_prepid}"
|
|
320
|
+
self.logger.error(msg)
|
|
321
|
+
raise RuntimeError(msg)
|
|
322
|
+
|
|
323
|
+
elif root_request_processing in (
|
|
324
|
+
RootRequestReset.SOFT_RESET,
|
|
325
|
+
RootRequestReset.FULL_RESET,
|
|
326
|
+
):
|
|
327
|
+
# Retrieve the newly created requests and
|
|
328
|
+
# make sure their approval/state is approve/approved
|
|
329
|
+
requests_in_chain = chained_request.get("chain", [])
|
|
330
|
+
to_approve = requests_in_chain[1:]
|
|
331
|
+
root_request_in_chain = requests_in_chain[0]
|
|
332
|
+
self.logger.info("Making sure non-root requests are approve/approved")
|
|
333
|
+
for req_prepid in to_approve:
|
|
334
|
+
self._approve_request_until(
|
|
335
|
+
request_prepid=req_prepid,
|
|
336
|
+
approval="approve",
|
|
337
|
+
status="approved",
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Operate the root request in the chain and
|
|
341
|
+
# make sure its state is submit/submitted.
|
|
342
|
+
# INFO: This takes time ~3 to 5 min.
|
|
343
|
+
self.logger.info("Injecting chained request")
|
|
344
|
+
self._approve_request_until(
|
|
345
|
+
request_prepid=root_request_in_chain,
|
|
346
|
+
approval="submit",
|
|
347
|
+
status="submitted",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
else:
|
|
351
|
+
raise NotImplementedError(
|
|
352
|
+
"There's no way to inject a chained request based on: %s",
|
|
353
|
+
root_request_processing,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def resubmit_chain_request(
|
|
357
|
+
self,
|
|
358
|
+
root_request_prepid: str,
|
|
359
|
+
datatier: str = "nanoaod",
|
|
360
|
+
soft_datatier: bool = False,
|
|
361
|
+
tracking_tag: Union[str, None] = None,
|
|
362
|
+
) -> None:
|
|
363
|
+
"""
|
|
364
|
+
Resubmit all the chained requests that reference a particular root request.
|
|
365
|
+
To achieve this, this procedure rewinds all the chained requests to the root step,
|
|
366
|
+
deleting and invalidating all the found requests except for the root one.
|
|
367
|
+
Then, it recreates them by reserving the chain to the desired data tier and finally reinjects them.
|
|
368
|
+
|
|
369
|
+
If you require only to resubmit a very specific part (some particular step like MiniAOD or NanoAOD)
|
|
370
|
+
of a chained request, avoid using this. Rewind the chain to the previous step manually and resubmit it.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
root_request_prepid: Root request identifier.
|
|
374
|
+
datatier: Limit data tier for reserving all the chained requests.
|
|
375
|
+
soft_datatier: Force using the latest campaign found in the chained request
|
|
376
|
+
if there is none related to the requested data tier.
|
|
377
|
+
tracking_tag: Includes a tracking tag for the root request
|
|
378
|
+
to monitor its progress after patching this.
|
|
379
|
+
"""
|
|
380
|
+
if not self._mcm.is_root_request(request=root_request_prepid):
|
|
381
|
+
raise ValueError(
|
|
382
|
+
f"Provided request ({root_request_prepid}) is not a root request"
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
chained_requests: list[dict] = self._mcm.get(
|
|
386
|
+
object_type="chained_requests", query=f"contains={root_request_prepid}"
|
|
387
|
+
)
|
|
388
|
+
chained_requests_prepid: list[str] = [
|
|
389
|
+
ch["prepid"] for ch in chained_requests if ch.get("prepid")
|
|
390
|
+
]
|
|
391
|
+
self.logger.info(
|
|
392
|
+
"Resubmitting the following chained request related to %s: %s",
|
|
393
|
+
root_request_prepid,
|
|
394
|
+
pformat(chained_requests_prepid),
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Operate the chained requests so that only the root request remains
|
|
398
|
+
self.logger.info(
|
|
399
|
+
"Rewinding chained requests to root and deleting intermediate requests"
|
|
400
|
+
)
|
|
401
|
+
self._invalidator.invalidate_delete_cascade_requests(
|
|
402
|
+
requests_prepid=[root_request_prepid]
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Reset the root request and include the tag
|
|
406
|
+
root_processing = self._root_request_to_approve(root_prepid=root_request_prepid)
|
|
407
|
+
if tracking_tag:
|
|
408
|
+
self.logger.info(
|
|
409
|
+
"Including tracking tag (%s) for the root request: %s",
|
|
410
|
+
tracking_tag,
|
|
411
|
+
root_request_prepid,
|
|
412
|
+
)
|
|
413
|
+
self._include_tag(request_prepid=root_request_prepid, tag=tracking_tag)
|
|
414
|
+
|
|
415
|
+
# Reserve the chained requests
|
|
416
|
+
for ch_r in chained_requests_prepid:
|
|
417
|
+
self.logger.info(
|
|
418
|
+
"Reserving chained requests (%s) up to data tier: %s", ch_r, datatier
|
|
419
|
+
)
|
|
420
|
+
self._reserve_chain_request(
|
|
421
|
+
chain_request_prepid=ch_r,
|
|
422
|
+
datatier=datatier,
|
|
423
|
+
soft_datatier=soft_datatier,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
# Reinject the chained requests
|
|
427
|
+
for ch_r in chained_requests_prepid:
|
|
428
|
+
self.logger.info("Reinjecting chained requests: %s", ch_r)
|
|
429
|
+
self._perform_chain_request_injection(
|
|
430
|
+
chain_request_prepid=ch_r, root_request_processing=root_processing
|
|
431
|
+
)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REST client for the ReReco application
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from rest.applications.base import BaseClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ReReco(BaseClient):
|
|
11
|
+
"""
|
|
12
|
+
Initializes an HTTP client for querying ReReco.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
id: str = BaseClient.SSO,
|
|
18
|
+
debug: bool = False,
|
|
19
|
+
cookie: Union[str, None] = None,
|
|
20
|
+
dev: bool = True,
|
|
21
|
+
client_id: str = "",
|
|
22
|
+
client_secret: str = "",
|
|
23
|
+
):
|
|
24
|
+
|
|
25
|
+
# Set the HTTP session
|
|
26
|
+
super().__init__(
|
|
27
|
+
app="rereco",
|
|
28
|
+
id=id,
|
|
29
|
+
debug=debug,
|
|
30
|
+
cookie=cookie,
|
|
31
|
+
dev=dev,
|
|
32
|
+
client_id=client_id,
|
|
33
|
+
client_secret=client_secret,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def get(self, object_type, object_id=None, query=None, method="get", page=-1):
|
|
37
|
+
object_type = object_type.strip()
|
|
38
|
+
if object_id:
|
|
39
|
+
object_id = object_id.strip()
|
|
40
|
+
url = "api/%s/%s/%s" % (object_type, method, object_id)
|
|
41
|
+
result = self._get(url).get("response")
|
|
42
|
+
if not result:
|
|
43
|
+
return None
|
|
44
|
+
return result
|
|
45
|
+
elif query:
|
|
46
|
+
if page != -1:
|
|
47
|
+
url = "api/search/?db_name=%s&page=%d&%s" % (
|
|
48
|
+
object_type,
|
|
49
|
+
page,
|
|
50
|
+
query,
|
|
51
|
+
)
|
|
52
|
+
results = self._get(url).get("response", {}).get("results", [])
|
|
53
|
+
return results
|
|
54
|
+
else:
|
|
55
|
+
page_results = [{}]
|
|
56
|
+
results = []
|
|
57
|
+
page = 0
|
|
58
|
+
while page_results:
|
|
59
|
+
page_results = self.get(
|
|
60
|
+
object_type=object_type, query=query, method=method, page=page
|
|
61
|
+
)
|
|
62
|
+
results += page_results
|
|
63
|
+
page += 1
|
|
64
|
+
|
|
65
|
+
return results
|
|
66
|
+
else:
|
|
67
|
+
# nothing to do
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
def put(self, object_type, object_data, method="create"):
|
|
71
|
+
url = "api/%s/%s" % (object_type, method)
|
|
72
|
+
res = self._put(url, object_data)
|
|
73
|
+
return res
|
|
74
|
+
|
|
75
|
+
def post(self, object_type, object_data, method="update"):
|
|
76
|
+
url = "api/%s/%s" % (object_type, method)
|
|
77
|
+
res = self._post(url, object_data)
|
|
78
|
+
return res
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
REST client for the Stats2 application.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
from rest.applications.base import BaseClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Stats2(BaseClient):
|
|
11
|
+
"""
|
|
12
|
+
Initializes an HTTP client for querying Stats2.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
id: str = BaseClient.SSO,
|
|
18
|
+
debug: bool = False,
|
|
19
|
+
cookie: Union[str, None] = None,
|
|
20
|
+
dev: bool = True,
|
|
21
|
+
client_id: str = "",
|
|
22
|
+
client_secret: str = "",
|
|
23
|
+
):
|
|
24
|
+
# Set the HTTP session
|
|
25
|
+
super().__init__(
|
|
26
|
+
app="stats",
|
|
27
|
+
id=id,
|
|
28
|
+
debug=debug,
|
|
29
|
+
cookie=cookie,
|
|
30
|
+
dev=dev,
|
|
31
|
+
client_id=client_id,
|
|
32
|
+
client_secret=client_secret,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def get_workflow(self, workflow_name: str) -> dict:
|
|
36
|
+
"""
|
|
37
|
+
Retrieves the Stats2 document related to the given workflow.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
workflow_name: ReqMgr2 Request ID (a.k.a workflow in PdmV applications).
|
|
41
|
+
"""
|
|
42
|
+
url = f"api/get_json/{workflow_name}"
|
|
43
|
+
return self._get(url=url)
|
|
44
|
+
|
|
45
|
+
def update_workflow(self, workflow_name: str) -> dict:
|
|
46
|
+
"""
|
|
47
|
+
Updates the content of a document, from dbs and reqmgr
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
workflow_name: ReqMgr2 Request ID (a.k.a workflow in PdmV applications).
|
|
51
|
+
"""
|
|
52
|
+
url = f"api/update?workflow_name={workflow_name}"
|
|
53
|
+
return self._get(url=url)
|
|
54
|
+
|
|
55
|
+
def get_prepid(self, prepid: str) -> list[dict]:
|
|
56
|
+
"""
|
|
57
|
+
Retrieves a list of Stats2 documents related to the given prepid.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
prepid: The main identifier for requests in PdmV applications.
|
|
61
|
+
"""
|
|
62
|
+
url = f"api/fetch?prepid={prepid}"
|
|
63
|
+
return self._get(url=url)
|
|
64
|
+
|
|
65
|
+
def get_input_dataset(self, input_dataset: str) -> list[dict]:
|
|
66
|
+
"""
|
|
67
|
+
Retrieves a list of Stats2 documents related to the given input dataset.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
input_dataset: Input dataset name. It follows DBS and DAS
|
|
71
|
+
schema.
|
|
72
|
+
"""
|
|
73
|
+
url = f"api/fetch?input_dataset={input_dataset}"
|
|
74
|
+
return self._get(url=url)
|
|
75
|
+
|
|
76
|
+
def get_output_dataset(self, output_dataset: str) -> list[dict]:
|
|
77
|
+
"""
|
|
78
|
+
Retrieves the Stats2 document related to the given output dataset.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
output_dataset: Output dataset name. It follows DBS and DAS
|
|
82
|
+
schema.
|
|
83
|
+
"""
|
|
84
|
+
url = f"api/fetch?output_dataset={output_dataset}"
|
|
85
|
+
return self._get(url=url)
|
|
86
|
+
|
|
87
|
+
def get_request(self, prepid: str):
|
|
88
|
+
"""
|
|
89
|
+
Retrieves the Stats2 document related to the request.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
prepid: Request's prepid. This is only useful for McM requests.
|
|
93
|
+
"""
|
|
94
|
+
url = f"api/fetch?request={prepid}"
|
|
95
|
+
return self._get(url=url)
|
rest/client/__init__.py
ADDED
|
File without changes
|
|
File without changes
|