Skip to content

Commit 8f19c8a

Browse files
authored
Merge pull request #53 from SolidLabResearch/ontology-link
Ontology link
2 parents 7c3518f + ba9e6fe commit 8f19c8a

File tree

5 files changed

+143
-7
lines changed

5 files changed

+143
-7
lines changed

cypress/e2e/spec.cy.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ describe("Web app", () => {
6969
);
7070
});
7171

72+
it("Variables in column header contain link to ontology", () => {
73+
cy.visit("/");
74+
75+
cy.contains("My favourite musicians").click();
76+
cy.contains("Finished in:");
77+
cy.get('a[href="http://schema.org/name"]');
78+
})
79+
7280
it("When one source throws an error, the results of other sources are still shown", () => {
7381
cy.visit("/");
7482

src/components/ListResultTable/QueryResultList/QueryResultList.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ActionBar from "../../ActionBar/ActionBar";
44
import GenericField from "../../../representationProvider/GenericField";
55
import { Term } from "sparqljs";
66
import config from "../../../config";
7+
import TableHeader from "./TableHeader/TableHeader";
78

89
/**
910
* @param {object} props - the props passed down to the component
@@ -25,7 +26,7 @@ function QueryResultList(props) {
2526
<Title title={config.title} />
2627
<ListView title=" " actions={<ActionBar />} {...props}>
2728
{values && (
28-
<Datagrid>
29+
<Datagrid header={<TableHeader config={config}/>}>
2930
{Object.keys(values).map((key) => {
3031
return (
3132
<GenericField
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
.header-button{
3+
height: 100%;
4+
vertical-align: middle;
5+
}
6+
7+
.header-button:hover{
8+
cursor: pointer;
9+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Link, TableCell, TableHead, TableRow } from "@mui/material";
2+
import React from "react";
3+
import { useListContext } from "react-admin";
4+
import "./TableHeader.css";
5+
import NorthIcon from "@mui/icons-material/North";
6+
import SouthIcon from "@mui/icons-material/South";
7+
import LinkIcon from "@mui/icons-material/Link";
8+
import PropTypes from "prop-types";
9+
import { Component } from "react";
10+
11+
/**
12+
*
13+
* @param {object} props - the props passed down to the component
14+
* @param {Array<Component>} props.children - the children of the component
15+
* @param {object} props.config - the config object of the application
16+
* @returns {Component} the header of the table containing the column names, the sort icons and ontology links
17+
*/
18+
function TableHeader({ children, config }) {
19+
const { sort, setSort, resource } = useListContext();
20+
const { variableOntology } = config.queries.filter(
21+
(query) => query.id === resource
22+
)[0];
23+
24+
/**
25+
* Handles the click on a header and sets the sort state accordingly
26+
* @param {string} target - the source of the column that was clicked
27+
*/
28+
function handleHeaderClick(target) {
29+
const newSort = { field: target, order: "DESC" };
30+
if (sort) {
31+
if (sort.order === "ASC") {
32+
newSort.order = "DESC";
33+
} else {
34+
newSort.order = "ASC";
35+
}
36+
}
37+
setSort(newSort);
38+
}
39+
40+
return (
41+
<TableHead>
42+
<TableRow>
43+
<TableCell> </TableCell>
44+
{React.Children.map(children, (child) => (
45+
<>
46+
<TableCell
47+
key={child.props.source}
48+
sx={{ height: "100%", "& > *": { verticalAlign: "middle" } }}
49+
>
50+
<span
51+
role="button"
52+
className="header-button"
53+
onClick={() => handleHeaderClick(child.props.source)}
54+
>
55+
{child.props.label}
56+
</span>
57+
{variableOntology[child.props.source] && (
58+
<Link
59+
target="_blank"
60+
href={variableOntology[child.props.source]}
61+
sx={{ height: "100%", margin: "0 5px", "& > *": { verticalAlign: "middle" } }}
62+
>
63+
<LinkIcon
64+
fontSize="small"
65+
sx={{ height: "100%", color: "gray" }}
66+
/>
67+
</Link>
68+
)}
69+
{sort.field === child.props.source && (
70+
<>
71+
{sort && sort.order === "DESC" && (
72+
<NorthIcon
73+
fontSize="small"
74+
sx={{ height: "100%", color: "gray" }}
75+
/>
76+
)}
77+
{sort && sort.order === "ASC" && (
78+
<SouthIcon
79+
fontSize="small"
80+
sx={{ height: "100%", color: "gray" }}
81+
/>
82+
)}
83+
</>
84+
)}
85+
</TableCell>
86+
</>
87+
))}
88+
</TableRow>
89+
</TableHead>
90+
);
91+
}
92+
93+
TableHeader.propTypes = {
94+
children: PropTypes.node,
95+
config: PropTypes.object.isRequired,
96+
97+
}
98+
export default TableHeader;

src/dataProvider/SparqlDataProvider.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ async function fetchQuery(query) {
9494
const rawText = await result.text();
9595
query.rawText = rawText;
9696
const parsedQuery = parser.parse(rawText);
97+
if (!query.variableOntology) {
98+
query.variableOntology = findPredicates(parsedQuery);
99+
}
97100
if (!parsedQuery.limit) {
98101
parsedQuery.limit = query.limit;
99102
}
@@ -116,6 +119,26 @@ async function fetchQuery(query) {
116119
}
117120
}
118121

122+
/**
123+
* Given a query and an object, this function returns the predicate of the object in the query.
124+
* @param {object} query - the paresed query in which the predicate is to be looked for.
125+
* @returns {object} an object with the variable as key and the predicate as value.
126+
*/
127+
function findPredicates(query) {
128+
const ontologyMapper = {};
129+
if (!query.variables) {
130+
return query;
131+
}
132+
for (const part of query.where) {
133+
for (const triple of part.triples) {
134+
if(triple.predicate.termType !== "Variable"){
135+
ontologyMapper[triple.object.value] = triple.predicate.value;
136+
}
137+
}
138+
}
139+
return ontologyMapper;
140+
}
141+
119142
/**
120143
* A function that executes a given query and processes every result.
121144
* @param {object} query - the query which is to be executed and additional information about the query.
@@ -125,12 +148,9 @@ async function executeQuery(query) {
125148
try {
126149
query.queryText = await fetchQuery(query);
127150
return handleQueryExecution(
128-
await myEngine.query(
129-
query.queryText,
130-
{
131-
...generateContext(query.comunicaContext)
132-
}
133-
),
151+
await myEngine.query(query.queryText, {
152+
...generateContext(query.comunicaContext),
153+
}),
134154
query
135155
);
136156
} catch (error) {

0 commit comments

Comments
 (0)