oceanprotocol-job-details 0.0.5__tar.gz → 0.0.6__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.
Files changed (14) hide show
  1. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/PKG-INFO +1 -1
  2. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/dataclasses/constants.py +3 -0
  3. oceanprotocol_job_details-0.0.6/oceanprotocol_job_details/dataclasses/job_details.py +96 -0
  4. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/job_details.py +19 -5
  5. oceanprotocol_job_details-0.0.5/oceanprotocol_job_details/loaders/impl/environment.py → oceanprotocol_job_details-0.0.6/oceanprotocol_job_details/loaders/impl/map.py +46 -46
  6. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/pyproject.toml +1 -1
  7. oceanprotocol_job_details-0.0.5/oceanprotocol_job_details/dataclasses/job_details.py +0 -35
  8. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/LICENSE +0 -0
  9. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/README.md +0 -0
  10. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/__init__.py +0 -0
  11. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/dataclasses/__init__.py +0 -0
  12. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/loaders/__init__.py +0 -0
  13. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/loaders/impl/__init__.py +0 -0
  14. {oceanprotocol_job_details-0.0.5 → oceanprotocol_job_details-0.0.6}/oceanprotocol_job_details/loaders/loader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oceanprotocol-job-details
3
- Version: 0.0.5
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
 
@@ -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()
@@ -0,0 +1,96 @@
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import InitVar, dataclass
5
+ from pathlib import Path
6
+ from typing import Any, Mapping, Optional, Sequence
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
+
22
+
23
+ @dataclass(frozen=True)
24
+ class Algorithm:
25
+ """Details of the algorithm used to process the data"""
26
+
27
+ did: str
28
+ """The DID of the algorithm used to process the data"""
29
+
30
+ ddo: Path
31
+ """The DDO path of the algorithm used to process the data"""
32
+
33
+
34
+ @dataclass
35
+ class JobDetails:
36
+ """Details of the current job, such as the used inputs and algorithm"""
37
+
38
+ root: Path
39
+ """The root folder of the Ocean Protocol directories"""
40
+
41
+ dids: Sequence[Path]
42
+ """Identifiers for the inputs"""
43
+
44
+ files: Mapping[str, Sequence[Path]]
45
+ """Paths to the input files"""
46
+
47
+ secret: Optional[str]
48
+ """The secret used to process the data"""
49
+
50
+ algorithm: Optional[Algorithm]
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
 
@@ -11,13 +23,15 @@ class OceanProtocolJobDetails(Loader[JobDetails]):
11
23
 
12
24
  def __init__(
13
25
  self,
14
- implementation: Optional[_Implementations] = "env",
26
+ implementation: Optional[_Implementations] = "map",
27
+ mapper: Mapping[str, Any] = os.environ,
28
+ keys: Keys = Keys(),
15
29
  *args,
16
30
  **kwargs,
17
31
  ):
18
- if implementation == "env":
32
+ if implementation == "map":
19
33
  # As there are not more implementations, we can use the EnvironmentLoader directly
20
- self._loader = lambda: EnvironmentLoader(*args, **kwargs)
34
+ self._loader = lambda: Map(mapper=mapper, keys=keys, *args, **kwargs)
21
35
  else:
22
36
  raise NotImplementedError(f"Implementation {implementation} not supported")
23
37
 
@@ -1,23 +1,20 @@
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
- import os
4
- from collections.abc import Mapping, Sequence
5
3
  from dataclasses import dataclass
6
- from json import load, loads
4
+ from json import JSONDecodeError, load, loads
5
+ from logging import getLogger
7
6
  from pathlib import Path
8
- from typing import Optional, final
7
+ from typing import Mapping, Optional, Sequence, final
9
8
 
10
- from oceanprotocol_job_details.dataclasses.constants import (
11
- DidKeys,
12
- Paths,
13
- ServiceType,
14
- )
9
+ from oceanprotocol_job_details.dataclasses.constants import DidKeys, Paths, ServiceType
15
10
  from oceanprotocol_job_details.dataclasses.job_details import Algorithm, JobDetails
16
11
  from oceanprotocol_job_details.loaders.loader import Loader
17
12
 
13
+ logger = getLogger(__name__)
14
+
18
15
 
19
16
  @dataclass(frozen=True)
20
- class _Keys:
17
+ class Keys:
21
18
  """Environment keys passed to the algorithm"""
22
19
 
23
20
  ROOT: str = "ROOT_FOLDER"
@@ -26,17 +23,15 @@ class _Keys:
26
23
  DIDS: str = "DIDS"
27
24
 
28
25
 
29
- Keys = _Keys()
30
- del _Keys
31
-
32
-
33
26
  @final
34
- class EnvironmentLoader(Loader[JobDetails]):
27
+ class Map(Loader[JobDetails]):
35
28
  """Loads the current Job Details from the environment variables"""
36
29
 
37
- def __init__(self, mapper: Mapping[str, str] = os.environ):
38
- super().__init__()
39
- self.mapper = mapper
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
40
35
 
