Skip to content

Commit 050cff8

Browse files
authored
Merge pull request #5 from kshepherd/dso_types_refactor
Small class, method changes
2 parents 64fc62e + 90150d4 commit 050cff8

File tree

2 files changed

+87
-29
lines changed

2 files changed

+87
-29
lines changed

dspace.py

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from requests import Request
2121
import os
2222
from uuid import UUID
23-
from enum import Enum
2423

2524

2625
class DSpaceObject:
@@ -39,13 +38,17 @@ class DSpaceObject:
3938
type = None
4039
parent = None
4140

42-
def __init__(self, api_resource=None):
41+
def __init__(self, api_resource=None, dso=None):
4342
"""
4443
Default constructor
4544
@param api_resource: optional API resource (JSON) from a GET response or successful POST can populate instance
4645
"""
46+
4747
self.type = None
48-
self.metadata = {}
48+
self.metadata = dict()
49+
50+
if dso is not None:
51+
api_resource = dso.as_dict()
4952
if api_resource is not None:
5053
if 'id' in api_resource:
5154
self.id = api_resource['id']
@@ -63,6 +66,10 @@ def __init__(self, api_resource=None):
6366
# alternatively - each item could implement getters, or a public method to return links
6467
if '_links' in api_resource:
6568
self.links = api_resource['_links']
69+
else:
70+
# TODO - write 'construct self URI method'... all we need is type, UUID and some mapping of type
71+
# to the URI type segment eg community -> communities
72+
self.links = {'self': {'href': ''}}
6673

6774
def add_metadata(self, field, value, language=None, authority=None, confidence=-1, place=None):
6875
"""
@@ -111,8 +118,6 @@ def clear_metadata(self, field=None, value=None):
111118
updated.append(v)
112119
self.metadata[field] = updated
113120

114-
115-
116121
def as_dict(self):
117122
"""
118123
Return custom dict of this DSpaceObject with specific attributes included (no _links, etc.)
@@ -134,7 +139,14 @@ def to_json_pretty(self):
134139
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
135140

136141

137-
class Item(DSpaceObject):
142+
class SimpleDSpaceObject(DSpaceObject):
143+
"""
144+
Objects that share similar simple API methods eg. PUT update for full metadata replacement, can have handles, etc.
145+
By default this is Item, Community, Collection classes
146+
"""
147+
148+
149+
class Item(SimpleDSpaceObject):
138150
"""
139151
Extends DSpaceObject to implement specific attributes and functions for items
140152
"""
@@ -143,13 +155,17 @@ class Item(DSpaceObject):
143155
discoverable = False
144156
withdrawn = False
145157

146-
def __init__(self, api_resource=None):
158+
def __init__(self, api_resource=None, dso=None):
147159
"""
148160
Default constructor. Call DSpaceObject init then set item-specific attributes
149161
@param api_resource: API result object to use as initial data
150162
"""
163+
if dso is not None:
164+
api_resource = dso.as_dict()
165+
166+
super(Item, self).__init__(api_resource)
167+
151168
if api_resource is not None:
152-
super(Item, self).__init__(api_resource)
153169
self.type = 'item'
154170
self.inArchive = api_resource['inArchive'] if 'inArchive' in api_resource else False
155171
self.discoverable = api_resource['discoverable'] if 'discoverable' in api_resource else False
@@ -176,15 +192,15 @@ def as_dict(self):
176192
return {**dso_dict, **item_dict}
177193

178194
@classmethod
179-
def from_DSpaceObject(cls, dso: DSpaceObject):
180-
# Create new b_obj
195+
def from_dso(cls, dso: DSpaceObject):
196+
# Create new Item and copy everything over from this dso
181197
item = cls()
182198
for key, value in dso.__dict__.items():
183199
item.__dict__[key] = value
184200
return item
185201

186202

187-
class Community(DSpaceObject):
203+
class Community(SimpleDSpaceObject):
188204
"""
189205
Extends DSpaceObject to implement specific attributes and functions for communities
190206
"""
@@ -209,7 +225,7 @@ def as_dict(self):
209225
return {**dso_dict, **community_dict}
210226

211227

212-
class Collection(DSpaceObject):
228+
class Collection(SimpleDSpaceObject):
213229
"""
214230
Extends DSpaceObject to implement specific attributes and functions for collections
215231
"""
@@ -629,23 +645,53 @@ def create_dso(self, url, params, data):
629645
print(f'create operation failed: {r.status_code}: {r.text} ({url})')
630646
return r
631647

