Extracting values with a shape map

A shape map returns the values that satisfied a shape’s property constraints. Use one to find sensors, build a configuration, or feed validated values into an application. Install Shifty as described in Install Shifty.

Describe the values to find

In this example, each VAV should have a temperature sensor and an airflow sensor. The shapes name those two slots. Save and run this Python script:

import shifty


shapes = """
@prefix ex: <http://example.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

ex:VavShape a sh:NodeShape ;
    sh:targetClass ex:Vav ;
    sh:property [
        sh:path ex:hasPoint ;
        sh:name "supply air temperature" ;
        sh:qualifiedValueShape [ sh:class ex:SupplyAirTemperatureSensor ] ;
        sh:qualifiedMinCount 1 ] ;
    sh:property [
        sh:path ex:hasPoint ;
        sh:name "airflow" ;
        sh:qualifiedValueShape [ sh:class ex:AirFlowSensor ] ;
        sh:qualifiedMinCount 1 ] .
"""

data = """
@prefix ex: <http://example.org/> .
ex:vav-1 a ex:Vav ; ex:hasPoint ex:sat-1 .
ex:sat-1 a ex:SupplyAirTemperatureSensor .
ex:vav-2 a ex:Vav ; ex:hasPoint ex:sat-2, ex:flow-2 .
ex:sat-2 a ex:SupplyAirTemperatureSensor .
ex:flow-2 a ex:AirFlowSensor .
"""

smap = shifty.shape_map(data, shapes, infer=False)

for mapping in smap["http://example.org/VavShape"]:
    print(mapping.focus, "conforms:", mapping.conforms)
    for key, binding in mapping.successful:
        print("   ", binding.name, binding.values)
<http://example.org/vav-1> conforms: False
    supply air temperature [Iri(value='http://example.org/sat-1')]
<http://example.org/vav-2> conforms: True
    airflow [Iri(value='http://example.org/flow-2')]
    supply air temperature [Iri(value='http://example.org/sat-2')]

vav-2 satisfies both requirements. vav-1 lacks an airflow sensor, but its temperature sensor is still available as a successful binding.

Use the bindings

Add this after the loop to build a dictionary for the conforming VAV:

mapping = next(
    row for row in smap["http://example.org/VavShape"] if row.conforms
)
for name, values in sorted(mapping.value_map(by="name", python=True).items()):
    print(name, values)
airflow ['http://example.org/flow-2']
supply air temperature ['http://example.org/sat-2']

The keys come from sh:name. Each value is a list because a property may match several nodes. python=True converts RDF terms to Python values. Failed bindings are omitted from this dictionary.

Continue with Extract bindings with a shape map to inspect missing values, annotate bindings, and serialize a map. The shape-map reference lists the available fields and methods.