diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..92c1ccc --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,42 @@ +name: Build and Release Ontology + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d %H:%M')" + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: 3.9 + architecture: x64 + + - name: Install owlready2 + run: pip3 install git+https://github.com/csse-uoft/owlready2.git + + - name: Generate ontologies + run: python -m src.main + + - name: Release & Assets + uses: johnwbyrd/update-release@v1.0.0 + with: + release: Latest + token: ${{ secrets.GITHUB_TOKEN }} + body: Generated on ${{ steps.date.outputs.date }} + files: + - ./cudr-maturity-model.owl + - ./fair.owl diff --git a/src/addon.py b/src/addon.py new file mode 100644 index 0000000..d5e473f --- /dev/null +++ b/src/addon.py @@ -0,0 +1,24 @@ +# This file includes the missing entities in owlready2 + +from owlready2 import Thing, default_world +from src.namespaces import rdfs, xsd + + +def add_missing_entities(): + default_world.get_ontology("https://vocab.org/vann/vann-vocab-20100607.rdf").load() + with rdfs: + class Literal(Thing): + pass + + class Resource(Thing): + pass + + with xsd: + class duration(Thing): + pass + + class positiveInteger(Thing): + pass + + class decimal(Thing): + pass diff --git a/src/classes/catalogue.py b/src/classes/catalogue.py new file mode 100644 index 0000000..e4d6bbf --- /dev/null +++ b/src/classes/catalogue.py @@ -0,0 +1,91 @@ +from owlready2 import default_world, Thing +from src.namespaces import vann, dct, dcat, skos, cudr, rdfs, vcard, foaf, fair, owl, adms, xsd, sc, odrl, dqv +from datetime import date, datetime + + +class Catalogue(Thing): + label = "CUDR Catalogue Entry" + is_a = [ + # ------ + # Level 1 + # ------ + dcat.theme.only(skos.Concept), + dct.title.only(rdfs.Literal), + dct.description.only(rdfs.Literal), + dcat.keyword.only(rdfs.Literal), # comma separated tags + dct.issued.only(datetime), + cudr.hasTemporalStart.only(datetime), # missing in the paper + cudr.hasTemporalEnd.only(datetime), # missing in the paper + dct.temporal.only(dct.PeriodOfTime), # not implemented in CKAN + dct.spatial.only(dct.Location), + + # ------ + # Level 2 + # ------ + dct.identifier.exactly(1, str), # !! lowercase i + cudr.accessCategory.only(str), + dct.license.only(dct.LicenseDocument), # !! not license_id + dcat.accessURL.only(rdfs.Resource), + dcat.accessService.only(dcat.DataService), + dcat.contactPoint.only(vcard.Kind), + dct.publisher.only(foaf.Agent), + dct.creator.only(foaf.Agent), + cudr.accessedAt.only(datetime), + + # ------ + # Level 3 + # ------ + dcat.landingPage.only(rdfs.Resource), + dct.language.only(dct.LinguisticSystem), + fair["rda-f1-01d"].only(bool), + fair["rda-f1-02d"].only(bool), + dct.format.only(dct.MediaTypeOrExtent), + dcat.downloadURL.only(rdfs.Resource), + owl.versionInfo.only(rdfs.Literal), + adms.versionNotes.only(rdfs.Literal), + dct.isVersionOf.only(dcat.Dataset), + dct.hasVersion.only(dcat.Dataset), + dct.provenance.only(dct.ProvenanceStatement), + dcat.temporalResolution.only(xsd.duration), + dcat.spatialResolutionInMeters.only(xsd.decimal), + cudr.spatialResolutionInRegion.only(sc.AdministrativeArea), # No such property in dcat, using cudr + + + # ------ + # Level 4 + # ------ + cudr.containsIndividualData.only(bool), + cudr.containsIdentifiableIndividualData.only(bool), + cudr.containsIndigenousData.only(bool), + odrl.hasPolicy.only(odrl.Policy), + cudr.indigenousRightsHolder.only(foaf.Agent), + cudr.spatialIndigenousCommunity.only(dct.Location), + + # ------ + # Level 5 + # ------ + fair["rda-r1.3-01d"].only(bool), + fair["rda-i1-01d"].only(bool), + fair["rda-i1-02d"].only(bool), + fair["rda-i2-01d"].only(bool), + fair["rda-i3-01d"].only(bool), + fair["rda-a1.2-01d"].only(bool), + fair["rda-a1-02d"].only(bool), + fair["rda-a1-03d"].only(bool), + fair["rda-a1-04d"].only(bool), + fair["rda-a1-05d"].only(bool), + fair["rda-a1.1-01d"].only(bool), + + # ------ + # Level 6 + # ------ + cudr.rows.only(xsd.positiveInteger), + cudr.columns.only(xsd.positiveInteger), + cudr.cells.only(xsd.positiveInteger), + cudr.triples.only(xsd.positiveInteger), + cudr.entities.only(xsd.positiveInteger), + cudr["properties"].only(xsd.positiveInteger), + dqv.hasQualityAnnotation.only(xsd.positiveInteger), + dqv.inDimension.only(dqv.Dimension), + + ] diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..ad65ee5 --- /dev/null +++ b/src/main.py @@ -0,0 +1,22 @@ +from owlready2 import default_world +from src.namespaces import fair, cudr +from src.metadata import set_cudr_metadata, set_fair_metadata +from src.addon import add_missing_entities + +if __name__ == '__main__': + print('running') + add_missing_entities() + + set_cudr_metadata(cudr, fair) + set_fair_metadata(fair) + + with fair: + import src.properties.fair + + fair.save('./fair.owl') + + with cudr: + import src.properties.cudr + import src.classes.catalogue + + cudr.save('./cudr-maturity-model.owl') diff --git a/src/metadata.py b/src/metadata.py new file mode 100644 index 0000000..2b86afd --- /dev/null +++ b/src/metadata.py @@ -0,0 +1,26 @@ +from owlready2 import default_world +from src.namespaces import vann, dct, dct_onto, dcat_onto, skos_onto, adms_onto, foaf_onto, sc_onto, odrl_onto, vcard_onto + + +def set_dependencies(base_onto, fair_onto): + # Set where the fair ontology can be loaded, i.e. the target url to the fair ontology + fair_onto.set_location("https://github.com/csse-uoft/maturity-model-ontology/releases/download/latest/fair.owl") + + base_onto.imported_ontologies = [fair_onto, dct_onto, dcat_onto, skos_onto, adms_onto, foaf_onto, sc_onto, odrl_onto, vcard_onto] + + +def set_fair_metadata(base_onto): + # Load vann ontology + vann.preferredNamespacePrefix[base_onto.metadata] = ['fair'] + vann.preferredNamespaceUri[base_onto.metadata] = ['http://ontology.eil.utoronto.ca/fair#'] + dct.title[base_onto.metadata] = ['FAIR Ontology'] + dct.description[base_onto.metadata] = ["FAIR Ontology"] + + +def set_cudr_metadata(base_onto, fair_onto): + set_dependencies(base_onto, fair_onto) + # Load vann ontology + vann.preferredNamespacePrefix[base_onto.metadata] = ['cudr'] + vann.preferredNamespaceUri[base_onto.metadata] = ['http://data.urbandatacentre.ca/'] + dct.title[base_onto.metadata] = ['CUDR Maturity Model Ontology'] + dct.description[base_onto.metadata] = ["CUDR Maturity Model Ontology integrated in CKAN"] diff --git a/src/namespaces.py b/src/namespaces.py new file mode 100644 index 0000000..3f48f1a --- /dev/null +++ b/src/namespaces.py @@ -0,0 +1,44 @@ +from owlready2 import default_world + +# Load external ontologies, in case some ontology does not specify the ontology IRI, we assign them the ontology IRI +dct_onto = default_world.get_ontology("https://csse-uoft.github.io/ontologies/dct.rdf").load() +dct = dct_onto.get_namespace("http://purl.org/dc/terms/") + +dcat_onto = default_world.get_ontology("https://www.w3.org/ns/dcat2.rdf").load() +dcat = dcat_onto.get_namespace("http://www.w3.org/ns/dcat#") + +skos_onto = default_world.get_ontology("http://www.w3.org/TR/skos-reference/skos.rdf").load() +skos = skos_onto.get_namespace("http://www.w3.org/2004/02/skos/core#") + +adms_onto = default_world.get_ontology("http://www.w3.org/ns/adms.rdf").load() +adms = adms_onto.get_namespace("http://www.w3.org/ns/adms#") + +dqv_onto = default_world.get_ontology("https://csse-uoft.github.io/ontologies/dqv.owl").load() +dqv = dqv_onto.get_namespace("http://www.w3.org/ns/dqv#") + +foaf_onto = default_world.get_ontology("https://csse-uoft.github.io/ontologies/foaf.rdf").load() +foaf = foaf_onto.get_namespace("http://xmlns.com/foaf/0.1/") + +sc_onto = default_world.get_ontology("https://schema.org/docs/schemaorg.owl").load() +sc = sc_onto.get_namespace("https://schema.org/") + +odrl_onto = default_world.get_ontology("https://www.w3.org/ns/odrl/2/ODRL22.rdf").load() +odrl = odrl_onto.get_namespace("http://www.w3.org/ns/odrl/2/") + +vcard_onto = default_world.get_ontology("https://www.w3.org/2006/vcard/ns.rdf").load() +vcard = vcard_onto.get_namespace("http://www.w3.org/2006/vcard/ns#") + + +# Use `get_ontology` for the ontologies we want to create/modify, otherwise use `get_namespace` +vann = default_world.get_namespace("http://purl.org/vocab/vann/") +dc = default_world.get_namespace("http://purl.org/dc/elements/1.1/") +cc = default_world.get_namespace("http://creativecommons.org/ns#") +owl = default_world.get_namespace("http://www.w3.org/2002/07/owl#") +prov = default_world.get_namespace("http://www.w3.org/ns/prov#") +oa = default_world.get_namespace("http://www.w3.org/ns/oa#") +vcard = default_world.get_namespace("http://www.w3.org/2006/vcard/ns#") + +xsd = default_world.get_ontology("http://www.w3.org/2001/XMLSchema#") +rdfs = default_world.get_ontology("http://www.w3.org/2000/01/rdf-schema#") +cudr = default_world.get_ontology("http://data.urbandatacentre.ca/") +fair = default_world.get_ontology("http://ontology.eil.utoronto.ca/fair#") diff --git a/src/properties/cudr.py b/src/properties/cudr.py new file mode 100644 index 0000000..c814a6f --- /dev/null +++ b/src/properties/cudr.py @@ -0,0 +1,68 @@ +from owlready2 import DataProperty, ObjectProperty +from datetime import date +from src.namespaces import sc, foaf, dct, xsd + + +class accessCategory(DataProperty): + range = [str] + + +class hasTemporalStart(DataProperty): + range = [date] + + +class hasTemporalEnd(DataProperty): + range = [date] + + +class accessedAt(DataProperty): + range = [date] + + +class spatialResolutionInRegion(ObjectProperty): + range = [sc.AdministrativeArea] + pass + + +class containsIndividualData(DataProperty): + range = [bool] + + +class containsIdentifiableIndividualData(DataProperty): + range = [bool] + + +class containsIndigenousData(DataProperty): + range = [bool] + + +class indigenousRightsHolder(ObjectProperty): + range = [foaf.Agent] + + +class spatialIndigenousCommunity(ObjectProperty): + range = [dct.Location] + + +class rows(DataProperty): + range = [xsd.positiveInteger] + + +class columns(DataProperty): + range = [xsd.positiveInteger] + + +class cells(DataProperty): + range = [xsd.positiveInteger] + + +class triples(DataProperty): + range = [xsd.positiveInteger] + + +class entities(DataProperty): + range = [xsd.positiveInteger] + + +class properties(DataProperty): + range = [xsd.positiveInteger] diff --git a/src/properties/fair.py b/src/properties/fair.py new file mode 100644 index 0000000..c1881a2 --- /dev/null +++ b/src/properties/fair.py @@ -0,0 +1,23 @@ +from owlready2 import DataProperty + + +# FAIR +# Level 3 +type("rda-f1-01d", (DataProperty,), {"range": [bool]}) +type("rda-f1-02d", (DataProperty,), {"range": [bool]}) + +# Level 5 +# Content +type("rda-r1.3-01d", (DataProperty,), {"range": [bool]}) +type("rda-i1-01d", (DataProperty,), {"range": [bool]}) +type("rda-i1-02d", (DataProperty,), {"range": [bool]}) +type("rda-i2-01d", (DataProperty,), {"range": [bool]}) +type("rda-i3-01d", (DataProperty,), {"range": [bool]}) + +# Access +type("rda-a1.2-01d", (DataProperty,), {"range": [bool]}) +type("rda-a1-02d", (DataProperty,), {"range": [bool]}) +type("rda-a1-03d", (DataProperty,), {"range": [bool]}) +type("rda-a1-04d", (DataProperty,), {"range": [bool]}) +type("rda-a1-05d", (DataProperty,), {"range": [bool]}) +type("rda-a1.1-01d", (DataProperty,), {"range": [bool]})