I want to model Person class which takes data property givenName of type xsd:string. How to specify length restriction of this property (say maxLength=50) that is applicable only for Person class? For example, I want to allow other classes to use the same property and choose different value for restriction.
First of all, OWL is not a constraint language. It is intended rather to define classes based on restrictions, than to set up restrictions for classes.
However, one can define anonymous “restriction-based” class and declare another class to be a subclass of this anonymous class.
In the Manchester syntax, you can write something like this:
Class: Person
SubClassOf: givenName only xsd:string[maxLength 5]
In the Functional syntax:
SubClassOf(
:Person
DataAllValuesFrom(
:givenName
DatatypeRestriction(
xsd:string
xsd:maxLength "5"^^xsd:string
)
)
)
In the Turtle syntax:
:Person rdfs:subClassOf
[ rdf:type owl:Restriction ;
owl:onProperty :givenName ;
owl:allValuesFrom
[ rdf:type rdfs:Datatype ;
owl:onDatatype xsd:string ;
owl:withRestrictions ( [ xsd:maxLength "5"^^xsd:string ] )
]
] .
The image below is the "Class description" view in Protégé:
Now suppose you declare that
Individual: He
Types: Person
Facts: givenName "Alexander"^^xsd:string
Then reasoner (e.g. HermiT) have to say that your ontology is inconsistent:
Related
I am working with the FoodOn ontology and need to figure out if a certain class is somehow related to another class. Typical use case: Can a vegan person eat honey? No, because honey is a subsub class of "invertebrate animal food product"!
I am using the python owlready2 library which allows me to run SPARQL queries and query subclass relations like this:
SELECT ?class
WHERE
{
# honey
<http://purl.obolibrary.org/obo/UBERON_0036016> rdfs:subClassOf* ?class .
# animal food product
?class rdfs:subClassOf* <http://purl.obolibrary.org/obo/FOODON_00001176>
}
This code gives me the full subclass path between honey and animal food product - great.
My problem is, that the relationship is not always that of a subclass. Let's look at "vegetarian food product" using the Protege editor:
We can see that "vegetarian food product" is a subclass of "food product by organism" but at the same time it is also equivalent to 'food product'
and (not ('derives from' some
('invertebrate animal' or 'vertebrate animal'))).
If I look at all triples using SPARQL I get the subclass relationship but the equivalentClass is just a bnode:
(rdflib.term.URIRef('http://www.w3.org/2000/01/rdf-schema#subClassOf'),
rdflib.term.URIRef('http://purl.obolibrary.org/obo/FOODON_00002381')),
(rdflib.term.URIRef('http://www.w3.org/2002/07/owl#equivalentClass'),
rdflib.term.BNode('5846'))
[stripped some output]
Why does SPARQL not return all relationships?
How can I query for all classes that are either a subclass of 'vegetarian food product' or related to it by some other object property?
Also, how can I query for the 'AND' and 'OR' component in the example above?
I do accept non-python solutions as long as it can be automated. Protege has a DL Query tab but I think that I cannot export the results using the UI...
Thank you!
Does anyone have or know of a recipe (sample code and/or instructions) on setting up many-to-many relationships between different Page models? If I have PersonPage and SitePage models, how do I connect the pages (a person can work at multiple sites and a site can have multiple people working there)?
Here's what I've found related to, but not directly on, this topic—
Wagtail docs: from a search for "many-to-many" the only hit is in the section on the taggit module (Recipes page).
Wagtail docs: the only reference to the ParentalManyToManyField is a demo of how it can be used to create M2Ms between pages and categories (Tutorial)
This 2015 post on M2M relationships in Wagtail (it's referenced in an SO 'answer' to basically the same question I'm asking here). Although it doesn't discuss page-page relationships the approach presented might be adapted to work. My modified imitation failed with various errors depending on how I tried to set up the InlinePanel call — but the sample code from the post fails in just the same ways, so either it wasn't tested or it's been made obsolete in 2.x.
class PersonPage(Page):
pass
PersonPage.content_panels = [
InlinePanel('ps_links', label='PS Links'),
]
class PersonSitePageLink():
spage = models.ForeignKey('SitePage', on_delete=models.SET_NULL, related_name='sites')
ppage = ParentalKey('PersonPage', related_name='ps_links', on_delete=models.SET_NULL,)
panels = [
FieldPanel('spage')
]
class SitePage(Page):
pass
This technique works fine for relating a Page model to itself, but expanding it to encompass two distinct models creates two parallel but unconnected sets of relationships (you can pick arbitrary Bug pages to link to any Plant page, or vice versa, but the Plants you picked don't show when you edit Bugs). I see why in the code, I think, but I don't see how to make a single M2M connection between the two pages.
class PlantPage(Page):
related_bugs = ParentalManyToManyField('BugPage', blank=True)
content_panels = Page.content_panels + [
FieldPanel('related_bugs'),
]
class BugPage(Page):
related_plants = ParentalManyToManyField('PlantPage', blank=True)
content_panels = Page.content_panels + [
FieldPanel('related_plants'),
]
This one also only talks about intra-page model (rather than inter-page model) M2Ms. (It is pre-ParentalManyToManyField and in fact only available from the Wayback Machine.)
I hope this helps, I took inspiration from this article about moving from ParentalManyToManyField to a central model that 'links' each page from this AccordBox article.
It turns out that InlinePanel does not fully support ParentalManyToManyField, hence the issues you were running into.
I was able to implement a refined approach to your option one above and it should solve your problem.
A reminder that all Page models already extend ClusterableModel so there is no need to add that to any models you create.
Overview
Create a new 'relation' that extends models.Model which will be the relation between these two page models.
Each field within this new model will be the two page types via the model-cluster ParentalKey each with a logical related_name set that is the OTHER side of the relationship.
No need to set panels on this model as we will declare the panels individually via the panels kwarg to InlinePanel - see the InlinePanel docs.
Finally, each individual Page's content_panels has an InlinePanel added that refers to the central relation model indirectly via that model's related_name, adding the other side reference to PageChooserPanel.
Example Code
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, PageChooserPanel
class PersonPageSitePageRelation(models.Model):
person = ParentalKey('app.PersonPage', on_delete=models.CASCADE, related_name='sites')
site = ParentalKey('app.SitePage', on_delete=models.CASCADE, related_name='people')
# Optional: some additional fields (e.g. 'note') for this relation
# Important: NOT setting any `panels` here, will be set individually for each 'direction'
class Meta:
unique_together = ('person', 'site')
class PersonPage(Page):
# ... fields (note: `sites` does NOT need to be declared as a field)
# Now we add an `InlinePanel` that will connect to the parental connection to PersonPageSitePageRelation via the related name `sites`, but the panels available will be the PersonPageSitePageRelation's field `site`
content_panels = Page.content_panels + [
# ... other FieldPanel etc
InlinePanel('sites', label='Related Sites', [PageChooserPanel('site')]),
]
class SitePage(Page):
# ... fields (note: `people` does NOT need to be declared as a field)
# Now we add an `InlinePanel` that will connect to the parental connection to PersonPageSitePageRelation via the related name `people`, but the panels available will be the PersonPageSitePageRelation's field `person`
content_panels = Page.content_panels + [
# ... other FieldPanel etc
InlinePanel('people', label='Related People', panels=[PageChooserPanel('person')]),
]
Further Reading
Read about Django Modelcluster - which is the library that ParentalKey comes from.
I would like to allow a dropdown within wagtail admin to select from values (blocks) contained within another model's streamfield, is that possible? I imagined something like:
Feedback(page):
paper = models.ForeignKey('PaperPage', on_delete=models.CASCADE, null=True, blank=False, help_text="The paper associated with this feedback. Auto assigned.")
content_panels = [
InlinePanel('paper__drafts_id', label='Draft') <--- this doesn't work
]
where
Paper(page):
drafts = StreamField(
[
('draft_block', blocks.ListBlock(blocks.StructBlock([
('date', blocks.DateTimeBlock(required=True, label='Date draft uploaded')),
('uploaded_by', MemberBlock(required=False, label='Uploaded by',
help_text="Who is uploading this draft.")),
('draft_file', DocumentChooserBlock(required=False, label='Upload file')),
]), template='papers/blocks/drafts.html')),
],
blank=True
)
but I'm not sure if this is even possible? Any suggestions would be most appreciated. Thanks!
This is one downside of StreamField - the data is not stored as "true" database objects, but only as JSON text stored against the page, so there's no way to define relations such as ForeignKeys pointing to individual items in that data.
If there's only one block type in the stream, as in your example code, then it would be a better fit to define 'draft' as a child object (with an InlinePanel) on the Paper model instead; it will then exist as a true database model.
I'd be grateful for some help on what I'd assumed was a very simple scenario; but being a relative newcomer to OWL and GraphDB I may have made some basic error.
I have a very simple Turtle-specified OWL example as follows:
#prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
#prefix owl: <http://www.w3.org/2002/07/owl#> .
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
#prefix demo: <urn:demo> .
demo:Gender a owl:Class .
demo:Male a demo:Gender .
demo:Female a demo:Gender .
demo:Male owl:differentFrom demo:Female .
demo:Person a owl:Class .
demo:hasGender a owl:ObjectProperty, owl:FunctionalProperty;
rdfs:domain demo:Person;
rdfs:range demo:Gender .
demo:Per1 a demo:Person;
demo:hasGender demo:Male;
demo:hasGender demo:Female .
In essence, I have a class called Gender and assert that there are 2 distinct members Male and Female.
Then I define another class Person with a functional property hasGender whose range is Gender.
Finally I assert an instance of Person, and also two separate assertions that it is both Male and Female.
Now as I understand it this is something of a contradiction; I've asserted that the hasGender property is functional so that, for a given Person, there should be only one gender. I've also asserted that Male and Female are different, so when I import this into GraphDB I was expecting it to fail because of this.
But GraphDB is happy to load both assertions. Have I missed something?
When creating a repository:
select the Check for inconsistencies checkbox;
select the ruleset called OWL2-RL.
If you try to import your data, GraphDB will have to say:
Could not import data; com.ontotext.trree.consistency.ConsistencyException:
Consistency check eq_diff1_1 failed:
urn:demoMale owl:differentFrom urn:demoMale
urn:demoMale owl:sameAs urn:demoMale
Alternatively, unselect the checkbox, import your data and then execute:
PREFIX sys: <http://www.ontotext.com/owlim/system#>
INSERT DATA { [] sys:consistencyCheckAgainstRuleset "owl2-rl" }
Another modelling approach is to create Male and Female as disjoint subclasses of Person.
Unlike owl:FunctionalProperty, owl:AllDisjointClasses is covered by OWL 2 QL.
In the ontology editor Protegé there is a tab called Class hierarchy (inferred). I am looking for a minimal example to create such an inferred class, e.g. is it mainly that :RedCar rdfs:subClassOf :Car, and that's all?
:RedCar a owl:Class ;
owl:equivalentClass [ a owl:Class ;
owl:intersectionOf
(
:Car
[ a owl:Restriction ;
owl:onProperty :hasColor ;
owl:hasValue :Red
]
)
] .
There are some possibilies that may produce this behavior. One example is due to general class axioms (see last line of example below).
Human rdf:type owl:Class
Man rdf:type owl:Class
[rdf:type owl:Class ; owl:complementOf Man ; rdfs:subClassOf Human]
You will notice, that in this ontology thing is equivalent to human, if you switch to the inference view.
Other reasons are found in the pizza ontology. If you take a look at VegetableTopping and VegetarianTopping, you will notice that the first one is subsumed by the second one in the inference view, because of the equivalentTo relation on VegetarianTopping. Hope this helps.