Skip to content

Commit

Permalink
Fixed file lists
Browse files Browse the repository at this point in the history
  • Loading branch information
hvarg committed Apr 3, 2024
1 parent b4d8a12 commit aaef3df
Show file tree
Hide file tree
Showing 19 changed files with 362 additions and 203 deletions.
2 changes: 1 addition & 1 deletion src/DISK/interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export interface WorkflowInstantiation extends WorkflowSeed {

export interface Execution extends ExecutionRecord {
externalId: string ;
result: GoalResult ;
result: GoalResult | null;
steps: ExecutionRecord[];
inputs: VariableBinding[];
outputs: VariableBinding[];
Expand Down
2 changes: 1 addition & 1 deletion src/components/DataQuery/DataQueryResultView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const DataQueryResultView = ({result} : DataQueryResultViewProps) => {
{formalView && <Fragment>
<TypographySection>Data query:</TypographySection>
<Box sx={{fontSize: "0.94rem"}} >
<CodeMirror readOnly value={result.query}
<CodeMirror value={result.query}
extensions={[StreamLanguage.define(sparql)]}
onChange={(value, viewUpdate) => {
}}
Expand Down
89 changes: 79 additions & 10 deletions src/components/DataQuery/DataQueryTemplateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sparql } from "@codemirror/legacy-modes/mode/sparql";
import { Box, Select, MenuItem, Card, FormHelperText, Skeleton } from "@mui/material";
import { Box, Select, MenuItem, Card, FormHelperText, Skeleton, Chip, FormControl, InputLabel, OutlinedInput, SelectChangeEvent, useTheme, Theme } from "@mui/material";
import { DataEndpoint, DataQueryTemplate, Endpoint } from "DISK/interfaces"
import { renderDescription } from "DISK/util";
import { QueryTester } from "components/QueryTester";
Expand All @@ -9,21 +9,41 @@ import { useGetEndpointsQuery } from "redux/apis/server";
import CodeMirror, { EditorState, ViewUpdate, lineNumbers } from '@uiw/react-codemirror';
import { StreamLanguage } from '@codemirror/language';


interface DataQueryTemplateFormProps {
value: Partial<DataQueryTemplate>,
onChange?: (newValue:DataQueryTemplate) => void;
showErrors: boolean;
}

function getStyles(name: string, personName: readonly string[], theme: Theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
const ITEM_HEIGHT = 24;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemplateFormProps) => {
const theme = useTheme();
const [dataQueryExplanation, setDataQueryExplanation] = useState<string>("");
const [sourceUrl, setSourceUrl] = useState<string>("");
const [sourceID, setSourceID] = useState<string>("");
const [dataSourceDescription, setDataSourceDescription] = useState<string>("");
const [template, setTemplate] = useState("");
const [tableFootnote, setTableFootnote] = useState("");
const [tableVariables, setTableVariables] = useState("");
const [tableVariables, setTableVariables] = useState<string[]>([]);
const [allVariables, setAllVariables] = useState<string[]>([]);
const [lastLine, setLastLine] = useState(0);

const [errorDataSource, setErrorDataSource] = useState<boolean>(false);
Expand All @@ -40,7 +60,7 @@ export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemp
if (value.description !== dataQueryExplanation) setDataQueryExplanation(value.description || "");
if (value.footnote !== tableFootnote) setTableFootnote(value.footnote || "");
if (value.template !== template) setTemplate(value.template || "");
if (value.variablesToShow && value.variablesToShow.join(", ") !== tableVariables) setTableVariables(value.variablesToShow.join(", ") || "");
if (value.variablesToShow) setTableVariables(value.variablesToShow);
if (value.endpoint && value.endpoint.url && value.endpoint.url !== sourceUrl) setSourceUrl(value.endpoint.url);
if (showErrors) {
if (!value.endpoint || !value.endpoint.url) setErrorDataSource(true);
Expand All @@ -50,6 +70,13 @@ export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemp

useEffect(() => {
setLastLine(template.split("\n").length + 2);
// Compute variables
let candidates : Set<string> = new Set<string>();
let dataVars : RegExpMatchArray | null = (template || "").match(/\?[\w\d]+/g);
if (dataVars) {
dataVars.forEach((varName:string) => candidates.add(varName));
}
setAllVariables(Array.from(candidates));
}, [template]);

const onTemplateChange = (newValue:string, update:ViewUpdate) => {
Expand All @@ -74,11 +101,25 @@ export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemp
description: dataQueryExplanation,
template: template,
footnote: tableFootnote,
variablesToShow: tableVariables.split(/, */g),
variablesToShow: tableVariables,
endpoint: {id:sourceID},
})
}, [dataQueryExplanation, template, tableFootnote, tableVariables, sourceID]);

const handleChange = (event: SelectChangeEvent<string[]>) => {
const {
target: { value },
} = event;
setTableVariables(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};

const removeElement = (varToRemove:string) => {
setTableVariables(tableVariables.filter(c => c!==varToRemove));
}

return <>
<TextFieldBlock fullWidth size="small" id="LOIQueryExplanation" label="Write an explanation for your data query:"
value={dataQueryExplanation} onChange={(e) => setDataQueryExplanation(e.target.value)} />
Expand Down Expand Up @@ -113,22 +154,22 @@ export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemp
<CodeMirror value={QUERY_HEADER}
extensions={[StreamLanguage.define(sparql)]}
readOnly
className={"cm-readonly header" + (lastLine>11 ? " bigger" : "")}
className={"cm-readonly header" + (lastLine > 11 ? " bigger" : "")}
/>
<CodeMirror value={template}
extensions={[
StreamLanguage.define(sparql),
lineNumbers({formatNumber: (lineNo:number, state: EditorState) => `${lineNo+1}`})
lineNumbers({ formatNumber: (lineNo: number, state: EditorState) => `${lineNo + 1}` })
]}
onChange={onTemplateChange}
/>
<CodeMirror value={QUERY_FOOTER}
extensions={[
StreamLanguage.define(sparql),
lineNumbers({formatNumber: (lineNo:number, state: EditorState) => `${lastLine}`})
lineNumbers({ formatNumber: (lineNo: number, state: EditorState) => `${lastLine}` })
]}
readOnly
className={"cm-readonly footer " + (lastLine>11 ? "bigger" : "")}
className={"cm-readonly footer " + (lastLine > 11 ? "bigger" : "")}
/>
</Card>
</Box>
Expand All @@ -137,7 +178,35 @@ export const DataQueryTemplateForm = ({value,onChange, showErrors}:DataQueryTemp
<FormHelperText sx={{ fontSize: ".9rem" }}>
When the data source is accessed, a table will be generated that will show the following information about the datasets retrieved:
</FormHelperText>
<TextFieldBlock fullWidth size="small" id="LOITableVars" label="Dataset information to be shown:" placeholder="?var1 ?var2 ..." value={tableVariables} onChange={(e) => setTableVariables(e.target.value)} />
<FormControl sx={{ width: "100%" }}>
<InputLabel style={{marginTop: "-7px"}} id="demo-multiple-chip-label">Dataset information to be shown</InputLabel>
<Select
disabled={allVariables.length === 0}
size="small"
labelId="demo-multiple-chip-label"
multiple
value={tableVariables}
onChange={handleChange}
input={<OutlinedInput id="select-multiple-chip" label="Dataset information to be shown" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} onMouseDown={e => e.stopPropagation()} onDelete={() => removeElement(value)}/>
))}
</Box>
)}
MenuProps={MenuProps}
>
{allVariables.map((name) => (
<MenuItem key={name} value={name}
style={getStyles(name, tableVariables, theme)}>
{name}
</MenuItem>
))}
</Select>
</FormControl>


