Skip to content

Commit

Permalink
Update TLOI views and visualizations
Browse files Browse the repository at this point in the history
  • Loading branch information
hvarg committed Mar 18, 2024
1 parent 00d6f90 commit 13033b4
Show file tree
Hide file tree
Showing 25 changed files with 794 additions and 369 deletions.
1 change: 1 addition & 0 deletions src/DISK/interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export interface TriggeredLineOfInquiry extends LOICommon{
queryResults: DataQueryResult;
workflows: WorkflowInstantiation[];
metaWorkflows: WorkflowInstantiation[];
result: GoalResult | null;
}

export type Status = 'QUEUED' | 'RUNNING' | 'FAILED' | 'SUCCESSFUL' | 'PENDING';
Expand Down
49 changes: 49 additions & 0 deletions src/components/DataQuery/DataQueryResultTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { DataQueryResult } from "DISK/interfaces";
import { SimpleTable } from "components/SimpleTable";
import { useEffect, useState } from "react";

interface DataQueryResultTableProps {
result: DataQueryResult
}

export const DataQueryResultTable = ({result}:DataQueryResultTableProps) => {
console.log(result.variablesToShow);
const [csv, setCSV] = useState<{[varName: string]: string[]}>({});

useEffect(() => {
if (result.results && result.variablesToShow && result.variablesToShow.length > 0) {
let lines = result.results.split('\n');
let header = true;
let csvMap : {[varName: string]: string[]} = {};
let indexToName : {[index:number]: string} = {};
for (const line of lines) {
let cells = line.split(/,\s*/);
if (header) {
header = false;
for (let i = 0; i < cells.length; i++) {
if (result.variablesToShow.some(v => v === "?" + cells[i]) ) {
indexToName[i] = cells[i];
csvMap[cells[i]] = [];
}
}
} else {
for (let i = 0; i < cells.length; i++) {
if (!!indexToName[i]) {
csvMap[indexToName[i]].push(cells[i]);
}
}
}
}
console.log(csvMap);
setCSV(csvMap);
//FIXME: we are missing some variables of the query here, we need to fix it server side.
} else {
setCSV({});
}
}, [result])

if (Object.keys(csv).length === 0)
return <></>;

return <SimpleTable data={csv}/>
}
89 changes: 89 additions & 0 deletions src/components/DataQuery/DataQueryResultView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { sparql } from "@codemirror/legacy-modes/mode/sparql"
import { Box, IconButton, Divider, Skeleton, Tooltip } from "@mui/material"
import { DataEndpoint, DataQueryResult, Endpoint } from "DISK/interfaces"
import { renderDescription } from "DISK/util"
import { TypographySubtitle, TypographyLabel, TypographyInline, InfoInline, TypographySection } from "components/Styles"
import { Fragment, useEffect, useState } from "react"
import { useGetEndpointsQuery } from "redux/apis/server"
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
import CodeMirror from '@uiw/react-codemirror';
import { StreamLanguage } from '@codemirror/language';
import { DataQueryResultTable } from "./DataQueryResultTable"

interface DataQueryResultViewProps {
result: DataQueryResult
}