632-
def update_dso(self, url, params=None, data=None):
648+
def update_dso(self, dso, params=None):
633649
"""
634-
Base 'update DSpace Object' function.
635-
@param url: DSpace REST API URL
636-
@param params: Any parameters to pass in the request.
637-
@param data: JSON data to apply in teh update - note, this is not a patch operation, this is the full data
638-
@return: Raw API response. Updated DSO *could* be returned but for error checking purposes, raw response
639-
is nice too and can always be parsed from this response later.
650+
Update DSpaceObject. Takes a DSpaceObject and any optional parameters. Will send a PUT update to the remote
651+
object and return the updated object, typed correctly.
652+
:param dso: DSpaceObject with locally updated data, to send in PUT request
653+
:param params: Optional parameters
654+
:return:
655+
640656
"""
641-
r = self.api_put(url, params=params, json=data)
642-
if r.status_code == 200:
643-
# 200 OK - success!
644-
updated_dso = r.json()
645-
print(f'{updated_dso["type"]} {updated_dso["uuid"]} updated sucessfully!')
646-
else:
647-
print(f'update operation failed: {r.status_code}: {r.text} ({url})')
648-
return r
657+
if dso is None:
658+
return None
659+
dso_type = type(dso)
660+
if not isinstance(dso, SimpleDSpaceObject):
661+
print(f'Only SimpleDSpaceObject types (eg Item, Collection, Community) '
662+
f'are supported by generic update_dso PUT.')
663+
return dso
664+
try:
665+
# Get self URI from HAL links
666+
url = dso.links['self']['href']
667+
# Get and clean data - there are some unalterable fields that could cause errors
668+
data = dso.as_dict()
669+
670+
if 'lastModified' in data:
671+
data.pop('lastModified')
672+
"""
673+
if 'id' in data:
674+
data.pop('id')
675+
if 'handle' in data:
676+
data.pop('handle')
677+
if 'uuid' in data:
678+
data.pop('uuid')
679+
if 'type' in data:
680+
data.pop('type')
681+
"""
682+
r = self.api_put(url, params=params, json=data)
683+
if r.status_code == 200:
684+
# 200 OK - success!
685+
updated_dso = dso_type(parse_json(r))
686+
print(f'{updated_dso.type} {updated_dso.uuid} updated sucessfully!')
687+
return updated_dso
688+
else:
689+
print(f'update operation failed: {r.status_code}: {r.text} ({url})')
690+
return None
691+
692+
except ValueError as e:
693+
print(f'{e}')
694+
return None
649695

650696
def get_bundles(self, parent=None, uuid=None):
651697
"""
@@ -926,8 +972,7 @@ def update_item(self, item):
926972
if not isinstance(item, Item):
927973
print('Need a valid item')
928974
return None
929-
url = f'{self.API_ENDPOINT}/core/items/{item.uuid}'
930-
return Item(api_resource=parse_json(self.update_dso(url, params=None, data=item.as_dict())))
975+
return self.update_dso(item, params=None)
931976

932977
def add_metadata(self, dso, field, value, language=None, authority=None, confidence=-1, place=''):
933978
"""

example.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@
5555
print(f'Error! Giving up.')
5656
exit(1)
5757

58+
# Update the community metadata
59+
new_community.name = 'Community created by the Python REST Client - Updated Name'
60+
new_community.metadata['dc.title'][0] = {
61+
'value': 'Community created by the Python REST Client - Updated Name',
62+
'language': 'en', 'authority': None, 'confidence': -1
63+
}
64+
65+
d.update_dso(new_community)
66+
5867
# Put together some basic Collection data.
5968
# See https://github.com/DSpace/RestContract/blob/main/collections.md
6069
collection_data = {
@@ -134,6 +143,10 @@
134143
print(f'Error! Giving up.')
135144
exit(1)
136145

146+
# Add a single metadata field+value to the item (PATCH operation)
147+
updated_item = d.add_metadata(dso=new_item, field='dc.description.abstract', value='Added abstract to an existing item',
148+
language='en', authority=None, confidence=-1)
149+
137150
# Create a new ORIGINAL bundle
138151
# See https://github.com/DSpace/RestContract/blob/main/bundles.md
139152
new_bundle = d.create_bundle(parent=new_item, name='ORIGINAL')

0 commit comments

Comments
 (0)