<TextFieldBlock fullWidth size="small" id="LOITableDesc" label="Description of the datasets:" value={tableFootnote} onChange={(e) => setTableFootnote(e.target.value)} />
</Box>
</>
Expand Down
7 changes: 5 additions & 2 deletions src/components/SimpleTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const SimpleTable = ({data,showIndex=true,perPage=10}:SimpleTableProps) =
if (data) {
let cols: number = Object.values(data).length;
setNCols(cols);
setTotal(Object.values(data)[0].length);
setTotal(cols > 0 ? Object.values(data)[0].length : 0);
} else {
setNCols(0);
setTotal(0);
Expand All @@ -25,13 +25,16 @@ export const SimpleTable = ({data,showIndex=true,perPage=10}:SimpleTableProps) =

const renderText = (txt: string) => {
if (txt) {
let name: string = txt.replace(/.*\//g, "").replace(/.*#/g, "");
let name: string = txt.replace(/.*\//g, "").replace(/.*#/g, "").replaceAll("_", " ");
if (txt.startsWith("http"))
return (
<MuiLink target="_blank" href={txt}>
{name}
</MuiLink>
);
name = name.replaceAll(" (E)", "");
if (name.startsWith("Has"))
name = name.substring(3);
return <span>{name}</span>;
}
};
Expand Down
133 changes: 77 additions & 56 deletions src/components/files/ExecutionOutputs.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,61 @@
import { Box, Card, Divider, Grid, Link, Typography } from "@mui/material"
import { Box, Card, Divider, Grid, Link, Skeleton, Typography } from "@mui/material"
import { Endpoint, Execution, VariableBinding } from "DISK/interfaces"
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
import { useEffect, useState } from "react";
import { displayConfidenceValue } from "constants/general";
import { BrainModal } from "components/modal/BrainModal";
import { ShinyModal } from "components/modal/ShinyModal";
import { ImagePreview } from "./ImagePreview";

interface ExecutionOutputProps {
execution: Execution
outputs: VariableBinding[]
source: Endpoint
noModals?: boolean
}

export const ExecutionOutput = ({execution, outputs, source}:ExecutionOutputProps) => {
const [shinyLog, setShinyLog] = useState<string>("");
const [brainCfg, setBrainCfg] = useState<string>("");
const [visibleOutputs, setVisibleOutputs] = useState<VariableBinding[]>([]);
interface OutputVariableAndValue {
def: VariableBinding,
value: VariableBinding | null,
};

type RenderableOutput = '_SHINY_LOG_' | '_BRAIN_VISUALIZATION_' | '_CONFIDENCE_VALUE_';
useEffect(() => {
let renderable : RenderableOutput[] = ['_SHINY_LOG_', '_BRAIN_VISUALIZATION_'];
let variables: Partial<Record<string, RenderableOutput>> = {};
let values: Partial<Record<RenderableOutput, string>> = {};
let map: Partial<Record<string, VariableBinding>> = {};
export const ExecutionOutput = ({execution, outputs, source, noModals=false}:ExecutionOutputProps) => {
const [outputTable, setOutputTable] = useState<OutputVariableAndValue[]>([]);

(outputs || [])
.filter(b => b.binding && b.binding.length > 0 && renderable.some(r => r === b.binding[0]))
.forEach(b => variables[b.variable] = b.binding[0] as RenderableOutput);
useEffect(() => {
// Map outputs by variable name, except confidence value.
let mappedOutputs = (outputs || [])
.reduce<Record<string,OutputVariableAndValue>>((acc, item) => {
let value : string = item.binding && item.binding.length > 0 ? item.binding[0] : "";
if (value !== "" && value !== '_CONFIDENCE_VALUE_') {
if (!(noModals && (value === '_SHINY_LOG_' || value === '_BRAIN_VISUALIZATION_'))) {
acc[item.variable] = {
def: item,
value: null // Value will be set next
}
}
}
return acc;
},{});

// Search for values on extras
if (execution && execution.result && execution.result.extras) {
execution.result.extras.forEach(b => {
let key = variables[b.variable];
if (key && b.binding && b.binding.length > 0) {
values[key] = b.binding[0];
let cur = mappedOutputs[b.variable];
if (cur && b.binding && b.binding.length > 0) {
cur.value = b;
} else {
// Output not found!
delete mappedOutputs[b.variable];
}
map[b.variable] = b;
});
}
setShinyLog(values['_SHINY_LOG_'] ? values['_SHINY_LOG_'] : "");
setBrainCfg(values['_BRAIN_VISUALIZATION_'] ? values['_BRAIN_VISUALIZATION_'] : "");
// Now remove processed outputs and bind to execution outputs.
renderable.push("_CONFIDENCE_VALUE_");
setOutputTable(Object.values(mappedOutputs));
return;
}, [execution, outputs, noModals]);

setVisibleOutputs(outputs
.filter(o => !(!o.binding || o.binding.length === 0 || renderable.some(r => r === o.binding[0])))
.map(o => map[o.variable] ? map[o.variable] as VariableBinding : o)
);
}, [execution, outputs]);

const renderOneBinding = (binding:VariableBinding, value:string) => {
const renderLiteral = (binding:VariableBinding, value:string) => {
let isLiteral = binding.datatype?.endsWith("string") || !value.startsWith("http");
let isURL = value.startsWith("http") || value.startsWith("www") || value.startsWith("ftp");
return <span style={{color:isLiteral?"green":"orange"}}>
Expand All @@ -58,16 +65,8 @@ export const ExecutionOutput = ({execution, outputs, source}:ExecutionOutputProp
</span>
}

const renderBindings = (binding: VariableBinding) => {
if (!binding.isArray || binding.binding.length === 1) {
return renderOneBinding(binding, binding.binding[0]);
} else {
return binding.binding.map(str => renderOneBinding(binding, str));
}
}

const getExecutionLink = () => {
if (source && execution.externalId.includes("localhost")) {
if (source && execution && execution.externalId.includes("localhost")) {
let sp1 = source.url.split('wings-portal');
let sp2 = execution.externalId.split('wings-portal');
if (sp1.length == 2 && sp2.length == 2)
Expand All @@ -76,6 +75,26 @@ export const ExecutionOutput = ({execution, outputs, source}:ExecutionOutputProp
return execution.externalId;
}

const renderBindings = ({def, value} : OutputVariableAndValue) => {
if (value == null)
return <></>;
let cfg = def.binding[0];
if (def.type === 'PROCESS') {
if (cfg === '_SHINY_LOG_') {
return <ShinyModal shinyLog={value.binding[0]} iconOnly={false}/>
} else if (cfg === '_BRAIN_VISUALIZATION_') {
return <BrainModal brainCfg={value.binding[0]} iconOnly={false}/>
}
} else if (def.type === 'SAVE') {
if (cfg === '_IMAGE_' || cfg === "_VISUALIZE_") {
return value.binding.map((b,i) => <a href={b} target="_blank" key={`${def.variable}_${i}`}>
<img src={b} style={{maxWidth:"300px", maxHeight:"300px"}}/>
</a>);
}
}
return value.binding.map(str => renderLiteral(def, str));
}

return <Card style={{padding: "5px"}} variant="outlined">
<Box sx={{display: "flex", justifyContent: "space-between", fontSize: ".9em"}}>
<a target="_blank" rel="noreferrer" href={getExecutionLink()} style={{display: "inline-flex", alignItems: "center", textDecoration: "none", color: "black"}}>
Expand All @@ -85,27 +104,29 @@ export const ExecutionOutput = ({execution, outputs, source}:ExecutionOutputProp
</Typography>
<OpenInNewIcon sx={{fontSize: "1rem"}}/>
</a>
{execution.result.confidenceValue > 0 && <span>
<b>{execution.result.confidenceType || "confidence"}: </b>
{execution.result && execution.result.confidenceValue > 0 && <span>
<b>{(execution.result && execution.result.confidenceType) || "confidence"}: </b>
{ displayConfidenceValue(execution.result.confidenceValue)}
</span>}
</Box>
<Divider/>
<Box sx={{ fontSize: ".85rem", mt:"10px" }}>
{visibleOutputs.map((binding: VariableBinding) =>
<Grid key={`var_${binding.variable}`} container spacing={1}>
<Grid item xs={3} md={2} sx={{ textAlign: "right" }}>
<b>{binding.variable}: </b>
</Grid>
<Grid item xs={9} md={10}>
{renderBindings(binding)}
</Grid>
</Grid>
)}
</Box>
<Box style={{display:"flex", justifyContent:"space-around", padding: "10px"}}>
{shinyLog !== "" && <ShinyModal shinyLog={shinyLog} iconOnly={false}/>}
{brainCfg !== "" && <BrainModal brainCfg={brainCfg} iconOnly={false}/>}
</Box>
{execution.result === null ? <Skeleton/> :
<>
<Box sx={{ fontSize: ".85rem", mt:"10px" }}>
{outputTable.map(({def, value}) =>
<Box key={`var_${def.variable}`} style={{marginBottom: "5px"}}>
<Grid container spacing={2}>
<Grid item xs={3} md={2} sx={{ textAlign: "right", width: "50%", margin: "auto" }}>
<b>{def.variable}: </b>
</Grid>
<Grid item xs={9} md={10}>
{renderBindings({def, value})}
</Grid>
</Grid>
</Box>
)}
</Box>
</>
}
</Card>
}
Loading

0 comments on commit aaef3df

Please sign in to comment.