export const DataQueryResultView = ({result} : DataQueryResultViewProps) => {
const { data:endpoints, isLoading:loadingEndpoints } = useGetEndpointsQuery();
const [dataSource, setDataSource] = useState<DataEndpoint|null>(null);
const [formalView, setFormalView] = useState<boolean>(false);

useEffect(() => {
if (endpoints && endpoints.length > 0 && result.endpoint) {
for (const e of endpoints) {
if (e.name === result.endpoint.name) {
setDataSource(e);
return;
}
}
} else {
setDataSource(null);
}

}, [endpoints, result])

return <Box sx={{padding:"5px 10px"}}>
<TypographySubtitle>Data:</TypographySubtitle>
<Box>
<TypographyLabel sx={{whiteSpace: 'nowrap'}}>Data source: </TypographyLabel>
{loadingEndpoints ?
<Skeleton sx={{display:"inline-block", width: "400px"}}/>
: dataSource?.name && <b style={{whiteSpace: 'nowrap'}}> { dataSource.name.replaceAll("_"," ") } </b>
}
<Box>
{dataSource?.description && renderDescription(dataSource.description)}
</Box>
</Box>
<Box sx={{display:"flex", justifyContent:"space-between", alignItems: "center"}}>
<Box>
<TypographyLabel>Data query explanation:</TypographyLabel>
{(result.description ?
<TypographyInline> {result.description} </TypographyInline>
: <InfoInline> None specified </InfoInline>)}
</Box>
<Tooltip arrow title={(formalView? "Hide" : "Show") + " data query"}>
<IconButton onClick={() => setFormalView(!formalView)}>
{formalView? <VisibilityIcon/> : <VisibilityOffIcon/>}
</IconButton>
</Tooltip>
</Box>

{formalView && <Fragment>
<TypographySection>Data query:</TypographySection>
<Box sx={{fontSize: "0.94rem"}} >
<CodeMirror readOnly value={result.query}
extensions={[StreamLanguage.define(sparql)]}
onChange={(value, viewUpdate) => {
}}
/>
</Box>
</Fragment>}

{ (result.results && result.variablesToShow ) &&
<Box>
<Divider/>
<Box>
<TypographySection>Input data retrieved:</TypographySection>
<DataQueryResultTable result={result}/>
{result.footnote &&
<Box sx={{display: "flex", justifyContent: "center", alignItems: "center"}}>
<TypographyLabel sx={{mr:"5px"}}>Table: </TypographyLabel>
<TypographyInline> {result.footnote} </TypographyInline>
</Box>}
</Box>
</Box>
}
</Box>
}
85 changes: 2 additions & 83 deletions src/components/ResultTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Box, Button, Skeleton, Table, TableBody, TableCell, TableContainer, Tab
import { DataEndpoint } from "DISK/interfaces";
import { useEffect, useState } from "react";
import { useQueryExternalSourceQuery } from "redux/apis/server";
import { SimpleTable } from "./SimpleTable";