41
36
  def load(self, *args, **kwargs) -> JobDetails:
42
37
  root, dids = self._root(), self._dids()
@@ -44,29 +39,34 @@ class EnvironmentLoader(Loader[JobDetails]):
44
39
  return JobDetails(
45
40
  root=root,
46
41
  dids=dids,
47
- metadata=self._metadata(),
48
42
  files=self._files(root, dids),
49
- algorithm=self._algorithm(root=root),
43
+ algorithm=self._algorithm(root),
50
44
  secret=self._secret(),
51
45
  )
52
46
 
53
47
  def _root(self) -> Path:
54
- root = Path(self.mapper.get(Keys.ROOT, Path.home()))
48
+ root = Path(self._mapper.get(self._keys.ROOT, Path.home()))
55
49
 
56
50
  if not root.exists():
57
51
  raise FileNotFoundError(f"Root folder {root} does not exist")
58
52
 
59
53
  return root
60
54
 
61
- def _dids(self) -> Sequence[str]:
62
- return loads(self.mapper.get(Keys.DIDS)) if Keys.DIDS in self.mapper else []
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
+ )
63
61
 
64
62
  def _files(
65
63
  self,
66
64
  root: Path,
67
65
  dids: Optional[Sequence[Path]],
68
66
  ) -> Mapping[str, Sequence[Path]]:
67
+
69
68
  files: Mapping[str, Sequence[Path]] = {}
69
+
70
70
  for did in dids:
71
71
  # Retrieve DDO from disk
72
72
  file_path = root / Paths.DDOS / did
@@ -74,27 +74,30 @@ class EnvironmentLoader(Loader[JobDetails]):
74
74
  raise FileNotFoundError(f"DDO file {file_path} does not exist")
75
75
 
76
76
  with open(file_path, "r") as f:
77
- ddo = load(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
+
78
83
  for service in ddo[DidKeys.SERVICE]:
79
- if service[DidKeys.SERVICE_TYPE] == ServiceType.METADATA:
80
- base_path = root / Paths.INPUTS / did
81
- files[did] = [
82
- base_path / str(idx)
83
- for idx in range(
84
- len(
85
- service[DidKeys.ATTRIBUTES][DidKeys.MAIN][
86
- DidKeys.FILES
87
- ]
88
- )
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]
89
93
  )
90
- ]
91
- return files
94
+ )
95
+ ]
92
96
 
93
- def _metadata(self) -> Mapping[str, str]:
94
- return {}
97
+ return files
95
98
 
96
- def _algorithm(self, root: Path) -> Algorithm:
97
- did = self.mapper.get(Keys.ALGORITHM, None)
99
+ def _algorithm(self, root: Path) -> Optional[Algorithm]:
100
+ did = self._mapper.get(self._keys.ALGORITHM, None)
98
101
 
99
102
  if not did:
100
103
  return None
@@ -103,10 +106,7 @@ class EnvironmentLoader(Loader[JobDetails]):
103
106
  if not ddo.exists():
104
107
  raise FileNotFoundError(f"DDO file {ddo} does not exist")
105
108
 
106
- return Algorithm(
107
- did=did,
108
- ddo=ddo,
109
- )
109
+ return Algorithm(did, ddo)
110
110
 
111
- def _secret(self) -> str:
112
- return self.mapper.get(Keys.SECRET, "")
111
+ def _secret(self) -> Optional[str]:
112
+ return self._mapper.get(self._keys.SECRET, None)
@@ -2,7 +2,7 @@
2
2
  license = { file = "LICENSE" }
3
3
 
4
4
  name = "oceanprotocol-job-details"
5
- version = "0.0.5"
5
+ version = "0.0.6"
6
6
  authors = [
7
7
  { name = "Christian López García", email = "christian.lopez@udl.cat" },
8
8
  ]
@@ -1,35 +0,0 @@
1
- from dataclasses import dataclass
2
- from pathlib import Path
3
- from typing import Any, Mapping, Optional, Sequence
4
-
5
-
6
- @dataclass(frozen=True)
7
- class Algorithm:
8
- did: str
9
- """The DID of the algorithm used to process the data"""
10
-
11
- ddo: Path
12
- """The DDO path of the algorithm used to process the data"""
13
-
14
-
15
- @dataclass(frozen=True)
16
- class JobDetails:
17
- """Details of the current job, such as the used inputs and algorithm"""
18
-
19
- root: Path
20
- """The root folder of the Ocean Protocol directories"""
21
-
22
- dids: Optional[Sequence[Path]]
23
- """Identifiers for the inputs"""
24
-
25
- metadata: Mapping[str, Any]
26
- """TODO: To define"""
27
-
28
- files: Mapping[str, Sequence[Path]]
29
- """Paths to the input files"""
30
-
31
- secret: Optional[str]
32
- """The secret used to process the data"""
33
-
34
- algorithm: Optional[Algorithm]
35
- """Details of the used algorithm"""