oceanprotocol-job-details 0.0.8__tar.gz → 0.0.9__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.
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/PKG-INFO +11 -2
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/README.md +10 -1
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/dataclasses/constants.py +8 -4
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/dataclasses/job_details.py +17 -10
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/job_details.py +12 -3
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/loaders/impl/map.py +48 -35
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/loaders/loader.py +9 -2
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/pyproject.toml +1 -1
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/LICENSE +0 -0
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/__init__.py +0 -0
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/dataclasses/__init__.py +0 -0
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/loaders/__init__.py +0 -0
- {oceanprotocol_job_details-0.0.8 → oceanprotocol_job_details-0.0.9}/oceanprotocol_job_details/loaders/impl/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oceanprotocol-job-details
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.9
|
|
4
4
|
Summary: A Python package to get details from OceanProtocol jobs
|
|
5
5
|
License: Copyright 2025 Agrospai
|
|
6
6
|
|
|
@@ -41,6 +41,15 @@ from oceanprotocol_job_details.job_details import OceanProtocolJobDetails
|
|
|
41
41
|
job_details = OceanProtocolJobDetails().load()
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
Assumes the following directory structure:
|
|
45
|
+
```
|
|
46
|
+
<ROOT_FOLDER>
|
|
47
|
+
└───data
|
|
48
|
+
├───ddos
|
|
49
|
+
├───inputs
|
|
50
|
+
└───logs
|
|
51
|
+
```
|
|
52
|
+
|
|
44
53
|
### Advanced Usage (not recommended)
|
|
45
54
|
|
|
46
55
|
If instead of the environment variables, we want to use another kind of mapping, can pass it as a parameter and it will work as long as it has the same key values (Can be implemented in a more generic way, but there is no need right now).
|
|
@@ -51,9 +60,9 @@ from oceanprotocol_job_details.loaders.impl.environment import Keys
|
|
|
51
60
|
|
|
52
61
|
# Fill in with values that will be used instead of env
|
|
53
62
|
custom_mapper = {
|
|
63
|
+
Keys.ROOT_FOLDER: " ... ", # Use when you don't want the algorithm to take '/' as base Path
|
|
54
64
|
Keys.ALGORITHM: " ... ",
|
|
55
65
|
Keys.DIDS: " ... ",
|
|
56
|
-
Keys.ROOT: " ... ",
|
|
57
66
|
Keys.SECRET: " ... ",
|
|
58
67
|
}
|
|
59
68
|
|
|
@@ -19,6 +19,15 @@ from oceanprotocol_job_details.job_details import OceanProtocolJobDetails
|
|
|
19
19
|
job_details = OceanProtocolJobDetails().load()
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
Assumes the following directory structure:
|
|
23
|
+
```
|
|
24
|
+
<ROOT_FOLDER>
|
|
25
|
+
└───data
|
|
26
|
+
├───ddos
|
|
27
|
+
├───inputs
|
|
28
|
+
└───logs
|
|
29
|
+
```
|
|
30
|
+
|
|
22
31
|
### Advanced Usage (not recommended)
|
|
23
32
|
|
|
24
33
|
If instead of the environment variables, we want to use another kind of mapping, can pass it as a parameter and it will work as long as it has the same key values (Can be implemented in a more generic way, but there is no need right now).
|
|
@@ -29,9 +38,9 @@ from oceanprotocol_job_details.loaders.impl.environment import Keys
|
|
|
29
38
|
|
|
30
39
|
# Fill in with values that will be used instead of env
|
|
31
40
|
custom_mapper = {
|
|
41
|
+
Keys.ROOT_FOLDER: " ... ", # Use when you don't want the algorithm to take '/' as base Path
|
|
32
42
|
Keys.ALGORITHM: " ... ",
|
|
33
43
|
Keys.DIDS: " ... ",
|
|
34
|
-
Keys.ROOT: " ... ",
|
|
35
44
|
Keys.SECRET: " ... ",
|
|
36
45
|
}
|
|
37
46
|
|
|
@@ -2,7 +2,9 @@ from dataclasses import dataclass
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
@dataclass(
|
|
5
|
+
@dataclass(
|
|
6
|
+
frozen=True,
|
|
7
|
+
)
|
|
6
8
|
class _DidKeys:
|
|
7
9
|
"""Common keys inside the DIDs"""
|
|
8
10
|
|
|
@@ -13,18 +15,20 @@ class _DidKeys:
|
|
|
13
15
|
FILES: str = "files"
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
@dataclass(
|
|
18
|
+
@dataclass(
|
|
19
|
+
frozen=True,
|
|
20
|
+
)
|
|
17
21
|
class _ServiceType:
|
|
18
22
|
"""Service types inside the DIDs"""
|
|
19
23
|
|
|
20
24
|
METADATA: str = "metadata"
|
|
21
25
|
|
|
22
26
|
|
|
23
|
-
@dataclass(
|
|
27
|
+
@dataclass()
|
|
24
28
|
class _Paths:
|
|
25
29
|
"""Common paths used in the Ocean Protocol directories"""
|
|
26
30
|
|
|
27
|
-
DATA: Path = Path("data")
|
|
31
|
+
DATA: Path = Path("/data")
|
|
28
32
|
|
|
29
33
|
INPUTS: Path = DATA / "inputs"
|
|
30
34
|
DDOS: Path = DATA / "ddos"
|
|
@@ -12,7 +12,9 @@ _MetadataType = Mapping[str, Any]
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
@dataclass(
|
|
15
|
+
@dataclass(
|
|
16
|
+
frozen=True,
|
|
17
|
+
)
|
|
16
18
|
class Parameters:
|
|
17
19
|
"""Custom data for the algorithm, such as the algorithm's parameters"""
|
|
18
20
|
|
|
@@ -20,7 +22,9 @@ class Parameters:
|
|
|
20
22
|
"""The parameters used by the algorithm"""
|
|
21
23
|
|
|
22
24
|
|
|
23
|
-
@dataclass(
|
|
25
|
+
@dataclass(
|
|
26
|
+
frozen=True,
|
|
27
|
+
)
|
|
24
28
|
class Algorithm:
|
|
25
29
|
"""Details of the algorithm used to process the data"""
|
|
26
30
|
|
|
@@ -35,9 +39,6 @@ class Algorithm:
|
|
|
35
39
|
class JobDetails:
|
|
36
40
|
"""Details of the current job, such as the used inputs and algorithm"""
|
|
37
41
|
|
|
38
|
-
root: Path
|
|
39
|
-
"""The root folder of the Ocean Protocol directories"""
|
|
40
|
-
|
|
41
42
|
dids: Sequence[Path]
|
|
42
43
|
"""Identifiers for the inputs"""
|
|
43
44
|
|
|
@@ -53,22 +54,28 @@ class JobDetails:
|
|
|
53
54
|
# Cache parameters, should not be included as _fields_ of the class
|
|
54
55
|
_parameters: InitVar[Optional[_MetadataType]] = None
|
|
55
56
|
|
|
56
|
-
def __post_init__(
|
|
57
|
-
|
|
57
|
+
def __post_init__(
|
|
58
|
+
self,
|
|
59
|
+
_,
|
|
60
|
+
):
|
|
61
|
+
os.makedirs(Paths.LOGS, exist_ok=True)
|
|
58
62
|
|
|
59
63
|
logging.getLogger().addHandler(
|
|
60
64
|
logging.FileHandler(
|
|
61
|
-
|
|
65
|
+
Paths.LOGS / "job_details.log",
|
|
62
66
|
mode="w",
|
|
63
67
|
)
|
|
64
68
|
)
|
|
65
69
|
|
|
66
70
|
@property
|
|
67
|
-
def parameters(
|
|
71
|
+
def parameters(
|
|
72
|
+
self,
|
|
73
|
+
parameters: Optional[Path] = None,
|
|
74
|
+
) -> _MetadataType:
|
|
68
75
|
"""Parameters for algorithm job, read from default path"""
|
|
69
76
|
|
|
70
77
|
if parameters is None:
|
|
71
|
-
parameters =
|
|
78
|
+
parameters = Paths.ALGORITHM_CUSTOM_PARAMETERS
|
|
72
79
|
|
|
73
80
|
if self._parameters is None:
|
|
74
81
|
if not parameters.exists():
|
|
@@ -18,7 +18,9 @@ logging.basicConfig(
|
|
|
18
18
|
_Implementations = Literal["env"]
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class OceanProtocolJobDetails(
|
|
21
|
+
class OceanProtocolJobDetails(
|
|
22
|
+
Loader[JobDetails],
|
|
23
|
+
):
|
|
22
24
|
"""Decorator that loads the JobDetails from the given implementation"""
|
|
23
25
|
|
|
24
26
|
def __init__(
|
|
@@ -31,11 +33,18 @@ class OceanProtocolJobDetails(Loader[JobDetails]):
|
|
|
31
33
|
):
|
|
32
34
|
if implementation == "map":
|
|
33
35
|
# As there are not more implementations, we can use the EnvironmentLoader directly
|
|
34
|
-
self._loader = lambda: Map(
|
|
36
|
+
self._loader = lambda: Map(
|
|
37
|
+
mapper=mapper,
|
|
38
|
+
keys=keys,
|
|
39
|
+
*args,
|
|
40
|
+
**kwargs,
|
|
41
|
+
)
|
|
35
42
|
else:
|
|
36
43
|
raise NotImplementedError(f"Implementation {implementation} not supported")
|
|
37
44
|
|
|
38
|
-
def load(
|
|
45
|
+
def load(
|
|
46
|
+
self,
|
|
47
|
+
) -> JobDetails:
|
|
39
48
|
return self._loader().load()
|
|
40
49
|
|
|
41
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Loads the current Job Details from the environment variables, could be abstracted to a more general 'mapper loader' but won't, since right now it fits our needs"""
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, asdict
|
|
4
4
|
from json import JSONDecodeError, load, loads
|
|
5
5
|
from logging import getLogger
|
|
6
6
|
from pathlib import Path
|
|
@@ -13,56 +13,66 @@ from oceanprotocol_job_details.loaders.loader import Loader
|
|
|
13
13
|
logger = getLogger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@dataclass(
|
|
16
|
+
@dataclass(
|
|
17
|
+
frozen=True,
|
|
18
|
+
)
|
|
17
19
|
class Keys:
|
|
18
20
|
"""Environment keys passed to the algorithm"""
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
ROOT_FOLDER = "ROOT_FOLDER"
|
|
21
23
|
SECRET: str = "secret"
|
|
22
24
|
ALGORITHM: str = "TRANSFORMATION_DID"
|
|
23
25
|
DIDS: str = "DIDS"
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
@final
|
|
27
|
-
class Map(
|
|
29
|
+
class Map(
|
|
30
|
+
Loader[JobDetails],
|
|
31
|
+
):
|
|
28
32
|
"""Loads the current Job Details from the environment variables"""
|
|
29
33
|
|
|
30
|
-
def __init__(
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
mapper: Mapping[str, str],
|
|
37
|
+
keys: Keys,
|
|
38
|
+
*args,
|
|
39
|
+
**kwargs,
|
|
40
|
+
) -> None:
|
|
31
41
|
super().__init__(*args, **kwargs)
|
|
32
42
|
|
|
33
43
|
self._mapper = mapper
|
|
34
44
|
self._keys = keys
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
# Update the default Paths if the user has passed a root folder
|
|
47
|
+
if Keys.ROOT_FOLDER in self._mapper:
|
|
48
|
+
root = self._mapper[Keys.ROOT_FOLDER]
|
|
49
|
+
|
|
50
|
+
# Update the rest of paths
|
|
51
|
+
Paths.DATA = Path(root) / "data"
|
|
52
|
+
Paths.INPUTS = Paths.DATA / "inputs"
|
|
53
|
+
Paths.DDOS = Paths.DATA / "ddos"
|
|
54
|
+
Paths.OUTPUTS = Paths.DATA / "outputs"
|
|
55
|
+
Paths.LOGS = Paths.DATA / "logs"
|
|
56
|
+
Paths.ALGORITHM_CUSTOM_PARAMETERS = Paths.INPUTS / "algoCustomData.json"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load(
|
|
60
|
+
self,
|
|
61
|
+
*args,
|
|
62
|
+
**kwargs,
|
|
63
|
+
) -> JobDetails:
|
|
64
|
+
dids = self._dids()
|
|
38
65
|
|
|
39
66
|
return JobDetails(
|
|
40
|
-
root=root,
|
|
41
67
|
dids=dids,
|
|
42
|
-
files=self._files(
|
|
43
|
-
algorithm=self._algorithm(
|
|
68
|
+
files=self._files(dids),
|
|
69
|
+
algorithm=self._algorithm(),
|
|
44
70
|
secret=self._secret(),
|
|
45
71
|
)
|
|
46
72
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Raises:
|
|
52
|
-
FileNotFoundError: If the given root folder does not exist
|
|
53
|
-
|
|
54
|
-
Returns:
|
|
55
|
-
Path: the root folder
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
root = Path(self._mapper.get(self._keys.ROOT, Path("/")))
|
|
59
|
-
|
|
60
|
-
if not root.exists():
|
|
61
|
-
raise FileNotFoundError(f"Root folder {root} does not exist")
|
|
62
|
-
|
|
63
|
-
return root
|
|
64
|
-
|
|
65
|
-
def _dids(self) -> Sequence[Path]:
|
|
73
|
+
def _dids(
|
|
74
|
+
self,
|
|
75
|
+
) -> Sequence[Path]:
|
|
66
76
|
return (
|
|
67
77
|
loads(self._mapper.get(self._keys.DIDS))
|
|
68
78
|
if self._keys.DIDS in self._mapper
|
|
@@ -71,14 +81,13 @@ class Map(Loader[JobDetails]):
|
|
|
71
81
|
|
|
72
82
|
def _files(
|
|
73
83
|
self,
|
|
74
|
-
root: Path,
|
|
75
84
|
dids: Optional[Sequence[Path]],
|
|
76
85
|
) -> Mapping[str, Sequence[Path]]:
|
|
77
86
|
files: Mapping[str, Sequence[Path]] = {}
|
|
78
87
|
|
|
79
88
|
for did in dids:
|
|
80
89
|
# Retrieve DDO from disk
|
|
81
|
-
file_path =
|
|
90
|
+
file_path = Paths.DDOS / did
|
|
82
91
|
if not file_path.exists():
|
|
83
92
|
raise FileNotFoundError(f"DDO file {file_path} does not exist")
|
|
84
93
|
|
|
@@ -93,7 +102,7 @@ class Map(Loader[JobDetails]):
|
|
|
93
102
|
if service[DidKeys.SERVICE_TYPE] != ServiceType.METADATA:
|
|
94
103
|
continue
|
|
95
104
|
|
|
96
|
-
did_path =
|
|
105
|
+
did_path = Paths.INPUTS / did
|
|
97
106
|
files[did] = [
|
|
98
107
|
did_path / str(idx)
|
|
99
108
|
for idx in range(
|
|
@@ -105,17 +114,21 @@ class Map(Loader[JobDetails]):
|
|
|
105
114
|
|
|
106
115
|
return files
|
|
107
116
|
|
|
108
|
-
def _algorithm(
|
|
117
|
+
def _algorithm(
|
|
118
|
+
self,
|
|
119
|
+
) -> Optional[Algorithm]:
|
|
109
120
|
did = self._mapper.get(self._keys.ALGORITHM, None)
|
|
110
121
|
|
|
111
122
|
if not did:
|
|
112
123
|
return None
|
|
113
124
|
|
|
114
|
-
ddo =
|
|
125
|
+
ddo = Paths.DDOS / did
|
|
115
126
|
if not ddo.exists():
|
|
116
127
|
raise FileNotFoundError(f"DDO file {ddo} does not exist")
|
|
117
128
|
|
|
118
129
|
return Algorithm(did, ddo)
|
|
119
130
|
|
|
120
|
-
def _secret(
|
|
131
|
+
def _secret(
|
|
132
|
+
self,
|
|
133
|
+
) -> Optional[str]:
|
|
121
134
|
return self._mapper.get(self._keys.SECRET, None)
|
|
@@ -5,9 +5,16 @@ from typing import Generic, TypeVar
|
|
|
5
5
|
T = TypeVar("T")
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class Loader(
|
|
8
|
+
class Loader(
|
|
9
|
+
ABC,
|
|
10
|
+
Generic[T],
|
|
11
|
+
):
|
|
9
12
|
@abstractmethod
|
|
10
|
-
def load(
|
|
13
|
+
def load(
|
|
14
|
+
self,
|
|
15
|
+
*args,
|
|
16
|
+
**kwargs,
|
|
17
|
+
) -> T:
|
|
11
18
|
"""Load an instance of the given type"""
|
|
12
19
|
pass
|
|
13
20
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|