Skip to content

Commit 9e5734e

Browse files
authored
Merge pull request #78 from briansumma/feature/object-metadata-retrieval
feat(metadata): add generic object metadata retrieval methods
2 parents f0bca37 + 3c6658e commit 9e5734e

File tree

6 files changed

+1162
-31
lines changed

6 files changed

+1162
-31
lines changed

pythonik/models/metadata/views.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,51 @@ class View(BaseModel):
8585
class ViewMetadata(BaseModel):
8686
date_created: Optional[str] = ""
8787
date_modified: Optional[str] = ""
88-
metadata_values: Optional[MetadataValues] = MetadataValues({})
88+
metadata_values: Optional[MetadataValues] = None
8989
object_id: Optional[str] = ""
9090
object_type: Optional[str] = ""
9191
version_id: Optional[str] = ""
92+
93+
def __init__(self, **data: Any) -> None:
94+
"""Initialize with fallback for metadata_values.
95+
96+
This method transforms the input data structure when 'metadata_values'
97+
is not provided by moving 'values' fields to 'field_values' within a
98+
nested structure.
99+
100+
Args:
101+
**data: Input data for initialization
102+
"""
103+
if "metadata_values" not in data or data["metadata_values"] is None:
104+
metadata_values = {}
105+
106+
# Check if any dictionary in values contains a 'values' key
107+
has_values = any(
108+
"values" in item
109+
for item in data.values()
110+
if isinstance(item, dict)
111+
)
112+
113+
if has_values:
114+
# Transform each field
115+
for key, value in list(data.items()):
116+
if isinstance(value, dict) and "values" in value:
117+
# Get the values list, ensuring it's not None
118+
values_list = value.get("values", [])
119+
if values_list is None:
120+
# If values is None, create an empty FieldValues with None
121+
metadata_values[key] = {
122+
"field_values": None
123+
}
124+
else:
125+
# Otherwise, use the values list
126+
metadata_values[key] = {
127+
"field_values": values_list
128+
}
129+
# Don't remove the key from the original data to preserve it
130+
131+
# Set metadata_values in the data dictionary
132+
data["metadata_values"] = MetadataValues(root=metadata_values)
133+
134+
# Initialize with all data fields
135+
super().__init__(**data)

pythonik/specs/metadata.py

Lines changed: 221 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,44 @@
4242
class MetadataSpec(Spec):
4343
server = "API/metadata/"
4444

