├── .gitignore
├── .idea
├── encodings.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── CHANGELOG.md
├── DEVELOPER.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── app.py
├── config.py
├── controller
└── routes.py
├── docker-compose.yml
├── graph_management.py
├── helper.py
├── local_vocabs
├── cover-methods.ttl
├── disturbance-assessment.ttl
└── skos.ttl
├── requirements.txt
├── skos
├── __init__.py
├── collection.py
├── common_properties.py
├── concept.py
├── concept_scheme.py
├── method.py
├── register.py
└── schema_org.py
├── static
├── clipboard.js
├── clippy.svg
└── vocview.css
├── tasks.py
├── templates
├── alternates.html
├── base.html
├── concept-scheme-change-note.html
├── elements
│ ├── alt_label.html
│ ├── date.html
│ └── uri.html
├── index.html
├── info.html
├── macros
│ ├── alt_labels.html
│ ├── bibliographic_citation.html
│ ├── broaders.html
│ ├── change-note.html
│ ├── close_match.html
│ ├── concept_hierarchy.html
│ ├── date-metadata.html
│ ├── definition.html
│ ├── description.html
│ ├── exact_match.html
│ ├── header.html
│ ├── in_scheme.html
│ ├── label.html
│ ├── mapping_statement.html
│ ├── members.html
│ ├── narrowers.html
│ ├── popover.html
│ ├── properties.html
│ ├── rdfs_is_defined_by.html
│ ├── render.html
│ ├── schemaorg_contact_point.html
│ ├── schemaorg_family_name.html
│ ├── schemaorg_given_name.html
│ ├── schemaorg_honorific_prefix.html
│ ├── schemaorg_job_title.html
│ ├── schemaorg_member_of.html
│ ├── schemaorg_members.html
│ ├── schemaorg_parent_org.html
│ ├── schemaorg_sub_orgs.html
│ └── top_concept_of.html
├── method.html
├── register.html
└── skos.html
├── triplestore.py
├── vocabs.yaml
└── worker.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | data.ttl
3 | local_vocabs
4 |
5 | # Created by https://www.gitignore.io/api/venv,flask,python,pycharm+all
6 | # Edit at https://www.gitignore.io/?templates=venv,flask,python,pycharm+all
7 |
8 | ### Flask ###
9 | instance/*
10 | !instance/.gitignore
11 | .webassets-cache
12 |
13 | ### Flask.Python Stack ###
14 | # Byte-compiled / optimized / DLL files
15 | __pycache__/
16 | *.py[cod]
17 | *$py.class
18 |
19 | # C extensions
20 | *.so
21 |
22 | # Distribution / packaging
23 | .Python
24 | build/
25 | develop-eggs/
26 | dist/
27 | downloads/
28 | eggs/
29 | .eggs/
30 | lib/
31 | lib64/
32 | parts/
33 | sdist/
34 | var/
35 | wheels/
36 | pip-wheel-metadata/
37 | share/python-wheels/
38 | *.egg-info/
39 | .installed.cfg
40 | *.egg
41 | MANIFEST
42 |
43 | # PyInstaller
44 | # Usually these files are written by a python script from a template
45 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
46 | *.manifest
47 | *.spec
48 |
49 | # Installer logs
50 | pip-log.txt
51 | pip-delete-this-directory.txt
52 |
53 | # Unit test / coverage reports
54 | htmlcov/
55 | .tox/
56 | .nox/
57 | .coverage
58 | .coverage.*
59 | .cache
60 | nosetests.xml
61 | coverage.xml
62 | *.cover
63 | .hypothesis/
64 | .pytest_cache/
65 |
66 | # Translations
67 | *.mo
68 | *.pot
69 |
70 | # Scrapy stuff:
71 | .scrapy
72 |
73 | # Sphinx documentation
74 | docs/_build/
75 |
76 | # PyBuilder
77 | target/
78 |
79 | # pyenv
80 | .python-version
81 |
82 | # pipenv
83 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
84 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
85 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
86 | # install all needed dependencies.
87 | #Pipfile.lock
88 |
89 | # celery beat schedule file
90 | celerybeat-schedule
91 |
92 | # SageMath parsed files
93 | *.sage.py
94 |
95 | # Spyder project settings
96 | .spyderproject
97 | .spyproject
98 |
99 | # Rope project settings
100 | .ropeproject
101 |
102 | # Mr Developer
103 | .mr.developer.cfg
104 | .project
105 | .pydevproject
106 |
107 | # mkdocs documentation
108 | /site
109 |
110 | # mypy
111 | .mypy_cache/
112 | .dmypy.json
113 | dmypy.json
114 |
115 | # Pyre type checker
116 | .pyre/
117 |
118 | ### PyCharm+all ###
119 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
120 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
121 |
122 | # User-specific stuff
123 | .idea/**/workspace.xml
124 | .idea/**/tasks.xml
125 | .idea/**/usage.statistics.xml
126 | .idea/**/dictionaries
127 | .idea/**/shelf
128 |
129 | # Generated files
130 | .idea/**/contentModel.xml
131 |
132 | # Sensitive or high-churn files
133 | .idea/**/dataSources/
134 | .idea/**/dataSources.ids
135 | .idea/**/dataSources.local.xml
136 | .idea/**/sqlDataSources.xml
137 | .idea/**/dynamic.xml
138 | .idea/**/uiDesigner.xml
139 | .idea/**/dbnavigator.xml
140 |
141 | # Gradle
142 | .idea/**/gradle.xml
143 | .idea/**/libraries
144 |
145 | # Gradle and Maven with auto-import
146 | # When using Gradle or Maven with auto-import, you should exclude module files,
147 | # since they will be recreated, and may cause churn. Uncomment if using
148 | # auto-import.
149 | # .idea/modules.xml
150 | # .idea/*.iml
151 | # .idea/modules
152 | # *.iml
153 | # *.ipr
154 |
155 | # CMake
156 | cmake-build-*/
157 |
158 | # Mongo Explorer plugin
159 | .idea/**/mongoSettings.xml
160 |
161 | # File-based project format
162 | *.iws
163 |
164 | # IntelliJ
165 | out/
166 |
167 | # mpeltonen/sbt-idea plugin
168 | .idea_modules/
169 |
170 | # JIRA plugin
171 | atlassian-ide-plugin.xml
172 |
173 | # Cursive Clojure plugin
174 | .idea/replstate.xml
175 |
176 | # Crashlytics plugin (for Android Studio and IntelliJ)
177 | com_crashlytics_export_strings.xml
178 | crashlytics.properties
179 | crashlytics-build.properties
180 | fabric.properties
181 |
182 | # Editor-based Rest Client
183 | .idea/httpRequests
184 |
185 | # Android studio 3.1+ serialized cache file
186 | .idea/caches/build_file_checksums.ser
187 |
188 | ### PyCharm+all Patch ###
189 | # Ignores the whole .idea folder and all .iml files
190 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
191 |
192 | .idea/
193 |
194 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
195 |
196 | *.iml
197 | modules.xml
198 | .idea/misc.xml
199 | *.ipr
200 |
201 | # Sonarlint plugin
202 | .idea/sonarlint
203 |
204 | ### Python ###
205 | # Byte-compiled / optimized / DLL files
206 |
207 | # C extensions
208 |
209 | # Distribution / packaging
210 |
211 | # PyInstaller
212 | # Usually these files are written by a python script from a template
213 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
214 |
215 | # Installer logs
216 |
217 | # Unit test / coverage reports
218 |
219 | # Translations
220 |
221 | # Scrapy stuff:
222 |
223 | # Sphinx documentation
224 |
225 | # PyBuilder
226 |
227 | # pyenv
228 |
229 | # pipenv
230 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
231 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
232 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
233 | # install all needed dependencies.
234 |
235 | # celery beat schedule file
236 |
237 | # SageMath parsed files
238 |
239 | # Spyder project settings
240 |
241 | # Rope project settings
242 |
243 | # Mr Developer
244 |
245 | # mkdocs documentation
246 |
247 | # mypy
248 |
249 | # Pyre type checker
250 |
251 | ### venv ###
252 | # Virtualenv
253 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
254 | pyvenv.cfg
255 | .env
256 | .venv
257 | env/
258 | venv/
259 | ENV/
260 | env.bak/
261 | venv.bak/
262 | pip-selfcheck.json
263 |
264 | # End of https://www.gitignore.io/api/venv,flask,python,pycharm+all
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 |
8 | ## [1.2.3] - 2021-07-05
9 | ### Added
10 | - Error handling when reading data from file.
11 |
12 |
13 | ## [1.2.2] - 2021-06-23
14 | ### Added
15 | - Perform OWL RL reasoning if enabled in background task.
16 |
17 |
18 | ## [1.2.1-postfix1] - 2021-06-23
19 | ### Fixed
20 | - Create missing folders for Celery filesystem broker on web app startup.
21 |
22 |
23 | ## [1.2.1] - 2021-06-23
24 | ### Added
25 | - Trigger background task on before_first_request.
26 |
27 |
28 | ## [1.2.0] - 2021-06-22
29 | ### Added
30 | - Background tasks with Celery + filesystem broker.
31 | - Watchdog to reload data in memory when background task pulls down new data.
32 | ### Changed
33 | - RDFLib 4.2.2 -> 5.0.0
34 |
35 |
36 | ## [1.1.5] - 2021-05-21
37 | ### Added
38 | - MathJax JavaScript dependency to render Math symbols in LateX.
39 |
40 |
41 | ## [1.1.4] - 2021-05-18
42 | ### Fixed
43 | - Conditionally renders the Method's equipments list correctly now for both HTML values as well as for individuals.
44 |
45 |
46 | ## [1.1.3] - 2021-05-18
47 | ### Fixed
48 | - Rendering of the Method's equipments list. It now renders correctly a list of individuals.
49 |
50 |
51 | ## [1.1.2] - 2021-05-18
52 | ### Added
53 | - Methods template now renders additional properties.
54 | - Accessing the viewer still works when viewer re-pulls new triples from sources.
55 |
56 |
57 | ## [1.1.1] - 2021-05-11
58 | ### Added
59 | - CORS support.
60 |
61 |
62 | ## [1.1.0] - 2020-02-22
63 | ### Added
64 | - Re-pull triples for 'memory' store mode.
65 | ### Changed
66 | - Improved the views for different SKOS types (Concept, ConceptScheme, Collection). No requirement for use with OrderedCollection, so will be ignored for now. This improvement will allow us to manage and add different views more cleanly in the future.
67 |
68 |
69 | ## [1.0.6] - 2020-11-09
70 | ### Added
71 | - python-dotenv
72 | - Load from .env file for config.py
73 | - Viewer now understands owl:deprecated and does not show it to the user through SKOS properties narrower, broader, hasTopConcept and in the search.
74 |
75 |
76 | ## [1.0.5] - 2020-11-03
77 | ### Fixed
78 | - Time since last pull of remote data showing 'None' in the jinja template. Now it shows the time since last pull as expected (regardless of triplestore config type).
79 |
80 |
81 | ## [1.0.4] - 2020-11-03
82 | ### Changed
83 | - Triplestore type in config.py changed to 'memory' as default. Better for deployments by removing the overhead of loading from disk a pickle file on each request.
84 | - Only call the get_db function in @app.before_request if the RDF triples are not yet loaded in memory.
85 | - get_properties function calls get_label function with the parameter 'create' as False. Significantly improving the speed of loading concepts since properties don't need to make a HTTP call the dereference the URI of properties without labels loaded in memory.
86 |
87 |
88 | ## [1.0.3] - 2020-10-23
89 | ### Added
90 | - Functionality to show and redirect externally linked concepts.
91 |
92 |
93 | ## [1.0.2] - 2020-07-30
94 | ### Added
95 | - Methods now show hasParameter and hasCategoricalVariableCollection properties in the html view.
96 |
97 |
98 | ## [1.0.1] - 2020-07-28
99 | ### Added
100 | - 404 status code for URIs not found or does not have a recognised class type.
101 | - New Method view. See http://linked.data.gov.au/def/ausplots-cv/03ba5e75-f322-4f80-a1e3-5a845e4dd807 as an example.
102 | - Started moving macros that do not need to be macros into `templates/elements`. These can be used with the Jinja2 `{% includes %}` statement and no longer need to be imported (like with macros).
103 | - VocView can now dynamically read and present the version number from the CHANGELOG.md file.
104 | ### Removed
105 | - The default alternates view options as 'skos' for the header bar in the HTML view.
106 | ### Changed
107 | - Improved some of the macros in Jinja2. Some don't need to be macros and have since been changed to a normal Jinja2 template.
108 |
109 |
110 | ## [1.0.0] - 2020-04-17
111 | ### Added
112 | - "Version 1.0.0 release"
--------------------------------------------------------------------------------
/DEVELOPER.md:
--------------------------------------------------------------------------------
1 | # Developer's Guide
2 |
3 | ## Extending the properties displayed for SKOS things
4 | By default, all properties which are not on the "ignored list of properties" are displayed with their property name, URI, and the property value. VocView has the capability of extending on this to display certain properties in a custom way. For example, the list of broader and narrower concepts are displayed differently where each concept is displayed with its readable label with a clickable link. This link resolves within the VocView system to display the information related to the concept.
5 |
6 | To make this possible, a few steps were taken:
7 | - First, add the property to the ignore list in the function `get_properties()` in [skos/__init__.py](skos/__init__.py).
8 | - The ignored property should be `SKOS.broader`.
9 | - In the `Concept` class in [skos/concept.py](skos/concept.py), add an attribute to the class as `self.broaders = []`, which will be a list (since a concept can have zero to many broader relationships).
10 | - In [skos/__init__.py](skos/__init__.py), write a function which will retrieve all the broader concepts for the given concept. The function signature should take in one argument `uri`, which will be the URI of the *focus* concept. Append the results to a list and return it.
11 | - Back in the `Concept` class in [concept.py](skos/concept.py), assign `self.broaders = skos.get_broaders(uri)`.
12 | - Now create a html file in the directory [templates/macros](templates/macros) called `broaders.html`. Write a Jinja2 macro on how you want the broaders to be displayed for a concept.
13 | - In [templates/skos.html](templates/skos.html), add the import statement for the new macro and render it here.
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3-alpine
2 |
3 | RUN apk add --no-cache git bash vim
4 |
5 | WORKDIR /app
6 |
7 | RUN pip3 install --no-cache-dir --upgrade pip \
8 | && pip3 install --no-cache-dir --upgrade setuptools
9 |
10 | COPY controller /app/controller
11 | COPY skos /app/skos
12 | COPY static /app/static
13 | COPY templates /app/templates
14 |
15 | COPY app.py /app
16 | COPY config.py /app
17 | COPY helper.py /app
18 | COPY requirements.txt /app
19 | COPY triplestore.py /app
20 | COPY vocabs.yaml /app
21 | COPY graph_management.py /app/graph_management.py
22 | COPY tasks.py /app/tasks.py
23 | COPY worker.py /app/worker.py
24 |
25 | COPY CHANGELOG.md /app
26 |
27 | RUN pip install --no-cache-dir -r requirements.txt
28 | RUN pip install --no-cache-dir gunicorn
29 |
30 | RUN mkdir /app/data
31 | RUN mkdir /app/broker
32 | RUN chown -R 1000:1000 /app
33 | USER 1000
34 |
35 | #CMD gunicorn --workers=1 --threads=2 --forwarded-allow-ips=* --bind=0.0.0.0:8000 --limit-request-line=8190 --log-level=info app:application
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Edmond Chuc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | version = 1.2.3
2 |
3 | build:
4 | docker build -t ternau/vocview:$(version) .
5 | docker build -t ternau/vocview:latest .
6 |
7 | run:
8 | docker run --rm --name vocview -p 8000:8000 ternau/vocview:$(version)
9 |
10 | push:
11 | docker push ternau/vocview:$(version)
12 | docker push ternau/vocview:latest
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VocView
2 | [](https://www.python.org/)
3 | [](https://opensource.org/licenses/MIT)
4 | [](https://github.com/edmondchuc/vocview/graphs/commit-activity)
5 | [](https://www.edmondchuc.com)
6 |
7 | *A Python web application to serve SKOS-encoded vocabularies as Linked Data.*
8 |
9 | ### VocView instances
10 | Click on a badge to see a live instance of VocView.
11 |
12 | [](http://linkeddata.tern.org.au/viewer/corveg)
13 |
14 | [](http://linkeddata.tern.org.au/viewer/tern)
15 |
16 | [](http://linkeddata.tern.org.au/viewer/ausplots)
17 |
18 |
19 | ## SKOS
20 | SKOS stands for [Simple Knowledge Organization System](https://www.w3.org/2004/02/skos/), a W3C recommendation, as of April, 2019.
21 |
22 | SKOS is used to create controlled vocabularies and taxonomies using the [RDF](https://en.wikipedia.org/wiki/Resource_Description_Framework) model. Combined with models created in the [Web Ontology Language](https://en.wikipedia.org/wiki/Web_Ontology_Language) (OWL), SKOS allows [Semantic Web](https://en.wikipedia.org/wiki/Semantic_Web) users to provide information in an unambiguous, interoperable, and reusable manner.
23 |
24 |
25 | ## Linked Data
26 | VocView uses the [pyLDAPI](https://pyldapi.readthedocs.io/en/latest/) library to provide registry information as Linked Data. The library also provides VocView with different *views* of things. For example, a register view to describe a register and its items, an alternates view, to describe the alternative views of the same resource, and a SKOS view, to describe SKOS concept schemes and concepts.
27 |
28 | VocView also provides different *formats* for things, such as text/html, text/turtle, application/rdf+xml, etc.
29 |
30 | Specifying views and formats can be done by using the query string arguments `_view` and `_format` or content negotiation with an *Accept* header.
31 |
32 | ##### Example usage
33 | ```bash
34 | http://localhost:5000/vocabulary/?_view=alternates&format=html
35 | ```
36 | The above request will show the HTML page of the alternates view, and display all the available views and formats in a table.
37 |
38 | All vocabulary data are accessible via the Linked Data API.
39 |
40 |
41 | ## Getting started
42 |
43 | ### Installation
44 | Clone this repository's master branch
45 | ```bash
46 | git clone https://github.com/edmondchuc/vocview.git
47 | ```
48 |
49 | Change directory into vocview and install the Python dependencies
50 | ```bash
51 | pip install -r requirements.txt
52 | ```
53 |
54 | Note: Python *virtualenv* is highly recommended. See [this](https://docs.python-guide.org/dev/virtualenvs/) article on Python virtual environments.
55 |
56 | ### Configuration
57 | Add some vocabulary sources in the `vocabs.yaml` file
58 | ```yaml
59 | download:
60 | material_types:
61 | source: https://vocabs.ands.org.au/registry/api/resource/downloads/524/ga_material-type_v1-0.ttl
62 | format: turtle
63 | local:
64 | skos:
65 | source: skos.ttl
66 | format: turtle
67 | rva:
68 | resource_endpoint: 'https://demo.ands.org.au/vocabs-registry/api/resource/vocabularies/{}?includeVersions=true&includeAccessPoints=true&includeRelatedEntitiesAndVocabularies=false'
69 | download_endpoint: 'https://demo.ands.org.au/vocabs-registry/api/resource/downloads/{}'
70 | extension: ttl # Use Turtle as it has excellent compression compared to other RDF serialisations
71 | format: turtle
72 | ids: [
73 | 245, # CORVEG
74 | ]
75 | ```
76 | The example snippet above shows how to enable three types of vocabulary files, an online file, a local file, and a file accessed via [Research Vocabularies Australia](https://vocabs.ands.org.au/)'s (RVA) API.
77 |
78 | The `material_types` is an online file (hence the `download` node), where the `source` node lists the absolute URL of the file. The `format` node tells the RDFLib parser what format the file is in. See the list of available parsers for RDFLib [here](https://rdflib.readthedocs.io/en/stable/plugin_parsers.html).
79 |
80 | The `local` node lists RDF files on the local filesystem. By default, the path of the `source` node is relative to the `local_vocabs` directory in this repository.
81 |
82 | The RVA resource finds the latest API endpoint for a given project referenced by the ID `245`. The extension informs the API what format we want to download and the format informs the VocView system what file type to expect. The `resource_endpoint` is used to determine the RVA project's latest version's *download ID*. The *download ID* is then used with the `download_endpoint` to download the latest RDF resource.
83 |
84 | > Note: there is significance with loading in the `skos.ttl` file, which is a modified version of the SKOS definition. The modifications consist of removing a few `rdfs:subPropertyOf`statements used by the rule-based inference engine (discussed later). Loading this file in to the graph allows the inferencer to create new triples.
85 |
86 |
87 | ## Rule-based inferencing
88 | ### OWLRL
89 | VocView utilises the Python rule-based inferencer for RDF known as [owlrl](https://owl-rl.readthedocs.io/en/latest/). The inferencer is used in VocView to expand the graph on SKOS-specific properties. To expand the graph on SKOS properties, ensure that the `skos.ttl` is declared in `vocabs.yaml`. Additional ontologies can also be loaded in to expand the graph further.
90 |
91 | A good example of why an inferencing engine is used in VocView is to expand properties that have inverse properties of itself. For example, declaring top concepts with `skos:topConceptOf` need only be declared within concepts of a concept scheme. The inferencing engine is capable of performing a deductive closure and add the inverse statements of `skos:topConceptOf` to the concept scheme as `skos:hasTopConcept`. The HTML view of the concept scheme will now display a listing of the top concepts. Without the additional triples added by the inferencing engine, the listing will not be displayed (as the information is missing).
92 |
93 | The downside of using a rule-based inferencer like owlrl is the expensive calculations. This causes a slow start-up time for VocView.
94 |
95 | It is recommended to have inferencing performed on the source repository before loading the data into VocView.
96 |
97 | Therefore, inferencing is an optional feature.
98 |
99 |
100 | ### Skosify
101 | An alternative to rule-based inferencing is the Python [skosify](https://skosify.readthedocs.io/en/latest/index.html) library. This library contains a collection of inferencing functions specifically for SKOS properties. Since this library only focuses on SKOS things, it may be much faster than the owlrl library, thus reducing start-up time.
102 |
103 | *This is a potential feature for a future VocView version*.
104 |
105 |
106 | ## Search within registers
107 | VocView contains registers for vocabularies and concepts.
108 |
109 | ### Naive search
110 | The current search implementation is a naive, string-based text-match on the register items' labels. For simple searches on the label, this works well (and fast) enough. Time complexity is Θ(n) where *n* is the number of items in the register.
111 |
112 | This naive search matches not only full-text in labels, but also partial text. It is also case-insensitive.
113 |
114 | E.g. a search query "*form*" will match:
115 | - *Landform type concepts* .
116 | - *Structural formation classification system concepts*
117 |
118 | ### Whoosh (full text search)
119 | *To be implemented in VocView...*
120 |
121 | Whoosh is a pure Python library text indexer for full text search. It is suitable for light-weight Python applications like VocView, to provide full text search, without requiring external dependencies (outside of Python).
122 |
123 | A description of Whoosh from the official [documentation](https://whoosh.readthedocs.io/en/latest/index.html):
124 |
125 | - *Like one of its ancestors, Lucene, Whoosh is not really a search engine, it’s a programmer library for creating a search engine.*
126 |
127 | Creating a full text search engine using Whoosh would greatly improve the capabilities of searching items within registers. Whoosh would allow users to search for not only key words to match register items' labels, but also their *definition*, *date*, or any other interesting properties.
128 |
129 | Whoosh has been used in related projects already, and it will only take *probably* a full weekend to implement.
130 |
131 |
132 | ## Persistent store
133 | On start-up, the first request performs the loading of all the RDF files into an in-memory graph. It then performs a deductive closure to expand the graph with additional triples outlined in the `skos.ttl`. This process makes the initial start-up time very slow.
134 |
135 | One way to solve this is to have persistence of the graph between server restarts.
136 |
137 | There are three options to choose from in `config.py`'s `Config` class.
138 |
139 | - memory
140 | - pickle
141 | - sleepycat
142 | - sqlite (not implemented)
143 |
144 | > Note: to re-index, simply delete the `triplestore.p` file if using the **pickle** method or delete the `triplestore` directory if using the **sleepycat** method.
145 |
146 | ### Memory
147 | There is no *persistence* when using `memory` as this mode requires loading all the RDF files into the graph on start-up each time.
148 |
149 | #### Pros
150 | Start-up time is very slow but performance is fast as the queries are performed on the graph, which is in memory (RAM).
151 |
152 | #### Cons
153 | Memory use is high as it requires the whole graph to be stored in the application's memory.
154 |
155 | ### Pickle
156 | VocView supports saving the graph object to disk as a binary file. In Python, this is known as [pickling](https://docs.python.org/3/library/pickle.html). On start-up, VocView loads in the graph and saves it to disk. Each subsequent restart, VocView will automatically load in the pickled graph object from disk if it exists.
157 |
158 | #### Pros
159 | Performance is very fast compared to other persistent store methods. Queries made by each web request are performed on the in-memory graph, which is very fast.
160 |
161 | #### Cons
162 | Memory use is high as it requires the whole graph to be stored in the application's memory.
163 |
164 | ### Sleepycat
165 | (The defunct) Sleepycat was the company that maintained the freely-licensed Berkeley DB, a data store written in C for embedded systems.
166 |
167 | RDFLib currently ships Sleepycat by default. See RDFLib's documentation on persistence [here](https://rdflib.readthedocs.io/en/stable/persistence.html).
168 |
169 | #### Pros
170 | Extremely low memory usage compared to a method using in-memory graph. Good for instances of VocView with a large collection of vocabularies.
171 |
172 | #### Cons
173 | Roughly 10-20% slower than in-memory graph (due to filesystem read/write speeds). Requires installing Berkeley DB to the host system as well as downloading the Python **bsddb3** package source and installing it manually.
174 |
175 | #### Installing Sleepycat (Berkeley DB)
176 | ##### Ubuntu 18.04 and above
177 | Install the Berkeley DB
178 | ```bash
179 | sudo apt install python3-bsddb3
180 | ```
181 | Install the Python package
182 | ```bash
183 | pip install bsddb3
184 | ```
185 | You now can use the Sleepycat as a persistent store.
186 |
187 | ##### macOS Mojave and above
188 | First, ensure that [brew](https://brew.sh/) is installed on macOS, then run
189 | ```bash
190 | brew install berkeley-db
191 | ```
192 | Download the **bsddb3** source from PyPI like https://pypi.python.org/packages/source/b/bsddb3/bsddb3-5.3.0.tar.gz#md5=d5aa4f293c4ea755e84383537f74be82
193 |
194 | Once unzipped and inside the package, run
195 | ```bash
196 | python setup.py install --berkeley-db=$(brew --prefix)/berkeley-db/5.3.21/
197 | ```
198 | You now can use the Sleepycat as a persistent store.
199 |
200 | ### SQLite (not implemented)
201 | According to the textbook *Programming the Semantic Web* [1], it is possible to use [SQLite](https://www.sqlite.org/index.html) as the persistent data store for an RDFLib graph.
202 |
203 | It will be a good experiment to investigate on the ease of using this, since most systems come with SQLite pre-installed (unlike Sleepycat's Berkeley DB).
204 |
205 | It will also be interesting to see the speed differences between SQLite and Sleepycat's store.
206 |
207 |
208 | ## References
209 | [1] Segaran, Evans, & Taylor. (2009). Programming the Semantic Web (1st ed.). Beijing ; Sebastopol, CA: O'Reilly.
210 |
211 |
212 | ## License
213 | See [LICENSE](LICENSE).
214 |
215 | ## Developer's guide
216 | See [DEVELOPER.md](DEVELOPER.md).
217 |
218 |
219 | ## Contact
220 | **Edmond Chuc**
221 | [e.chuc@uq.edu.au](mailto:e.chuc@uq.edu.au)
222 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import atexit
2 | import logging
3 |
4 | from flask import Flask, request
5 | from flask_cors import CORS
6 | from werkzeug.middleware.dispatcher import DispatcherMiddleware
7 | from werkzeug.serving import run_simple
8 | from watchdog.observers import Observer
9 |
10 | from config import Config
11 | from controller.routes import routes
12 | import helper
13 | from graph_management import get_graph, VocviewFileSystemEventHandler
14 |
15 | logger = logging.getLogger(__name__)
16 | logging.basicConfig(level=logging.INFO)
17 |
18 | app = Flask(__name__)
19 | cors = CORS(app)
20 |
21 | app.register_blueprint(routes)
22 |
23 | application = DispatcherMiddleware(
24 | None, {
25 | Config.SUB_URL: app
26 | }
27 | )
28 |
29 |
30 | # Set up python-watchdog
31 | path = 'data'
32 | observer = Observer()
33 | observer.schedule(VocviewFileSystemEventHandler(), path)
34 | observer.start()
35 |
36 |
37 | @app.before_request
38 | def before():
39 | # Config.g = Triplestore.get_db(Config.triplestore_type)
40 | Config.g = get_graph(Config)
41 |
42 |
43 | @app.after_request
44 | def after(response):
45 | return response
46 |
47 |
48 | @app.before_first_request
49 | def init():
50 | # Set the URL root of this web application
51 | Config.url_root = request.url_root
52 | logging.info('Loaded config:')
53 | logging.info(Config.__dict__)
54 |
55 | logging.info('Triggering background task from vocview app.')
56 | import worker # Import to create directories if missing.
57 | from tasks import fetch_data
58 | fetch_data.s().apply_async()
59 |
60 |
61 | @app.context_processor
62 | def context_processor():
63 | return dict(h=helper, config=Config)
64 |
65 |
66 | @atexit.register
67 | def shutdown():
68 | logger.info('Performing cleanup')
69 | observer.stop()
70 |
71 |
72 | if __name__ == '__main__':
73 | # Run this only for development. Production version should use a dedicated WSGI server.
74 | if Config.SUB_URL:
75 | print('Starting simple server')
76 | run_simple('0.0.0.0', port=5000, application=application, use_reloader=True)
77 | else:
78 | print('Starting Flask server')
79 | app.run(host='0.0.0.0', port='5000', debug=True)
80 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 |
4 | from dotenv import load_dotenv
5 | from rdflib import Graph
6 |
7 | load_dotenv()
8 |
9 |
10 | def get_version():
11 | with open('CHANGELOG.md', 'r') as f:
12 | while True:
13 | line = f.readline()
14 | if re.search('## \[[0-9].[0-9].[0-9]\]', line):
15 | return line.split('[')[1].split(']')[0]
16 |
17 |
18 | class Config:
19 | title = os.environ.get('VOCVIEW_TITLE', 'VocView')
20 |
21 | # Used in the DCAT metadata.
22 | description = os.environ.get('VOCVIEW_DESCRIPTION', 'SKOS controlled vocabulary viewer')
23 |
24 | # URL root of this web application. This gets set in the before_first_request function.
25 | url_root = None # No need to set.
26 |
27 | # Subdirectory of base URL. Example, the '/corveg' part of 'vocabs.tern.org.au/corveg'
28 | SUB_URL = os.environ.get('VOCVIEW_SUB_URL', '')
29 |
30 | # Path of the application's directory.
31 | APP_DIR = os.path.dirname(os.path.realpath(__file__))
32 |
33 | # Vocabulary sources config. file.
34 | VOCAB_SOURCES = 'vocabs.yaml'
35 |
36 | # Rule-based reasoner
37 | reasoner = True
38 |
39 | FLASK_ENV = os.environ.get('FLASK_ENV', 'production')
40 |
41 | # -- Triplestore ---------------------------------------------------------------------------------------------------
42 | #
43 | # Options:
44 | #
45 | # - memory
46 | # - No persistence, load in triples on instance start-up (slow start-up time). Graph is required to be kept in
47 | # memory during application's lifetime. Not recommended due to slow start-up.
48 | # - Difficulty: easy
49 | #
50 | # - pickle
51 | # - Persistent store by saving a binary (pickle) copy of the Python rdflib.Graph object to disk. Graph is
52 | # required to be in memory during application's lifetime. Fast start-up time and fast performance, uses
53 | # significantly more memory than Sleepycat. Exact same as the memory method except it persists between
54 | # application restarts.
55 | # - Difficulty: easy
56 | #
57 | # - sleepycat
58 | # - Persistent store by storing the triples in the now defunct Sleepycat's Berkeley DB store. Requires external
59 | # libraries to be installed on the system before using. Does not require to have the whole triplestore in
60 | # memory. Performance is slightly slower than the pickle method (maybe around 10-20%) but uses much less memory.
61 | # For each request, only the required triples are loaded into the application's memory.
62 | # - Difficulty: intermediate
63 | triplestore_type = 'memory' if FLASK_ENV == 'production' else 'pickle'
64 |
65 | # The time which the store is valid before re-harvesting in the background.
66 | # store_hours = int(os.environ.get('VOCVIEW_STORE_HOURS', '0'))
67 | # store_minutes = int(os.environ.get('VOCVIEW_STORE_MINUTES', '60'))
68 | store_seconds = int(os.environ.get('VOCVIEW_STORE_SECONDS', '3600'))
69 |
70 | # Triplestore disk path
71 | _triplestore_name_pickle = 'triplestore.p'
72 | triplestore_path_pickle = os.path.join(APP_DIR, _triplestore_name_pickle)
73 | _triplestore_name_sleepy_cat = 'triplestore'
74 | triplestore_path_sleepy_cat = os.path.join(APP_DIR, _triplestore_name_sleepy_cat)
75 |
76 | _version = get_version()
77 |
78 | g: Graph
79 |
--------------------------------------------------------------------------------
/controller/routes.py:
--------------------------------------------------------------------------------
1 | from flask import Blueprint, render_template, request, Response, redirect
2 | from munch import munchify
3 | from pyldapi import Renderer
4 |
5 | from config import Config
6 | import skos
7 |
8 | routes = Blueprint('routes', __name__)
9 |
10 |
11 | def match(vocabs, query):
12 | """
13 | Generate a generator of vocabulary items that match the search query
14 | :param vocabs: The vocabulary list of items.
15 | :param query: The search query string.
16 | :return: A generator of words that match the search query.
17 | :rtype: generator
18 | """
19 | for word in vocabs:
20 | if query.lower() in word[1].lower():
21 | yield word
22 |
23 |
24 | def process_search(query, items):
25 | results = []
26 |
27 | if query:
28 | for m in match(items, query):
29 | results.append(m)
30 | results.sort(key=lambda v: v[1])
31 |
32 | return results
33 |
34 | return items
35 |
36 |
37 | @routes.route('/download', methods=['GET'])
38 | def download():
39 | format = request.args.get('format')
40 |
41 | if format is None:
42 | format = 'turtle'
43 |
44 | # Check if format is one of the supported formats
45 | if format not in Renderer.RDF_SERIALIZER_MAP.values():
46 | return '
Invalid download format type
\nPlease set the format type to be one of the following values: {}'\
47 | .format(set(Renderer.RDF_SERIALIZER_MAP.values()))
48 |
49 | mimetype=None
50 | for key, val in Renderer.RDF_SERIALIZER_MAP.items():
51 | if val == format:
52 | mimetype = key
53 |
54 | file_extension = {'turtle': '.ttl', 'n3': '.n3', 'json-ld': '.jsonld', 'nt': '.nt', 'xml': '.rdf'}.get(format)
55 |
56 | rdf_content = Config.g.serialize(format=format)
57 |
58 | return Response(
59 | response=rdf_content, mimetype=mimetype,
60 | headers={'Content-Disposition': 'attachment; filename={}'.format(Config.title + file_extension)}
61 | )
62 |
63 |
64 | @routes.route('/', methods=['GET'])
65 | def index():
66 | return render_template('index.html')
67 |
68 |
69 | @routes.route('/vocabulary/', methods=['GET'])
70 | def render_vocabulary_register():
71 | page = request.values.get('page')
72 | if page is None:
73 | page = 1
74 |
75 | items = skos.list_concept_schemes_and_collections()
76 |
77 | query = request.values.get('search')
78 | items = process_search(query, items)
79 |
80 | total_items_count = len(items)
81 | page_from = int(page)
82 | page_size = 20
83 |
84 | items = items[(page_from - 1) * page_size:page_size * page_from]
85 |
86 | # TODO: Check why munchify is duplicating the dct:created and dct:modified data in an element of item.
87 | # items = munchify(items)
88 |
89 | r = skos.Register(request, 'Register of SKOS vocabularies',
90 | 'This register contains a listing of SKOS vocabularies as concept schemes or collections.',
91 | items, ['http://www.w3.org/2004/02/skos/core#ConceptScheme', 'http://www.w3.org/2004/02/skos/core#Collection'],
92 | total_items_count=total_items_count,
93 | register_template='register.html',
94 | title='Vocabularies',
95 | description='Register of all vocabularies in this system.',
96 | search_query=query)
97 | return r.render()
98 |
99 |
100 | @routes.route('/concept/', methods=['GET'])
101 | def render_concept_register():
102 | page = request.values.get('page')
103 | if page is None:
104 | page = 1
105 |
106 | items = skos.list_concepts()
107 |
108 | query = request.values.get('search')
109 | items = process_search(query, items)
110 |
111 | total_items_count = len(items)
112 | page_from = int(page)
113 | page_size = 20
114 |
115 | items = items[(page_from - 1) * page_size:page_size * page_from]
116 |
117 | # TODO: Check why munchify is duplicating the dct:created and dct:modified data in an element of item.
118 | # items = munchify(items)
119 |
120 | r = skos.Register(request,
121 | 'Register of SKOS concepts',
122 | 'This register contains a listing of all SKOS concepts within this system.',
123 | items, ['http://www.w3.org/2004/02/skos/core#Concept'],
124 | total_items_count=total_items_count,
125 | register_template='register.html',
126 | title='Concepts',
127 | description='Register of all vocabulary concepts in this system.',
128 | search_query=query)
129 | return r.render()
130 |
131 |
132 | @routes.route('/id/', methods=['GET'])
133 | def ob(uri):
134 | # TODO: Issue with Apache, Flask, and WSGI interaction where multiple slashes are dropped to 1 (19/04/2019).
135 | # E.g. The URI http://linked.data.gov.au/cv/corveg/cover-methods when received at this endpoint becomes
136 | # http:/linked.data.gov.au/cv/corveg/cover-methods. Missing slash after the HTTP protocol. This only happens
137 | # using the Flask URL variable. It worked fine with query string arguments.
138 | # .
139 | # Reference issue online which describes the exact same issue here:
140 | # - http://librelist.com/browser/flask/2012/8/24/not-able-to-pass-a-url-parameter-with-in-it/
141 |
142 | # Ugly fix for Apache multiple slash issue.
143 |
144 | # Get the protocol (http/https)
145 | protocol = uri.split(':')[0]
146 |
147 | # Fix the URI if it is missing double slash after the HTTP protocol.
148 | if protocol == 'http':
149 | if uri[6] != '/':
150 | uri = 'http://' + uri[6:]
151 | elif protocol == 'https':
152 | if uri[7] != '/':
153 | uri = 'https://' + uri[7:]
154 |
155 | # Check if the URI has a file extension-like suffix
156 | rdf_suffix = uri.split('.')[-1]
157 | rdf_format = None
158 | file_extensions = ['rdf', 'ttl', 'xml', 'nt', 'jsonld', 'n3']
159 | rdf_formats = ['application/rdf+xml', 'text/turtle', 'application/rdf+xml', 'application/n-triples', 'application/ld+json', 'text/n3']
160 | for i in range(len(file_extensions)):
161 | if file_extensions[i] == rdf_suffix:
162 | rdf_format = rdf_formats[i]
163 | uri = uri.replace('.' + rdf_suffix, '')
164 | break
165 |
166 | skos_type = skos.get_uri_skos_type(uri)
167 |
168 | if skos_type == skos.METHOD:
169 | from skos.method import MethodRenderer
170 | r = MethodRenderer(uri, request)
171 | if rdf_format:
172 | r.format = rdf_format
173 | return r.render()
174 |
175 | if skos_type == skos.CONCEPTSCHEME:
176 | r = skos.ConceptSchemeRenderer(uri, request)
177 | if rdf_format:
178 | r.format = rdf_format
179 | return r.render()
180 | elif skos_type == skos.COLLECTION:
181 | r = skos.CollectionRenderer(uri, request)
182 | if rdf_format:
183 | r.format = rdf_format
184 | return r.render()
185 | elif skos_type == skos.CONCEPT:
186 | r = skos.ConceptRenderer(uri, request)
187 | if rdf_format:
188 | r.format = rdf_format
189 | return r.render()
190 |
191 | return redirect(uri, code=302)
192 |
193 | return '
404 :(
URI supplied does not exist or is not a recognised class type.
'.format(url_for('routes.ob', uri=uri), label)
101 |
--------------------------------------------------------------------------------
/local_vocabs/cover-methods.ttl:
--------------------------------------------------------------------------------
1 | @prefix apc: .
2 | @prefix apni: .
3 | @prefix biod: .
4 | @prefix corveg: .
5 | @prefix corveg-def: .
6 | @prefix cv-corveg: .
7 | @prefix data: .
8 | @prefix dcterms: .
9 | @prefix disturbance: .
10 | @prefix dwct: .
11 | @prefix geo: .
12 | @prefix location-corveg: .
13 | @prefix locn: .
14 | @prefix ogroup: .
15 | @prefix op: .
16 | @prefix owl: .
17 | @prefix plot: .
18 | @prefix prov: .
19 | @prefix rdf: .
20 | @prefix rdfs: .
21 | @prefix site-corveg: .
22 | @prefix site-strata-corveg: .
23 | @prefix site-tax-strata-corveg: .
24 | @prefix skos: .
25 | @prefix sosa: .
26 | @prefix ssn-ext: .
27 | @prefix unit: .
28 | @prefix xml: .
29 | @prefix xsd: .
30 |
31 | cv-corveg:cover-methods a skos:ConceptScheme ;
32 | skos:prefLabel "Cover methods" ;
33 | rdfs:comment "Vocabulary of cover method terms used in CORVEG." ;
34 | skos:altLabel "Cov. methods", "Cov. meth.", "C. M.", "COVER METHODS" ;
35 | dcterms:created "2019-04-10"^^xsd:dateTime ;
36 | dcterms:creator "e.chuc@uq.edu.au" ;
37 | .
38 |
39 | cv-corveg:cover-method-i a skos:Concept,
40 | sosa:Procedure ;
41 | skos:definition "Line intercept" ;
42 | skos:hiddenLabel "3" ;
43 | skos:notation "I" ;
44 | skos:prefLabel "Line intercept" ;
45 | skos:inScheme cv-corveg:cover-methods ;
46 | dcterms:bibliographicCitation "Neldner, V.J., Wilson, B.A., Dillewaard, H.A., Ryan, T.S. and Butler, D.W. (2017) Methodology for Survey and Mapping of Regional Ecosystems and Vegetation Communities in Queensland. Version 4.0. Updated May 2017. Queensland Herbarium, Queensland Department of Science, Information Technology and Innovation, Brisbane. 124 pp." ;
47 | skos:topConceptOf cv-corveg:cover-methods ;
48 | skos:exactMatch ;
49 | .
50 |
51 | cv-corveg:cover-method-l a skos:Concept,
52 | sosa:Procedure ;
53 | skos:definition "Photographic (e.g. fish-eye lens)" ;
54 | skos:hiddenLabel "1" ;
55 | skos:notation "L" ;
56 | skos:prefLabel "Photographic" ;
57 | skos:inScheme cv-corveg:cover-methods ;
58 | dcterms:bibliographicCitation "Neldner, V.J., Wilson, B.A., Dillewaard, H.A., Ryan, T.S. and Butler, D.W. (2017) Methodology for Survey and Mapping of Regional Ecosystems and Vegetation Communities in Queensland. Version 4.0. Updated May 2017. Queensland Herbarium, Queensland Department of Science, Information Technology and Innovation, Brisbane. 124 pp." ;
59 | skos:topConceptOf cv-corveg:cover-methods ;
60 | .
61 |
62 | cv-corveg:cover-methods-unrecorded a skos:Concept,
63 | sosa:Procedure ;
64 | skos:definition "Unrecorded." ;
65 | skos:hiddenLabel "999" ;
66 | skos:notation "null" ;
67 | skos:prefLabel "Unrecorded" ;
68 | skos:inScheme cv-corveg:cover-methods ;
69 | dcterms:bibliographicCitation "Neldner, V.J., Wilson, B.A., Dillewaard, H.A., Ryan, T.S. and Butler, D.W. (2017) Methodology for Survey and Mapping of Regional Ecosystems and Vegetation Communities in Queensland. Version 4.0. Updated May 2017. Queensland Herbarium, Queensland Department of Science, Information Technology and Innovation, Brisbane. 124 pp." ;
70 | skos:topConceptOf cv-corveg:cover-methods ;
71 | .
72 |
73 | cv-corveg:cover-method-v a skos:Concept,
74 | sosa:Procedure ;
75 | skos:definition "Visual estimate of cover in subordinate layers" ;
76 | skos:hiddenLabel "2" ;
77 | skos:notation "V" ;
78 | skos:prefLabel "Visual estimate" ;
79 | skos:inScheme cv-corveg:cover-methods ;
80 | dcterms:bibliographicCitation "Neldner, V.J., Wilson, B.A., Dillewaard, H.A., Ryan, T.S. and Butler, D.W. (2017) Methodology for Survey and Mapping of Regional Ecosystems and Vegetation Communities in Queensland. Version 4.0. Updated May 2017. Queensland Herbarium, Queensland Department of Science, Information Technology and Innovation, Brisbane. 124 pp." ;
81 | skos:topConceptOf cv-corveg:cover-methods ;
82 | .
--------------------------------------------------------------------------------
/local_vocabs/disturbance-assessment.ttl:
--------------------------------------------------------------------------------
1 | # baseURI: http://linked.data.gov.au/cv/corveg/corveg-methods
2 | # imports: http://www.w3.org/2004/02/skos/core
3 | # imports: http://www.w3.org/ns/sosa/
4 |
5 | @prefix corveg: .
6 | @prefix cv-corveg: .
7 | @prefix data: .
8 | @prefix dct: .
9 | @prefix dcterms: .
10 | @prefix locn: .
11 | @prefix ogroup: .
12 | @prefix op: .
13 | @prefix owl: .
14 | @prefix plot: .
15 | @prefix prov: .
16 | @prefix rdf: .
17 | @prefix rdfs: .
18 | @prefix site-corveg: .
19 | @prefix site-strata-corveg: .
20 | @prefix skos: .
21 | @prefix sosa: .
22 | @prefix ssn-ext: .
23 | @prefix unit: .
24 | @prefix xml: .
25 | @prefix xsd: .
26 |
27 | cv-corveg:corveg-methods
28 | a owl:Ontology ;
29 | owl:imports ;
30 | owl:imports sosa: ;
31 | .
32 | cv-corveg:disturbance-assessment-methods
33 | a skos:ConceptScheme ;
34 | skos:prefLabel "Disturbance Assessment Methods" ;
35 | rdfs:comment "Description of disturbance assessment methods used in CORVEG Methodology." ;
36 | .
37 | cv-corveg:methods-disturbance-assessment
38 | a skos:Concept ;
39 | a sosa:Procedure ;
40 | skos:inScheme cv-corveg:disturbance-assessment-methods ;
41 | skos:topConceptOf cv-corveg:disturbance-assessment-methods ;
42 | dct:source "Methodology for Survey and Mapping of Regional Ecosystems and Vegetation Communities in Queensland Version 5.0" ;
43 | dcterms:bibliographicCitation "Neldner, V.J., Wilson, B.A., Dillewaard, H.A., Ryan, T.S., Butler, D.W., McDonald, W.J.F, Addicott,E.P. and Appelman, C.N. (2019) Methodology for survey and mapping of regional ecosystems and vegetation communities in Queensland. Version 5.0. Updated March 2019. Queensland Herbarium, Queensland Department of Environment and Science, Brisbane." ;
44 | rdfs:isDefinedBy ;
45 | skos:definition """The disturbance data are designed to record whether the site may be unrepresentative because they have been subject to abnormal disturbance. Therefore, generally sites will only be located in a disturbed area where no undisturbed sites could be located. Disturbance abundance estimates are made for the proportion of the disturbance occurring within the 10 m to 50 m quadrat into the classes listed in Table 14. These fields are left blank where disturbances are absent.
46 |
47 | ###### Storm damage
48 | - This is identifiable by the presence of broken branches in the crowns. Estimate the proportion of
49 | crown cover damaged on the site and the age of the damage using Table 14.
50 |
51 | ###### Logging
52 | - Record the number of stumps in the site.
53 |
54 | ###### Ringbarking, poisoning, thinning
55 | - Record the number of stems in the site.
56 |
57 | ###### Grazing
58 | - Grazing by domestic stock, feral animals and native animals evident in damage to the ground layer
59 | plants and/or presence of animal faeces or tracks is recorded as:
60 | - not apparent
61 | - present
62 | - severe (enough to make major impact on ground cover abundance or composition).
63 |
64 | ###### Extensive clearing
65 | A record of if the site has been previously cleared/thinned and regrown. Recorded as:
66 |
67 | - not apparent
68 | - present (i.e. most of the vegetation is regrowth from a previous clearing/thinning event).
69 |
70 | ###### Animal diggings
71 | These are recorded as:
72 |
73 | - not apparent
74 | - present.
75 |
76 | ###### Roadworks
77 | Record old snig tracks and other tracks; record proportion and age of the site affected using Table 14.
78 |
79 | ###### Salinity
80 | Record the proportion of the site affected by severe anthropogenic-caused salinity.
81 |
82 | ###### Fire
83 | Record the proportion of the site burnt, age (from above) and tallest vegetation impacted by fire from table 15.
84 |
85 | ###### Weeds
86 | The percentage cover of exotic species at the site is estimated. In the case of secondary sites this
87 | figure is derived by adding the cover of weed species from the comprehensive species list (the cover values are added ignoring any overlap between strata).
88 |
89 | ###### Erosion
90 | An estimate of the area as a proportion (Table 14) and type and severity of accelerated erosion (Table 16: as compared to natural erosion as discussed by McDonald, Isbell and Speight 2009) is recorded.""" ;
91 | skos:prefLabel "Disturbance Assessment Method" ;
92 | skos:closeMatch ;
93 | skos:closeMatch ;
94 | skos:closeMatch ;
95 | .
--------------------------------------------------------------------------------
/local_vocabs/skos.ttl:
--------------------------------------------------------------------------------
1 | @prefix owl: .
2 | @prefix dc: .
3 | @prefix rdfs: .
4 | @prefix skos: .
5 | @prefix rdf: .
6 |
7 |
8 | a owl:Ontology ;
9 | dc:title "SKOS Vocabulary"@en ;
10 | dc:contributor "Dave Beckett", "Nikki Rogers", "Participants in W3C's Semantic Web Deployment Working Group." ;
11 | dc:description "An RDF vocabulary for describing the basic structure and content of concept schemes such as thesauri, classification schemes, subject heading lists, taxonomies, 'folksonomies', other types of controlled vocabulary, and also concept schemes embedded in glossaries and terminologies."@en ;
12 | dc:creator "Alistair Miles", "Sean Bechhofer" ;
13 | rdfs:seeAlso .
14 |
15 | skos:Concept
16 | rdfs:label "Concept"@en ;
17 | rdfs:isDefinedBy ;
18 | skos:definition "An idea or notion; a unit of thought."@en ;
19 | a owl:Class .
20 |
21 | skos:ConceptScheme
22 | rdfs:label "Concept Scheme"@en ;
23 | rdfs:isDefinedBy ;
24 | skos:definition "A set of concepts, optionally including statements about semantic relationships between those concepts."@en ;
25 | skos:scopeNote "A concept scheme may be defined to include concepts from different sources."@en ;
26 | skos:example "Thesauri, classification schemes, subject heading lists, taxonomies, 'folksonomies', and other types of controlled vocabulary are all examples of concept schemes. Concept schemes are also embedded in glossaries and terminologies."@en ;
27 | a owl:Class ;
28 | owl:disjointWith skos:Concept .
29 |
30 | skos:Collection
31 | rdfs:label "Collection"@en ;
32 | rdfs:isDefinedBy ;
33 | skos:definition "A meaningful collection of concepts."@en ;
34 | skos:scopeNote "Labelled collections can be used where you would like a set of concepts to be displayed under a 'node label' in the hierarchy."@en ;
35 | a owl:Class ;
36 | owl:disjointWith skos:Concept, skos:ConceptScheme .
37 |
38 | skos:OrderedCollection
39 | rdfs:label "Ordered Collection"@en ;
40 | rdfs:isDefinedBy ;
41 | skos:definition "An ordered collection of concepts, where both the grouping and the ordering are meaningful."@en ;
42 | skos:scopeNote "Ordered collections can be used where you would like a set of concepts to be displayed in a specific order, and optionally under a 'node label'."@en ;
43 | a owl:Class ;
44 | rdfs:subClassOf skos:Collection .
45 |
46 | skos:inScheme
47 | rdfs:label "is in scheme"@en ;
48 | rdfs:isDefinedBy ;
49 | skos:definition "Relates a resource (for example a concept) to a concept scheme in which it is included."@en ;
50 | skos:scopeNote "A concept may be a member of more than one concept scheme."@en ;
51 | a owl:ObjectProperty, rdf:Property ;
52 | rdfs:range skos:ConceptScheme .
53 |
54 | skos:hasTopConcept
55 | rdfs:label "has top concept"@en ;
56 | rdfs:isDefinedBy ;
57 | skos:definition "Relates, by convention, a concept scheme to a concept which is topmost in the broader/narrower concept hierarchies for that scheme, providing an entry point to these hierarchies."@en ;
58 | a owl:ObjectProperty, rdf:Property ;
59 | rdfs:domain skos:ConceptScheme ;
60 | rdfs:range skos:Concept ;
61 | owl:inverseOf skos:topConceptOf .
62 |
63 | skos:topConceptOf
64 | rdfs:label "is top concept in scheme"@en ;
65 | rdfs:isDefinedBy ;
66 | skos:definition "Relates a concept to the concept scheme that it is a top level concept of."@en ;
67 | a owl:ObjectProperty, rdf:Property ;
68 | rdfs:subPropertyOf skos:inScheme ;
69 | owl:inverseOf skos:hasTopConcept ;
70 | rdfs:domain skos:Concept ;
71 | rdfs:range skos:ConceptScheme .
72 |
73 | skos:prefLabel
74 | rdfs:label "preferred label"@en ;
75 | rdfs:isDefinedBy ;
76 | skos:definition "The preferred lexical label for a resource, in a given language."@en ;
77 | a owl:AnnotationProperty, rdf:Property ;
78 | # rdfs:subPropertyOf rdfs:label ;
79 | rdfs:comment "A resource has no more than one value of skos:prefLabel per language tag, and no more than one value of skos:prefLabel without language tag."@en, "The range of skos:prefLabel is the class of RDF plain literals."@en, """skos:prefLabel, skos:altLabel and skos:hiddenLabel are pairwise
80 | disjoint properties."""@en .
81 |
82 | skos:altLabel
83 | rdfs:label "alternative label"@en ;
84 | rdfs:isDefinedBy ;
85 | skos:definition "An alternative lexical label for a resource."@en ;
86 | skos:example "Acronyms, abbreviations, spelling variants, and irregular plural/singular forms may be included among the alternative labels for a concept. Mis-spelled terms are normally included as hidden labels (see skos:hiddenLabel)."@en ;
87 | a owl:AnnotationProperty, rdf:Property ;
88 | # rdfs:subPropertyOf rdfs:label ;
89 | rdfs:comment "The range of skos:altLabel is the class of RDF plain literals."@en, "skos:prefLabel, skos:altLabel and skos:hiddenLabel are pairwise disjoint properties."@en .
90 |
91 | skos:hiddenLabel
92 | rdfs:label "hidden label"@en ;
93 | rdfs:isDefinedBy ;
94 | skos:definition "A lexical label for a resource that should be hidden when generating visual displays of the resource, but should still be accessible to free text search operations."@en ;
95 | a owl:AnnotationProperty, rdf:Property ;
96 | # rdfs:subPropertyOf rdfs:label ;
97 | rdfs:comment "The range of skos:hiddenLabel is the class of RDF plain literals."@en, "skos:prefLabel, skos:altLabel and skos:hiddenLabel are pairwise disjoint properties."@en .
98 |
99 | skos:notation
100 | rdfs:label "notation"@en ;
101 | rdfs:isDefinedBy ;
102 | skos:definition "A notation, also known as classification code, is a string of characters such as \"T58.5\" or \"303.4833\" used to uniquely identify a concept within the scope of a given concept scheme."@en ;
103 | skos:scopeNote "By convention, skos:notation is used with a typed literal in the object position of the triple."@en ;
104 | a owl:DatatypeProperty, rdf:Property .
105 |
106 | skos:note
107 | rdfs:label "note"@en ;
108 | rdfs:isDefinedBy ;
109 | skos:definition "A general note, for any purpose."@en ;
110 | skos:scopeNote "This property may be used directly, or as a super-property for more specific note types."@en ;
111 | a owl:AnnotationProperty, rdf:Property .
112 |
113 | skos:changeNote
114 | rdfs:label "change note"@en ;
115 | rdfs:isDefinedBy ;
116 | skos:definition "A note about a modification to a concept."@en ;
117 | a owl:AnnotationProperty, rdf:Property ;
118 | # rdfs:subPropertyOf skos:note ;
119 | .
120 |
121 | skos:definition
122 | rdfs:label "definition"@en ;
123 | rdfs:isDefinedBy ;
124 | skos:definition "A statement or formal explanation of the meaning of a concept."@en ;
125 | a owl:AnnotationProperty, rdf:Property ;
126 | # rdfs:subPropertyOf skos:note ;
127 | .
128 |
129 | skos:editorialNote
130 | rdfs:label "editorial note"@en ;
131 | rdfs:isDefinedBy ;
132 | skos:definition "A note for an editor, translator or maintainer of the vocabulary."@en ;
133 | a owl:AnnotationProperty, rdf:Property ;
134 | # rdfs:subPropertyOf skos:note ;
135 | .
136 |
137 | skos:example
138 | rdfs:label "example"@en ;
139 | rdfs:isDefinedBy ;
140 | skos:definition "An example of the use of a concept."@en ;
141 | a owl:AnnotationProperty, rdf:Property ;
142 | # rdfs:subPropertyOf skos:note ;
143 | .
144 |
145 | skos:historyNote
146 | rdfs:label "history note"@en ;
147 | rdfs:isDefinedBy ;
148 | skos:definition "A note about the past state/use/meaning of a concept."@en ;
149 | a owl:AnnotationProperty, rdf:Property ;
150 | # rdfs:subPropertyOf skos:note ;
151 | .
152 |
153 | skos:scopeNote
154 | rdfs:label "scope note"@en ;
155 | rdfs:isDefinedBy ;
156 | skos:definition "A note that helps to clarify the meaning and/or the use of a concept."@en ;
157 | a owl:AnnotationProperty, rdf:Property ;
158 | # rdfs:subPropertyOf skos:note ;
159 | .
160 |
161 | skos:semanticRelation
162 | rdfs:label "is in semantic relation with"@en ;
163 | rdfs:isDefinedBy ;
164 | skos:definition "Links a concept to a concept related by meaning."@en ;
165 | skos:scopeNote "This property should not be used directly, but as a super-property for all properties denoting a relationship of meaning between concepts."@en ;
166 | a owl:ObjectProperty, rdf:Property ;
167 | rdfs:domain skos:Concept ;
168 | rdfs:range skos:Concept .
169 |
170 | skos:broader
171 | rdfs:label "has broader"@en ;
172 | rdfs:isDefinedBy ;
173 | skos:definition "Relates a concept to a concept that is more general in meaning."@en ;
174 | rdfs:comment "Broader concepts are typically rendered as parents in a concept hierarchy (tree)."@en ;
175 | skos:scopeNote "By convention, skos:broader is only used to assert an immediate (i.e. direct) hierarchical link between two conceptual resources."@en ;
176 | a owl:ObjectProperty, rdf:Property ;
177 | # rdfs:subPropertyOf skos:broaderTransitive ;
178 | owl:inverseOf skos:narrower .
179 |
180 | skos:narrower
181 | rdfs:label "has narrower"@en ;
182 | rdfs:isDefinedBy ;
183 | skos:definition "Relates a concept to a concept that is more specific in meaning."@en ;
184 | skos:scopeNote "By convention, skos:broader is only used to assert an immediate (i.e. direct) hierarchical link between two conceptual resources."@en ;
185 | rdfs:comment "Narrower concepts are typically rendered as children in a concept hierarchy (tree)."@en ;
186 | a owl:ObjectProperty, rdf:Property ;
187 | # rdfs:subPropertyOf skos:narrowerTransitive ;
188 | owl:inverseOf skos:broader .
189 |
190 | skos:related
191 | rdfs:label "has related"@en ;
192 | rdfs:isDefinedBy ;
193 | skos:definition "Relates a concept to a concept with which there is an associative semantic relationship."@en ;
194 | a owl:ObjectProperty, owl:SymmetricProperty, rdf:Property ;
195 | rdfs:subPropertyOf skos:semanticRelation ;
196 | rdfs:comment "skos:related is disjoint with skos:broaderTransitive"@en .
197 |
198 | skos:broaderTransitive
199 | rdfs:label "has broader transitive"@en ;
200 | rdfs:isDefinedBy ;
201 | skos:definition "skos:broaderTransitive is a transitive superproperty of skos:broader." ;
202 | skos:scopeNote "By convention, skos:broaderTransitive is not used to make assertions. Rather, the properties can be used to draw inferences about the transitive closure of the hierarchical relation, which is useful e.g. when implementing a simple query expansion algorithm in a search application."@en ;
203 | a owl:ObjectProperty, owl:TransitiveProperty, rdf:Property ;
204 | rdfs:subPropertyOf skos:semanticRelation ;
205 | owl:inverseOf skos:narrowerTransitive .
206 |
207 | skos:narrowerTransitive
208 | rdfs:label "has narrower transitive"@en ;
209 | rdfs:isDefinedBy ;
210 | skos:definition "skos:narrowerTransitive is a transitive superproperty of skos:narrower." ;
211 | skos:scopeNote "By convention, skos:narrowerTransitive is not used to make assertions. Rather, the properties can be used to draw inferences about the transitive closure of the hierarchical relation, which is useful e.g. when implementing a simple query expansion algorithm in a search application."@en ;
212 | a owl:ObjectProperty, owl:TransitiveProperty, rdf:Property ;
213 | rdfs:subPropertyOf skos:semanticRelation ;
214 | owl:inverseOf skos:broaderTransitive .
215 |
216 | skos:member
217 | rdfs:label "has member"@en ;
218 | rdfs:isDefinedBy ;
219 | skos:definition "Relates a collection to one of its members."@en ;
220 | a owl:ObjectProperty, rdf:Property ;
221 | rdfs:domain skos:Collection ;
222 | rdfs:range [
223 | a owl:Class ;
224 | owl:unionOf (
225 | skos:Concept
226 | skos:Collection
227 | )
228 | ] .
229 |
230 | skos:memberList
231 | rdfs:label "has member list"@en ;
232 | rdfs:isDefinedBy ;
233 | skos:definition "Relates an ordered collection to the RDF list containing its members."@en ;
234 | a owl:ObjectProperty, owl:FunctionalProperty, rdf:Property ;
235 | rdfs:domain skos:OrderedCollection ;
236 | rdfs:range rdf:List ;
237 | rdfs:comment """For any resource, every item in the list given as the value of the
238 | skos:memberList property is also a value of the skos:member property."""@en .
239 |
240 | skos:mappingRelation
241 | rdfs:label "is in mapping relation with"@en ;
242 | rdfs:isDefinedBy ;
243 | skos:definition "Relates two concepts coming, by convention, from different schemes, and that have comparable meanings"@en ;
244 | rdfs:comment "These concept mapping relations mirror semantic relations, and the data model defined below is similar (with the exception of skos:exactMatch) to the data model defined for semantic relations. A distinct vocabulary is provided for concept mapping relations, to provide a convenient way to differentiate links within a concept scheme from links between concept schemes. However, this pattern of usage is not a formal requirement of the SKOS data model, and relies on informal definitions of best practice."@en ;
245 | a owl:ObjectProperty, rdf:Property ;
246 | # rdfs:subPropertyOf skos:semanticRelation ;
247 | .
248 |
249 | skos:broadMatch
250 | rdfs:label "has broader match"@en ;
251 | rdfs:isDefinedBy ;
252 | skos:definition "skos:broadMatch is used to state a hierarchical mapping link between two conceptual resources in different concept schemes."@en ;
253 | a owl:ObjectProperty, rdf:Property ;
254 | rdfs:subPropertyOf skos:mappingRelation, skos:broader ;
255 | owl:inverseOf skos:narrowMatch .
256 |
257 | skos:narrowMatch
258 | rdfs:label "has narrower match"@en ;
259 | rdfs:isDefinedBy ;
260 | skos:definition "skos:narrowMatch is used to state a hierarchical mapping link between two conceptual resources in different concept schemes."@en ;
261 | a owl:ObjectProperty, rdf:Property ;
262 | rdfs:subPropertyOf skos:mappingRelation, skos:narrower ;
263 | owl:inverseOf skos:broadMatch .
264 |
265 | skos:relatedMatch
266 | rdfs:label "has related match"@en ;
267 | rdfs:isDefinedBy ;
268 | skos:definition "skos:relatedMatch is used to state an associative mapping link between two conceptual resources in different concept schemes."@en ;
269 | a owl:ObjectProperty, owl:SymmetricProperty, rdf:Property ;
270 | # rdfs:subPropertyOf skos:mappingRelation, skos:related ;
271 | .
272 |
273 | skos:exactMatch
274 | rdfs:label "has exact match"@en ;
275 | rdfs:isDefinedBy ;
276 | skos:definition "skos:exactMatch is used to link two concepts, indicating a high degree of confidence that the concepts can be used interchangeably across a wide range of information retrieval applications. skos:exactMatch is a transitive property, and is a sub-property of skos:closeMatch."@en ;
277 | a owl:ObjectProperty, owl:SymmetricProperty, owl:TransitiveProperty, rdf:Property ;
278 | # rdfs:subPropertyOf skos:closeMatch ;
279 | rdfs:comment "skos:exactMatch is disjoint with each of the properties skos:broadMatch and skos:relatedMatch."@en .
280 |
281 | skos:closeMatch
282 | rdfs:label "has close match"@en ;
283 | rdfs:isDefinedBy ;
284 | skos:definition "skos:closeMatch is used to link two concepts that are sufficiently similar that they can be used interchangeably in some information retrieval applications. In order to avoid the possibility of \"compound errors\" when combining mappings across more than two concept schemes, skos:closeMatch is not declared to be a transitive property."@en ;
285 | a owl:ObjectProperty, owl:SymmetricProperty, rdf:Property ;
286 | # rdfs:subPropertyOf skos:mappingRelation ;
287 | .
288 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | beautifulsoup4==4.9.0
2 | certifi==2020.4.5.1
3 | chardet==3.0.4
4 | click==7.1.1
5 | connegp==0.2
6 | Flask==1.1.2
7 | flask-paginate==0.5.5
8 | idna==2.9
9 | isodate==0.6.0
10 | itsdangerous==1.1.0
11 | Jinja2==2.11.3
12 | Markdown==3.2.1
13 | MarkupSafe==1.1.1
14 | munch==2.5.0
15 | owlrl==5.2.1
16 | pyldapi==2.1.4
17 | pyparsing==2.4.7
18 | PyYAML==5.4
19 | rdflib==5.0.0
20 | rdflib-jsonld==0.5.0
21 | requests==2.25.1
22 | six==1.14.0
23 | soupsieve==2.0
24 | SPARQLWrapper==1.8.5
25 | urllib3==1.26.5
26 | Werkzeug==0.16.0
27 | python-dotenv==0.15.0
28 | Flask-Cors==3.0.10
29 | watchdog==2.1.2
30 | celery==5.1.1
31 | https://bitbucket.org/terndatateam/tern_rdf/get/0.0.33.zip
--------------------------------------------------------------------------------
/skos/__init__.py:
--------------------------------------------------------------------------------
1 | from rdflib.namespace import RDF, SKOS, DCTERMS, RDFS, OWL, DC
2 | from rdflib import URIRef, Namespace, Literal, Graph
3 | import markdown
4 | from flask import url_for
5 | import requests
6 |
7 | from config import Config
8 | from skos.concept_scheme import ConceptScheme, ConceptSchemeRenderer
9 | from skos.concept import Concept, ConceptRenderer
10 | from skos.collection import CollectionRenderer, Collection
11 | from skos.register import Register
12 | import helper
13 |
14 | from datetime import date
15 | from urllib import parse
16 |
17 |
18 | # Controlled values
19 | CONCEPT = 0
20 | CONCEPTSCHEME = 1
21 | COLLECTION = 2
22 | METHOD = 3
23 |
24 | SCHEMAORG = Namespace('http://schema.org/')
25 |
26 |
27 | def list_concepts():
28 | concepts = []
29 | for c in Config.g.subjects(RDF.type, SKOS.Concept):
30 | label = get_label(c)
31 | date_created = get_created_date(c)
32 | date_modified = get_modified_date(c)
33 | definition = get_definition(c)
34 | scheme = get_in_scheme(c)
35 | concepts.append((c, label, [
36 | (URIRef('http://purl.org/dc/terms/created'), date_created),
37 | (URIRef('http://purl.org/dc/terms/modified'), date_modified),
38 | (URIRef('http://www.w3.org/2004/02/skos/core#definition'), definition),
39 | (URIRef('http://www.w3.org/2004/02/skos/core#inScheme'), scheme)
40 | ]))
41 | return sorted(concepts, key=lambda i: i[1])
42 |
43 |
44 | def list_concept_schemes():
45 | concept_schemes = []
46 |
47 | for cc in Config.g.subjects(RDF.type, SKOS.ConceptScheme):
48 | label = get_label(cc)
49 | date_created = get_created_date(cc)
50 | date_modified = get_modified_date(cc)
51 | description = get_description(cc)
52 | concept_schemes.append((cc, label, [
53 | (URIRef('http://purl.org/dc/terms/created'), date_created),
54 | (URIRef('http://purl.org/dc/terms/modified'), date_modified),
55 | description
56 | ]))
57 |
58 | return sorted(concept_schemes, key=lambda i: i[1])
59 |
60 |
61 | def list_concept_schemes_and_collections():
62 | items = []
63 |
64 | for cc in Config.g.subjects(RDF.type, SKOS.ConceptScheme):
65 | if not is_deprecated(cc):
66 | label = get_label(cc)
67 | date_created = get_created_date(cc)
68 | date_modified = get_modified_date(cc)
69 | description = get_description(cc)
70 | items.append((cc, label, [
71 | (URIRef('http://purl.org/dc/terms/created'), date_created),
72 | (URIRef('http://purl.org/dc/terms/modified'), date_modified),
73 | description
74 | ]))
75 |
76 | for cc in Config.g.subjects(RDF.type, SKOS.Collection):
77 | if not is_deprecated(cc):
78 | label = get_label(cc)
79 | date_created = get_created_date(cc)
80 | date_modified = get_modified_date(cc)
81 | description = get_description(cc)
82 | items.append((cc, label, [
83 | (URIRef('http://purl.org/dc/terms/created'), date_created),
84 | (URIRef('http://purl.org/dc/terms/modified'), date_modified),
85 | description
86 | ]))
87 |
88 | return sorted(items, key=lambda i: i[1])
89 |
90 |
91 | def _split_camel_case_label(label):
92 | new_label = ''
93 | last = 0
94 | for i, letter in enumerate(label):
95 | if letter.isupper():
96 | new_label += ' {}'.format(label[last:i])
97 | last = i
98 |
99 | new_label += ' {}'.format(label[last:])
100 | new_label = new_label.strip()
101 | return new_label
102 |
103 |
104 | def get_label(uri, create=True):
105 | # TODO: title() capitalises all words, we need a post-process function to lower case words that are of types
106 | # such as preposition and conjunction.
107 | for label in Config.g.objects(URIRef(uri), SKOS.prefLabel):
108 | return label
109 | for label in Config.g.objects(URIRef(uri), DCTERMS.title):
110 | return label
111 | for label in Config.g.objects(URIRef(uri), RDFS.label):
112 | return label
113 |
114 | # Fetch label by dereferencing URI.
115 | if create:
116 | headers = {'accept': 'text/turtle'}
117 | response_g = Graph()
118 | try:
119 | r = requests.get(uri, headers=headers)
120 | assert 200 <= r.status_code < 300
121 | response_g.parse(data=r.content.decode('utf-8'), format='turtle')
122 | for _, _, label in response_g.triples((uri, SKOS.prefLabel, None)):
123 | return label
124 | for _, _, label in response_g.triples((uri, RDFS.label, None)):
125 | return label
126 | except Exception as e:
127 | # print(uri)
128 | # print('Error dereferencing external URI:', str(e))
129 | # print(r.content.decode('utf-8'))
130 | # print('Create label from the local name of the URI instead.')
131 |
132 | # Create label out of the local segment of the URI.
133 | label = helper.uri_label(uri)
134 | label = _split_camel_case_label(label)
135 | return Literal(label)
136 | else:
137 | return Literal(str(uri).split('#')[-1].split('/')[-1])
138 |
139 |
140 | def get_description(uri):
141 | for description in Config.g.objects(URIRef(uri), DCTERMS.description):
142 | return (DCTERMS.description, description)
143 | for description in Config.g.objects(URIRef(uri), DC.description):
144 | return (DC.description, description)
145 | for description in Config.g.objects(URIRef(uri), RDFS.comment):
146 | return (RDFS.comment, description)
147 |
148 |
149 | def get_definition(uri):
150 | for definition in Config.g.objects(URIRef(uri), SKOS.definition):
151 | return definition
152 |
153 |
154 | def get_class_types(uri):
155 | types = []
156 | for type in Config.g.objects(URIRef(uri), RDF.type):
157 | # Only add URIs (and not blank nodes!)
158 | if str(type)[:4] == 'http' \
159 | and str(type) != 'http://www.w3.org/2004/02/skos/core#ConceptScheme' \
160 | and str(type) != 'http://www.w3.org/2004/02/skos/core#Concept' \
161 | and str(type) != 'http://www.w3.org/2004/02/skos/core#Collection':
162 | types.append(type)
163 | return types
164 |
165 |
166 | def is_deprecated(uri):
167 | for value in Config.g.objects(URIRef(uri), OWL.deprecated):
168 | return bool(value)
169 | return False
170 |
171 |
172 | def get_narrowers(uri):
173 | narrowers = []
174 | for narrower in Config.g.objects(URIRef(uri), SKOS.narrower):
175 | if not is_deprecated(narrower):
176 | label = get_label(narrower)
177 | narrowers.append((narrower, label))
178 | return sorted(narrowers, key=lambda i: i[1])
179 |
180 |
181 | def get_broaders(uri):
182 | broaders = []
183 | for broader in Config.g.objects(URIRef(uri), SKOS.broader):
184 | if not is_deprecated(broader):
185 | label = get_label(broader)
186 | broaders.append((broader, label))
187 | return sorted(broaders, key=lambda i: i[1])
188 |
189 |
190 | def get_members(uri):
191 | members = []
192 | for member in Config.g.objects(URIRef(uri), SKOS.member):
193 | label = get_label(member)
194 | members.append((member, label))
195 | return sorted(members, key=lambda i: i[1])
196 |
197 |
198 | def get_top_concept_of(uri):
199 | top_concept_ofs = []
200 | for tco in Config.g.objects(URIRef(uri), SKOS.topConceptOf):
201 | label = get_label(tco)
202 | top_concept_ofs.append((tco, label))
203 | return sorted(top_concept_ofs, key=lambda i: i[1])
204 |
205 |
206 | def get_top_concepts(uri):
207 | top_concepts = []
208 | for tc in Config.g.objects(URIRef(uri), SKOS.hasTopConcept):
209 | label = get_label(tc)
210 | top_concepts.append((tc, label))
211 | return sorted(top_concepts, key=lambda i: i[1])
212 |
213 |
214 | def get_change_note(uri):
215 | for cn in Config.g.objects(URIRef(uri), SKOS.changeNote):
216 | return cn
217 |
218 |
219 | def get_alt_labels(uri):
220 | labels = []
221 | for alt_label in Config.g.objects(URIRef(uri), SKOS.altLabel):
222 | labels.append(alt_label)
223 | return sorted(labels)
224 |
225 |
226 | def get_created_date(uri):
227 | for created in Config.g.objects(URIRef(uri), DCTERMS.created):
228 | created = created.split('-')
229 | created = date(int(created[0]), int(created[1]), int(created[2][:2]))
230 | return created
231 |
232 |
233 | def get_modified_date(uri):
234 | for modified in Config.g.objects(URIRef(uri), DCTERMS.modified):
235 | modified = modified.split('-')
236 | modified = date(int(modified[0]), int(modified[1]), int(modified[2][:2]))
237 | return modified
238 |
239 |
240 | def get_uri_skos_type(uri):
241 | uri = parse.unquote_plus(uri)
242 | for _ in Config.g.triples((URIRef(uri), RDF.type, URIRef('https://w3id.org/tern/ontologies/tern/Method'))):
243 | return METHOD
244 | for _ in Config.g.triples((URIRef(uri), RDF.type, SKOS.ConceptScheme)):
245 | return CONCEPTSCHEME
246 | for _ in Config.g.triples((URIRef(uri), RDF.type, SKOS.Concept)):
247 | return CONCEPT
248 | for _ in Config.g.triples((URIRef(uri), RDF.type, SKOS.Collection)):
249 | return COLLECTION
250 | return None
251 |
252 |
253 | def get_properties(uri):
254 | ignore = [
255 | # Common
256 | RDF.type, SKOS.prefLabel, DCTERMS.title, RDFS.label, DCTERMS.description, SKOS.definition, SKOS.changeNote,
257 | DCTERMS.created, DCTERMS.modified, OWL.sameAs, RDFS.comment, SKOS.altLabel, DCTERMS.bibliographicCitation,
258 | RDFS.isDefinedBy, DC.description, DCTERMS.creator, DCTERMS.contributor, SCHEMAORG.parentOrganization,
259 | SCHEMAORG.contactPoint, SCHEMAORG.member, SCHEMAORG.subOrganization, SCHEMAORG.familyName,
260 | URIRef('http://schema.semantic-web.at/ppt/propagateType'), SCHEMAORG.givenName, SCHEMAORG.honorificPrefix,
261 | SCHEMAORG.jobTitle, SCHEMAORG.memberOf, URIRef('http://schema.semantic-web.at/ppt/appliedType'), SKOS.member,
262 |
263 | # Concept
264 | SKOS.narrower, SKOS.broader, SKOS.topConceptOf, SKOS.inScheme, SKOS.closeMatch, SKOS.exactMatch,
265 |
266 | # Concept Scheme
267 | SKOS.hasTopConcept
268 | ]
269 |
270 | properties = []
271 | for _, property, value in Config.g.triples((URIRef(uri), None, None)):
272 | if property in ignore:
273 | continue
274 |
275 | label = get_label(value, create=False) if type(value) == URIRef else None
276 | properties.append(((property, get_label(property, create=False)), value, label))
277 |
278 | properties.sort(key=lambda x: x[0])
279 | return properties
280 |
281 |
282 | def get_in_scheme(uri):
283 | """A concept scheme in which the concept is a part of. A concept may be a member of more than one concept scheme"""
284 | schemes = []
285 | for scheme in Config.g.objects(URIRef(uri), SKOS.inScheme):
286 | label = get_label(scheme)
287 | schemes.append((scheme, label))
288 | return schemes
289 |
290 |
291 | def _add_narrower(uri, hierarchy, indent):
292 | concepts = []
293 |
294 | for concept in Config.g.objects(URIRef(uri), SKOS.narrower):
295 | if not is_deprecated(concept):
296 | label = get_label(concept)
297 | concepts.append((concept, label))
298 |
299 | for concept in Config.g.objects(URIRef(uri), SKOS.member):
300 | if not is_deprecated(concept):
301 | label = get_label(concept)
302 | concepts.append((concept, label))
303 |
304 | concepts.sort(key=lambda i: i[1])
305 |
306 | for concept in concepts:
307 | tab = indent * '\t'
308 | hierarchy += tab + '- [{}]({})\n'.format(concept[1], url_for('routes.ob', uri=concept[0]))
309 | hierarchy = _add_narrower(concept[0], hierarchy, indent + 1)
310 |
311 | return hierarchy
312 |
313 |
314 | def get_concept_hierarchy_collection(uri):
315 | hierarchy = ''
316 | members = []
317 |
318 | for concept_or_collection in Config.g.objects(URIRef(uri), SKOS.member):
319 | if not is_deprecated(concept_or_collection):
320 | label = get_label(concept_or_collection)
321 | members.append((concept_or_collection, label))
322 |
323 | members.sort(key=lambda i: i[1])
324 |
325 | for member in members:
326 | hierarchy += '- [{}]({})\n'.format(member[1], url_for('routes.ob', uri=member[0]))
327 | hierarchy = _add_narrower(member[0], hierarchy, 1)
328 |
329 | return '