Good practice: CIDOC-CRM data properties and classes

Hello,
i am trying to understand what would be the best practice in Omeka-S to describe objects, which according to CIDOC-CRM, would have a nested structure.

For instance if i create an item with the E24 Class ( Physical man made thing), i can then insert a field with the P1 Property ( is identified by), and then i can add a literal as text to fill the field.

Nevertheless, according to CIDOC-CRM, ideally i should connect a class E42 for the identifier, which then contains my literal.

I have the feeling that applying this practice in Omeka-S it would lead me to an overload of Omeka resources that i would then have to create for each objects to get a similar output. It would easily get out of hand.

As mentioned here i could just stick to the data properties. Though, if i would later set up a script to convert the JSON-LD Omeka-S provides to RDF, i would have to do quite some hard coding into converting the fields to a proper structure.

Wouldn’t it be possible to define classes in the resource template as data type? Or something similar, where i could at least add a key to our json-ld objects to make the later conversion just a bit easier?

How should i proceed?

Thanks

You could use Omeka’s rep.resource.json event to modify cidoc-crm:P1 value nodes to include a CIDOC-CRM type of cidoc-crm:E42. Something like this:

    "cidoc-crm:P1": [
        {
            "@value": "M_186_555",
            "cidoc_crm_type": "E42"
        }
    ],

This is still valid JSON-LD since parsers are supposed to ignore non-prefixed keys in value nodes. Then, your script could make the conversion based on the cidoc_crm_type.

However, I sense that it would take a comparable amount of effort just to do all this in your script. Simply detect CIDOC-CRM properties and map them to their respective classes, all at runtime.

Okay, thanks for the suggestion.
I’ll try to discover how to detect the properties with certain libraries such as rdflib or easyrdf.
If you have some more advices, i’m all ears.
Giacomo

You won’t need to use a RDF parser to discover the properties. Just iterate the JSON object provided by the Omeka API, and any key that begins with cidoc-crm: (or however you prefix the vocabulary) is a CIDOC-CRM property that will need further processing.

Yes, i meant using the library to iterate the properties i export from Omeka, so that i can automatise the proper structure and hierarchies.
E.g. if P1 always has a child class which is E42, i will automatically parse the proper rdf.

At the end I decided to create an Omeka-S module to deal with multi-input fields. (I am now extending my question, to a module specific one).
This became necessary as certain instances in CIDOC-CRM, such as the dimension class usually end up having 3 properties, e.g:

