Create items and relations via API

Hi all,
I’m new to omeka s and I’m struggling to understand how to create/update relations between items via REST API. Unfortunately, the few examples available in the forum are not complete. In particular I have the following cases:

(1) an item with data properties
e.g. the name of a person. My JSON payload looks like this:

"wdp:P2561":[{ "property_id":1, 
     "property_label":"person name", 
     "@value": "Last name, First name", 
     "type":"literal", 
     "lang":"en" 
}]

(2) an item with object properties
Here comes the problem, objects can be one of the following three options:

(2.1) the object is a resource already created
e.g. a book of my author.

"wdp:P50":[ {"property_id":1, 
     "property_label":"authorOf", 
     "@id": "http://xxx/items/item-id", 
     "type":"resource:item" ,
     "value_resource_id": item-id,
}]

(2.2) the object is an external URI
e.g. a VIAF ID. In these cases I don’t need to know what it redirects to.

"wdp:P214":[{ "property_id":1, 
     "property_label":"VIAF ID", 
     "@id": "http://viaf.org/xxx", 
     "type":"uri" }]

(2.3) the object is a term from a controlled vocabulary (built-in OmekaS value suggest)
e.g. a geonames. In this case, when users type via Omeka interface the placename, they can select the desired place from the dropdown. However, it’s not clear how these entities are treated in Omeka.

So my questions are:

  • Are controlled vocabulary terms just URIs (and the valuesuggest plugin does the magic) or are these items?
  • I’m not sure what to do in this case when uploading new data via API, should I treat it as a URI, like I did for the VIAF, or should I look for Omeka IDs? an example would be of help!

Moreover:

  • Are the examples correct and complete (for what concerns the property at hand)? I couldn’t find any example of object properties, so I’m not even sure whether keys and values are correct (!)
  • Do I need to specify property_label all the times?

Thank you!

  • You never need to specify property_label; it’s merely in the output as a convenience to consumers of the API
  • In the “existing Omeka S resource” case, the mandatory key is value_resource_id. Type of resource works for any “resource” (items, sets), also resource:item in the case of limiting to only items.
  • In the “external URI” case, @id is the mandatory key.
  • ValueSuggest, as you say, “does the magic,” it creates values that act as “external URI” type if the external source has URIs, or else as literals. There are no Omeka S ids for ValueSuggest terms. It’s really intended as a data-entry UI tool: if you’re creating values through the API, it probably makes most sense to simply use the uri type.

awesome!

thank you for the quick reply

I’ve put this information and a little more into a new developer documentation page.

2 Likes

The property ID’s created a stumbling block for me, and I’m noticing that you are using a value of 1 for all your property id’s. Here’s a quick-and-dirty extraction script I used to pull all the resource classes and property id’s (from 1-500) from my site.

This way, when I went back in to post new items, I could quickly look up what the magical secret arbitrary site-specific id number for that property should be.

def get_ids():
	
	
	lookup=['resource_classes','properties']
	for l in lookup:
		id=1
		id_dict = {}
		while id <500:
			this_url = 'http://xxx/api/%s/%s' %(l,str(id))
			response = requests.get(this_url)
			print(id,response)
			results = json.loads(response.text)
			code = response.status_code
			if code == 200:
				label = results['o:label']
				term = results['o:term']
				id_dict[term]={'label':label,'id':id}
				print(term)
			id +=1
	
		d=open('%s_ids.json'%l,'w')
		d.write(json.dumps(id_dict))
		d.close()

After that, I used the following functions to create items, upload images, and, what will probably be of most use to you, layer properties onto those items – property types of course can be: literal, uri, resource:

import json
import requests

d = open('omekakeys.json','r')
t = d.read()
d.close()

j = json.loads(t)

params = {
    'key_identity': j['key_identity'],
    'key_credential': j['key_credential']
}

#print(params)

d=open('properties_ids.json','r')
t = d.read()
j = json.loads(t)
d.close()
properties = j

d=open('resource_classes_ids.json','r')
t = d.read()
j = json.loads(t)
d.close()
resource_classes= j

def upload_image(id,fname):
	
	data = {
	"o:ingester": "upload", 
    "file_index": "0", 
    "o:item": {"o:id": id},
    "dcterms:title": [{"type": "literal","property_id": 1,"property_label": "Title","@value": fname}]
    }
    	
	files = [('data', (None, json.dumps(data), 'application/json')),('file[0]', (fname, open(fname,'rb'),'image'))]

	response = requests.post('http://xxx/api/media', params=params, files=files)

	#print(id,fname)
	#print(response)

def create_item(item_class,property):

	resource_class_id = resource_classes[item_class]
	
	data = {

	"@type": ["o:Item", item_class],
	
	"o:resource_class": 
			{
				"o:id": resource_class_id,
				"@id": 'http://xxx/api/resource_classes/%d' %resource_class_id
			}
	}
	
	#print(data)

	headers = {
	'Content-type': 'application/json'
	}
	response = requests.post(url, params=params, data=json.dumps(data), headers=headers)
	#print(response)
	j = json.loads(response.text)
	#print j
	#print(j['o:id'])
	#return j['o:id']

def create_properties(item_id,property,value,type,omeka_s_valuetype,label=None):

	url = 'http://xxx/items'
	headers = {
	'Content-type': 'application/json'
	}
	
	#print(properties[property])
	
	property_id = properties[property]['id']	

	params['id']=item_id
	data = json.loads(requests.get(url,params=params).text)	
		
	#print(resource_id)
	
	propertyname = property.split(':')[1]
	
	print(item_id,property,value,property_id,type,omeka_s_valuetype)
	
	if property not in data.keys():
		data[property] = []
	
	d=	{
		"property_label":propertyname,
		"type":omeka_s_valuetype,
		"property_id":property_id
		}
	
	if omeka_s_valuetype == 'literal':
		d["@value"]=value
	elif omeka_s_valuetype == 'uri':
		d["@id"]=value
		d['o:label']=label
	else:
		d['value_resource_id']=value
	data[property].append(d)
	
	#print(data)
	
	response = requests.patch(url, params=params, data=json.dumps(data), headers=headers)
	#print(response.text)
	

Obviously this is just my fix and it’s a little clunky – requires you to make one call per property created, item uploaded, or image posted – but I found it useful as an interface that let me handle the imports. The fastest way of course is to run your scripts on the VM that’s hosting the site so that the calls all go to localhost.

2 Likes

Thanks for this! I might have misunderstood then, I thought that property_id is the incremental number corresponding to the (RDF) objects I’m uploading.

E.g. if I have two values for the property rdfs:label that I’ll have two dictionaries with property_id=1 and property_id=2. Is this completely wrong? I mean, what is the purpose of using namespaces and prefixes (e.g. rdfs:label) as keys if I have to repeat the information for identifying the property in the dictionary?

[EDIT] I had a look at the new documentation, thanks for the tip!

1 Like

There’s an extended conversation about working with property id’s here: https://github.com/omeka/omeka-s/issues/1171

Yet another question. When importing new items is it sufficient to declare the class to associate the item with an existing template or do I need to import also the resource template along with the payload?

Many thanks

Selecting a class doesn’t imply a template: there’s no restriction that a class be assigned to one and only one template, much like any of the template options.

Thanks, so I can assume I have to specify the resource template id in the json payload with "o:resource_template": {"o:id": id_num}, correct?

That’s right, if you want to set a template that’s how it is done.

This topic was automatically closed 250 days after the last reply. New replies are no longer allowed.