45-
def get_asset_metadata(
45+
def get_object_metadata(
4646
self,
47-
asset_id: str,
48-
view_id: str,
47+
object_type: Literal["assets", "collections", "segments"],
48+
object_id: str,
49+
view_id: str = None,
4950
intercept_404: ViewMetadata | bool = False,
5051
**kwargs,
5152
) -> Response:
52-
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
53+
"""
54+
Get object metadata by object type, object ID and view ID.
5355
5456
Args:
55-
asset_id: The asset ID to get metadata for
56-
view_id: The view ID to get metadata from
57-
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
58-
and return the ViewMetadata model provided
59-
**kwargs: Additional kwargs to pass to the request
57+
object_type: The type of object to retrieve
58+
object_id: ID of the object to retrieve
59+
view_id: ID of the view to retrieve
60+
intercept_404: Iconik returns a 404 when a view has no metadata,
61+
intercept_404 will intercept that error and return the
62+
ViewMetadata model provided
63+
**kwargs: Additional arguments to pass to the request
6064
61-
Note:
62-
You can no longer call response.raise_for_status, so be careful using this.
63-
Call raise_for_status_404 if you still want to raise status on 404 error
65+
Returns:
66+
Response with ViewMetadata model containing the object metadata
67+
68+
Raises:
69+
ValueError: If object_type is not 'assets', 'collections', or
70+
'segments'
6471
"""
65-
resp = self._get(
66-
ASSET_METADATA_FROM_VIEW_PATH.format(asset_id, view_id), **kwargs
72+
if object_type not in ["assets", "collections", "segments"]:
73+
raise ValueError(
74+
"object_type must be one of assets, collections, or segments"
75+
)
76+
77+
url = (
78+
self.gen_url(f"{object_type}/{object_id}/views/{view_id}/")
79+
if view_id is not None
80+
else self.gen_url(f"{object_type}/{object_id}/")
6781
)
82+
resp = self._get(url, **kwargs)
6883

6984
if intercept_404 and resp.status_code == 404:
7085
parsed_response = self.parse_response(resp, ViewMetadata)
@@ -81,6 +96,198 @@ def get_asset_metadata(
8196

8297
return self.parse_response(resp, ViewMetadata)
8398

99+
def get_asset_metadata(
100+
self,
101+
asset_id: str,
102+
view_id: str,
103+
intercept_404: ViewMetadata | bool = False,
104+
**kwargs,
105+
) -> Response:
106+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
107+
108+
Args:
109+
asset_id: The asset ID to get metadata for
110+
view_id: The view ID to get metadata from
111+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
112+
and return the ViewMetadata model provided
113+
**kwargs: Additional kwargs to pass to the request
114+
115+
Note:
116+
You can no longer call response.raise_for_status, so be careful using this.
117+
Call raise_for_status_404 if you still want to raise status on 404 error
118+
"""
119+
return self.get_object_metadata(
120+
object_type="assets",
121+
object_id=asset_id,
122+
view_id=view_id,
123+
intercept_404=intercept_404,
124+
**kwargs,
125+
)
126+
127+
def get_collection_metadata(
128+
self,
129+
collection_id: str,
130+
view_id: str,
131+
intercept_404: ViewMetadata | bool = False,
132+
**kwargs,
133+
) -> Response:
134+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
135+
136+
Args:
137+
collection_id: The collection ID to get metadata for
138+
view_id: The view ID to get metadata from
139+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
140+
and return the ViewMetadata model provided
141+
**kwargs: Additional kwargs to pass to the request
142+
143+
Note:
144+
You can no longer call response.raise_for_status, so be careful using this.
145+
Call raise_for_status_404 if you still want to raise status on 404 error
146+
"""
147+
return self.get_object_metadata(
148+
object_type="collections",
149+
object_id=collection_id,
150+
view_id=view_id,
151+
intercept_404=intercept_404,
152+
**kwargs,
153+
)
154+
155+
def get_segment_metadata(
156+
self,
157+
segment_id: str,
158+
view_id: str,
159+
intercept_404: ViewMetadata | bool = False,
160+
**kwargs,
161+
) -> Response:
162+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
163+
164+
Args:
165+
segment_id: The segment ID to get metadata for
166+
view_id: The view ID to get metadata from
167+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
168+
and return the ViewMetadata model provided
169+
**kwargs: Additional kwargs to pass to the request
170+
171+
Note:
172+
You can no longer call response.raise_for_status, so be careful using this.
173+
Call raise_for_status_404 if you still want to raise status on 404 error
174+
"""
175+
return self.get_object_metadata(
176+
object_type="segments",
177+
object_id=segment_id,
178+
view_id=view_id,
179+
intercept_404=intercept_404,
180+
**kwargs,
181+
)
182+
183+
def get_object_metadata_direct(
184+
self,
185+
object_type: Literal["assets", "collections", "segments"],
186+
object_id: str,
187+
intercept_404: ViewMetadata | bool = False,
188+
**kwargs,
189+
) -> Response:
190+
"""
191+
Get object metadata by object type and object ID.
192+
193+
Args:
194+
object_type: The type of object to retrieve
195+
object_id: ID of the object to retrieve
196+
intercept_404: Iconik returns a 404 when a view has no metadata,
197+
intercept_404 will intercept that error and return the
198+
ViewMetadata model provided
199+
**kwargs: Additional arguments to pass to the request
200+
201+
Returns:
202+
Response with ViewMetadata model containing the object metadata
203+
204+
Raises:
205+
ValueError: If object_type is not 'assets', 'collections', or
206+
'segments'
207+
"""
208+
return self.get_object_metadata(
209+
object_type=object_type,
210+
object_id=object_id,
211+
view_id=None,
212+
intercept_404=intercept_404,
213+
**kwargs,
214+
)
215+
216+
def get_asset_metadata_direct(
217+
self,
218+
asset_id: str,
219+
intercept_404: ViewMetadata | bool = False,
220+
**kwargs,
221+
) -> Response:
222+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
223+
224+
Args:
225+
asset_id: The asset ID to get metadata for
226+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
227+
and return the ViewMetadata model provided
228+
**kwargs: Additional kwargs to pass to the request
229+
230+
Note:
231+
You can no longer call response.raise_for_status, so be careful using this.
232+
Call raise_for_status_404 if you still want to raise status on 404 error
233+
"""
234+
return self.get_object_metadata_direct(
235+
object_type="assets",
236+
object_id=asset_id,
237+
intercept_404=intercept_404,
238+
**kwargs,
239+
)
240+
241+
def get_collection_metadata_direct(
242+
self,
243+
collection_id: str,
244+
intercept_404: ViewMetadata | bool = False,
245+
**kwargs,
246+
) -> Response:
247+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
248+
249+
Args:
250+
collection_id: The collection ID to get metadata for
251+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
252+
and return the ViewMetadata model provided
253+
**kwargs: Additional kwargs to pass to the request
254+
255+
Note:
256+
You can no longer call response.raise_for_status, so be careful using this.
257+
Call raise_for_status_404 if you still want to raise status on 404 error
258+
"""
259+
return self.get_object_metadata_direct(
260+
object_type="collections",
261+
object_id=collection_id,
262+
intercept_404=intercept_404,
263+
**kwargs,
264+
)
265+
266+
def get_segment_metadata_direct(
267+
self,
268+
segment_id: str,
269+
intercept_404: ViewMetadata | bool = False,
270+
**kwargs,
271+
) -> Response:
272+
"""Given an asset id and the asset's view id, fetch metadata from the asset's view
273+
274+
Args:
275+
segment_id: The segment ID to get metadata for
276+
intercept_404: Iconik returns a 404 when a view has no metadata, intercept_404 will intercept that error
277+
and return the ViewMetadata model provided
278+
**kwargs: Additional kwargs to pass to the request
279+
280+
Note:
281+
You can no longer call response.raise_for_status, so be careful using this.
282+
Call raise_for_status_404 if you still want to raise status on 404 error
283+
"""
284+
return self.get_object_metadata_direct(
285+
object_type="segments",
286+
object_id=segment_id,
287+
intercept_404=intercept_404,
288+
**kwargs,
289+
)
290+
84291
def update_asset_metadata(
85292
self,
86293
asset_id: str,

0 commit comments

Comments
 (0)