-
Notifications
You must be signed in to change notification settings - Fork 1
Download view
The download view component is responsible for generation four different output types.
- Turtle (an RDF graph notation)
- JSON-LD (a JSON RDF graph notation)
- N-triples (a raw textual representation of an RDF graph)
- SPARQL (a SPARQL query where these graphs are inserted)
- As a user, I want to download the newly generated dataset.
- As a user, I want to publish my dataset online
- As a user, I want to have a choice
- As a user, I want to be notified when an upload succeeds
- As a developer, I want that all graphs have the same name structure to identify them in an open graph
- As a user, I want that my graph has metadata in order know which graph is mine
- Data can not be downloaded or published when there is no data to upload/publish
The component exists of a download button, a publish button, an output-type selector and the generated output.
The download button prompts uses a href to download the file. As react re-renders the page with every update, all the references are updated as well. Below is the implementation of the download button.
<RaisedButton
label="download"
href={`data:${this.state.dataType};charset=utf-8,${encodeURIComponent(
this.state.displayText)}`}
download={`${this.props.filename}${this.state.text}`}
disabled={this.props.processing}
/>
The output type selector is a select-field with four options as described above. Next, to switching the format of the data available for download and the data visible on the screen it also changes the file type.
Format | File-type |
---|---|
Turtle | .turtle |
Json-LD | .json |
N-triples | .txt |
SPARQL | .txt |
For the output viewer, the React-highlight a React implementation of Highlight.js is used. It contains styling for various languages and makes the output better to look at.
When the user clicks on the publish button a dialog will show up where the user is required to fill in meta-data about their datasets. The dialog contains four fields;
Name | Description | editable |
---|---|---|
Dataset URI | The URI the graph will receive when published | ❌ |
Title | The title of the data set | ✔️ |
Description | a small description where the data is about | ✔️ |
Date | The date of creation of this dataset | ❌ |
The dataset URI is set programmatically generated from the URL of the page with the title added. Because the title is used as a unique identifier the name will first have its spaces replaced by underscores and is then URI encoded to make sure the name won't mess with the query that will be fired.
With the previous components of the DataCreation component, we were able to gather all the instructions we need for creating a Linked data graph. To make the process simpler we convert the URI-classified nodes and their relations to 'class-definitions'. These 'class-definitions' contain information about themselves, a list of the relations of the object and in which column the relation maps to, in order to make the algorithm more efficient by supplying the information the conversion algorithm needs in a logical structure.
function createClassDefinitions(nodes, links) {
const classDefinitions = [];
nodes.forEach((node) => {
// Skip when the nodes is a literal, as they can't have subjects
if (node.type === 'Literal') return;
// Get all the relations of this node
let relations = links.map((link) => {
if (link.source === node.id) {
// Push relation and class
return ({
link,
target:
nodes.filter(nodeF => nodeF.id === link.target)[0],
});
}
return undefined;
});
relations = relations.filter(relation => relation !== undefined);
// When there are no relations skip the node
if (relations.length === 0) return;
classDefinitions.push({
subject: node,
relations,
});
});
return classDefinitions;
}
Using the 'class-definitions' we are able to convert the raw data we have to a RDF-graph.
function convertDataToTriples(data, links, nodes) {
// Map relations
const classDefinitions = createClassDefinitions(nodes, links);
// For every node
const graph = rdf.createGraph();
// For every row of data except the header
data.forEach((dataRow, dataIndex) => {
if (dataIndex === 0) return; // Skip the header
classDefinitions.forEach((classDefinition) => {
// Skip when the value is not present in that row
if (!dataRow[classDefinition.subject.column]) return;
// Skip when there are no relations
if (classDefinition.relations.length === 0) return;
// If there is no item create a new one
const dataSubject = rdf.createNamedNode(dataRow[classDefinition.subject.column]);
const typeOf = rdf.createNamedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
if (graph.filter(relation => relation.object === dataSubject
&& relation.predicate === typeOf).length === 0) {
graph.add(rdf.createTriple(
dataSubject,
typeOf,
rdf.createNamedNode(classDefinition.subject.uri)));
}
// For every relation check if there is a value
classDefinition.relations.forEach((relation) => {
const relationNode = rdf.createNamedNode(relation.link.link);
if (dataRow[relation.target.column]) {
// The target value
let targetValue = dataRow[relation.target.column];
// If the relation is not mentioned yet
if (relation.target.type === 'literal') {
if (!Number(targetValue)) { // String literal
targetValue = rdf.createLiteral(targetValue, 'en', 'http://www.w3.org/2001/XMLSchema#string');
} else if (targetValue % 1 === 0) { // Integer Literal
targetValue = rdf.createLiteral(targetValue, null, 'https://www.w3.org/2001/XMLSchema#integer');
} else { // Float Literal
targetValue = rdf.createLiteral(targetValue, null, 'https://www.w3.org/2001/XMLSchema#float');
}
} else {
targetValue = rdf.createNamedNode(targetValue);
}
// eslint-disable-next-line no-underscore-dangle
if (graph._graph.filter(node => (node.object === dataSubject
&& node.predicate === relationNode
&& node.subject === targetValue)).length === 0) {
graph.add(rdf.createTriple(dataSubject, relationNode, targetValue));
}
}
});
});
});
return graph;
}
When the user wants to publish his dataset two queries are fired to the endpoint. The first one stores the meta-data of the graph. The URI
variable contains the name URL the graph will be known as on the SPARQL endpoint. The other variables are related to the meta-data of these graphs.
`INSERT DATA { GRAPH <http://gerwinbosch.nl/rdf-paqt/metadata> {
<${uri}> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://rdfs.org/ns/void#Dataset> .
<${uri}> <http://purl.org/dc/terms/title> "${graphName}" .
<${uri}> <http://purl.org/dc/terms/description> "${description}" .
<${uri}> <http://purl.org/dc/terms/created> "${date}"^^<http://www.w3.org/2001/XMLSchema#date> .}}`;
The second query will insert that data within the SPARQL endpoint. The 'URI' variable is the URI of the new graph. The GRAPH will create a new GRAPH when it doesn't exist yet and add data to an already existing graph. The 'graph' variable is the N-triple representation of the dataset
`INSERT DATA { GRAPH <${uri}> {${graph}}}`;
The design consists of two b
name | type | required | description |
---|---|---|---|
graph | Array | ❌ | A rdf graph |
filename | String | ❌ | A |
processing | boolean | ✔️ | True when converting the dataset to an rdf graph |
executeQuery | function | ✔️ | function with which we are able to call the SPARQL store |
RDF-PAQT is the result of the bachelor thesis of Gerwin Bosch commissioned by the Kadaster