interface ResultTableProps {
query: string;
Expand All @@ -10,98 +11,16 @@ interface ResultTableProps {
indexes?: boolean;
}

const MAX_PER_PAGE = 10;

export const ResultTable = ({query, variables, dataSource, indexes = true}: ResultTableProps) => {
const [total, setTotal] = useState(0);
const [nCols, setNCols] = useState(0);
const [curPage, setCurPage] = useState(0);
const {data:results,isLoading:waiting,isError} = useQueryExternalSourceQuery({dataSource:dataSource,query:query,variables:variables.split(" ")});

useEffect(() => {
if (results) {
let cols: number = Object.values(results).length;
setNCols(cols);
setTotal(Object.values(results)[0].length);
} else {
setNCols(0);
setTotal(0);
}
}, [results]);

const renderText = (txt: string) => {
if (txt) {
let name: string = txt.replace(/.*\//g, "").replace(/.*#/g, "");
if (txt.startsWith("http"))
return (
<MuiLink target="_blank" href={txt}>
{name}
</MuiLink>
);
return <span>{name}</span>;
}
};

return (
<Box>
{waiting ? (
<Skeleton />
) : results && Object.keys(results).length > 0 ? (
<Box>
<TableContainer sx={{ display: "flex", justifyContent: "center" }}>
<Table sx={{ width: "unset", border: "1px solid rgb(223 223 223)", borderRadius: "5px", mt: "4px", }}>
<TableHead>
<TableRow>
{indexes && (
<TableCell sx={{ padding: "0 10px", textAlign: "end" }}>#</TableCell>
)}
{Object.keys(results).map((varname: string) => (
<TableCell key={`header_${varname}`} sx={{ padding: "0 10px" }}>
{varname}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{Array(MAX_PER_PAGE).fill(0)
.map((_, i) => curPage * MAX_PER_PAGE + i)
.map((i) => (
<TableRow key={`row_${i}`}>
{indexes && i < total && (
<TableCell sx={{ padding: "0 10px", textAlign: "end" }}>
{i + 1}
</TableCell>
)}
{i < total &&
Object.values(results).map((values: string[], j) => (
<TableCell key={`cell_${i}_${j}`} sx={{ padding: "0 10px" }}>
{renderText(values[i])}
</TableCell>
))}
</TableRow>
))}
<TableRow>
<TableCell sx={{ padding: "0 10px" }} colSpan={nCols + (indexes ? 1 : 0)}>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", }}>
<Button sx={{ padding: "0" }} disabled={curPage === 0} onClick={() => setCurPage(curPage - 1)}>
Previous
</Button>
<Box sx={{ fontWeight: "bold", color: "darkgray", padding: "0 10px" }}>
Showing {curPage * MAX_PER_PAGE} -{" "}
{(curPage + 1) * MAX_PER_PAGE < total
? (curPage + 1) * MAX_PER_PAGE
: total}{" "}
of {total} results
</Box>
<Button sx={{ padding: "0" }} disabled={(1 + curPage) * MAX_PER_PAGE >= total} onClick={() => setCurPage(curPage + 1)}>
Next
</Button>
</Box>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<SimpleTable data={results}/>
</Box>
) : (
<Box>No data found</Box>
Expand Down
93 changes: 93 additions & 0 deletions src/components/SimpleTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Box, Button, Link as MuiLink } from "@mui/material"
import { useState, useEffect } from "react";

interface SimpleTableProps {
data: {[varName: string]: string[]},
showIndex?: boolean,
perPage?: number,

}
export const SimpleTable = ({data,showIndex=true,perPage=10}:SimpleTableProps) => {
const [total, setTotal] = useState(0);
const [nCols, setNCols] = useState(0);
const [curPage, setCurPage] = useState(0);

useEffect(() => {
if (data) {
let cols: number = Object.values(data).length;
setNCols(cols);
setTotal(Object.values(data)[0].length);
} else {
setNCols(0);
setTotal(0);
}
}, [data]);

const renderText = (txt: string) => {
if (txt) {
let name: string = txt.replace(/.*\//g, "").replace(/.*#/g, "");
if (txt.startsWith("http"))
return (
<MuiLink target="_blank" href={txt}>
{name}
</MuiLink>
);
return <span>{name}</span>;
}
};

return <TableContainer sx={{ display: "flex", justifyContent: "center" }}>
<Table sx={{ width: "unset", border: "1px solid rgb(223 223 223)", borderRadius: "5px", mt: "4px", }}>
<TableHead>
<TableRow>
{showIndex && (
<TableCell sx={{ padding: "0 10px", textAlign: "end" }}>#</TableCell>
)}
{Object.keys(data).map((varname: string) => (
<TableCell key={`header_${varname}`} sx={{ padding: "0 10px" }}>
{varname}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{Array(perPage).fill(0)
.map((_, i) => curPage * perPage + i)
.map((i) => (
<TableRow key={`row_${i}`}>
{showIndex && i < total && (
<TableCell sx={{ padding: "0 10px", textAlign: "end" }}>
{i + 1}
</TableCell>
)}
{i < total &&
Object.values(data).map((values: string[], j) => (
<TableCell key={`cell_${i}_${j}`} sx={{ padding: "0 10px" }}>
{renderText(values[i])}
</TableCell>
))}
</TableRow>
))}
<TableRow>
<TableCell sx={{ padding: "0 10px" }} colSpan={nCols + (showIndex ? 1 : 0)}>
<Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", }}>
<Button sx={{ padding: "0" }} disabled={curPage === 0} onClick={() => setCurPage(curPage - 1)}>
Previous
</Button>
<Box sx={{ fontWeight: "bold", color: "darkgray", padding: "0 10px" }}>
Showing {curPage * perPage} -{" "}
{(curPage + 1) * perPage < total
? (curPage + 1) * perPage
: total}{" "}
of {total} results
</Box>
<Button sx={{ padding: "0" }} disabled={(1 + curPage) * perPage >= total} onClick={() => setCurPage(curPage + 1)}>
Next
</Button>
</Box>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
}
Loading

0 comments on commit 13033b4

Please sign in to comment.