oceanprotocol-job-details 0.0.2__py3-none-any.whl → 0.0.6__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.
Files changed (24) hide show
  1. oceanprotocol_job_details/dataclasses/constants.py +3 -0
  2. oceanprotocol_job_details/dataclasses/job_details.py +67 -6
  3. oceanprotocol_job_details/job_details.py +27 -5
  4. oceanprotocol_job_details/loaders/impl/map.py +112 -0
  5. {oceanprotocol_job_details-0.0.2.dist-info → oceanprotocol_job_details-0.0.6.dist-info}/METADATA +3 -4
  6. oceanprotocol_job_details-0.0.6.dist-info/RECORD +13 -0
  7. oceanprotocol_job_details/dataclasses/__pycache__/__init__.cpython-313.pyc +0 -0
  8. oceanprotocol_job_details/dataclasses/__pycache__/__init__.cpython-39.pyc +0 -0
  9. oceanprotocol_job_details/dataclasses/__pycache__/constants.cpython-313.pyc +0 -0
  10. oceanprotocol_job_details/dataclasses/__pycache__/constants.cpython-39.pyc +0 -0
  11. oceanprotocol_job_details/dataclasses/__pycache__/job_details.cpython-313.pyc +0 -0
  12. oceanprotocol_job_details/dataclasses/__pycache__/job_details.cpython-39.pyc +0 -0
  13. oceanprotocol_job_details/loaders/__pycache__/__init__.cpython-313.pyc +0 -0
  14. oceanprotocol_job_details/loaders/__pycache__/__init__.cpython-39.pyc +0 -0
  15. oceanprotocol_job_details/loaders/__pycache__/loader.cpython-313.pyc +0 -0
  16. oceanprotocol_job_details/loaders/__pycache__/loader.cpython-39.pyc +0 -0
  17. oceanprotocol_job_details/loaders/impl/__pycache__/__init__.cpython-313.pyc +0 -0
  18. oceanprotocol_job_details/loaders/impl/__pycache__/__init__.cpython-39.pyc +0 -0
  19. oceanprotocol_job_details/loaders/impl/__pycache__/environment.cpython-313.pyc +0 -0
  20. oceanprotocol_job_details/loaders/impl/__pycache__/environment.cpython-39.pyc +0 -0
  21. oceanprotocol_job_details/loaders/impl/environment.py +0 -98
  22. oceanprotocol_job_details-0.0.2.dist-info/RECORD +0 -27
  23. {oceanprotocol_job_details-0.0.2.dist-info → oceanprotocol_job_details-0.0.6.dist-info}/LICENSE +0 -0
  24. {oceanprotocol_job_details-0.0.2.dist-info → oceanprotocol_job_details-0.0.6.dist-info}/WHEEL +0 -0
@@ -25,11 +25,14 @@ class _Paths:
25
25
  """Common paths used in the Ocean Protocol directories"""
26
26
 
27
27
  DATA: Path = Path("data")
28
+
28
29
  INPUTS: Path = DATA / "inputs"
29
30
  DDOS: Path = DATA / "ddos"
30
31
  OUTPUTS: Path = DATA / "outputs"
31
32
  LOGS: Path = DATA / "logs"
32
33
 
34
+ ALGORITHM_CUSTOM_PARAMETERS: Path = INPUTS / "algoCustomData.json"
35
+
33
36
 
34
37
  DidKeys = _DidKeys()
35
38
  ServiceType = _ServiceType()
@@ -1,10 +1,29 @@
1
- from dataclasses import dataclass
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import InitVar, dataclass
2
5
  from pathlib import Path
3
6
  from typing import Any, Mapping, Optional, Sequence
4
7
 
8
+ from oceanprotocol_job_details.dataclasses.constants import Paths
9
+
10
+ _MetadataType = Mapping[str, Any]
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class Parameters:
17
+ """Custom data for the algorithm, such as the algorithm's parameters"""
18
+
19
+ parameters: _MetadataType
20
+ """The parameters used by the algorithm"""
21
+
5
22
 
