feat: cleanup
This commit is contained in:
parent
4fddea47bf
commit
ec9dd815cc
92
README.md
92
README.md
|
@ -1,28 +1,14 @@
|
|||
## About
|
||||
|
||||
This repository creates a react webpage that allows to extract a utility function from possibly inconsistent binary comparisons.
|
||||
|
||||
It presents the users with a series of elements to compare, using merge-sort in the background to cleverly minimize the number of choices needed.
|
||||
This repository displays hierarchical estimates. It presents the user with a tree with nodes which can be clicked on and inspected, which visualizes the underlying squiggle calculation.
|
||||
|
||||
<p align="center">
|
||||
<img width="50%" height="50%" src="./public/example-prompt.png">
|
||||
<img width="50%" height="50%" src="./public/example-hierarchical.png">
|
||||
</p>
|
||||
|
||||
Then, it cleverly aggregates them, on the one hand by producing a graphical representation:
|
||||
So far, this mostly has visualization capabilities, meaning that edits are impermanent.
|
||||
|
||||
<p align="center">
|
||||
<img width="50%" height="50%" src="./public/example-graph.png">
|
||||
</p>
|
||||
|
||||
and on the other hand doing some fast and clever mean aggregation [^1]:
|
||||
|
||||
<p align="center">
|
||||
<img width="50%" height="50%" src="./public/example-table.png">
|
||||
</p>
|
||||
|
||||
Initially, users could only input numbers, e.g., "A is `3` times better than B". But now, users can also input distributions, using the [squiggle](https://www.squiggle-language.com/) syntax, e.g., "A is `1 to 10` times better than B", or "A is `mm(normal(1, 10), uniform(0,100))` better than B".
|
||||
|
||||
**If you want to use the utility function extractor for a project, we are happy to add a page for your project, like `utility-function-extractor.quantifieduncertainty.org/your-project`**.
|
||||
**If you want to use the utility function extractor for a project, we are happy to add a page for your project, like `hierarchical-visualization.quantifieduncertainty.org/your-project`**.
|
||||
|
||||
## Built with
|
||||
|
||||
|
@ -30,56 +16,10 @@ Initially, users could only input numbers, e.g., "A is `3` times better than B".
|
|||
- [Netlify](https://github.com/netlify/netlify-plugin-nextjs/#readme)
|
||||
- [React](https://reactjs.org/)
|
||||
- [Squiggle](https://www.squiggle-language.com/)
|
||||
- [Utility tools](https://github.com/quantified-uncertainty/utility-function-extractor/tree/master/packages/utility-tools)
|
||||
|
||||
## Usage
|
||||
|
||||
Navigate to [utility-function-extractor.quantifieduncertainty.org/](https://utility-function-extractor.quantifieduncertainty.org/), and start comparing objects.
|
||||
|
||||
You can change the list of objects to be compared by clicking on "advanced options".
|
||||
|
||||
After comparing objects for a while, you will get a table and a graph with results. You can also use the [utility tools](https://github.com/quantified-uncertainty/utility-function-extractor/tree/master/packages/utility-tools) package to process these results, for which you will need the json of comparisons, which can be found in "Advanced options" -> "Load comparisons"
|
||||
|
||||
## Notes
|
||||
|
||||
The core structure is json array of objects. Only the "name" attribute is required. If there is a "url", it is displayed nicely.
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"name": "Peter Parker",
|
||||
"someOptionalKey": "...",
|
||||
"anotherMoreOptionalKey": "...",
|
||||
},
|
||||
{
|
||||
"name": "Spiderman",
|
||||
"someOptionalKey": "...",
|
||||
"anotherMoreOptionalKey": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
The core structure for links is as follows:
|
||||
|
||||
```
|
||||
[
|
||||
{
|
||||
"source": "Peter Parker",
|
||||
"target": "Spiderman",
|
||||
"squiggleString": "1 to 100",
|
||||
"distance": 26.639800977355474
|
||||
},
|
||||
{
|
||||
"source": "Spiderman",
|
||||
"target": "Jonah Jameson",
|
||||
"squiggleString": "20 to 2000",
|
||||
"distance": 6.76997149080232
|
||||
},
|
||||
|
||||
]
|
||||
```
|
||||
|
||||
A previous version of this webpage had a more complicated structure, but it has since been simplified.
|
||||
Navigate to [hierarchical-visualization.quantifieduncertainty.org/](https://utility-function-extractor.quantifieduncertainty.org/) (to do: point to subdomain), and start visualizing.
|
||||
|
||||
## Contributions and help
|
||||
|
||||
|
@ -91,24 +31,4 @@ Distributed under the MIT License. See LICENSE.txt for more information.
|
|||
|
||||
## To do
|
||||
|
||||
- [x] Extract merge, findPath and aggregatePath functionality into different repos
|
||||
- [x] Send to mongo upon completion
|
||||
- [x] Push to github
|
||||
- [x] Push to netlify
|
||||
- [x] Don't allow further comparisons after completion
|
||||
- [x] Paths table
|
||||
- [x] Add paths table
|
||||
- [x] warn that the paths table is approximate.
|
||||
- I really don't feel like re-adding this after having worked out the distribution rather than the mean aggregation
|
||||
- On the other hand, I think it does make it more user to other users.
|
||||
- [x] Change README.
|
||||
- [ ] Add functionality like names, etc.
|
||||
- I also don't feel like doing this
|
||||
- [ ] Look back at Amazon thing which has been running
|
||||
- [ ] Simplify Graph and DynamicSquiggleChart components
|
||||
- [ ] Add squiggle component to initial comparison?
|
||||
- [ ] Understand why the rewrite doesn't
|
||||
- Maybe: When two elements are judged to be roughly equal
|
||||
- Maybe: Slightly different merge-sort algorithm.
|
||||
|
||||
[^1]: The program takes each element as a reference point in turn, and computing the possible distances from that reference point to all other points, and taking the geometric mean of these distances. This produces a number representing the value of each element, such that the ratios between elements represent the user's preferences: a utility function. However, this isn't perfect; the principled approach woud be to aggregate the distributions rather than their means. But this principled approach is much more slowly. For the principled approach, see the `utility-tools` repository.
|
||||
- [ ] Allow for edits.
|
|
@ -1,85 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import { ShowComparisons } from "./showComparisons.js";
|
||||
import { ComparisonsChanger } from "./comparisonsChanger.js";
|
||||
import { DataSetChanger } from "./datasetChanger.js";
|
||||
import { setRevalidateHeaders } from "next/dist/server/send-payload/revalidate-headers.js";
|
||||
|
||||
const effectButtonStyle =
|
||||
"bg-transparent m-2 hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5";
|
||||
|
||||
export function AdvancedOptions({
|
||||
links,
|
||||
setLinks,
|
||||
listOfElements,
|
||||
moveToNextStep,
|
||||
onChangeOfDataset,
|
||||
}) {
|
||||
const [showAdvancedOptions, changeShowAdvanceOptions] = useState(false);
|
||||
|
||||
const [showComparisons, setShowComparisons] = useState(false);
|
||||
const toggleShowComparisons = () => setShowComparisons(!showComparisons);
|
||||
|
||||
const [showLoadComparisons, setShowLoadComparisons] = useState(false);
|
||||
const toggleShowLoadComparisons = () =>
|
||||
setShowLoadComparisons(!showLoadComparisons);
|
||||
|
||||
const [showChangeDataset, setShowChangeDataset] = useState(false);
|
||||
const toggleShowChangeDataset = () =>
|
||||
setShowChangeDataset(!showChangeDataset);
|
||||
|
||||
const buttonNames = [
|
||||
// "Show Comparisons",
|
||||
"Load comparisons",
|
||||
"Use your own data",
|
||||
];
|
||||
const buttonToggles = [
|
||||
// toggleShowComparisons,
|
||||
toggleShowLoadComparisons,
|
||||
toggleShowChangeDataset,
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
{/* Show advanced options*/}
|
||||
<button
|
||||
key={"advancedOptionsButton-top"}
|
||||
className="text-gray-500 text-sm "
|
||||
onClick={() => changeShowAdvanceOptions(!showAdvancedOptions)}
|
||||
>
|
||||
Advanced options ▼
|
||||
</button>
|
||||
{/* Toggle buttons */}
|
||||
<div className={showAdvancedOptions ? "" : "hidden"}>
|
||||
{buttonNames.map((buttonName, i) => {
|
||||
return (
|
||||
<button
|
||||
className={effectButtonStyle}
|
||||
onClick={() => buttonToggles[i]()}
|
||||
key={`advancedOptionsButton-${i}`}
|
||||
>
|
||||
{buttonName}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{/* Element: Show comparisons */}
|
||||
{/* <ShowComparisons links={links} show={showComparisons} /> */}
|
||||
|
||||
{/* Element: Change comparisons */}
|
||||
<ComparisonsChanger
|
||||
setLinks={setLinks}
|
||||
listOfElements={listOfElements}
|
||||
show={showLoadComparisons}
|
||||
moveToNextStep={moveToNextStep}
|
||||
links={links}
|
||||
/>
|
||||
|
||||
{/* Element: Dataset changer */}
|
||||
<DataSetChanger
|
||||
onChangeOfDataset={onChangeOfDataset}
|
||||
show={showChangeDataset}
|
||||
listOfElements={listOfElements}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Separator } from "../separator.js";
|
||||
|
||||
const checkLinksAreOk = (links, listOfElements) => {
|
||||
let linkSourceNames = links.map((link) => link.source.name);
|
||||
let linkTargetNames = links.map((link) => link.target.name);
|
||||
let allLinkNames = [...linkSourceNames, ...linkTargetNames];
|
||||
let uniqueNames = [...new Set(allLinkNames)];
|
||||
|
||||
let listOfElementNames = listOfElements.map((element) => element.name);
|
||||
let anyInvalidNames = uniqueNames.indexOf(
|
||||
(name) => !listOfElementNames.includes(name)
|
||||
);
|
||||
let anyElementsWithoutDistances = links.indexOf(
|
||||
(link) => !link.distance && link.distance != 0
|
||||
);
|
||||
if (anyInvalidNames == -1 && anyElementsWithoutDistances == -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export function ComparisonsChanger({
|
||||
setLinks,
|
||||
listOfElements,
|
||||
show,
|
||||
moveToNextStep,
|
||||
links,
|
||||
}) {
|
||||
let [value, setValue] = useState(JSON.stringify(links, null, 4));
|
||||
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false);
|
||||
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] =
|
||||
useState(null);
|
||||
|
||||
let handleTextChange = (event) => {
|
||||
setValue(event.target.value);
|
||||
};
|
||||
|
||||
let handleSubmitInner = (event) => {
|
||||
clearTimeout(displayingDoneMessageTimer);
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
let newData = JSON.parse(value);
|
||||
|
||||
if (checkLinksAreOk(newData, listOfElements)) {
|
||||
setLinks(newData);
|
||||
moveToNextStep({
|
||||
listOfElements,
|
||||
whileChangingStuff: true,
|
||||
newLinksFromChangingStuff: newData,
|
||||
});
|
||||
setDisplayingDoneMessage(true);
|
||||
let timer = setTimeout(() => setDisplayingDoneMessage(false), 3000);
|
||||
setDisplayingDoneMessageTimer(timer);
|
||||
} else {
|
||||
throw Error("Links are not ok");
|
||||
}
|
||||
} catch (error) {
|
||||
setDisplayingDoneMessage(false);
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setValue(JSON.stringify(links, null, 4));
|
||||
}, [links]);
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmitInner}
|
||||
className={`inline ${show ? "" : "hidden"}`}
|
||||
>
|
||||
<Separator />
|
||||
<h3 className="text-lg mt-8">Load comparisons</h3>
|
||||
<p>These can be edited, which will override your current comparisons.</p>
|
||||
<br />
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={handleTextChange}
|
||||
rows={4 + JSON.stringify(links, null, 4).split("\n").length}
|
||||
cols={70}
|
||||
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
||||
/>
|
||||
<br />
|
||||
<button
|
||||
className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5 p-10"
|
||||
onClick={handleSubmitInner}
|
||||
>
|
||||
Change comparisons
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={
|
||||
displayingDoneMessage
|
||||
? "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5 p-10"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
Done!
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
import React, { useState } from "react";
|
||||
import { Separator } from "../separator.js";
|
||||
|
||||
export function DataSetChanger({ onChangeOfDataset, show, listOfElements }) {
|
||||
/*let [value, setValue] = useState(`[
|
||||
{
|
||||
"name": "Some element. The name field is necessary",
|
||||
"url": "http://www.example.com",
|
||||
"somethirdfield": "a"
|
||||
},
|
||||
{
|
||||
"name": "Another element",
|
||||
"url": "http://www.example1.com",
|
||||
"somethirdfield": "b"
|
||||
},
|
||||
{
|
||||
"name": "A third element",
|
||||
"url": "http://www.example2.com",
|
||||
"isReferenceValue": true,
|
||||
"somethirdfield": "c"
|
||||
}
|
||||
]`);*/
|
||||
let [value, setValue] = useState(JSON.stringify(listOfElements, null, 4));
|
||||
|
||||
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false);
|
||||
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] =
|
||||
useState(null);
|
||||
|
||||
let handleChange = (event) => {
|
||||
setValue(event.target.value);
|
||||
};
|
||||
|
||||
let handleSubmitInner = (event) => {
|
||||
clearTimeout(displayingDoneMessageTimer);
|
||||
event.preventDefault();
|
||||
console.log("value@handleSubmitInner@DataSetChanger");
|
||||
console.log(value);
|
||||
try {
|
||||
let newData = JSON.parse(value);
|
||||
if (!newData.length || newData.length < 2) {
|
||||
throw Error("Not enough objects");
|
||||
}
|
||||
onChangeOfDataset(newData);
|
||||
setDisplayingDoneMessage(true);
|
||||
let timer = setTimeout(() => setDisplayingDoneMessage(false), 3000);
|
||||
setDisplayingDoneMessageTimer(timer);
|
||||
} catch (error) {
|
||||
setDisplayingDoneMessage(false);
|
||||
alert(error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={`${show ? "" : "hidden"} `}>
|
||||
<Separator />
|
||||
<form onSubmit={handleSubmitInner} className="inline mt-0">
|
||||
<h3 className="text-lg mt-8 mb-4">Change dataset</h3>
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
rows={
|
||||
1.2 * JSON.stringify(listOfElements, null, 4).split("\n").length
|
||||
}
|
||||
cols={70}
|
||||
className="text-left text-gray-600 bg-white rounded text-normal p-10 border-0 shadow outline-none focus:outline-none focus:ring "
|
||||
/>
|
||||
<br />
|
||||
<button
|
||||
className="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5 p-10"
|
||||
onClick={handleSubmitInner}
|
||||
>
|
||||
Change dataset
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={
|
||||
displayingDoneMessage
|
||||
? "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5 p-10"
|
||||
: "hidden"
|
||||
}
|
||||
>
|
||||
Done!
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import React, { state } from "react";
|
||||
import { CopyBlock, googlecode } from "react-code-blocks";
|
||||
import { Separator } from "../separator.js";
|
||||
|
||||
export function ShowComparisons({ links, show }) {
|
||||
return (
|
||||
<div className={`text-left ${show ? "" : "hidden"}`}>
|
||||
<Separator />
|
||||
<h3 className="text-lg mt-8">Comparisons</h3>
|
||||
<CopyBlock
|
||||
text={JSON.stringify(links, null, 4)}
|
||||
language={"js"}
|
||||
showLineNumbers={false}
|
||||
startingLineNumber={0}
|
||||
wrapLines={true}
|
||||
textColor={"black"}
|
||||
theme={googlecode}
|
||||
codeBlock={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
// import { SquiggleChart } from "@quri/squiggle-components";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
|
@ -10,20 +10,32 @@ const SquiggleChart = dynamic(
|
|||
ssr: false,
|
||||
}
|
||||
);
|
||||
/*
|
||||
const SquiggleChart = dynamic(
|
||||
() => import("@quri/squiggle-components").then((mod) => mod.SquiggleChart),
|
||||
|
||||
const SquiggleEditor = dynamic(
|
||||
() => import("@quri/squiggle-components").then((mod) => mod.SquiggleEditor),
|
||||
{
|
||||
suspense: true,
|
||||
loading: () => <p>Loading...</p>,
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
*/
|
||||
// ^ works, but updating the editor content from the outside would be tricky.
|
||||
// and so instead we are hacking our own mini-editor.
|
||||
|
||||
const effectButtonStyle =
|
||||
"bg-transparent m-2 hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded mt-5";
|
||||
|
||||
const countNumberOfLines = string => {
|
||||
return string.split("\n").length
|
||||
}
|
||||
|
||||
export function DynamicSquiggleChart({ element, stopShowing }) {
|
||||
const initialEditorState = !!element ? element.fn : ""
|
||||
const [editorState, setEditorState] = useState(initialEditorState)
|
||||
useEffect(() => {
|
||||
if (!!element && element.fn != "") {
|
||||
setEditorState(element.fn)
|
||||
}
|
||||
}, [element]);
|
||||
if (element == null) {
|
||||
return "";
|
||||
} else {
|
||||
|
@ -32,43 +44,28 @@ export function DynamicSquiggleChart({ element, stopShowing }) {
|
|||
squiggleString: element.fn,
|
||||
binding: element.binding || null
|
||||
};
|
||||
console.log(element.binding)
|
||||
// alert(usefulElement.squiggleString)
|
||||
return (
|
||||
<div className="">
|
||||
<h3 className="text-2xl font-bold mb-5">{usefulElement.name}</h3>
|
||||
<div className="bg-white p-8">
|
||||
<h3 className="text-2xl font-bold mb-4">{usefulElement.name}</h3>
|
||||
<textarea
|
||||
value={usefulElement.squiggleString}
|
||||
//onChange={handleChange}
|
||||
disabled={true}
|
||||
rows={5} // could compute from usefulElement.squiggleString
|
||||
value={editorState}
|
||||
onChange={(event) => setEditorState(event.target.value)}
|
||||
// disabled={true}
|
||||
rows={countNumberOfLines(editorState) + 1}
|
||||
cols={30}
|
||||
className="text-left text-gray-600 bg-white rounded text-normal p-8 m-8 border-0 shadow outline-none focus:outline-none focus:ring"
|
||||
className="text-left text-gray-600 bg-white rounded text-normal p-5 border-0 shadow outline-none focus:outline-none focus:ring"
|
||||
/>
|
||||
<SquiggleChart
|
||||
squiggleString={usefulElement.squiggleString}
|
||||
squiggleString={editorState}
|
||||
width={500}
|
||||
height={200}
|
||||
bindings={usefulElement.binding}
|
||||
showSummary={true}
|
||||
showTypes={true}
|
||||
/>
|
||||
{/*
|
||||
SquiggleChart props:
|
||||
squiggleString?: string;
|
||||
sampleCount?: number;
|
||||
environment?: environment;
|
||||
chartSettings?: FunctionChartSettings;
|
||||
onChange?(expr: squiggleExpression): void;
|
||||
width?: number;
|
||||
height?: number;
|
||||
bindings?: bindings;
|
||||
jsImports?: jsImports;
|
||||
showSummary?: boolean;
|
||||
showTypes?: boolean;
|
||||
showControls?: boolean;
|
||||
*/}
|
||||
|
||||
</div>
|
||||
<button className={effectButtonStyle} onClick={() => stopShowing()}>
|
||||
Hide chart
|
||||
</button>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { run, runPartial, mergeBindings } from "@quri/squiggle-lang";
|
||||
|
||||
|
||||
function miniReducer(obj, parentName) {
|
||||
let nodes = []
|
||||
let edges = []
|
||||
|
@ -93,7 +92,6 @@ ${parentName}`;
|
|||
|
||||
let parentValue = run(parentResultSquiggleString, mergeBindings(bindings));
|
||||
if (parentValue.tag == "Error") {
|
||||
// console.log(bindings)
|
||||
return parentValue
|
||||
}
|
||||
|
||||
|
@ -106,7 +104,6 @@ ${parentName}`;
|
|||
})
|
||||
|
||||
nodes.push(resultNode)
|
||||
// console.log("resultNode", resultNode)
|
||||
let result = {
|
||||
...resultNode,
|
||||
value: parentValue,
|
||||
|
@ -139,20 +136,10 @@ mean(r)`,
|
|||
if (reducerResult.tag == "Error") {
|
||||
return reducerResult
|
||||
} else {
|
||||
// console.log("reducerResult", reducerResult)
|
||||
let {nodes, edges} = reducerResult
|
||||
let nodeElements = nodes.map(node => ({ data: { ...node, name: node.id} }))
|
||||
let edgeElements = edges.map(edge => ({ data: { ...edge, name: edge.id } }))
|
||||
let answer = { nodeElements, edgeElements }
|
||||
return answer
|
||||
}
|
||||
|
||||
// return { nodeElements: [], edgeElements: [] }
|
||||
/*
|
||||
|
||||
*/
|
||||
// return (resultUtility)
|
||||
|
||||
// Then the rest should be doable without all that much work.
|
||||
// Some duplication of efforts, but I don't really care:
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@ import { createGraph } from "./createGraph"
|
|||
|
||||
export function Graph({ }) {
|
||||
const containerRef = useRef("hello-world");
|
||||
const [cs, setCs] = useState(null); /// useState("invisible");
|
||||
const [cs, setCs] = useState(null);
|
||||
const [selectedElement, setSelectedElement] = useState(null);
|
||||
const [selectedElementTimeout, setSelectedElementTimeout] = useState(null);
|
||||
//let { nodeElements, edgeElements } = createGraph()
|
||||
const graphInit = createGraph()
|
||||
const [nodeElements, setNodeElements] = useState(graphInit.nodeElements)
|
||||
const [edgeElements, setEdgeElements] = useState(graphInit.edgeElements)
|
||||
|
@ -104,7 +103,6 @@ export function Graph({ }) {
|
|||
// necessary for themes like spread, which have
|
||||
// a confusing animation at the beginning
|
||||
|
||||
|
||||
};
|
||||
useEffect(() => {
|
||||
callEffect({
|
||||
|
@ -128,18 +126,17 @@ export function Graph({ }) {
|
|||
cs.nodes().on("click", (event) => {
|
||||
clearTimeout(selectedElementTimeout);
|
||||
let node = event.target;
|
||||
console.log(JSON.stringify(node.json()));
|
||||
// console.log(JSON.stringify(node.json()));
|
||||
let newTimeout = setTimeout(() => {
|
||||
let selectedElementIncomplete = (JSON.parse(JSON.stringify(node.json())).data)
|
||||
let selectedElementName = selectedElementIncomplete.name
|
||||
let selectedElementInFull = nodeElements.filter(node => node.data.name == selectedElementName)
|
||||
console.log("selectedElementInFull", selectedElementInFull)
|
||||
// console.log("selectedElementInFull", selectedElementInFull)
|
||||
if(selectedElementInFull.length == 1){
|
||||
let elementToBeSelected = selectedElementInFull[0]
|
||||
console.log("elementToBeSelected", elementToBeSelected)
|
||||
// console.log("elementToBeSelected", elementToBeSelected)
|
||||
setSelectedElement(elementToBeSelected.data)
|
||||
}
|
||||
// setSelectedElement()
|
||||
});
|
||||
setSelectedElementTimeout(newTimeout)
|
||||
});
|
||||
|
@ -148,31 +145,6 @@ export function Graph({ }) {
|
|||
}
|
||||
}, [cs]);
|
||||
|
||||
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (cs != null) {
|
||||
clearTimeout(selectedElementTimeout);
|
||||
let newTimeout = setTimeout(() => {
|
||||
cs.edges().on("mouseover", (event) => {
|
||||
// on("click",
|
||||
let edge = event.target;
|
||||
// alert(JSON.stringify(edge.json()));
|
||||
console.log(JSON.stringify(edge.json()));
|
||||
setSelectedElement(JSON.parse(JSON.stringify(edge.json())).data);
|
||||
});
|
||||
cs.nodes().on("mouseover", (event) => {
|
||||
// on("click",
|
||||
let node = event.target;
|
||||
// alert(JSON.stringify(edge.json()));
|
||||
console.log(JSON.stringify(node.json()));
|
||||
setSelectedElement(JSON.parse(JSON.stringify(node.json())).data);
|
||||
});
|
||||
}, 100);
|
||||
setSelectedElementTimeout(newTimeout);
|
||||
}
|
||||
}, [cs]);
|
||||
*/
|
||||
return (
|
||||
<div className="grid place-items-center">
|
||||
<div
|
||||
|
@ -185,8 +157,8 @@ export function Graph({ }) {
|
|||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
height: "900px", // isListOrdered ? "900px" : "500px",
|
||||
width: "900px", // isListOrdered ? "900px" : "500px",
|
||||
height: "900px",
|
||||
width: "900px",
|
||||
}}
|
||||
className=""
|
||||
/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
export function Title() {
|
||||
return <h1 className="text-6xl font-bold ">Hierarchical Estimates Visualizer</h1>;
|
||||
return <h1 className="text-6xl font-bold mb-8">Hierarchical Estimates Visualizer</h1>;
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import axios from "axios";
|
||||
|
||||
const CONNECTION_IS_ACTIVE = true;
|
||||
|
||||
export async function pushToMongo(data) {
|
||||
if (CONNECTION_IS_ACTIVE) {
|
||||
let response = await axios.post(
|
||||
"https://server.loki.red/utility-function-extractor",
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
);
|
||||
console.log(response);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import { run } from "@quri/squiggle-lang";
|
||||
|
||||
export async function resolveToNumIfPossible(comparisonString) {
|
||||
if (!isNaN(comparisonString) && comparisonString != "") {
|
||||
let response = {
|
||||
asNum: true,
|
||||
num: Number(comparisonString),
|
||||
};
|
||||
return response;
|
||||
}
|
||||
let squiggleMeanCommand = `mean(${comparisonString})`;
|
||||
let squiggleResponse = await run(squiggleMeanCommand);
|
||||
console.log(squiggleResponse);
|
||||
if (squiggleResponse.tag == "Ok") {
|
||||
let responseAsNumber = squiggleResponse.value.value;
|
||||
let response = {
|
||||
asNum: true,
|
||||
num: Number(responseAsNumber),
|
||||
};
|
||||
return response;
|
||||
} else {
|
||||
let errorMsg = squiggleResponse.value;
|
||||
let response = {
|
||||
asNum: false,
|
||||
errorMsg: errorMsg,
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSquiggleSparkline(comparisonString) {
|
||||
if (!isNaN(comparisonString) && comparisonString != "") {
|
||||
let response = {
|
||||
success: true,
|
||||
sparkline: comparisonString,
|
||||
};
|
||||
return response;
|
||||
}
|
||||
let squiggleSparklineCommand = `sparkline(${comparisonString}, 20)`;
|
||||
let squiggleResponse = await run(squiggleSparklineCommand);
|
||||
console.log(squiggleResponse);
|
||||
if (squiggleResponse.tag == "Ok") {
|
||||
let responseAsNumber = squiggleResponse.value.value;
|
||||
let response = {
|
||||
success: true,
|
||||
sparkline: responseAsNumber,
|
||||
};
|
||||
return response;
|
||||
} else {
|
||||
let errorMsg = squiggleResponse.value;
|
||||
let response = {
|
||||
success: false,
|
||||
errorMsg: errorMsg,
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export const cutOffLongNames = (string) => {
|
||||
let maxLength = 40;
|
||||
let result;
|
||||
if (string.length < maxLength) {
|
||||
result = string;
|
||||
} else {
|
||||
result = string.slice(0, maxLength - 4);
|
||||
result = result + "...";
|
||||
}
|
||||
return result;
|
||||
};
|
|
@ -1,39 +0,0 @@
|
|||
let topOutAt100AndValidate = (x) => {
|
||||
if (x == x) {
|
||||
return x > 99 ? 99 : x < 0 ? 2 : x;
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
};
|
||||
|
||||
export const toLocale = (x) => Number(x).toLocaleString();
|
||||
|
||||
export const truncateValueForDisplay = (value) => {
|
||||
let result;
|
||||
if (value > 10) {
|
||||
result = Number(Math.round(value).toPrecision(2));
|
||||
} else if (value > 1) {
|
||||
result = Math.round(value * 10) / 10;
|
||||
} else if (value > 0) {
|
||||
let candidateNumSignificantDigits =
|
||||
-Math.floor(Math.log(value) / Math.log(10)) + 1;
|
||||
let numSignificantDigits = topOutAt100AndValidate(
|
||||
candidateNumSignificantDigits
|
||||
);
|
||||
result = value.toFixed(numSignificantDigits);
|
||||
} else if (value == 0) {
|
||||
return 0;
|
||||
} else if (-1 < value) {
|
||||
let candidateNumSignificantDigits =
|
||||
-Math.floor(Math.log(Math.abs(value)) / Math.log(10)) + 1;
|
||||
let numSignificantDigits = topOutAt100AndValidate(
|
||||
candidateNumSignificantDigits
|
||||
);
|
||||
result = value.toFixed(numSignificantDigits);
|
||||
} else if (value <= -1) {
|
||||
result = "-" + toLocale(truncateValueForDisplay(-value));
|
||||
} else {
|
||||
result = toLocale(value); //return "~0"
|
||||
}
|
||||
return result;
|
||||
};
|
Binary file not shown.
Before Width: | Height: | Size: 231 KiB |
Binary file not shown.
Before Width: | Height: | Size: 182 KiB |
Binary file not shown.
Before Width: | Height: | Size: 243 KiB |
BIN
public/example-hierarchical.png
Normal file
BIN
public/example-hierarchical.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 145 KiB |
Loading…
Reference in New Issue
Block a user