Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ See [the documentation](https://wragge.github.io/omeka_s_tools/api.html) for mor

----
Created by [Tim Sherratt](https://timsherratt.org) ([@wragge](https://twitter.com/wragge)) for the [GLAM Workbench](https://glam-workbench.net/).
Updated by [MatthieuQuantin](https://www.ec-nantes.fr/version-francaise/annuaire/matthieu-quantin) for École Centrale de Nantes
113 changes: 103 additions & 10 deletions api.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
" \n",
" def __init__(self, api_url, key_identity=None, key_credential=None, use_cache=True):\n",
" self.api_url = api_url\n",
" self.params = {\n",
" self.credentials = {\n",
" 'key_identity': key_identity,\n",
" 'key_credential': key_credential\n",
" }\n",
Expand Down Expand Up @@ -116,7 +116,8 @@
" * `total_results` - number of matching resources\n",
" * `results` - a list of dicts, each containing a JSON-LD formatted representation of a resource\n",
" '''\n",
" response = self.s.get(f'{self.api_url}/{resource_type}/', params=kwargs)\n",
" kwargsWithCredentials = kwargs | self.credentials#credential offers to get non-public (hidden) data\n",
" response = self.s.get(f'{self.api_url}/{resource_type}/', params=kwargsWithCredentials)\n",
" data = self.process_response(response)\n",
" return {'total_results': int(response.headers['Omeka-S-Total-Results']), 'results': data}\n",
" \n",
Expand Down Expand Up @@ -151,7 +152,7 @@
" Returns\n",
" * a dict containing a JSON-LD formatted representation of the resource\n",
" '''\n",
" response = self.s.get(f'{self.api_url}/{resource_type}/{resource_id}')\n",
" response = self.s.get(f'{self.api_url}/{resource_type}/{resource_id}/', params=self.credentials)\n",
" data = self.process_response(response)\n",
" return data\n",
"\n",
Expand Down Expand Up @@ -205,6 +206,50 @@
" resource = self.get_resource_by_term(term=term)\n",
" if resource:\n",
" return resource['o:id']\n",
" \n",
" def get_class_id(self, term):\n",
" '''\n",
" Get the numeric identifier associated with the supplied class term.\n",
"\n",
" Parameters:\n",
" * `term` - class label qualified with vocabulary prefix (eg: 'crm:E65_Creation')\n",
"\n",
" Returns:\n",
" * numeric identifier\n",
" '''\n",
" resource = self.get_resource_by_term(term=term, resource_type='resource_classes')\n",
" if resource:\n",
" return resource['o:id']\n",
"\n",
" def get_template_id(self, label):\n",
" '''\n",
" Get the numeric identifier associated with the supplied template label.\n",
"\n",
" Parameters:\n",
" * `label` - template label used in the omeka instance (eg: 'creation')\n",
"\n",
" Returns:\n",
" * numeric identifier\n",
" '''\n",
" resource = self.get_template_by_label(label)\n",
" if resource:\n",
" return resource['o:id']\n",
"\n",
" def get_itemset_id(self, label):\n",
" '''\n",
" Get the numeric identifier associated with the supplied item-set label.\n",
" \n",
" Parameters:\n",
" * `label` - item-set label as recorded in omeka (eg: 'my collection')\n",
" \n",
" Returns:\n",
" * numeric identifier\n",
" '''\n",
" resource = self.get_resource('item_sets', search=label)\n",
" if resource:\n",
" return resource['o:id']\n",
"\n",
"\n",
"\n",
" def filter_items(self, params, **extra_filters):\n",
" for filter_type in ['resource_template_id', 'resource_class_id', 'item_set_id', 'is_public']:\n",
Expand Down Expand Up @@ -336,8 +381,12 @@
" property_value['@id'] = f'{self.api_url}/items/{value[\"value\"]}'\n",
" property_value['value_resource_id'] = value['value']\n",
" property_value['value_resource_name'] = 'items'\n",
" elif data_type == 'numeric:timestamp':\n",
" property_value['@value'] = value['value']\n",
" elif data_type == 'uri':\n",
" property_value['@id'] = value['value']\n",
" if value['label']:\n",
" property_value['o:label'] = value['label']\n",
" else:\n",
" property_value['@value'] = value['value']\n",
" return property_value\n",
Expand Down Expand Up @@ -368,9 +417,9 @@
" payload['o:item_set'] = self.format_resource_id(item_set_id, 'item_sets')\n",
" if media_files:\n",
" files = self.add_media_to_payload(payload, media_files)\n",
" response = self.s.post(f'{self.api_url}/items', files=files, params=self.params)\n",
" response = self.s.post(f'{self.api_url}/items', files=files, params=self.credentials)\n",
" else:\n",
" response = self.s.post(f'{self.api_url}/items', json=payload, params=self.params)\n",
" response = self.s.post(f'{self.api_url}/items', json=payload, params=self.credentials)\n",
" #print(response.text)\n",
" data = self.process_response(response)\n",
" return data\n",
Expand Down Expand Up @@ -417,6 +466,9 @@
" template_properties = self.get_template_properties(template_id)\n",
" payload = {}\n",
" for term, values in terms.items():\n",
" if term.split(':', 1)[0] == 'o' or term in ['@context', '@id', '@type', 'thumbnail_display_urls']:\n",
" #its an internal omeka term\n",
" continue\n",
" if term in template_properties:\n",
" property_details = template_properties[term]\n",
" payload[term] = []\n",
Expand Down Expand Up @@ -490,7 +542,7 @@
" Returns:\n",
" * dict with JSON-LD representation of the deleted resource\n",
" '''\n",
" response = self.s.delete(f'{self.api_url}/{resource_type}/{resource_id}', params=self.params)\n",
" response = self.s.delete(f'{self.api_url}/{resource_type}/{resource_id}', params=self.credentials)\n",
" data = self.process_response(response)\n",
" return data\n",
" \n",
Expand All @@ -505,7 +557,7 @@
" To avoid problems, it's generally easiest to retrieve the resource first,\n",
" make your desired changes to it, then submit the updated resource as your payload.\n",
" '''\n",
" response = self.s.put(f'{self.api_url}/{resource_type}/{payload[\"o:id\"]}', json=payload, params=self.params)\n",
" response = self.s.put(f'{self.api_url}/{resource_type}/{payload[\"o:id\"]}', json=payload, params=self.credentials)\n",
" data = self.process_response(response)\n",
" return data\n",
" \n",
Expand Down Expand Up @@ -550,7 +602,7 @@
" payload.update(file_data)\n",
" files[f'file[0]'] = path.read_bytes()\n",
" files['data'] = (None, json.dumps(payload), 'application/json')\n",
" response = self.s.post(f'{self.api_url}/media', files=files, params=self.params)\n",
" response = self.s.post(f'{self.api_url}/media', files=files, params=self.credentials)\n",
" data = self.process_response(response)\n",
" return data\n",
" \n",
Expand Down Expand Up @@ -686,7 +738,7 @@
" * dict containing a JSON-LD representation of the uploaded template\n",
" '''\n",
" # Upload the template payload\n",
" response = self.s.post(f'{self.api_url}/resource_templates/', params=self.params, json=template_payload)\n",
" response = self.s.post(f'{self.api_url}/resource_templates/', params=self.credentials, json=template_payload)\n",
" data = self.process_response(response)\n",
" return data\n",
" \n",
Expand Down Expand Up @@ -729,7 +781,7 @@
" }\n",
" if media_id:\n",
" marker_payload['o:media'] = {'o:id': media_id}\n",
" response = self.s.post(f'{self.api_url}/mapping_markers/', json=marker_payload, params=self.params)\n",
" response = self.s.post(f'{self.api_url}/mapping_markers/', json=marker_payload, params=self.credentials)\n",
" data = self.process_response(response)\n",
" return data\n"
]
Expand Down Expand Up @@ -1883,6 +1935,47 @@
"assert title != updated_item['schema:name'][0]['@value']"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"Adding a new property to an existing item. \n",
"It's easier to use `OmekaAPIClient.prepare_property_value`. \n",
"The existing payload should be kept. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Get a random item from a list of items\n",
"data = omeka_auth.filter_items_by_property(filter_property='schema:name', filter_type='ex')\n",
"random_item = random.choice(data['results'])\n",
"\n",
"# New property with URI data (label and URI, as proposed in omeka UI)\n",
"new_propTerm = 'crm:P45_consists_of'\n",
"new_propId = omeka_auth.get_property_id(new_propTerm)\n",
"new_propValue = {'value': 'http://vocab.getty.edu/aat/300010814', 'type': 'uri', 'label': 'crystal (AAT)'}\n",
"formatted_new_prop = omeka_auth.prepare_property_value(new_propValue, new_propId)\n",
"\n",
"# Copy and update the original item\n",
"new_item = deepcopy(random_item)\n",
"new_item[new_propTerm] = [formatted_new_prop,]\n",
"# note: this overwrites values in `new_propTerm` if it already exists in item (manage exceptions by you own with tests)\n",
"\n",
"# Update the item \n",
"updated_item = omeka_auth.update_resource(new_item, 'items')\n",
"\n",
"# The id of the original and upated items should be the same\n",
"assert random_item['o:id'] == updated_item['o:id']\n",
"\n",
"# But the new property has been added to the updated item\n",
"assert new_propTerm in updated_item"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
71 changes: 60 additions & 11 deletions omeka_s_tools/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class OmekaAPIClient(object):

def __init__(self, api_url, key_identity=None, key_credential=None, use_cache=True):
self.api_url = api_url
self.params = {
self.credentials = {
'key_identity': key_identity,
'key_credential': key_credential
}
Expand Down Expand Up @@ -78,7 +78,8 @@ def get_resources(self, resource_type, **kwargs):
* `total_results` - number of matching resources
* `results` - a list of dicts, each containing a JSON-LD formatted representation of a resource
'''
response = self.s.get(f'{self.api_url}/{resource_type}/', params=kwargs)
kwargsWithCredentials = kwargs | self.credentials#credential offers to get non-public (hidden) data
response = self.s.get(f'{self.api_url}/{resource_type}/', params=kwargsWithCredentials)
data = self.process_response(response)
return {'total_results': int(response.headers['Omeka-S-Total-Results']), 'results': data}

Expand All @@ -93,7 +94,6 @@ def get_resource(self, resource_type, **kwargs):
Returns
* a dict containing a JSON-LD formatted representation of the resource
'''

data = self.get_resources(resource_type, **kwargs)
try:
resource = data['results'][0]
Expand All @@ -113,7 +113,7 @@ def get_resource_by_id(self, resource_id, resource_type='items'):
Returns
* a dict containing a JSON-LD formatted representation of the resource
'''
response = self.s.get(f'{self.api_url}/{resource_type}/{resource_id}')
response = self.s.get(f'{self.api_url}/{resource_type}/{resource_id}/', params=self.credentials)
data = self.process_response(response)
return data

Expand Down Expand Up @@ -167,6 +167,48 @@ def get_property_id(self, term):
resource = self.get_resource_by_term(term=term)
if resource:
return resource['o:id']

def get_template_id(self, label):
'''
Get the numeric identifier associated with the supplied template label.

Parameters:
* `label` - template label used in the omeka instance (eg: 'creation')

Returns:
* numeric identifier
'''
resource = self.get_template_by_label(label)
if resource:
return resource['o:id']

def get_class_id(self, term):
'''
Get the numeric identifier associated with the supplied class term.

Parameters:
* `term` - class label qualified with vocabulary prefix (eg: 'crm:E65_Creation')

Returns:
* numeric identifier
'''
resource = self.get_resource_by_term(term=term, resource_type='resource_classes')
if resource:
return resource['o:id']

def get_itemset_id(self, label):
'''
Get the numeric identifier associated with the supplied item-set label.

Parameters:
* `label` - item-set label as recorded in omeka (eg: 'my collection')

Returns:
* numeric identifier
'''
resource = self.get_resource('item_sets', search=label)
if resource:
return resource['o:id']

def filter_items(self, params, **extra_filters):
for filter_type in ['resource_template_id', 'resource_class_id', 'item_set_id', 'is_public']:
Expand Down Expand Up @@ -298,8 +340,12 @@ def prepare_property_value(self, value, property_id):
property_value['@id'] = f'{self.api_url}/items/{value["value"]}'
property_value['value_resource_id'] = value['value']
property_value['value_resource_name'] = 'items'
elif data_type == 'numeric:timestamp':
property_value['@value'] = value['value']
elif data_type == 'uri':
property_value['@id'] = value['value']
if value['label']:
property_value['o:label'] = value['label']
else:
property_value['@value'] = value['value']
return property_value
Expand Down Expand Up @@ -330,9 +376,9 @@ def add_item(self, payload, media_files=None, template_id=None, class_id=None, i
payload['o:item_set'] = self.format_resource_id(item_set_id, 'item_sets')
if media_files:
files = self.add_media_to_payload(payload, media_files)
response = self.s.post(f'{self.api_url}/items', files=files, params=self.params)
response = self.s.post(f'{self.api_url}/items', files=files, params=self.credentials)
else:
response = self.s.post(f'{self.api_url}/items', json=payload, params=self.params)
response = self.s.post(f'{self.api_url}/items', json=payload, params=self.credentials)
#print(response.text)
data = self.process_response(response)
return data
Expand Down Expand Up @@ -379,6 +425,9 @@ def prepare_item_payload_using_template(self, terms, template_id):
template_properties = self.get_template_properties(template_id)
payload = {}
for term, values in terms.items():
if term.split(':', 1)[0] == 'o' or term in ['@context', '@id', '@type', 'thumbnail_display_urls']:
#its an internal omeka term
continue
if term in template_properties:
property_details = template_properties[term]
payload[term] = []
Expand Down Expand Up @@ -452,7 +501,7 @@ def delete_resource(self, resource_id, resource_type):
Returns:
* dict with JSON-LD representation of the deleted resource
'''
response = self.s.delete(f'{self.api_url}/{resource_type}/{resource_id}', params=self.params)
response = self.s.delete(f'{self.api_url}/{resource_type}/{resource_id}', params=self.credentials)
data = self.process_response(response)
return data

Expand All @@ -467,7 +516,7 @@ def update_resource(self, payload, resource_type='items'):
To avoid problems, it's generally easiest to retrieve the resource first,
make your desired changes to it, then submit the updated resource as your payload.
'''
response = self.s.put(f'{self.api_url}/{resource_type}/{payload["o:id"]}', json=payload, params=self.params)
response = self.s.put(f'{self.api_url}/{resource_type}/{payload["o:id"]}', json=payload, params=self.credentials)
data = self.process_response(response)
return data

Expand Down Expand Up @@ -512,7 +561,7 @@ def add_media_to_item(self, item_id, media_file, payload={}, template_id=None, c
payload.update(file_data)
files[f'file[0]'] = path.read_bytes()
files['data'] = (None, json.dumps(payload), 'application/json')
response = self.s.post(f'{self.api_url}/media', files=files, params=self.params)
response = self.s.post(f'{self.api_url}/media', files=files, params=self.credentials)
data = self.process_response(response)
return data

Expand Down Expand Up @@ -648,7 +697,7 @@ def upload_template(self, template_payload):
* dict containing a JSON-LD representation of the uploaded template
'''
# Upload the template payload
response = self.s.post(f'{self.api_url}/resource_templates/', params=self.params, json=template_payload)
response = self.s.post(f'{self.api_url}/resource_templates/', params=self.credentials, json=template_payload)
data = self.process_response(response)
return data

Expand Down Expand Up @@ -691,6 +740,6 @@ def add_marker_to_item(self, item_id, coords=None, terms=None, label=None, media
}
if media_id:
marker_payload['o:media'] = {'o:id': media_id}
response = self.s.post(f'{self.api_url}/mapping_markers/', json=marker_payload, params=self.params)
response = self.s.post(f'{self.api_url}/mapping_markers/', json=marker_payload, params=self.credentials)
data = self.process_response(response)
return data