cmsdials 0.0.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 cmsdials might be problematic. Click here for more details.
- LICENSE +674 -0
- cmsdials/__init__.py +5 -0
- cmsdials/_version.py +1 -0
- cmsdials/auth/__init__.py +0 -0
- cmsdials/auth/_base.py +136 -0
- cmsdials/auth/bearer.py +140 -0
- cmsdials/auth/client.py +105 -0
- cmsdials/auth/exceptions.py +6 -0
- cmsdials/auth/models.py +25 -0
- cmsdials/auth/secret_key.py +42 -0
- cmsdials/clients/__init__.py +0 -0
- cmsdials/clients/dataset_index/__init__.py +0 -0
- cmsdials/clients/dataset_index/client.py +13 -0
- cmsdials/clients/dataset_index/models.py +30 -0
- cmsdials/clients/file_index/__init__.py +0 -0
- cmsdials/clients/file_index/client.py +13 -0
- cmsdials/clients/file_index/models.py +36 -0
- cmsdials/clients/h1d/__init__.py +0 -0
- cmsdials/clients/h1d/client.py +13 -0
- cmsdials/clients/h1d/models.py +47 -0
- cmsdials/clients/h2d/__init__.py +0 -0
- cmsdials/clients/h2d/client.py +13 -0
- cmsdials/clients/h2d/models.py +50 -0
- cmsdials/clients/lumisection/__init__.py +0 -0
- cmsdials/clients/lumisection/client.py +13 -0
- cmsdials/clients/lumisection/models.py +34 -0
- cmsdials/clients/mes/__init__.py +0 -0
- cmsdials/clients/mes/client.py +12 -0
- cmsdials/clients/mes/models.py +18 -0
- cmsdials/clients/ml_bad_lumisection/__init__.py +0 -0
- cmsdials/clients/ml_bad_lumisection/client.py +29 -0
- cmsdials/clients/ml_bad_lumisection/models.py +38 -0
- cmsdials/clients/ml_models_index/__init__.py +0 -0
- cmsdials/clients/ml_models_index/client.py +13 -0
- cmsdials/clients/ml_models_index/models.py +30 -0
- cmsdials/clients/oms_proxy/__init__.py +0 -0
- cmsdials/clients/oms_proxy/client.py +49 -0
- cmsdials/clients/oms_proxy/models.py +14 -0
- cmsdials/clients/run/__init__.py +0 -0
- cmsdials/clients/run/client.py +13 -0
- cmsdials/clients/run/models.py +29 -0
- cmsdials/cmsdials.py +27 -0
- cmsdials/filters.py +25 -0
- cmsdials/utils/__init__.py +0 -0
- cmsdials/utils/_json.py +24 -0
- cmsdials/utils/api_client.py +226 -0
- cmsdials/utils/base_model.py +33 -0
- cmsdials/utils/logger.py +6 -0
- cmsdials-0.0.0.dist-info/LICENSE +674 -0
- cmsdials-0.0.0.dist-info/METADATA +356 -0
- cmsdials-0.0.0.dist-info/RECORD +52 -0
- cmsdials-0.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AnyUrl, BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel, PaginatedBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LumisectionHistogram2D(BaseModel):
|
|
9
|
+
dataset: str = Field(..., max_length=255)
|
|
10
|
+
me: str = Field(..., max_length=255)
|
|
11
|
+
dataset_id: int
|
|
12
|
+
file_id: int
|
|
13
|
+
run_number: int
|
|
14
|
+
ls_number: int
|
|
15
|
+
me_id: int
|
|
16
|
+
x_min: float
|
|
17
|
+
x_max: float
|
|
18
|
+
x_bin: float
|
|
19
|
+
y_min: float
|
|
20
|
+
y_max: float
|
|
21
|
+
y_bin: float
|
|
22
|
+
entries: int
|
|
23
|
+
data: list[list[float]]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PaginatedLumisectionHistogram2DList(PaginatedBaseModel):
|
|
27
|
+
next: Optional[AnyUrl]
|
|
28
|
+
previous: Optional[AnyUrl]
|
|
29
|
+
results: list[LumisectionHistogram2D]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LumisectionHistogram2DFilters(OBaseModel):
|
|
33
|
+
next_token: Optional[str] = None
|
|
34
|
+
page_size: Optional[int] = None
|
|
35
|
+
dataset_id: Optional[int] = None
|
|
36
|
+
file_id: Optional[int] = None
|
|
37
|
+
run_number: Optional[int] = None
|
|
38
|
+
run_number__lte: Optional[int] = None
|
|
39
|
+
run_number__gte: Optional[int] = None
|
|
40
|
+
ls_number: Optional[int] = None
|
|
41
|
+
ls_numbet__lte: Optional[int] = None
|
|
42
|
+
ls_number__gte: Optional[int] = None
|
|
43
|
+
me_id: Optional[int] = None
|
|
44
|
+
entries__gte: Optional[int] = None
|
|
45
|
+
dataset: Optional[str] = None
|
|
46
|
+
dataset__regex: Optional[str] = None
|
|
47
|
+
logical_file_name: Optional[str] = None
|
|
48
|
+
logical_file_name__regex: Optional[str] = None
|
|
49
|
+
me: Optional[str] = None
|
|
50
|
+
me__regex: Optional[str] = None
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedAPIClient
|
|
2
|
+
from .models import Lumisection, LumisectionFilters, PaginatedLumisectionList
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LumisectionClient(BaseAuthorizedAPIClient):
|
|
6
|
+
data_model = Lumisection
|
|
7
|
+
pagination_model = PaginatedLumisectionList
|
|
8
|
+
filter_class = LumisectionFilters
|
|
9
|
+
lookup_url = "lumisection/"
|
|
10
|
+
|
|
11
|
+
def get(self, dataset_id: int, run_number: int, ls_number: int, **kwargs):
|
|
12
|
+
edp = f"{dataset_id}/{run_number}/{ls_number}/"
|
|
13
|
+
return super().get(edp, **kwargs)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AnyUrl, BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel, PaginatedBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Lumisection(BaseModel):
|
|
9
|
+
dataset_id: int
|
|
10
|
+
dataset: str = Field(..., max_length=255)
|
|
11
|
+
run_number: int
|
|
12
|
+
ls_number: int
|
|
13
|
+
th1_count: int
|
|
14
|
+
th2_count: int
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PaginatedLumisectionList(PaginatedBaseModel):
|
|
18
|
+
next: Optional[AnyUrl]
|
|
19
|
+
previous: Optional[AnyUrl]
|
|
20
|
+
results: list[Lumisection]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class LumisectionFilters(OBaseModel):
|
|
24
|
+
next_token: Optional[str] = None
|
|
25
|
+
page_size: Optional[int] = None
|
|
26
|
+
dataset_id: Optional[int] = None
|
|
27
|
+
run_number: Optional[int] = None
|
|
28
|
+
run_number__lte: Optional[int] = None
|
|
29
|
+
run_number__gte: Optional[int] = None
|
|
30
|
+
ls_number: Optional[int] = None
|
|
31
|
+
ls_number__lte: Optional[int] = None
|
|
32
|
+
ls_number__gte: Optional[int] = None
|
|
33
|
+
dataset: Optional[str] = None
|
|
34
|
+
dataset__regex: Optional[str] = None
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedAPIClient
|
|
2
|
+
from .models import MEFilters, MonitoringElement
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MonitoringElementClient(BaseAuthorizedAPIClient):
|
|
6
|
+
data_model = MonitoringElement
|
|
7
|
+
filter_class = MEFilters
|
|
8
|
+
lookup_url = "mes/"
|
|
9
|
+
|
|
10
|
+
def get(self, id: int, **kwargs): # noqa: A002
|
|
11
|
+
edp = f"{id}/"
|
|
12
|
+
return super().get(edp, **kwargs)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MonitoringElement(BaseModel):
|
|
9
|
+
me_id: int
|
|
10
|
+
me: str = Field(..., max_length=255)
|
|
11
|
+
count: int
|
|
12
|
+
dim: int
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MEFilters(OBaseModel):
|
|
16
|
+
me: Optional[str] = None
|
|
17
|
+
me__regex: Optional[str] = None
|
|
18
|
+
dim: Optional[int] = None
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedAPIClient
|
|
2
|
+
from .models import MLBadLumisection, MLBadLumisectionFilters, PaginatedMLBadLumisectionList
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MLBadLumisectionClient(BaseAuthorizedAPIClient):
|
|
6
|
+
data_model = MLBadLumisection
|
|
7
|
+
pagination_model = PaginatedMLBadLumisectionList
|
|
8
|
+
filter_class = MLBadLumisectionFilters
|
|
9
|
+
lookup_url = "ml-bad-lumisection/"
|
|
10
|
+
|
|
11
|
+
def get(self, model_id: int, dataset_id: int, run_number: int, ls_number: int, me_id: int, **kwargs):
|
|
12
|
+
edp = f"{model_id}/{dataset_id}/{run_number}/{ls_number}/{me_id}/"
|
|
13
|
+
return super().get(edp, **kwargs)
|
|
14
|
+
|
|
15
|
+
def cert_json(self, model_id__in: list[int], dataset_id__in: list[int], run_number__in: list[int], **kwargs):
|
|
16
|
+
edp = "cert-json/"
|
|
17
|
+
midin = ",".join(str(v) for v in model_id__in)
|
|
18
|
+
didin = ",".join(str(v) for v in dataset_id__in)
|
|
19
|
+
ridin = ",".join(str(v) for v in run_number__in)
|
|
20
|
+
params = {"model_id__in": midin, "dataset_id__in": didin, "run_number__in": ridin}
|
|
21
|
+
return super().get(edp, params=params, return_raw_json=True, **kwargs)
|
|
22
|
+
|
|
23
|
+
def golden_json(self, model_id__in: list[int], dataset_id__in: list[int], run_number__in: list[int], **kwargs):
|
|
24
|
+
edp = "golden-json/"
|
|
25
|
+
midin = ",".join(str(v) for v in model_id__in)
|
|
26
|
+
didin = ",".join(str(v) for v in dataset_id__in)
|
|
27
|
+
ridin = ",".join(str(v) for v in run_number__in)
|
|
28
|
+
params = {"model_id__in": midin, "dataset_id__in": didin, "run_number__in": ridin}
|
|
29
|
+
return super().get(edp, params=params, return_raw_json=True, **kwargs)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AnyUrl, BaseModel
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel, PaginatedBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MLBadLumisection(BaseModel):
|
|
9
|
+
model_id: int
|
|
10
|
+
dataset_id: int
|
|
11
|
+
file_id: int
|
|
12
|
+
run_number: int
|
|
13
|
+
ls_number: int
|
|
14
|
+
me_id: int
|
|
15
|
+
mse: float
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class PaginatedMLBadLumisectionList(PaginatedBaseModel):
|
|
19
|
+
next: Optional[AnyUrl]
|
|
20
|
+
previous: Optional[AnyUrl]
|
|
21
|
+
results: list[MLBadLumisection]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MLBadLumisectionFilters(OBaseModel):
|
|
25
|
+
next_token: Optional[str] = None
|
|
26
|
+
page_size: Optional[int] = None
|
|
27
|
+
model_id: Optional[int] = None
|
|
28
|
+
model_id__in: Optional[list[int]] = None
|
|
29
|
+
dataset_id: Optional[int] = None
|
|
30
|
+
dataset_id__in: Optional[list[int]] = None
|
|
31
|
+
dataset: Optional[str] = None
|
|
32
|
+
dataset__regex: Optional[str] = None
|
|
33
|
+
me_id: Optional[int] = None
|
|
34
|
+
me: Optional[str] = None
|
|
35
|
+
me__regex: Optional[str] = None
|
|
36
|
+
run_number: Optional[int] = None
|
|
37
|
+
run_number__in: Optional[list[int]] = None
|
|
38
|
+
ls_number: Optional[int] = None
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedAPIClient
|
|
2
|
+
from .models import MLModelsIndex, MLModelsIndexFilters, PaginatedMLModelsIndexList
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MLModelsIndexClient(BaseAuthorizedAPIClient):
|
|
6
|
+
data_model = MLModelsIndex
|
|
7
|
+
pagination_model = PaginatedMLModelsIndexList
|
|
8
|
+
filter_class = MLModelsIndexFilters
|
|
9
|
+
lookup_url = "ml-models-index/"
|
|
10
|
+
|
|
11
|
+
def get(self, model_id: int, **kwargs):
|
|
12
|
+
edp = f"{model_id}/"
|
|
13
|
+
return super().get(edp, **kwargs)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AnyUrl, BaseModel
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel, PaginatedBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MLModelsIndex(BaseModel):
|
|
9
|
+
model_id: int
|
|
10
|
+
filename: str
|
|
11
|
+
target_me: str
|
|
12
|
+
active: bool
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PaginatedMLModelsIndexList(PaginatedBaseModel):
|
|
16
|
+
next: Optional[AnyUrl]
|
|
17
|
+
previous: Optional[AnyUrl]
|
|
18
|
+
results: list[MLModelsIndex]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MLModelsIndexFilters(OBaseModel):
|
|
22
|
+
next_token: Optional[str] = None
|
|
23
|
+
page_size: Optional[int] = None
|
|
24
|
+
model_id: Optional[int] = None
|
|
25
|
+
model_id__in: Optional[list[int]] = None
|
|
26
|
+
filename: Optional[str] = None
|
|
27
|
+
filename__regex: Optional[str] = None
|
|
28
|
+
target_me: Optional[str] = None
|
|
29
|
+
target_me__regex: Optional[str] = None
|
|
30
|
+
active: Optional[bool] = None
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from requests.exceptions import HTTPError
|
|
5
|
+
|
|
6
|
+
from ...auth._base import BaseCredentials
|
|
7
|
+
from ...utils.api_client import BaseAPIClient
|
|
8
|
+
from ...utils.logger import logger
|
|
9
|
+
from .models import OMSFilter, OMSPage
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OMSProxyClient(BaseAPIClient):
|
|
13
|
+
default_timeout = 30
|
|
14
|
+
lookup_url = "oms-proxy/"
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
creds: BaseCredentials,
|
|
19
|
+
*args: str,
|
|
20
|
+
**kwargs: str,
|
|
21
|
+
) -> None:
|
|
22
|
+
super().__init__(*args, **kwargs)
|
|
23
|
+
self.creds = creds
|
|
24
|
+
|
|
25
|
+
def _build_headers(self) -> dict:
|
|
26
|
+
base = {
|
|
27
|
+
"accept": "application/json",
|
|
28
|
+
"User-Agent": self._build_user_agent()
|
|
29
|
+
}
|
|
30
|
+
self.creds.before_request(base)
|
|
31
|
+
return base
|
|
32
|
+
|
|
33
|
+
def query(self, endpoint: str, filters: list[OMSFilter], pages: Optional[list[OMSPage]] = None):
|
|
34
|
+
headers = self._build_headers()
|
|
35
|
+
endpoint_url = self.api_url + self.lookup_url
|
|
36
|
+
|
|
37
|
+
# Format filters
|
|
38
|
+
filters = {f"filter[{_filter.attribute_name}][{_filter.operator}]": _filter.value for _filter in filters}
|
|
39
|
+
pages = {f"page[{page.attribute_name}]": page.value for page in pages} if pages and len(pages) > 0 else {}
|
|
40
|
+
params = {"endpoint": endpoint, **filters, **pages}
|
|
41
|
+
|
|
42
|
+
response = requests.get(endpoint_url, headers=headers, params=params, timeout=self.default_timeout)
|
|
43
|
+
try:
|
|
44
|
+
response.raise_for_status()
|
|
45
|
+
except HTTPError as err:
|
|
46
|
+
logger.info(f"Api raw response: {response.text}")
|
|
47
|
+
raise err
|
|
48
|
+
|
|
49
|
+
return response.json()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Literal, Union
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class OMSFilter(BaseModel):
|
|
7
|
+
attribute_name: str
|
|
8
|
+
value: Union[str, int, float]
|
|
9
|
+
operator: Literal["EQ", "NEQ", "LT", "GT", "LE", "GE", "LIKE"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OMSPage(BaseModel):
|
|
13
|
+
attribute_name: str
|
|
14
|
+
value: int
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ...utils.api_client import BaseAuthorizedAPIClient
|
|
2
|
+
from .models import PaginatedRunList, Run, RunFilters
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RunClient(BaseAuthorizedAPIClient):
|
|
6
|
+
data_model = Run
|
|
7
|
+
pagination_model = PaginatedRunList
|
|
8
|
+
filter_class = RunFilters
|
|
9
|
+
lookup_url = "run/"
|
|
10
|
+
|
|
11
|
+
def get(self, dataset_id: int, run_number: int, **kwargs):
|
|
12
|
+
edp = f"{dataset_id}/{run_number}/"
|
|
13
|
+
return super().get(edp, **kwargs)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import AnyUrl, BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ...utils.base_model import OBaseModel, PaginatedBaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Run(BaseModel):
|
|
9
|
+
dataset_id: int
|
|
10
|
+
dataset: str = Field(..., max_length=255)
|
|
11
|
+
run_number: int
|
|
12
|
+
ls_count: int
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PaginatedRunList(PaginatedBaseModel):
|
|
16
|
+
next: Optional[AnyUrl]
|
|
17
|
+
previous: Optional[AnyUrl]
|
|
18
|
+
results: list[Run]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RunFilters(OBaseModel):
|
|
22
|
+
next_token: Optional[str] = None
|
|
23
|
+
page_size: Optional[int] = None
|
|
24
|
+
dataset_id: Optional[int] = None
|
|
25
|
+
run_number: Optional[int] = None
|
|
26
|
+
run_number__lte: Optional[int] = None
|
|
27
|
+
run_number__gte: Optional[int] = None
|
|
28
|
+
dataset: Optional[str] = None
|
|
29
|
+
dataset__regex: Optional[str] = None
|
cmsdials/cmsdials.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from .auth._base import BaseCredentials
|
|
4
|
+
from .clients.dataset_index.client import DatasetIndexClient
|
|
5
|
+
from .clients.file_index.client import FileIndexClient
|
|
6
|
+
from .clients.h1d.client import LumisectionHistogram1DClient
|
|
7
|
+
from .clients.h2d.client import LumisectionHistogram2DClient
|
|
8
|
+
from .clients.lumisection.client import LumisectionClient
|
|
9
|
+
from .clients.mes.client import MonitoringElementClient
|
|
10
|
+
from .clients.ml_bad_lumisection.client import MLBadLumisectionClient
|
|
11
|
+
from .clients.ml_models_index.client import MLModelsIndexClient
|
|
12
|
+
from .clients.oms_proxy.client import OMSProxyClient
|
|
13
|
+
from .clients.run.client import RunClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Dials:
|
|
17
|
+
def __init__(self, creds: BaseCredentials, workspace: Optional[str] = None, *args, **kwargs) -> None:
|
|
18
|
+
self.dataset_index = DatasetIndexClient(creds, workspace, *args, **kwargs)
|
|
19
|
+
self.file_index = FileIndexClient(creds, workspace, *args, **kwargs)
|
|
20
|
+
self.h1d = LumisectionHistogram1DClient(creds, workspace, *args, **kwargs)
|
|
21
|
+
self.h2d = LumisectionHistogram2DClient(creds, workspace, *args, **kwargs)
|
|
22
|
+
self.lumi = LumisectionClient(creds, workspace, *args, **kwargs)
|
|
23
|
+
self.run = RunClient(creds, workspace, *args, **kwargs)
|
|
24
|
+
self.mes = MonitoringElementClient(creds, workspace, *args, **kwargs)
|
|
25
|
+
self.oms = OMSProxyClient(creds, *args, **kwargs)
|
|
26
|
+
self.ml_bad_lumis = MLBadLumisectionClient(creds, workspace, *args, **kwargs)
|
|
27
|
+
self.ml_models_index = MLModelsIndexClient(creds, workspace, *args, **kwargs)
|
cmsdials/filters.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from .clients.dataset_index.models import DatasetIndexFilters
|
|
2
|
+
from .clients.file_index.models import FileIndexFilters
|
|
3
|
+
from .clients.h1d.models import LumisectionHistogram1DFilters
|
|
4
|
+
from .clients.h2d.models import LumisectionHistogram2DFilters
|
|
5
|
+
from .clients.lumisection.models import LumisectionFilters
|
|
6
|
+
from .clients.mes.models import MEFilters
|
|
7
|
+
from .clients.ml_bad_lumisection.models import MLBadLumisectionFilters
|
|
8
|
+
from .clients.ml_models_index.models import MLModelsIndexFilters
|
|
9
|
+
from .clients.oms_proxy.models import OMSFilter, OMSPage
|
|
10
|
+
from .clients.run.models import RunFilters
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"DatasetIndexFilters",
|
|
15
|
+
"FileIndexFilters",
|
|
16
|
+
"LumisectionHistogram1DFilters",
|
|
17
|
+
"LumisectionHistogram2DFilters",
|
|
18
|
+
"LumisectionFilters",
|
|
19
|
+
"RunFilters",
|
|
20
|
+
"MEFilters",
|
|
21
|
+
"MLBadLumisectionFilters",
|
|
22
|
+
"MLModelsIndexFilters",
|
|
23
|
+
"OMSFilter",
|
|
24
|
+
"OMSPage",
|
|
25
|
+
]
|
|
File without changes
|
cmsdials/utils/_json.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TokenEncoder(json.JSONEncoder):
|
|
6
|
+
def default(self, o):
|
|
7
|
+
if isinstance(o, datetime):
|
|
8
|
+
return o.isoformat()
|
|
9
|
+
|
|
10
|
+
return json.JSONEncoder.default(self, o)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TokenDecoder(json.JSONDecoder):
|
|
14
|
+
def __init__(self, *args, **kwargs):
|
|
15
|
+
json.JSONDecoder.__init__(self, *args, **kwargs, object_hook=self.object_hook)
|
|
16
|
+
|
|
17
|
+
def object_hook(self, obj):
|
|
18
|
+
ret = {}
|
|
19
|
+
for key, value in obj.items():
|
|
20
|
+
if key in ("expires_at", "refresh_expires_at"):
|
|
21
|
+
ret[key] = datetime.fromisoformat(value)
|
|
22
|
+
else:
|
|
23
|
+
ret[key] = value
|
|
24
|
+
return ret
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from importlib import util as importlib_util
|
|
3
|
+
from traceback import format_exc
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from warnings import warn
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
from pydantic import AnyUrl
|
|
9
|
+
from requests import Response, Session
|
|
10
|
+
from requests.adapters import DEFAULT_RETRIES, HTTPAdapter
|
|
11
|
+
from requests.exceptions import HTTPError
|
|
12
|
+
|
|
13
|
+
from .._version import __version__
|
|
14
|
+
from ..auth._base import BaseCredentials
|
|
15
|
+
from ..utils.logger import logger
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if importlib_util.find_spec("tqdm"):
|
|
19
|
+
from tqdm.auto import tqdm
|
|
20
|
+
|
|
21
|
+
TQDM_INSTALLED = True
|
|
22
|
+
else:
|
|
23
|
+
TQDM_INSTALLED = False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseAPIClient:
|
|
27
|
+
PRODUCTION_BASE_URL = "https://cmsdials-api.web.cern.ch/"
|
|
28
|
+
PRODUCTION_API_ROUTE = "api/"
|
|
29
|
+
PRODUCTION_API_VERSION = "v1/"
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self, base_url: Optional[str] = None, route: Optional[str] = None, version: Optional[str] = None
|
|
33
|
+
) -> None:
|
|
34
|
+
self.base_url = self.__endswithslash(base_url or self.PRODUCTION_BASE_URL)
|
|
35
|
+
self.route = self.__endswithslash(route or self.PRODUCTION_API_ROUTE)
|
|
36
|
+
self.version = self.__endswithslash(version or self.PRODUCTION_API_VERSION)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def __endswithslash(value: str) -> str:
|
|
40
|
+
if value.endswith("/") is False:
|
|
41
|
+
return value + "/"
|
|
42
|
+
return value
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def _requests_get_retriable(cls, *args, retries=DEFAULT_RETRIES, **kwargs) -> Response:
|
|
46
|
+
"""
|
|
47
|
+
requests.get() with an additional `retries` parameter.
|
|
48
|
+
|
|
49
|
+
Specify retries=<number of attempts - 1> for simple use cases.
|
|
50
|
+
For advanced usage, see https://docs.python-requests.org/en/latest/user/advanced/
|
|
51
|
+
"""
|
|
52
|
+
with Session() as s:
|
|
53
|
+
s.mount(cls.PRODUCTION_BASE_URL, HTTPAdapter(max_retries=retries))
|
|
54
|
+
ret = s.get(*args, **kwargs)
|
|
55
|
+
return ret
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _build_user_agent():
|
|
59
|
+
py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
60
|
+
req_version = requests.__version__
|
|
61
|
+
return f"dials-py/{__version__} (python/{py_version}, python-requests/{req_version})"
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def api_url(self):
|
|
65
|
+
return self.base_url + self.route + self.version
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class BaseAuthorizedAPIClient(BaseAPIClient):
|
|
69
|
+
data_model = None
|
|
70
|
+
pagination_model = None
|
|
71
|
+
filter_class = None
|
|
72
|
+
lookup_url = None
|
|
73
|
+
default_timeout = 30 # seconds
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
creds: BaseCredentials,
|
|
78
|
+
workspace: Optional[str] = None,
|
|
79
|
+
*args: str,
|
|
80
|
+
**kwargs: str,
|
|
81
|
+
) -> None:
|
|
82
|
+
super().__init__(*args, **kwargs)
|
|
83
|
+
self.creds = creds
|
|
84
|
+
self.workspace = workspace
|
|
85
|
+
|
|
86
|
+
def _build_headers(self) -> dict:
|
|
87
|
+
base = {
|
|
88
|
+
"accept": "application/json",
|
|
89
|
+
"User-Agent": self._build_user_agent()
|
|
90
|
+
}
|
|
91
|
+
if self.workspace is not None:
|
|
92
|
+
base["Workspace"] = self.workspace
|
|
93
|
+
self.creds.before_request(base)
|
|
94
|
+
return base
|
|
95
|
+
|
|
96
|
+
def get(self, edp: str, retries=DEFAULT_RETRIES, params: Optional[dict] = None, return_raw_json: bool = False):
|
|
97
|
+
endpoint_url = self.api_url + self.lookup_url + edp
|
|
98
|
+
headers = self._build_headers()
|
|
99
|
+
response = self._requests_get_retriable(
|
|
100
|
+
endpoint_url,
|
|
101
|
+
headers=headers,
|
|
102
|
+
params=params,
|
|
103
|
+
timeout=self.default_timeout,
|
|
104
|
+
retries=retries,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
response.raise_for_status()
|
|
109
|
+
except HTTPError as err:
|
|
110
|
+
logger.info(f"Api raw response: {response.text}")
|
|
111
|
+
raise err
|
|
112
|
+
|
|
113
|
+
response = response.json()
|
|
114
|
+
if return_raw_json:
|
|
115
|
+
return response
|
|
116
|
+
return self.data_model(**response)
|
|
117
|
+
|
|
118
|
+
def list(self, filters=None, retries=DEFAULT_RETRIES):
|
|
119
|
+
filters = filters or self.filter_class()
|
|
120
|
+
endpoint_url = self.api_url + self.lookup_url
|
|
121
|
+
headers = self._build_headers()
|
|
122
|
+
response = self._requests_get_retriable(
|
|
123
|
+
endpoint_url,
|
|
124
|
+
headers=headers,
|
|
125
|
+
params=filters.cleandict(),
|
|
126
|
+
timeout=self.default_timeout,
|
|
127
|
+
retries=retries,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
response.raise_for_status()
|
|
132
|
+
except HTTPError as err:
|
|
133
|
+
logger.info(f"Api raw response: {response.text}")
|
|
134
|
+
raise err
|
|
135
|
+
|
|
136
|
+
response = response.json()
|
|
137
|
+
if self.pagination_model:
|
|
138
|
+
return self.pagination_model(**response)
|
|
139
|
+
elif isinstance(response, list):
|
|
140
|
+
return [self.data_model(**res) for res in response]
|
|
141
|
+
|
|
142
|
+
raise ValueError("pagination model is None and response is not a list.")
|
|
143
|
+
|
|
144
|
+
def __list_sync(
|
|
145
|
+
self,
|
|
146
|
+
filters,
|
|
147
|
+
max_pages: Optional[int] = None,
|
|
148
|
+
enable_progress: bool = False,
|
|
149
|
+
retries=DEFAULT_RETRIES,
|
|
150
|
+
keep_failed: bool = False,
|
|
151
|
+
resume_from=None,
|
|
152
|
+
):
|
|
153
|
+
next_string: Optional[AnyUrl] = None
|
|
154
|
+
results = []
|
|
155
|
+
is_last_page = False
|
|
156
|
+
|
|
157
|
+
if resume_from is not None:
|
|
158
|
+
results = resume_from.results
|
|
159
|
+
next_string: AnyUrl = resume_from.next
|
|
160
|
+
if next_string is None and len(results):
|
|
161
|
+
warn(
|
|
162
|
+
"resume_from.next is None while resume_from.result is not empty, doing nothing.",
|
|
163
|
+
RuntimeWarning,
|
|
164
|
+
stacklevel=2,
|
|
165
|
+
)
|
|
166
|
+
is_last_page = True
|
|
167
|
+
|
|
168
|
+
total_pages = 0
|
|
169
|
+
use_tqdm = TQDM_INSTALLED and enable_progress
|
|
170
|
+
|
|
171
|
+
if use_tqdm:
|
|
172
|
+
progress = tqdm(desc="Progress", total=1)
|
|
173
|
+
|
|
174
|
+
while is_last_page is False:
|
|
175
|
+
next_token = dict(next_string.query_params()).get("next_token") if next_string else None
|
|
176
|
+
curr_filters = self.filter_class(**filters.model_dump())
|
|
177
|
+
curr_filters.next_token = next_token
|
|
178
|
+
try:
|
|
179
|
+
response = self.list(curr_filters, retries=retries)
|
|
180
|
+
except Exception as e: # noqa: BLE001
|
|
181
|
+
if use_tqdm:
|
|
182
|
+
progress.close()
|
|
183
|
+
|
|
184
|
+
if not keep_failed:
|
|
185
|
+
raise e
|
|
186
|
+
|
|
187
|
+
exc_formatted = format_exc()
|
|
188
|
+
warn(
|
|
189
|
+
"HTTP request failed, returning partial results. Exception: " + exc_formatted,
|
|
190
|
+
RuntimeWarning,
|
|
191
|
+
stacklevel=2,
|
|
192
|
+
)
|
|
193
|
+
return self.pagination_model(
|
|
194
|
+
next=next_string,
|
|
195
|
+
previous=None,
|
|
196
|
+
results=results,
|
|
197
|
+
exc_type=e.__class__.__name__,
|
|
198
|
+
exc_formatted=exc_formatted,
|
|
199
|
+
)
|
|
200
|
+
results.extend(response.results)
|
|
201
|
+
next_string: AnyUrl = response.next
|
|
202
|
+
is_last_page = next_string is None
|
|
203
|
+
total_pages += 1
|
|
204
|
+
max_pages_reached = max_pages and total_pages >= max_pages
|
|
205
|
+
if use_tqdm:
|
|
206
|
+
if is_last_page or max_pages_reached:
|
|
207
|
+
progress.update()
|
|
208
|
+
else:
|
|
209
|
+
progress.total = total_pages + 1
|
|
210
|
+
progress.update(1)
|
|
211
|
+
if max_pages_reached:
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
if use_tqdm:
|
|
215
|
+
progress.close()
|
|
216
|
+
|
|
217
|
+
return self.pagination_model(
|
|
218
|
+
next=next_string,
|
|
219
|
+
previous=None,
|
|
220
|
+
results=results, # No problem re-using last response count
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def list_all(self, filters, max_pages: Optional[int] = None, **kwargs):
|
|
224
|
+
if self.pagination_model is None:
|
|
225
|
+
return self.list(filters, retries=kwargs.get("retries", DEFAULT_RETRIES))
|
|
226
|
+
return self.__list_sync(filters, max_pages, **kwargs)
|