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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oceanprotocol-job-details
3
- Version: 0.0.8
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(frozen=True)
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(frozen=True)
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(frozen=True)
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(frozen=True)
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(frozen=True)
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__(self, _):
57
- os.makedirs(self.root / Paths.LOGS, exist_ok=True)
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
- self.root / Paths.LOGS / "job_details.log",
65
+ Paths.LOGS / "job_details.log",
62
66
  mode="w",
63
67
  )
64
68
  )
65
69
 
66
70
  @property
67
- def parameters(self, parameters: Optional[Path] = None) -> _MetadataType:
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 = self.root / Paths.ALGORITHM_CUSTOM_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(Loader[JobDetails]):
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(mapper=mapper, keys=keys, *args, **kwargs)
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(self) -> JobDetails:
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(frozen=True)
16
+ @dataclass(
17
+ frozen=True,
18
+ )
17
19
  class Keys:
18
20
  """Environment keys passed to the algorithm"""
19
21
 
20
- ROOT: str = "ROOT_FOLDER"
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(Loader[JobDetails]):
29
+ class Map(
30
+ Loader[JobDetails],
31
+ ):
28
32
  """Loads the current Job Details from the environment variables"""
29
33
 
30
- def __init__(self, mapper: Mapping[str, str], keys: Keys, *args, **kwargs):
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
- def load(self, *args, **kwargs) -> JobDetails:
37
- root, dids = self._root(), self._dids()
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(root, dids),
43
- algorithm=self._algorithm(root),
68
+ files=self._files(dids),
69
+ algorithm=self._algorithm(),
44
70
  secret=self._secret(),
45
71
  )
46
72
 
47
- def _root(self) -> Path:
48
- """
49
- Retrieves the root of the data from the default keys, defaults to '/'
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 = root / Paths.DDOS / did
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 = root / Paths.INPUTS / did
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(self, root: Path) -> Optional[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 = root / Paths.DDOS / did
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(self) -> Optional[str]:
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(ABC, Generic[T]):
8
+ class Loader(
9
+ ABC,
10
+ Generic[T],
11
+ ):
9
12
  @abstractmethod
10
- def load(self, *args, **kwargs) -> T:
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
 
@@ -2,7 +2,7 @@
2
2
  license = { file = "LICENSE" }
3
3
 
4
4
  name = "oceanprotocol-job-details"
5
- version = "0.0.8"
5
+ version = "0.0.9"
6
6
  authors = [
7
7
  { name = "Christian López García", email = "christian.lopez@udl.cat" },
8
8
  ]