Skip to content

Commit

Permalink
Allow multiple values
Browse files Browse the repository at this point in the history
  • Loading branch information
hvarg committed Feb 12, 2024
1 parent c242771 commit 7531301
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 78 deletions.
6 changes: 5 additions & 1 deletion src/DISK/interfaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,11 @@ export interface DataEndpoint {

export interface QuestionOptionsRequest {
id: string,
bindings: {[name:string] : string};
bindings: MultiValueAssignation,
}

export type MultiValueAssignation = {
[name:string] : string[]
}

const _names = {}
Expand Down
8 changes: 4 additions & 4 deletions src/components/questions/BoundingBoxMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ export const BoundingBoxMap = ({variable}: BoundingBoxMapProps) => {
const onSelect = () => {
let newBindings = { ...bindings };
if (boundingBox && variable) {
newBindings[variable.minLat.id] = boundingBox[0].toFixed(6);
newBindings[variable.minLng.id] = boundingBox[1].toFixed(6);
newBindings[variable.maxLat.id] = boundingBox[2].toFixed(6);
newBindings[variable.maxLng.id] = boundingBox[3].toFixed(6);
newBindings[variable.minLat.id] = [boundingBox[0].toFixed(6)];
newBindings[variable.minLng.id] = [boundingBox[1].toFixed(6)];
newBindings[variable.maxLat.id] = [boundingBox[2].toFixed(6)];
newBindings[variable.maxLng.id] = [boundingBox[3].toFixed(6)];
onCloseDialog();
} else {
delete newBindings[variable.minLat.id];
Expand Down
34 changes: 19 additions & 15 deletions src/components/questions/QuestionHelpers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import styled from "@emotion/styled";
import { Box } from "@mui/material";
import { AnyQuestionVariable, Question, QuestionVariable, Triple, VariableBinding, Workflow } from "DISK/interfaces";
import { StrStrMap } from "redux/slices/forms";
import { AnyQuestionVariable, MultiValueAssignation, Question, QuestionVariable, Triple, VariableBinding, Workflow } from "DISK/interfaces";

export type TemplateFragment = {
type: 'string',
Expand Down Expand Up @@ -41,19 +40,21 @@ export const createTemplateFragments : (question:Question) => TemplateFragment[
return fragments;
}

export const bindingsToIdValueMap : (bindings:VariableBinding[]) => StrStrMap = (bindings) => {
let r : StrStrMap = {};
export const bindingsToIdValueMap : (bindings:VariableBinding[]) => MultiValueAssignation = (bindings) => {
let r : MultiValueAssignation = {};
(bindings||[]).forEach((vb: VariableBinding) => {
r[vb.variable] = vb.binding[0]
console.log(vb);
if (vb.variable)
r[vb.variable] = [vb.binding[0]]
});
return r;
}

export const simpleMapToVariableBindings : (bindings:StrStrMap) => VariableBinding[] = (bindings) => {
export const simpleMapToVariableBindings : (bindings:MultiValueAssignation) => VariableBinding[] = (bindings) => {
return Object.keys(bindings).map((varId: string) => {
return {
variable: varId,
binding: [bindings[varId]],
binding: [bindings[varId]][0],
isArray: false,
type: "DEFAULT"
} as VariableBinding;
Expand All @@ -78,24 +79,27 @@ export const validURL = (str:string) => {
}


export const addBindingsToQuestionGraph : (question:Question, bindings:StrStrMap) => Triple[] = (question, bindings) => {
export const addBindingsToQuestionGraph : (question:Question, bindings:MultiValueAssignation) => Triple[] = (question, bindings) => {
let graph = getCompleteQuestionGraph(question);
// Add binding values
// TODO: This assumes we only have one
let updatedGraph : Triple[] = []
graph.forEach((t:Triple) => {
let obj = t.object;
let objVal : string = bindings[t.object.value];
if (objVal) {
let objVal : string[] = bindings[t.object.value];
if (objVal && objVal.length > 0) {
obj = {
type: validURL(objVal) ? 'URI' : 'LITERAL',
value: objVal,
type: validURL(objVal[0]) ? 'URI' : 'LITERAL',
value: objVal[0],
}
}
let sub = bindings[t.subject] && bindings[t.subject].length > 0 ? bindings[t.subject][0] : null;
let pre = bindings[t.predicate] && bindings[t.predicate].length > 0 ? bindings[t.predicate][0] : null;

updatedGraph.push({
subject: bindings[t.subject] || t.subject,
predicate: bindings[t.predicate] || t.predicate,
object: obj
subject: sub || t.subject,
predicate: pre || t.predicate,
object: obj
});
});
return updatedGraph;
Expand Down
15 changes: 7 additions & 8 deletions src/components/questions/QuestionPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { Box, Card, FormHelperText, IconButton, styled, Tooltip } from "@mui/material"
import { idPattern, Question, VariableBinding, varPattern, Triple, AnyQuestionVariable } from "DISK/interfaces"
import { idPattern, Question, VariableBinding, varPattern, Triple, AnyQuestionVariable, MultiValueAssignation } from "DISK/interfaces"
import React from "react";
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
import { normalizeTextValue } from "./QuestionList";
import { useGetQuestionsQuery } from "redux/apis/questions";
import { FormalExpressionView } from "./FormalExpressionView";
import { createTemplateFragments, addBindingsToQuestionGraph, TemplateFragment, bindingsToIdValueMap, getAllQuestionVariables } from "./QuestionHelpers";
import { StrStrMap } from "redux/slices/forms";

interface QuestionPreviewProps {
selected: string,
Expand All @@ -26,7 +25,7 @@ export const QuestionPreview = ({selected:selectedId, bindings, label} : Questio
const {data: questions} = useGetQuestionsQuery();
const [selectedQuestion, setSelectedQuestion] = React.useState<Question|null>(null);
const [formalView, setFormalView] = React.useState<boolean>(false);
const [idToRepresentation, setIdToRepresentation] = React.useState<StrStrMap>({});
const [idToRepresentation, setIdToRepresentation] = React.useState<MultiValueAssignation>({});
const [triplePattern, setTriplePattern] = React.useState<Triple[]>([]);
const [templateFragments, setTemplateFragments] = React.useState<TemplateFragment[]>([]);

Expand All @@ -48,16 +47,16 @@ export const QuestionPreview = ({selected:selectedId, bindings, label} : Questio
if (selectedQuestion) {
let allQuestionVariables = getAllQuestionVariables(selectedQuestion);
let nameToUri = allQuestionVariables.reduce((acc,cur) => {
acc[cur.variableName] = cur.id;
acc[cur.variableName] = [cur.id];
return acc;
}, {} as StrStrMap)
}, {} as MultiValueAssignation)
allQuestionVariables.forEach((qv) => {
if (qv.representation) {
let rep = "";
qv.representation.split(varPattern).forEach((part) => {
rep += part.startsWith('?') ? (nameToUri[part] && idValueMap[nameToUri[part]] ? idValueMap[nameToUri[part]] : part) : part;
rep += part.startsWith('?') ? (nameToUri[part] && idValueMap[nameToUri[part][0]] ? idValueMap[nameToUri[part][0]] : part) : part;
})
idValueMap[qv.id] = rep;
idValueMap[qv.id] = [rep];
}

})
Expand All @@ -73,7 +72,7 @@ export const QuestionPreview = ({selected:selectedId, bindings, label} : Questio
return <TextPart key={`p_${key}`}> {frag.value}</TextPart>
case "variable":
return <TextPart key={`p_${key}`} sx={{fontWeight: "500", color: 'darkgreen'}}>
{idToRepresentation[frag.value.id] ? normalizeTextValue(idToRepresentation[frag.value.id]) : "any"}
{idToRepresentation[frag.value.id] ? normalizeTextValue(idToRepresentation[frag.value.id][0]) : "any"}
</TextPart>
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/components/questions/QuestionSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Box, Card, FormHelperText, IconButton, Tooltip } from "@mui/material"
import { Question, VariableBinding, QuestionVariable, Triple, AnyQuestionVariable } from "DISK/interfaces"
import { Question, VariableBinding, QuestionVariable, Triple, AnyQuestionVariable, MultiValueAssignation } from "DISK/interfaces"
import React from "react";
import { useAppDispatch, useQuestionBindings } from "redux/hooks";
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
import { setQuestionBindings, StrStrMap, setSelectedQuestion as setQuestion } from "redux/slices/forms";
import { setQuestionBindings, setSelectedQuestion as setQuestion } from "redux/slices/forms";
import { FormalExpressionView } from "./FormalExpressionView";
import { QuestionTemplateSelector } from "./QuestionTemplateSelector";
import { QuestionTemplateFiller } from "./QuestionTemplateFiller";
Expand Down Expand Up @@ -36,12 +36,12 @@ export const QuestionSelector = ({questionId, bindings, onChange, required=false
React.useEffect(() => {
if (!selectedQuestion)
return;
let idToValue : StrStrMap = {};
let idToValue : MultiValueAssignation = {};
let allVariables = getAllQuestionVariables(selectedQuestion);
(bindings||[]).forEach((varBindings:VariableBinding) => {
let curVar = allVariables.filter((v:AnyQuestionVariable) => (v.id === varBindings.variable))[0];
if (curVar)
idToValue[curVar.id] = varBindings.binding[0];
idToValue[curVar.id] = [varBindings.binding[0]];
});
if (selectedQuestion) {
dispatch(setQuestionBindings(idToValue));
Expand Down
13 changes: 9 additions & 4 deletions src/components/questions/QuestionVariableSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ export const QuestionVariableSelector = ({questionId, variable}: QuestionVariabl

useEffect(() => {
if (options && bindings) {
let curValue : string = bindings[variable.id];
if (options.length === 0 || !curValue || !options.some(o => o.value === curValue)) {
let curValues : string[] = bindings[variable.id];
if (options.length === 0 || !curValues || curValues.length === 0) {
setSelectedOption(null);
setSelectedOptionLabel("");
} else if (!options.some(opt => curValues.some(val => val === opt.value)) ) {
//TODO: cuValues exists but its value is not on the available options.
setSelectedOption(null);
setSelectedOptionLabel(curValues.join(','));
} else {
let selectedOption : VariableOption = options.filter(o => o.value === curValue)[0];
//TODO: All of the multiple option stuff is WIP, lets assume we only have one value.
let selectedOption : VariableOption = options.filter(o => o.value === curValues[0])[0];
setSelectedOption(selectedOption);
setSelectedOptionLabel(selectedOption.label);
}
Expand All @@ -45,7 +50,7 @@ export const QuestionVariableSelector = ({questionId, variable}: QuestionVariabl
function onOptionChange(value: VariableOption|null): void {
let newBindings = { ...bindings };
if (value) {
newBindings[variable.id] = value.value;
newBindings[variable.id] = [value.value];
} else if (bindings[variable.id]) {
delete newBindings[variable.id];
}
Expand Down
6 changes: 3 additions & 3 deletions src/components/questions/TimeIntervalVariable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const TimeIntervalVariable = ({variable}: TimeIntervalVariableProps) => {
const changeTimeBinding = (timeUri:string, time:number|null) => {
let newBindings = { ...bindings };
if (time) {
newBindings[timeUri] = time.toString();
newBindings[TimeTypeURI] = CETime ? CETimeURI : BPTimeURI;
newBindings[timeUri] = [time.toString()];
newBindings[TimeTypeURI] = [CETime ? CETimeURI : BPTimeURI];
} else {
delete newBindings[timeUri];
delete newBindings[TimeTypeURI];
Expand Down Expand Up @@ -73,7 +73,7 @@ export const TimeIntervalVariable = ({variable}: TimeIntervalVariableProps) => {

const onTimeToggle = () => {
let newBindings = { ...bindings };
newBindings[TimeTypeURI] = !CETime ? CETimeURI : BPTimeURI;
newBindings[TimeTypeURI] = [!CETime ? CETimeURI : BPTimeURI];
setCETime(!CETime);
dispatch(setQuestionBindings(newBindings));
}
Expand Down
6 changes: 5 additions & 1 deletion src/constants/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ export const PATH_TLOI_ID = '/tlois/:tloiId';
export const PATH_TLOI_ID_RE = new RegExp(/\/tlois\/([\w-]{8,36})$/);

export const PATH_TERMINOLOGY = '/terminology';
export const PATH_DATA = '/data';
export const PATH_DATA = '/data';

export type UI_PARAMS = {
goalId: string;
}
42 changes: 21 additions & 21 deletions src/pages/Goals/GoalEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom'
import CancelIcon from '@mui/icons-material/Cancel';
import SaveIcon from '@mui/icons-material/Save';
import CopyIcon from '@mui/icons-material/ContentCopy';
import { PATH_GOALS } from "constants/routes";
import { PATH_GOALS, UI_PARAMS } from "constants/routes";
import { QuestionSelector } from "components/questions/QuestionSelector";
import { useAppDispatch, useQuestionBindings, useSelectedQuestion } from "redux/hooks";
import React from "react";
Expand All @@ -19,13 +19,11 @@ import { TextFieldBlock, TypographySubtitle } from "components/Styles";
export const HypothesisEditor = () => {
const navigate = useNavigate();
const dispatch = useAppDispatch();
const { goalId } = useParams();
const selectedId = goalId as string;
const { goalId } = useParams<UI_PARAMS>();
const [searchParams, _]= useSearchParams();
let initialQuestionId = searchParams.get("q");

// Redux data
const {data :hypothesis, isLoading:loading } = useGetGoalByIdQuery(selectedId, {skip:!selectedId});
const {data :hypothesis, isLoading:loading } = useGetGoalByIdQuery(goalId as string, {skip:!goalId});
const [postHypothesis] = usePostGoalMutation();
const [putHypothesis] = usePutGoalMutation();

Expand Down Expand Up @@ -79,6 +77,16 @@ export const HypothesisEditor = () => {
setQuestionId("");
}

const getQuestionBindings : () => VariableBinding[] = () => {
return Object.keys(formQuestionBindings).map<VariableBinding>((varId: string) => (
{
variable: varId,
binding: formQuestionBindings[varId],
type: "DEFAULT",
isArray: false,
}));
}

const onSaveButtonClicked = () => {
if (!name || !description || !editedQuestionId || !formSelectedQuestion) {
if (!name) setErrorName(true);
Expand All @@ -96,31 +104,28 @@ export const HypothesisEditor = () => {
// Add form data
let newHypothesis : Goal = {
...previous,
id: '',
author: undefined,
id: editing && previous.id ? previous.id : '',
name: name,
description: description,
notes: notes,
question: {
id: editedQuestionId,
},
questionBindings: previous.questionBindings || [],
//questionBindings: Object.keys(formQuestionBindings).map((varId:string) => {
// return {
// variable: varId,
// binding: formQuestionBindings[varId],
// type: null
// } as VariableBinding;
//}),
questionBindings: getQuestionBindings(),
graph: { triples: addBindingsToQuestionGraph(formSelectedQuestion, formQuestionBindings)}
};

dispatch(openBackdrop());
(editing?putHypothesis:postHypothesis)({data:newHypothesis})
.then((data : {data:Goal} | {error: any}) => {
let savedHypothesis = (data as {data:Goal}).data;
.then((response : {data?:Goal, error?:any} ) => {
let savedHypothesis = response.data;
if (savedHypothesis && savedHypothesis.id) {
dispatch(openNotification({severity:'success', text:'Successfully saved'}))
navigate(PATH_GOALS + "/" + savedHypothesis.id.replace(idPattern, ""));
} else if (response.error) {
dispatch(openNotification({severity:'error', text:'Error saving hypothesis. Please try again'}))
console.warn(response.error);
}
})
.catch((e) => {
Expand All @@ -140,11 +145,6 @@ export const HypothesisEditor = () => {
...hypothesis,
id: '',
name: hypothesis.name + " (copy)",
questionBindings: hypothesis.questionBindings.map((qv) => {return {
...qv,
collection: undefined,
bindingAsArray: undefined,
}})
};

dispatch(openBackdrop());
Expand Down
Loading

0 comments on commit 7531301

Please sign in to comment.