6
23
  @dataclass(frozen=True)
7
24
  class Algorithm:
25
+ """Details of the algorithm used to process the data"""
26
+
8
27
  did: str
9
28
  """The DID of the algorithm used to process the data"""
10
29
 
@@ -12,19 +31,16 @@ class Algorithm:
12
31
  """The DDO path of the algorithm used to process the data"""
13
32
 
14
33
 
15
- @dataclass(frozen=True)
34
+ @dataclass
16
35
  class JobDetails:
17
36
  """Details of the current job, such as the used inputs and algorithm"""
18
37
 
19
38
  root: Path
20
39
  """The root folder of the Ocean Protocol directories"""
21
40
 
22
- dids: Optional[Sequence[Path]]
41
+ dids: Sequence[Path]
23
42
  """Identifiers for the inputs"""
24
43
 
25
- metadata: Mapping[str, Any]
26
- """TODO: To define"""
27
-
28
44
  files: Mapping[str, Sequence[Path]]
29
45
  """Paths to the input files"""
30
46
 
@@ -33,3 +49,48 @@ class JobDetails:
33
49
 
34
50
  algorithm: Optional[Algorithm]
35
51
  """Details of the used algorithm"""
52
+
53
+ # Cache parameters, should not be included as _fields_ of the class
54
+ _parameters: InitVar[Optional[_MetadataType]] = None
55
+
56
+ def __post_init__(self, _):
57
+ os.makedirs(self.root / Paths.LOGS, exist_ok=True)
58
+
59
+ logging.getLogger().addHandler(
60
+ logging.FileHandler(
61
+ self.root / Paths.LOGS / "job_details.log",
62
+ mode="w",
63
+ )
64
+ )
65
+
66
+ @property
67
+ def parameters(self, parameters: Optional[Path] = None) -> _MetadataType:
68
+ """Parameters for algorithm job, read from default path"""
69
+
70
+ if parameters is None:
71
+ parameters = self.root / Paths.ALGORITHM_CUSTOM_PARAMETERS
72
+
73
+ if self._parameters is None:
74
+ if not parameters.exists():
75
+ logging.warning(
76
+ f"Parameters file {parameters} not found, supplying empty"
77
+ )
78
+ self._parameters = {}
79
+ else:
80
+ # Load the parameters from filesystem
81
+ with open(parameters, "r") as f:
82
+ try:
83
+ self._parameters = json.load(f)
84
+ except json.JSONDecodeError as e:
85
+ self._parameters = {}
86
+ logger.warning(
87
+ f"Error loading parameters file {parameters}: {e}"
88
+ )
89
+
90
+ return self._parameters
91
+
92
+
93
+ del _MetadataType
94
+
95
+
96
+ __all__ = ["Algorithm", "Parameters", "JobDetails"]
@@ -1,7 +1,19 @@
1
- from typing import Literal, Optional
1
+ import logging
2
+ import os
3
+ from typing import Any, Literal, Mapping, Optional
4
+
2
5
  from oceanprotocol_job_details.dataclasses.job_details import JobDetails
6
+ from oceanprotocol_job_details.loaders.impl.map import Keys, Map
3
7
  from oceanprotocol_job_details.loaders.loader import Loader
4
- from oceanprotocol_job_details.loaders.impl.environment import EnvironmentLoader
8
+
9
+ # Logging setup for the module
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s",
13
+ handlers=[
14
+ logging.StreamHandler(),
15
+ ],
16
+ )
5
17
 
6
18
  _Implementations = Literal["env"]
7
19
 
@@ -9,9 +21,19 @@ _Implementations = Literal["env"]
9
21
  class OceanProtocolJobDetails(Loader[JobDetails]):
10
22
  """Decorator that loads the JobDetails from the given implementation"""
11
23
 