crm:P43_has_dimension [ a crm:E54_Dimension ;
            crm:P2_has_type  “width” ;
            crm:P90_has_value "82" ;
            crm:P91_has_unit <http://vocab.getty.edu/aat/300379098> ], 

Therefore I found necessary to design something like a multi input field:

I therefore created a custom data type to parse all the existing classes installed in Omeka S and to create specific labels in the resource template.

I am now thinking of creating an array which contains the properties I select in the dropdown(s) and the string attached next to it.

I am planning to create a json-ld like this one:

{
         "type": "nesteddatatype#cidoc-crm:E54_Dimension",
         "property_label": "P43 has dimension",
         "@value": "value, value", <- all the @values of the resource, flattened and splitted by a comma
         "entity": "E54_Dimension" <- defined through the getJsonLd()
         "properties": [ 
                 "P2_has_type" : “value”,
                 "P90_has_value" : “value”,
          ] 

  }

My question would then be how can I select, perhaps through the hydrate function, a custom data-value-key I specify in my html, so that i can add it in my json and it will be maintained, when re-freshing/editing the item?

   <div class="repeat-property">
        <select name="property-dropdown" data-value-key="property"> // this one 
            <?php foreach($properties as $property): ?>
                <option value="<?= $property->term() ?>"><?= $property->label() ?></option>
            <?php endforeach; ?>
        </select>
        <textarea name="property-value" data-value-key="@value"></textarea>
    </div>

For the repeatable property/value pairs, you’ll need to somehow index your data-value-keys so Omeka’s client-side processing doesn’t overwrite duplicate keys. Something like:

<select data-value-key="property-1">
    <option value="P2_has_type">Has type</option>
    <option value="P90_has_value">Has value</option>
</select>
<textarea data-value-key="value-1"></textarea>
<select data-value-key="property-2">
    <option value="P2_has_type">Has type</option>
    <option value="P90_has_value">Has value</option>
</select>
<textarea data-value-key="value-2"></textarea>

In hydrate() you’ll need to package this data however you see fit and save it using $value->setValue($data). Then you’ll need to use the o:prepare-value JS event to prepare the fields as they are rendered. There are a lot of moving parts here, but I believe it is possible. It’ll just take trial and error.

Thank you! i managed to create the basic of my module, i now have json-ld as i want:

 {
            "type": "nesteddatatype#cidoc-crm:E54_Dimension",
            "property_id": 1262,
            "property_label": "P43 has dimension",
            "is_public": true,
            "@value": "1 2 3",
            "entity_label": "cidoc-crm:E54_Dimension",
            "properties": {
                "cidoc-crm:P1_is_identified_by": "1",
                "cidoc-crm:P2_has_type": "2",
                "cidoc-crm:P3_has_note": "3"
            }
        }

and a rough visual structure of it, where i use the valueObj to populate the frontend:

I still have to add language and uri filters in my module, but one step at a time.

1 Like

Hello, i am coming back here with a separate question, connected to the plugin i was developing.

I was wondering how to apply the function render() which i use to simplify the json i create in my plugin to the resource title in omeka-s.

e.g. i have a json-ld object like this:

{
            "type": "nesteddatatype#crm:E35_Title",
            "property_id": 1897,
            "property_label": "P102 has title",
            "is_public": true,
            "@value": "Bildnis einer Frau mit spanischem Hut und Halskrause",
            "properties": [
                {
                    "@type": "crm:E35_Title",
                    "crm:P190_has_symbolic_content": [
                        {
                            "@value": "Bildnis einer Frau mit spanischem Hut und Halskrause"
                        }
                    ]
                }
            ]
        }

techically the raw @value would be the content of my properties array, but i render() it for the view.

Nevertheless if i would then select this property as my o:title in omeka, i would then see the raw json:

How to solve it?
So far i am force to add a literal with the same value and hide it. But it’s not really a solution.

I don’t quite understand your question. I can’t imagine a circumstance where Omeka would choose the content of yourproperties array as the resource title. It shouldn’t even be aware of the array unless your code makes it aware.

let me rephrase it. My @value is exactly as the properties array, but i change it with the render() function and with the getJsonLd() one (link to the repo).
Therefore, both in the cms and both when i check the json-ld it creates, i see the @value as i want.

Nevertheless it seems that Omeka S keeps the raw value in the resource title, and i would not know how to change it.

Okay, I understand. This happens because your custom data type hydrates the @value exactly as it’s posted by the form. Omeka then sets the custom array as the title (evidently because the resource template sets that property as the title). Our title system circumvents the normal render() pathway. Instead of fetching and rendering the value during runtime, it detects and stores the title separately during create/update operations, and renders that.

Try using the rep.resource.title event to modify the title according to whatever logic you need.

1 Like

Yes that is what i meant! Thank you.
I managed to make it work immediately!

/**
     * Manage the parsing of the title.
     *
     * @param Event $event
     */
    public function handleResourceTitle(Event $event): void
    {
        $resource = $event->getTarget();
        $template = $resource->resourceTemplate();
        
        if ($template && $property = $template->titleProperty()) {
            $title = $resource->value($property->term());
            $properties = json_decode($title, true);

            $event->setParam('title', (string) $title);
            if( $properties[0]['@type']) {
                $values = [];
                
                foreach ($properties[0] as $key => $val) {
                    foreach ($val as $innerKey => $innerVal) {
                        if($innerVal['@value']){
                            $values[$key] = $innerVal['@value'];
                            continue;
                        }
                        if($innerVal['label']){
                            $values[$key] = $innerVal['label'];
                            continue;
                        }
                        foreach ($innerVal as $secondKey => $secondVal) {
                            $values[$key] = $secondVal['@value'];
                            if($secondVal['@value']){
                                $values[$key] = $secondVal['@value'];
                            }
                            if($secondVal['label']){
                                $values[$key] = $secondVal['label'];
                            }
                        }
                    }
                }
                $cleanedTitle = implode('; ', $values);
                $event->setParam('title', (string) $cleanedTitle);
            }
            else {
                $event->setParam('title', (string) $title);
            }
            
        }
    }
2 Likes

Still working on my plugin. Everything is working as planned. I was now wondering whether it is possible to trigger what I am referring to as the “linked resource event”.

E.g. When I link to an internal item I would like to then see in the linked item’s page the property that is referring to. Viceversa, when i delete the property, i would like to delete the connection.

I am not using the value_resource_id as I am extending the Literal class for my module.
But I hope it is possible to do something when I call the Hydrate() function.

I add two images to better explain my question:

I link the Q3 item here:

In the Q3 item I would like to see that the P1 property from the previous resource is referring to it:

Can someone point me to an example in the documentation?

You could use the view.show.section_nav and view.show.after events to append a section that contains your unconventionally linked resources.

1 Like