On 2021-08-13 2:35 am, Carl Burnett wrote:
Thanks, Holger. The main differences between the country/state
scenario and mine are:
1. The values of sh:in (for the PropertyShape declaration) and
dash:object (in the sh:target declaration) need to be resources,
rather than strings as in your example.
That should be fine
1.
2. My sh:target statement would need to be more complex, because I
want to invoke the special shape (analogous to
states:AUAddressShape) not when the instance has a certain value
for the “other property,” but when its value /has a certain rdf:type/.
This would require a different component and a different SHACL target
type etc.
Is this doable, either in an out-of-the box way or in a customized
way? (Feel free to reach out to me off of the list if that’s more
appropriate.)
As an example of what would be involved, I am attaching the source code
of the current implementation (for the sh:in pattern that I mentioned).
A new implementation can borrow from this, but would need to swap out
the SPARQL queries. Then there is the surrounding infrastructure such as
installing the JavaScript component. This is a non-trivial undertaking.
We could help doing this on something like Premium Support - this would
better become an off-list discussion then.
BTW are you sure you can use sh:in here? Your original message sounded
more like the concepts under a given skos:ConceptScheme.
Overall, I cannot tell how important this convenience feature is for
your users, but there is a considerable complexity involved here and
many variations of the same theme. All of them can be expressed in SHACL
for constraint validation after the editing, but it's tricky to build
this into editing widgets at edit time. You have (intentionally and
rightfully) abstracted away your example into generic names, but if you
want us to build this into the main product it would help us understand
some real-world examples, with realistic triples and class names.
Holger
thanks,
Carl
*From:* [email protected]
<[email protected]> *On Behalf Of *Holger Knublauch
*Sent:* Wednesday, August 11, 2021 3:10 PM
*To:* [email protected]
*Subject:* Re: [topbraid-users] Pre-limiting values available to the user
*** External email: use caution ***
I am attaching an example shapes graph for a scenario like the
country/state pairs. Applicable values are expressed by a SHACL node
shape that has a dash:HasValueTarget as its sh:target and uses a sh:in
enumeration for the permissible values. As an example choosing
'Temperature' as the quantity kind of the focus node needs to
constrain the values of unit for that same node to Celsius, Fahrenheit
or Kelvin.
This does not seem to be exactly the scenario you have, which seems to
be "all concepts under a given concept scheme"? As I said this is
difficult to generalize and may require a custom widget or custom
extension.
Holger
On 2021-08-12 7:48 am, Carl Burnett wrote:
Thanks for the quick reply, Holger.
Do you mean that you support only the scenario related to
geography, or also other similar scenarios? In our case, we want
to look at the specific rdf:type of the concept that has been
selected as the value of one property and use it to constrain the
values of another concept scheme that are in play for the second
property.
*From:* [email protected]
<mailto:[email protected]>
<[email protected]>
<mailto:[email protected]> *On Behalf Of *Holger
Knublauch
*Sent:* Wednesday, August 11, 2021 2:43 PM
*To:* [email protected]
<mailto:[email protected]>
*Subject:* Re: [topbraid-users] Pre-limiting values available to
the user
*** External email: use caution ***
Hello Carl,
no, the user input widget only looks for the declared sh:class.
Doing more sophisticated on-the-fly checking against something as
complex as SPARQL queries would likely be too slow, and people
would see the error when they try to press the Save Changes button
because the form will display an error/warning in that case. In
your specific case, the situation would become even harder to
check on the fly, because - as you say - the values depend on each
other and we would need to temporarily infer all currently entered
values etc.
We do have support for one specific scenario though, in cases such
as "when property1 (country) is 'USA' then property2 (state) must
be one of 'Alaska', 'Alabama', ... in case that patterns is what
you need. I can provide details if that sounds interesting.
Holger
On 2021-08-12 7:10 am, [email protected]
<mailto:[email protected]> wrote:
Suppose I have a data graph with an underlying ontology that
defines a class/NodeShape and two propertyShapes as follows:
ex:ClassA
a owl:Class ;
a sh:NodeShape ;
sh:property ex:ClassA-property1 ;
sh:property ex:ClassA-property2 ;
.
ex:ClassA-property1
a sh:PropertyShape ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:path ex:property1 ;
sh:class ex:ConceptType1 ;
.
ex:ClassA-property2
a sh:PropertyShape ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:path ex:property2 ;
sh:class ex:ConceptType2 ;
.
Now suppose I want to impose a further constraint on the
instances of ex:ClassA in my data graph: The value of
ex:ClassA-property2 is constrained in a specific way
(expressible by a SPARQL constraint) based on the
user-selected value for ex:ClassA-property1. I have written a
SPARQL-based SHACL constraint to enforce this.
But is there a way to use that constraint to actually/limit
the values presented to the user /for ex:ClassA-property2, so
that they cannot choose a "wrong" value in the first place? (I
understand that this would likely require the value of
ex:ClassA-property1 to be selected and saved first.)
Sorry if any of this is unclear -- happy to provide more
details if needed.
--
You received this message because you are subscribed to the
Google Groups "TopBraid Suite Users" group.
To unsubscribe from this group and stop receiving emails from
it, send an email to
[email protected]
<mailto:[email protected]>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/d85ea09f-4659-4a2e-b066-6d81799d94f4n%40googlegroups.com
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Ftopbraid-users%2Fd85ea09f-4659-4a2e-b066-6d81799d94f4n%2540googlegroups.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166226970644%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=6fgQ2itx0k7VHWIdqn8%2BkKyFi1NeYIn2HmDv91tafrs%3D&reserved=0>.
--
You received this message because you are subscribed to a topic in
the Google Groups "TopBraid Suite Users" group.
To unsubscribe from this topic, visit
https://groups.google.com/d/topic/topbraid-users/6hyN44MnaV8/unsubscribe
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Ftopic%2Ftopbraid-users%2F6hyN44MnaV8%2Funsubscribe&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166226980632%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=ReTUhpjgnwD9tuwszqITX31e%2BTrS66VulnvyjLeErtU%3D&reserved=0>.
To unsubscribe from this group and all its topics, send an email
to [email protected]
<mailto:[email protected]>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/ee3dca12-0c83-5c96-1462-8cf26a486ccc%40topquadrant.com
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Ftopbraid-users%2Fee3dca12-0c83-5c96-1462-8cf26a486ccc%2540topquadrant.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166226980632%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=x8gLsmIYS%2By%2F%2BNQtP1zYXko3Itf8SF4BcoKfj3tkQ6w%3D&reserved=0>.
--
You received this message because you are subscribed to the Google
Groups "TopBraid Suite Users" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected]
<mailto:[email protected]>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/MWHPR19MB1598A8BD87AA53630FB946C2B7F89%40MWHPR19MB1598.namprd19.prod.outlook.com
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Ftopbraid-users%2FMWHPR19MB1598A8BD87AA53630FB946C2B7F89%2540MWHPR19MB1598.namprd19.prod.outlook.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166226990625%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=K5woRTvNjBOqYcDqhdAIH0Z72LZaFUDfY2Tmbck0jk8%3D&reserved=0>.
--
You received this message because you are subscribed to a topic in the
Google Groups "TopBraid Suite Users" group.
To unsubscribe from this topic, visit
https://groups.google.com/d/topic/topbraid-users/6hyN44MnaV8/unsubscribe
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Ftopic%2Ftopbraid-users%2F6hyN44MnaV8%2Funsubscribe&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166227000630%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=tdr7n9fe0Q8Trsk2HUxWi5SzpG6moQcuU8G0GnwrVIE%3D&reserved=0>.
To unsubscribe from this group and all its topics, send an email to
[email protected]
<mailto:[email protected]>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/662890d3-6ce4-3ea9-c8c4-c4de1639a007%40topquadrant.com
<https://nam02.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Fmsgid%2Ftopbraid-users%2F662890d3-6ce4-3ea9-c8c4-c4de1639a007%2540topquadrant.com%3Futm_medium%3Demail%26utm_source%3Dfooter&data=04%7C01%7Ccburnett%40healthwise.org%7C6a50c0fb4071453e6ada08d95d14cdf1%7Ccee5d4e942e548c28a033406fd5b9242%7C0%7C1%7C637643166227010619%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&sdata=Yx1HJyxqNaebZfOH3R3rJX4ZZJrWJhNIqq9xRMd%2BsgM%3D&reserved=0>.
--
You received this message because you are subscribed to the Google
Groups "TopBraid Suite Users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/MWHPR19MB15988C0BB6456EFF61E327D0B7F99%40MWHPR19MB1598.namprd19.prod.outlook.com
<https://groups.google.com/d/msgid/topbraid-users/MWHPR19MB15988C0BB6456EFF61E327D0B7F99%40MWHPR19MB1598.namprd19.prod.outlook.com?utm_medium=email&utm_source=footer>.
--
You received this message because you are subscribed to the Google Groups "TopBraid
Suite Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/topbraid-users/712e2d29-42bf-57b6-076b-5c564c3588c9%40topquadrant.com.
import React from 'react';
import PropTypes from 'prop-types';
import { AppContext } from '../../../AppContextProvider';
import BusyIndicator from '../../../util/BusyIndicator';
import RDFValue from '../../../model/RDFValue';
import { ValueEditorPropTypes } from '../ValueEditor';
/**
* A drop-down editor whose options are dependent on the value of another
property.
* Applicable values are expressed by a SHACL node shape that has a
dash:HasValueTarget as its sh:target and uses
* a sh:in enumeration for the permissible values.
* As an example choosing 'Temperature' as the quantity kind of the focus node
needs to constrain the values of unit for that same node
* to Celsius, Fahrenheit or Kelvin.
*
* Example declarations:
ontologies\test\datashapes.org\dash\tests\targets\states.shapes.ttl
*
* This component subscribes to the 'formValueUpdated' event to update whenever
the trigger predicate's value has changed on the form.
*/
export default class DependentEnumSelectEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
enumValues: null,
error: null,
triggerPredicateURI: null,
};
}
componentDidMount() {
this.context.getEventManager().subscribe('formValueUpdated',
this.handleFormValueUpdated);
let q = {
message: 'Get predicate that triggers refreshing the available
values ' + this.props.field.path.predicate + ' of ' +
this.props.focusNode.label,
query:
`SELECT DISTINCT ?triggerPredicate
WHERE {
?ps sh:path $predicate .
?ps sh:in ?someEnum .
?ns sh:property ?ps .
?ns sh:target ?target .
?target dash:predicate ?triggerPredicate .
}`,
variables: {
focusNode: { uri: this.props.focusNode.uri },
predicate: { uri: this.props.field.path.predicate },
},
withDefaultPrefixes: true,
withImports: true,
}
this.context.sparqlQuery(q).then(data => {
if(!this.unmounted) {
if(data.bindings.length > 0) {
let triggerPredicateURI =
data.bindings[0].triggerPredicate.uri;
this.setState({
triggerPredicateURI: triggerPredicateURI,
}, this.initEnumValues)
}
else {
this.setState({
error: 'DependentEnumSelectEditor not suitable',
})
}
}
})
}
componentWillUnmount() {
this.context.getEventManager().unsubscribe('formValueUpdated',
this.handleFormValueUpdated);
this.unmounted = true;
}
handleFormValueUpdated = (data) => {
if(data.focusNode.uri == this.props.focusNode.uri &&
data.field.path.predicate == this.state.triggerPredicateURI) {
this.loadEnumValues(data.value);
}
}
initEnumValues = () => {
// Loads the initial enum values, based on the value asserted in the
graph
let q = {
message: 'Get value of the trigger predicate ' +
this.state.triggerPredicateURI + ' of ' + this.props.focusNode.label,
query:
`SELECT ?value
WHERE {
$focusNode <${this.state.triggerPredicateURI}> ?value .
} LIMIT 1`,
variables: {
focusNode: { uri: this.props.focusNode.uri },
},
withImports: true,
}
this.context.sparqlQuery(q).then(data => {
if(data.bindings.length > 0) {
let value = data.bindings[0].value;
this.loadEnumValues(value);
}
else {
this.setState({
enumValues: [],
})
}
})
}
loadEnumValues = (value) => {
// Loads the enum values, based on a given value
let q = {
message: 'Get enum values for the trigger predicate ' +
this.state.triggerPredicateURI + ' and ' + value,
query:
`SELECT ?enumValue
WHERE {
?ps sh:path $predicate .
?ps sh:in ?enum .
?ns sh:property ?ps .
?ns sh:target ?target .
?target dash:predicate <${this.state.triggerPredicateURI}> .
?target dash:object $value .
?enum rdf:rest*/rdf:first ?enumValue .
}`,
variables: {
focusNode: { uri: this.props.focusNode.uri },
value: value,
},
withDefaultPrefixes: true,
withImports: true,
}
this.context.sparqlQuery(q).then(data => {
if(!this.unmounted) {
let enumValues = data.bindings.map(binding => new
RDFValue(binding.enumValue));
this.setState({
enumValues: enumValues,
})
}
})
}
render() {
if(this.state.error) {
return (
<div className="alert alert-danger">{this.state.error}</div>
)
}
else if(this.state.warning) {
return (
<div className="alert alert-warn">{this.state.warning}</div>
)
}
else if(this.state.enumValues) {
return (
<select
className={'EnumSelectEditor form-control w-100' +
(this.props.isDirty() ? ' Dirty' : '')}
onChange={this.updateValue}
value={this.props.value.indexIn(this.state.enumValues)}
>
{this.props.value.indexIn(this.state.enumValues) == -1 && (
<option
value={-1}>{this.props.value.getDisplayText()}</option>
)}
{this.state.enumValues.map((value, index) => (
<option key={index} value={index}>
{value.getDisplayText()}
</option>
))}
</select>
);
}
else {
return (
<BusyIndicator />
)
}
}
updateValue = (e) => {
let index = e.target.value;
if (index >= 0) {
let newValue = this.state.enumValues[index];
this.props.changed(newValue);
}
};
}
DependentEnumSelectEditor.labels = {
en: 'dependent enum drop down',
};
DependentEnumSelectEditor.propTypes = {
...ValueEditorPropTypes,
};
DependentEnumSelectEditor.contextType = AppContext;