12
- def __init__(self, implementation: Optional[_Implementations], *args, **kwargs):
13
- # As there are not more implementations, we can use the EnvironmentLoader directly
14
- self._loader = lambda: EnvironmentLoader(*args, **kwargs)
24
+ def __init__(
25
+ self,
26
+ implementation: Optional[_Implementations] = "map",
27
+ mapper: Mapping[str, Any] = os.environ,
28
+ keys: Keys = Keys(),
29
+ *args,
30
+ **kwargs,
31
+ ):
32
+ if implementation == "map":
33
+ # As there are not more implementations, we can use the EnvironmentLoader directly
34
+ self._loader = lambda: Map(mapper=mapper, keys=keys, *args, **kwargs)
35
+ else:
36
+ raise NotImplementedError(f"Implementation {implementation} not supported")
15
37
 
16
38
  def load(self) -> JobDetails:
17
39
  return self._loader().load()
@@ -0,0 +1,112 @@
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
+
3
+ from dataclasses import dataclass
4
+ from json import JSONDecodeError, load, loads
5
+ from logging import getLogger
6
+ from pathlib import Path
7
+ from typing import Mapping, Optional, Sequence, final
8
+
9
+ from oceanprotocol_job_details.dataclasses.constants import DidKeys, Paths, ServiceType
10
+ from oceanprotocol_job_details.dataclasses.job_details import Algorithm, JobDetails
11
+ from oceanprotocol_job_details.loaders.loader import Loader
12
+
13
+ logger = getLogger(__name__)
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Keys:
18
+ """Environment keys passed to the algorithm"""
19
+
20
+ ROOT: str = "ROOT_FOLDER"
21
+ SECRET: str = "secret"
22
+ ALGORITHM: str = "TRANSFORMATION_DID"
23
+ DIDS: str = "DIDS"
24
+
25
+
26
+ @final
27
+ class Map(Loader[JobDetails]):
28
+ """Loads the current Job Details from the environment variables"""
29
+
30
+ def __init__(self, mapper: Mapping[str, str], keys: Keys, *args, **kwargs):
31
+ super().__init__(*args, **kwargs)
32
+
33
+ self._mapper = mapper
34
+ self._keys = keys
35
+
36
+ def load(self, *args, **kwargs) -> JobDetails:
37
+ root, dids = self._root(), self._dids()
38
+
39
+ return JobDetails(
40
+ root=root,
41
+ dids=dids,
42
+ files=self._files(root, dids),
43
+ algorithm=self._algorithm(root),
44
+ secret=self._secret(),
45
+ )
46
+
47
+ def _root(self) -> Path:
48
+ root = Path(self._mapper.get(self._keys.ROOT, Path.home()))
49
+
50
+ if not root.exists():
51
+ raise FileNotFoundError(f"Root folder {root} does not exist")
52
+
53
+ return root
54
+
55
+ def _dids(self) -> Sequence[Path]:
56
+ return (
57
+ loads(self._mapper.get(self._keys.DIDS))
58
+ if self._keys.DIDS in self._mapper
59
+ else []
60
+ )
61
+
62
+ def _files(
63
+ self,
64
+ root: Path,
65
+ dids: Optional[Sequence[Path]],
66
+ ) -> Mapping[str, Sequence[Path]]:
67
+
68
+ files: Mapping[str, Sequence[Path]] = {}
69
+
70
+ for did in dids:
71
+ # Retrieve DDO from disk
72
+ file_path = root / Paths.DDOS / did
73
+ if not file_path.exists():
74
+ raise FileNotFoundError(f"DDO file {file_path} does not exist")
75
+
76
+ with open(file_path, "r") as f:
77
+ try:
78
+ ddo = load(f)
79
+ except JSONDecodeError as e:
80
+ logger.warning(f"Error loading DDO file {file_path}: {e}")
81
+ continue
82
+
83
+ for service in ddo[DidKeys.SERVICE]:
84
+ if service[DidKeys.SERVICE_TYPE] != ServiceType.METADATA:
85
+ continue
86
+
87
+ did_path = root / Paths.INPUTS / did
88
+ files[did] = [
89
+ did_path / str(idx)
90
+ for idx in range(
91
+ len(
92
+ service[DidKeys.ATTRIBUTES][DidKeys.MAIN][DidKeys.FILES]
93
+ )
94
+ )
95
+ ]
96
+
97
+ return files
98
+
99
+ def _algorithm(self, root: Path) -> Optional[Algorithm]:
100
+ did = self._mapper.get(self._keys.ALGORITHM, None)
101
+
102
+ if not did:
103
+ return None
104
+
105
+ ddo = root / Paths.DDOS / did
106
+ if not ddo.exists():
107
+ raise FileNotFoundError(f"DDO file {ddo} does not exist")
108
+
109
+ return Algorithm(did, ddo)
110
+
111
+ def _secret(self) -> Optional[str]:
112
+ return self._mapper.get(self._keys.SECRET, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oceanprotocol-job-details
3
- Version: 0.0.2
3
+ Version: 0.0.6
4
4
  Summary: A Python package to get details from OceanProtocol jobs
5
5
  License: Copyright 2025 Agrospai
6
6
 
@@ -39,8 +39,6 @@ from oceanprotocol_job_details.job_details import OceanProtocolJobDetails
39
39
 
40
40
  # Using default parameters
41
41
  job_details = OceanProtocolJobDetails().load()
42
-
43
- job_details
44
42
  ```
45
43
 
46
44
  ### Advanced Usage (not recommended)
@@ -49,7 +47,7 @@ If instead of the environment variables, we want to use another kind of mapping,
49
47
 
50
48
  ```Python
51
49
  from oceanprotocol_job_details.job_details import OceanProtocolJobDetails
52
- from src.oceanprotocol_job_details.loaders.impl.environment import Keys
50
+ from oceanprotocol_job_details.loaders.impl.environment import Keys
53
51
 
54
52
  # Fill in with values that will be used instead of env
55
53
  custom_mapper = {
@@ -61,3 +59,4 @@ custom_mapper = {
61
59
 
62
60
  job_details = OceanProtocolJobDetails(mapper=custom_mapper).load()
63
61
  ```
62
+
@@ -0,0 +1,13 @@
1
+ oceanprotocol_job_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ oceanprotocol_job_details/dataclasses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ oceanprotocol_job_details/dataclasses/constants.py,sha256=0hUnY2ccbvbRmxwgJC3GYaN7yoG6fccLqjxQWkH88Bo,846
4
+ oceanprotocol_job_details/dataclasses/job_details.py,sha256=pnbNvCg0jg5rx8K9B8qbHPFetTGaMdXK94qP4PvGilw,2692
5
+ oceanprotocol_job_details/job_details.py,sha256=eelJK85bnJIYQie65nubIwnQK3Do9X5E8cOZ1FPxSz8,1275
6
+ oceanprotocol_job_details/loaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ oceanprotocol_job_details/loaders/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ oceanprotocol_job_details/loaders/impl/map.py,sha256=F6AxfRpPeRVdXtgS6jhZZM13EcGed0SszPPT5NVJmOo,3508
9
+ oceanprotocol_job_details/loaders/loader.py,sha256=JwR6OSkzIQQkeeyAU-ad_F89W9WNvoRwvHQY7Q3zIXI,256
10
+ oceanprotocol_job_details-0.0.6.dist-info/LICENSE,sha256=ni3ix7P_GxK1W3VGC4fJ3o6QoCngCEpSuTJwO4nkpbw,1055
11
+ oceanprotocol_job_details-0.0.6.dist-info/METADATA,sha256=j82kvwEl86u2N0zOcM8KOGrJyUZnKJJWYB9qKfm6N4s,2793
12
+ oceanprotocol_job_details-0.0.6.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
13
+ oceanprotocol_job_details-0.0.6.dist-info/RECORD,,
@@ -1,98 +0,0 @@
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
-
3
- import os
4
- from collections.abc import Mapping, Sequence
5
- from dataclasses import dataclass
6
- from json import load, loads
7
- from pathlib import Path
8
- from typing import Optional, final
9
-
10
- from oceanprotocol_job_details.dataclasses.constants import (
11
- DidKeys,
12
- Paths,
13
- ServiceType,
14
- )
15
- from oceanprotocol_job_details.dataclasses.job_details import Algorithm, JobDetails
16
- from oceanprotocol_job_details.loaders.loader import Loader
17
-
18
-
19
- @dataclass(frozen=True)
20
- class _Keys:
21
- """Environment keys passed to the algorithm"""
22
-
23
- ROOT: str = "ROOT_FOLDER"
24
- SECRET: str = "secret"
25
- ALGORITHM: str = "TRANSFORMATION_DID"
26
- DIDS: str = "DIDS"
27
-
28
-
29
- Keys = _Keys()
30
- del _Keys
31
-
32
-
33
- @final
34
- class EnvironmentLoader(Loader[JobDetails]):
35
- """Loads the current Job Details from the environment variables"""
36
-
37
- def __init__(self, mapper: Mapping[str, str] = os.environ):
38
- super().__init__()
39
- self.mapper = mapper
40
-
41
- def load(self, *args, **kwargs) -> JobDetails:
42
- root, dids = self._root(), self._dids()
43
-
44
- return JobDetails(
45
- root=root,
46
- dids=dids,
47
- metadata=self._metadata(),
48
- files=self._files(root, dids),
49
- algorithm=self._algorithm(root=root),
50
- secret=self._secret(),
51
- )
52
-
53
- def _root(self) -> Path:
54
- return Path(self.mapper.get(Keys.ROOT, ""))
55
-
56
- def _dids(self) -> Sequence[str]:
57
- return loads(self.mapper.get(Keys.DIDS)) if Keys.DIDS in self.mapper else []
58
-
59
- def _files(
60
- self,
61
- root: Path,
62
- dids: Optional[Sequence[Path]],
63
- ) -> Mapping[str, Sequence[Path]]:
64
- files: Mapping[str, Sequence[Path]] = {}
65
- for did in dids:
66
- # Retrieve DDO from disk
67
- file = root / Paths.DDOS / did
68
- with open(file, "r") as f:
69
- ddo = load(f)
70
- for service in ddo[DidKeys.SERVICE]:
71
- if service[DidKeys.SERVICE_TYPE] == ServiceType.METADATA:
72
- base_path = root / Paths.INPUTS / did
73
- files[did] = [
74
- base_path / str(idx)
75
- for idx in range(
76
- len(
77
- service[DidKeys.ATTRIBUTES][DidKeys.MAIN][
78
- DidKeys.FILES
79
- ]
80
- )
81
- )
82
- ]
83
- return files
84
-
85
- def _metadata(self) -> Mapping[str, str]:
86
- return {}
87
-
88
- def _algorithm(self, root: Path) -> Algorithm:
89
- did = self.mapper.get(Keys.ALGORITHM, None)
90
- if not did:
91
- return None
92
- return Algorithm(
93
- did=did,
94
- ddo=root / Paths.DDOS / did,
95
- )
96
-
97
- def _secret(self) -> str:
98
- return self.mapper.get(Keys.SECRET, "")
@@ -1,27 +0,0 @@
1
- oceanprotocol_job_details/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- oceanprotocol_job_details/dataclasses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- oceanprotocol_job_details/dataclasses/__pycache__/__init__.cpython-313.pyc,sha256=0ONMfxi9YdNbrlvd7HHyKh2D2cclkF8_sfHxnk4QPT4,210
4
- oceanprotocol_job_details/dataclasses/__pycache__/__init__.cpython-39.pyc,sha256=MkCv5cAjpecyHxAMz5DHQAXrEfnkndNFVNGLB20iBDA,210
5
- oceanprotocol_job_details/dataclasses/__pycache__/constants.cpython-313.pyc,sha256=_7cahtV77VBhIQRsHzVXEkdJy-hZcJfbx50EAvwhrB8,1886
6
- oceanprotocol_job_details/dataclasses/__pycache__/constants.cpython-39.pyc,sha256=FGoRRGmALOKQnWO34lRxSudHkMPhYo2_XKZDeS0PwIA,1424
7
- oceanprotocol_job_details/dataclasses/__pycache__/job_details.cpython-313.pyc,sha256=kUxopSviCQSMPQCP5YcApB9TXUwR6XN9BI9m3rE-UKo,1369
8
- oceanprotocol_job_details/dataclasses/__pycache__/job_details.cpython-39.pyc,sha256=_uENugVUrWWBcjqeXKKCeT9uXz8Yd7Qg6ivag2DL87M,1038
9
- oceanprotocol_job_details/dataclasses/constants.py,sha256=CWkWtBgKjI8xyHPKIwJ2lStpyCTDnfnWXvcXlIFYWig,773
10
- oceanprotocol_job_details/dataclasses/job_details.py,sha256=7y8LoiPmAPsS71HRzm6pWbB7TwV9qIwe1Cd2Dgd4liE,860
11
- oceanprotocol_job_details/job_details.py,sha256=9DHS2ffOC26q-TxB9M4EUVTdwbF5jozJ9V5MljXNEM0,753
12
- oceanprotocol_job_details/loaders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- oceanprotocol_job_details/loaders/__pycache__/__init__.cpython-313.pyc,sha256=caqlfFNy8aRS-NJB1cTGZcxYpTtEN-jo3ko3URP0xnA,206
14
- oceanprotocol_job_details/loaders/__pycache__/__init__.cpython-39.pyc,sha256=8FYNSbVtqAB4scwvaAMl93XuXNHn3I1j_60CdM-wKrY,206
15
- oceanprotocol_job_details/loaders/__pycache__/loader.cpython-313.pyc,sha256=vHfalXDEN178-qFNfAqPhkFTSB8JhlBG_lRcpPbddUU,827
16
- oceanprotocol_job_details/loaders/__pycache__/loader.cpython-39.pyc,sha256=hKNDCAWHospvkKWDlkknPc6MH4tkzTbx9-vpYhX83-s,689
17
- oceanprotocol_job_details/loaders/impl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- oceanprotocol_job_details/loaders/impl/__pycache__/__init__.cpython-313.pyc,sha256=SfhHqoTLJUeOLvPm9_x4niYNVumJaP4L4ilj8GNLbR0,211
19
- oceanprotocol_job_details/loaders/impl/__pycache__/__init__.cpython-39.pyc,sha256=RljrmsqcD5Ai1gkv0fjihE7GgTJfvugoIqyvnUBQ8r4,211
20
- oceanprotocol_job_details/loaders/impl/__pycache__/environment.cpython-313.pyc,sha256=hWSoqiSjFPAPuPZTiagjcHQpKDvHJU0feETedMVXenM,5628
21
- oceanprotocol_job_details/loaders/impl/__pycache__/environment.cpython-39.pyc,sha256=wDccIcI5AB2uBrKifVKI_MLa-bu1RDIYqICpxbMckSU,3808
22
- oceanprotocol_job_details/loaders/impl/environment.py,sha256=YrJwDsmHzYqd6RgNy-BwtrbdioO_1geUmigGkqbztM8,3029
23
- oceanprotocol_job_details/loaders/loader.py,sha256=JwR6OSkzIQQkeeyAU-ad_F89W9WNvoRwvHQY7Q3zIXI,256
24
- oceanprotocol_job_details-0.0.2.dist-info/LICENSE,sha256=ni3ix7P_GxK1W3VGC4fJ3o6QoCngCEpSuTJwO4nkpbw,1055
25
- oceanprotocol_job_details-0.0.2.dist-info/METADATA,sha256=abdDKU9LJIVynDY9ILeGlFMLlOrJyinllxfdMLQwjSA,2809
26
- oceanprotocol_job_details-0.0.2.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
27
- oceanprotocol_job_details-0.0.2.dist-info/RECORD,,