chore: Switch formatter.

This commit is contained in:
NunoSempere 2022-01-30 10:29:45 -05:00
parent bd5bdb9cd0
commit b86deafefd
11 changed files with 549 additions and 1066 deletions

583
lib/\
View File

@ -1,583 +0,0 @@
/* Imports */
import Head from "next/head";
import React, { useState } from "react";
import { DrawGraph, removeOldSvg } from "./labeledGraph";
import { SubmitSliderButton } from "./slider";
import { DisplayElement } from "./displayElement";
import { DisplayAsMarkdown } from "./displayAsMarkdown";
import { CreateTable, buildRows } from "./findPaths";
import { DataSetChanger } from "./datasetChanger";
import { ComparisonsChanger } from "./comparisonsChanger";
import { pushToMongo } from "./pushToMongo.js";
import {
increasingList,
maxMergeSortSteps,
expectedNumMergeSortSteps,
conservativeNumMergeSortSteps,
} from "./utils.js";
/* DEFINTIONS */
const DEFAULT_COMPARE = () => 1; // 1/Math.random() - 1
/* Misc. helpers */
let buildLinks = (quantitativeComparisons) =>
quantitativeComparisons.map(([element1, element2, distance, reasoning]) => ({
source: element1,
target: element2,
distance: distance,
reasoning: reasoning,
}));
Array.prototype.containsArray = function (val) {
var hash = {};
for (var i = 0; i < this.length; i++) {
hash[this[i]] = i;
}
return hash.hasOwnProperty(val);
};
let checkIfListIsOrdered = (arr, binaryComparisons) => {
let l = arr.length;
let isOrdered = true;
for (let i = 0; i < l - 1; i++) {
isOrdered =
isOrdered && binaryComparisons.containsArray([arr[i], arr[i + 1]]);
}
return isOrdered;
};
let nicelyFormatLinks = (quantitativeComparisons, listOfElements) =>
quantitativeComparisons.map(([element1, element2, distance, reasoning]) => ({
source: listOfElements[element1].name,
target: listOfElements[element2].name,
distance: distance,
reasoning: reasoning,
}));
// Main react component
export default function ComparisonView({ listOfElementsForView }) {
/* State */
// initial state values
let initialListOfElements = listOfElementsForView.map((element, i) => ({
...element,
id: i,
}));
let initialPosList = increasingList(listOfElementsForView.length); // [0,1,2,3,4]
let initialComparePair = [
initialPosList[initialPosList.length - 2],
initialPosList[initialPosList.length - 1],
];
let initialSliderValue = 1;
let initialReasoning = "";
let initialBinaryComparisons = [];
let initialQuantitativeComparisons = [];
let initialIsListOdered = false;
let initialOrderedList = [];
let initialShowAdvancedOptions = false;
let initialShowComparisons = false;
let initialShowLoadComparisons = false;
let initialShowChangeDataSet = false;
let initialNumSteps = 0;
let initialMaxSteps = maxMergeSortSteps(listOfElementsForView.length);
let initialExpectedSteps = expectedNumMergeSortSteps(
listOfElementsForView.length
);
let initialTableRows = [];
let initialDontPushSubmitButtonAnyMore = false;
// state variables and functions
const [listOfElements, setListOfElements] = useState(initialListOfElements);
const [posList, setPosList] = useState(initialPosList);
const [toComparePair, setToComparePair] = useState(initialComparePair);
const [sliderValue, setSliderValue] = useState(initialSliderValue);
const [reasoning, setReasoning] = useState(initialReasoning);
const [binaryComparisons, setBinaryComparisons] = useState(
initialBinaryComparisons
);
const [quantitativeComparisons, setQuantitativeComparisons] = useState(
initialQuantitativeComparisons
); // More expressive, but more laborious to search through. For the ordering step, I only manipulate the binaryComparisons.
const [isListOrdered, setIsListOrdered] = useState(initialIsListOdered);
const [orderedList, setOrderedList] = useState(initialOrderedList);
const [dontPushSubmitButtonAnyMore, setDontPushSubmitButtonAnyMore] =
useState(initialDontPushSubmitButtonAnyMore);
let [showAdvancedOptions, changeShowAdvanceOptions] = useState(
initialShowAdvancedOptions
);
let [showComparisons, changeShowComparisons] = useState(
initialShowComparisons
);
let [showLoadComparisons, changeShowLoadComparisons] = useState(
initialShowLoadComparisons
);
let [showChangeDataSet, changeshowChangeDataSet] = useState(
initialShowChangeDataSet
);
let [numSteps, changeNumSteps] = useState(initialNumSteps);
let [maxSteps, changeMaxSteps] = useState(initialMaxSteps);
let [expectedSteps, changeExpectedSteps] = useState(initialExpectedSteps);
let [tableRows, setTableRows] = useState(initialTableRows);
/* Convenience utils: restart + changeDataSet */
let restart = (
posList,
initialBinaryComparisons2,
initialQuantitativeComparisons2
) => {
//({posList, initialBinaryComparisons2, initialQuantitativeComparisons2}) => {
setToComparePair([
posList[posList.length - 2],
posList[posList.length - 1],
]);
setSliderValue(initialSliderValue);
setBinaryComparisons(initialBinaryComparisons2 || initialBinaryComparisons);
setQuantitativeComparisons(
initialQuantitativeComparisons2 || initialQuantitativeComparisons
);
setIsListOrdered(initialIsListOdered);
setOrderedList(initialOrderedList);
changeNumSteps(initialNumSteps);
removeOldSvg();
setTableRows(initialTableRows);
setDontPushSubmitButtonAnyMore(initialDontPushSubmitButtonAnyMore);
};
let changeDataSet = (listOfElementsNew) => {
listOfElementsNew = listOfElementsNew.map((element, i) => ({
...element,
id: i,
}));
let newPosList = increasingList(listOfElementsNew.length);
let newListLength = listOfElementsNew.length;
setListOfElements(listOfElementsNew);
setPosList(increasingList(listOfElementsNew.length));
setToComparePair([
newPosList[newPosList.length - 2],
newPosList[newPosList.length - 1],
]);
changeExpectedSteps(expectedNumMergeSortSteps(newListLength));
changeMaxSteps(maxMergeSortSteps(newListLength));
restart(newPosList);
// restart({posList: newPosList})
};
let changeComparisons = async (links) => {
let quantitativeComparisons2 = [];
let binaryComparisons2 = [];
links.shift();
for (let link of links) {
let { source, target, distance, reasoning } = link;
let searchByName = (name, candidate) => candidate.name == name;
let testForSource = (candidate) => searchByName(source, candidate);
let testForTarget = (candidate) => searchByName(target, candidate);
let element1 = listOfElements.findIndex(testForSource);
let element2 = listOfElements.findIndex(testForTarget);
if (element1 == -1 || element2 == -1) {
console.log("link", link);
console.log(source);
console.log(target);
throw new Error("Comparisons include unknown elements, please retry");
}
quantitativeComparisons2.push([element1, element2, distance, reasoning]);
binaryComparisons2.push([element1, element2]);
}
// return ({quantitativeComparisons: quantitativeComparisons2, binaryComparisons: binaryComparisons2})
//restart({posList, initialBinaryComparisons2=initialBinaryComparisons, initialQuantitativeComparisons2=initialQuantitativeComparisons})
// restart(posList, binaryComparisons2, quantitativeComparisons2)
setQuantitativeComparisons(quantitativeComparisons2);
setBinaryComparisons(binaryComparisons2);
};
// Manipulations
let compareTwoElements = (newBinaryComparisons, element1, element2) => {
let element1Greater = newBinaryComparisons.containsArray([
element1,
element2,
]);
let element2Greater = newBinaryComparisons.containsArray([
element2,
element1,
]);
if (element1Greater || element2Greater) {
return element1Greater && !element2Greater;
} else {
setToComparePair([element1, element2]);
//console.log(`No comparison found between ${element1} and ${element2}`)
//console.log(`Comparisons:`)
//console.log(JSON.stringify(newBinaryComparisons, null, 4));
return "No comparison found";
}
};
function merge(newBinaryComparisons, left, right) {
let sortedArr = []; // the sorted elements will go here
while (left.length && right.length) {
// insert the biggest element to the sortedArr
let comparison = compareTwoElements(
newBinaryComparisons,
left[0],
right[0]
);
if (comparison == "No comparison found") {
return "No comparison found; unable to proceed";
} else if (comparison) {
// left[0] > right[0]
sortedArr.push(left.shift());
} else {
sortedArr.push(right.shift());
}
}
// use spread operator and create a new array, combining the three arrays
return [...sortedArr, ...left, ...right]; // if they don't have the same size, the remaining ones will be greater than the ones before
}
function mergeSort({ array, comparisons }) {
if (array == "No comparison found; unable to proceed") {
return "No comparison found; unable to proceed";
}
const half = array.length / 2;
// the base case is array length <=1
if (array.length <= 1) {
return array;
}
const left = array.slice(0, half); // the first half of the array
const right = array.slice(half, array.length); // Note that splice is destructive.
let orderedFirstHalf = mergeSort({ array: left, comparisons });
let orderedSecondHalf = mergeSort({ array: right, comparisons });
if (
orderedFirstHalf != "No comparison found; unable to proceed" &&
orderedSecondHalf != "No comparison found; unable to proceed"
) {
let result = merge(comparisons, orderedFirstHalf, orderedSecondHalf);
return result;
} else {
return "No comparison found; unable to proceed";
}
}
let nextStepSimple = (posList, binaryComparisons, element1, element2) => {
//console.log("Binary comparisons: ")
//console.log(JSON.stringify(binaryComparisons, null, 4));
let newComparison = [element1, element2]; // [element1, element2]
let newBinaryComparisons = [...binaryComparisons, newComparison];
//console.log("New binaryComparisons: ")
//console.log(JSON.stringify(newBinaryComparisons, null, 4));
setBinaryComparisons(newBinaryComparisons);
let result = mergeSort({
array: posList,
comparisons: newBinaryComparisons,
});
//console.log(result)
if (
result != "No comparison found; unable to proceed" &&
checkIfListIsOrdered(result, newBinaryComparisons)
) {
// console.log(`isListOrdered: ${isListOrdered}`)
console.log("poslist@nextStepSimple");
console.log(posList);
console.log("result@nextStepSimple");
console.log(result);
return [true, result];
} else {
return [false, result];
}
};
let nextStepSlider = async ({
posList,
binaryComparisons,
sliderValue,
reasoning,
element1,
element2,
}) => {
if (!dontPushSubmitButtonAnyMore) {
if (sliderValue < 1 && sliderValue > 0) {
sliderValue = 1 / sliderValue;
[element1, element2] = [element2, element1];
}
console.log(`posList@nextStepSlider:`);
console.log(posList);
let [successStatus, result] = nextStepSimple(
posList,
binaryComparisons,
element1,
element2
);
let newQuantitativeComparison = [
element1,
element2,
sliderValue,
reasoning,
];
let newQuantitativeComparisons = [
...quantitativeComparisons,
newQuantitativeComparison,
];
setQuantitativeComparisons(newQuantitativeComparisons);
setSliderValue(DEFAULT_COMPARE());
setReasoning("");
changeNumSteps(numSteps + 1);
if (successStatus) {
setDontPushSubmitButtonAnyMore(true);
let jsObject = nicelyFormatLinks(
quantitativeComparisons,
listOfElements
);
await pushToMongo(jsObject);
console.log(jsObject);
alert(
"Comparisons completed. Background work might take a while, or straight-out fail"
);
setIsListOrdered(true);
setOrderedList(result);
await buildRows({
isListOrdered: true,
orderedList: result,
listOfElements,
links: buildLinks(newQuantitativeComparisons),
rows: tableRows,
setTableRows,
});
/*
setTimeout(async () => {
// Make sure to do this after the
}, 100);
*/
}
}
};
// Html
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
{/* Webpage name & favicon */}
<div className="mt-20">
<Head>
<title>Utility Function Extractor</title>
<link rel="icon" href="/favicon.ico" />
</Head>
</div>
<main className="flex flex-col items-center w-full flex-1 px-20 text-center">
{/* Heading */}
<h1 className="text-6xl font-bold">Utility Function Extractor</h1>
{/* Approximate progress indicator */}
<p>{`${numSteps} out of ~${expectedSteps} (max ${maxSteps}) comparisons`}</p>
{/* Comparison section */}
<div className={isListOrdered ? "hidden" : ""}>
<div className="flex flex-wrap items-center max-w-4xl sm:w-full mt-10">
{/* Element 1 */}
<div className="flex m-auto border-gray-300 border-4 h-72 w-72 p-5">
<div className="block m-auto text-center">
<DisplayElement
element={listOfElements[toComparePair[0]]}
></DisplayElement>
</div>
</div>
{/* Comparison actuator (text, previously slider) */}
<div className="flex m-auto w-72">
<div className="block m-auto text-center">
<br />
<label>
{`... is `}
<br />
<input
type="number"
className="text-center text-blueGray-600 bg-white rounded text-lg border-0 shadow outline-none focus:outline-none focus:ring w-8/12 h-10 m-2"
value={sliderValue}
onChange={(event) => {
//console.log(event)
//console.log(event.target.value)
setSliderValue(event.target.value);
}}
/>
<br />
{`times as valuable as ...`}
</label>
<br />
<SubmitSliderButton
posList={posList}
binaryComparisons={binaryComparisons}
sliderValue={sliderValue}
reasoning={reasoning}
toComparePair={toComparePair}
nextStepSlider={nextStepSlider}
dontPushSubmitButtonAnyMore={dontPushSubmitButtonAnyMore}
/>
</div>
</div>
{/* Element 2 */}
<div className="flex m-auto border-gray-300 border-4 h-72 w-72 p-5">
<div className="block m-auto text-center">
<DisplayElement
element={listOfElements[toComparePair[1]]}
></DisplayElement>
</div>
</div>
</div>
<br />
<label className="">
Reasoning (optional):
<textarea
className="mt-2 px-3 py-4 placeholder-blueGray-300 text-blueGray-600 relative bg-white bg-white rounded text-base border-0 shadow outline-none focus:outline-none focus:ring w-full"
value={reasoning}
onChange={(event) => setReasoning(event.target.value)}
/>
</label>
<br />
<div></div>
{/* Old slider element (previous actuator)
<div className={`flex row-start-3 row-end-3 col-start-1 col-end-4 md:col-start-3 md:col-end-3 md:row-start-1 md:row-end-1 lg:col-start-3 lg:col-end-3 lg:row-start-1 lg:row-end-1 items-center justify-center mb-4 mt-10 ${isListOrdered ? "hidden" : ""}`}>
<SliderElement
className="flex items-center justify-center"
onChange={(event) => (setSliderValue(event[0]))}
value={sliderValue}
displayFunction={displayFunctionSlider}
domain={domain}
/>
</div>
*/}
</div>
{/* Results section */}
<div className={isListOrdered ? "" : "hidden"}>
{/* Graph */}
<div className="flex items-center text-center ">
<DrawGraph
isListOrdered={isListOrdered}
orderedList={orderedList}
listOfElements={listOfElements}
links={buildLinks(quantitativeComparisons)}
></DrawGraph>
</div>
{/* Comparison table */}
<div className="flex items-center text-center ">
<CreateTable
isListOrdered={isListOrdered}
orderedList={orderedList}
listOfElements={listOfElements}
links={buildLinks(quantitativeComparisons)}
tableRows={tableRows}
setTableRows={setTableRows}
></CreateTable>
</div>
</div>
{/* Convenience functions */}
<div className="w-2/12 flex justify-center mt-10">
<button
className="text-gray-500 text-sm"
onClick={() => changeShowAdvanceOptions(!showAdvancedOptions)}
>
Advanced options ▼
</button>
</div>
<div
className={
showAdvancedOptions
? "flex flex-wrap -mx-4 overflow-hidden"
: "hidden"
}
>
{/* Button: Restart */}
<div className="my-4 px-4 w-1/4 overflow-hidden">
<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"
onClick={() => restart(posList)}
>
{/* onClick={() => restart({posList})}> */}
Restart
</button>
</div>
{/* Button: Show comparisons */}
<div className="my-4 px-4 w-1/4 overflow-hidden">
<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"
onClick={() => changeShowComparisons(!showComparisons)}
>
Show comparisons
</button>
</div>
{/* Button: Load comparisons */}
<div className="my-4 px-4 w-1/4 overflow-hidden">
<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"
onClick={() => changeShowLoadComparisons(!showLoadComparisons)}
>
Load comparisons
</button>
</div>
{/* Button: Change dataset */}
<div className="my-4 px-4 w-1/4 overflow-hidden">
<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"
onClick={() => changeshowChangeDataSet(!showChangeDataSet)}
>
Use your own data
</button>
</div>
</div>
{/* Change dataset section */}
<div className={showChangeDataSet ? "inline mt-5" : "hidden"}>
<DataSetChanger handleSubmit={changeDataSet} />
</div>
{/* Show comparisons section */}
<div className={showComparisons ? "inline mt-5" : "hidden"}>
<div className="text-left">
<DisplayAsMarkdown
markdowntext={`
${
"" /*
## Comparisons
### Binary comparisons
${JSON.stringify(binaryComparisons, null, 4)}
*/
}
### Numerical comparisons
${JSON.stringify(
nicelyFormatLinks(quantitativeComparisons, listOfElements),
null,
4
)}
`}
className={""}
></DisplayAsMarkdown>
</div>
</div>
{/* Load comparisons section */}
<div className={showLoadComparisons ? "inline mt-5" : "hidden"}>
<ComparisonsChanger handleSubmit={changeComparisons} />
{/*<ComparisonsChanger handleSubmit={changeComparisons} />*/}
</div>
</main>
</div>
);
}

View File

@ -1,38 +1,39 @@
import React, { useState } from 'react';
import React, { useState } from "react";
export function ComparisonsChanger({handleSubmit}){
export function ComparisonsChanger({ handleSubmit }) {
// let defaultText=JSON.stringify(nicelyFormatLinks(quantitativeComparisons, listOfElements), null, 4)
let [value, setValue] = useState(``)
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false)
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] = useState(null)
let [value, setValue] = useState(``);
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false);
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] =
useState(null);
let handleChange = (event) => {
setValue(event.target.value)
}
setValue(event.target.value);
};
let handleSubmitInner = (event) => {
clearTimeout(displayingDoneMessageTimer)
clearTimeout(displayingDoneMessageTimer);
event.preventDefault();
//console.log(event)
console.log("value@handleSubmitInner@ComparisonsChanger")
console.log("value@handleSubmitInner@ComparisonsChanger");
//console.log(typeof(value));
console.log(value);
try{
let newData = JSON.parse(value)
try {
let newData = JSON.parse(value);
//console.log(typeof(newData))
//console.log(newData)
handleSubmit(newData)
handleSubmit(newData);
/*
if(!newData.length || newData.length < 2){
throw Error("Not enough objects")
}
*/
setDisplayingDoneMessage(true)
setDisplayingDoneMessage(true);
let timer = setTimeout(() => setDisplayingDoneMessage(false), 3000);
setDisplayingDoneMessageTimer(timer)
}catch(error){
setDisplayingDoneMessage(false)
setDisplayingDoneMessageTimer(timer);
} catch (error) {
setDisplayingDoneMessage(false);
//alert(error)
//console.log(error)
let substituteText = `Error: ${error.message}
@ -59,35 +60,40 @@ Try something like:
}
]
Your old input was: ${value}`
setValue(substituteText)
Your old input was: ${value}`;
setValue(substituteText);
}
}
};
return (
<form onSubmit={handleSubmitInner} className="inline">
<p>Load comparisons in the same style as in "Show comparisons".</p>
<p>These will override your current comparisons.</p>
<br/>
<textarea
value={value}
onChange={handleChange}
rows="10" cols="50"
<br />
<textarea
value={value}
onChange={handleChange}
rows="10"
cols="50"
className=""
/>
<br/>
<button
<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}>
onClick={handleSubmitInner}
>
Change comparisons
</button>
&nbsp;
<button
className={displayingDoneMessage ? "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5 p-10" : "hidden"}
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>
);
}
}

518
lib/d3experiment.js vendored
View File

@ -1,262 +1,280 @@
import React, { useState, useEffect } from "react";
import * as d3 from 'd3';
import * as d3 from "d3";
export function drawChart(height, width){
d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid black")
.append("text")
.attr("fill", "green")
.attr("x", 50)
.attr("y", 50)
.text("Hello D3")
}
export function drawCircles(){
var svg = d3.select("#circles")
.append("svg")
.attr("width", 960)
.attr("height", 500)
.attr("bgcolor", "blue")
var background = svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "blue")
var ball = svg.append("circle")
.attr("r", 73)
.attr("cx", 480)
.attr("cy", 250)
.style("fill", "#ffe41e");
}
export function drawGraph(){
let margin = {top: 20, right: 30, bottom: 20, left: 30};
let width = 900 - margin.left - margin.right;
let height = 600 - margin.top - margin.bottom;
var svg = d3.select("#graph")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let data = ({
"nodes": [
{
"id": 1,
"name": "A"
},
{
"id": 2,
"name": "B"
},
{
"id": 3,
"name": "C"
},
{
"id": 4,
"name": "D"
},
{
"id": 5,
"name": "E"
},
{
"id": 6,
"name": "F"
},
{
"id": 7,
"name": "G"
},
{
"id": 8,
"name": "H"
},
{
"id": 9,
"name": "I"
},
{
"id": 10,
"name": "J"
}
],
"links": [
{
"source": 1,
"target": 2
},
{
"source": 1,
"target": 5
},
{
"source": 1,
"target": 6
},
{
"source": 2,
"target": 3
},
{
"source": 2,
"target": 7
}
,
{
"source": 3,
"target": 4
},
{
"source": 8,
"target": 3
}
,
{
"source": 4,
"target": 5
}
,
{
"source": 4,
"target": 9
},
{
"source": 5,
"target": 10
}
]
})
console.log(data)
// List of node names
var allNodes = data.nodes.map(function(d){return d.name})
// A linear scale to position the nodes on the X axis
var x = d3.scalePoint()
.range([0, width])
.domain(allNodes)
// Add the circle for the nodes
svg
.selectAll("mynodes")
.data(data.nodes)
.enter()
.append("circle")
.attr("cx", function(d){ return(x(d.name))})
.attr("cy", height-30)
.attr("r", 8)
.style("fill", "#69b3a2")
// And give them a label
svg
.selectAll("mylabels")
.data(data.nodes)
.enter()
.append("text")
.attr("x", function(d){ return(x(d.name))})
.attr("y", height-10)
.text(function(d){ return(d.name)})
.style("text-anchor", "middle")
// Add links between nodes. Here is the tricky part.
// In my input data, links are provided between nodes -id-, NOT between node names.
// So I have to do a link between this id and the name
var idToNode = {};
data.nodes.forEach(function (n) {
idToNode[n.id] = n;
});
// Cool, now if I do idToNode["2"].name I've got the name of the node with id 2
// Add the links
svg
.selectAll('mylinks')
.data(data.links)
.enter()
.append('path')
.attr('d', function (d) {
let start = x(idToNode[d.source].name)
// X position of start node on the X axis
let end = x(idToNode[d.target].name)
// X position of end node
return ['M',
start,
height-30,
// the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
'A',
// This means we're gonna build an elliptical arc
(start - end)/2, ',',
// Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
(start - end)/2, 0, 0, ',',
start < end ? 1 : 0, end, ',', height-30]
// We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
.join(' ');
})
.style("fill", "none")
.attr("stroke", "black")
// labels for links
svg
.selectAll('mylinks')
.data(data.links)
.enter()
.append("text")
.attr("x", function(d){
let start = x(idToNode[d.source].name)
// X position of start node on the X axis
let end = x(idToNode[d.target].name)
// X position of end node
return start + (end-start)/2
})
.attr("y", function(d){
let start = x(idToNode[d.source].name)
// X position of start node on the X axis
let end = x(idToNode[d.target].name)
// X position of end node
return height-30-(Math.abs(start-end)/2)//height-30
})
.text(function(d){ return(`${idToNode[d.source].name}-${idToNode[d.target].name}`)})
.style("text-anchor", "top")
svg.selectAll("text").data(data.links).enter()
export function drawChart(height, width) {
d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.style("border", "1px solid black")
.append("text")
.attr("x", function(d) { return d.source.x + (d.target.x - d.source.x)/2; })
.attr("y", function(d) { return d.source.y + (d.target.y - d.source.y)/2; })
.text(function(d) { return d.something; });
.attr("fill", "green")
.attr("x", 50)
.attr("y", 50)
.text("Hello D3");
}
export function drawCircles() {
var svg = d3
.select("#circles")
.append("svg")
.attr("width", 960)
.attr("height", 500)
.attr("bgcolor", "blue");
var background = svg
.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "blue");
var ball = svg
.append("circle")
.attr("r", 73)
.attr("cx", 480)
.attr("cy", 250)
.style("fill", "#ffe41e");
}
export function drawGraph() {
let margin = { top: 20, right: 30, bottom: 20, left: 30 };
let width = 900 - margin.left - margin.right;
let height = 600 - margin.top - margin.bottom;
var svg = d3
.select("#graph")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
let data = {
nodes: [
{
id: 1,
name: "A",
},
{
id: 2,
name: "B",
},
{
id: 3,
name: "C",
},
{
id: 4,
name: "D",
},
{
id: 5,
name: "E",
},
{
id: 6,
name: "F",
},
{
id: 7,
name: "G",
},
{
id: 8,
name: "H",
},
{
id: 9,
name: "I",
},
{
id: 10,
name: "J",
},
],
links: [
{
source: 1,
target: 2,
},
{
source: 1,
target: 5,
},
{
source: 1,
target: 6,
},
{
source: 2,
target: 3,
},
{
source: 2,
target: 7,
},
{
source: 3,
target: 4,
},
{
source: 8,
target: 3,
},
{
source: 4,
target: 5,
},
{
source: 4,
target: 9,
},
{
source: 5,
target: 10,
},
],
};
console.log(data);
// List of node names
var allNodes = data.nodes.map(function (d) {
return d.name;
});
// A linear scale to position the nodes on the X axis
var x = d3.scalePoint().range([0, width]).domain(allNodes);
// Add the circle for the nodes
svg
.selectAll("mynodes")
.data(data.nodes)
.enter()
.append("circle")
.attr("cx", function (d) {
return x(d.name);
})
.attr("cy", height - 30)
.attr("r", 8)
.style("fill", "#69b3a2");
// And give them a label
svg
.selectAll("mylabels")
.data(data.nodes)
.enter()
.append("text")
.attr("x", function (d) {
return x(d.name);
})
.attr("y", height - 10)
.text(function (d) {
return d.name;
})
.style("text-anchor", "middle");
// Add links between nodes. Here is the tricky part.
// In my input data, links are provided between nodes -id-, NOT between node names.
// So I have to do a link between this id and the name
var idToNode = {};
data.nodes.forEach(function (n) {
idToNode[n.id] = n;
});
// Cool, now if I do idToNode["2"].name I've got the name of the node with id 2
// Add the links
svg
.selectAll("mylinks")
.data(data.links)
.enter()
.append("path")
.attr("d", function (d) {
let start = x(idToNode[d.source].name);
// X position of start node on the X axis
let end = x(idToNode[d.target].name);
// X position of end node
return (
[
"M",
start,
height - 30,
// the arc starts at the coordinate x=start, y=height-30 (where the starting node is)
"A",
// This means we're gonna build an elliptical arc
(start - end) / 2,
",",
// Next 2 lines are the coordinates of the inflexion point. Height of this point is proportional with start - end distance
(start - end) / 2,
0,
0,
",",
start < end ? 1 : 0,
end,
",",
height - 30,
]
// We always want the arc on top. So if end is before start, putting 0 here turn the arc upside down.
.join(" ")
);
})
.style("fill", "none")
.attr("stroke", "black");
// labels for links
svg
.selectAll("mylinks")
.data(data.links)
.enter()
.append("text")
.attr("x", function (d) {
let start = x(idToNode[d.source].name);
// X position of start node on the X axis
let end = x(idToNode[d.target].name);
// X position of end node
return start + (end - start) / 2;
})
.attr("y", function (d) {
let start = x(idToNode[d.source].name);
// X position of start node on the X axis
let end = x(idToNode[d.target].name);
// X position of end node
return height - 30 - Math.abs(start - end) / 2; //height-30
})
.text(function (d) {
return `${idToNode[d.source].name}-${idToNode[d.target].name}`;
})
.style("text-anchor", "top");
svg
.selectAll("text")
.data(data.links)
.enter()
.append("text")
.attr("x", function (d) {
return d.source.x + (d.target.x - d.source.x) / 2;
})
.attr("y", function (d) {
return d.source.y + (d.target.y - d.source.y) / 2;
})
.text(function (d) {
return d.something;
});
}
export function D3Experiment() {
useEffect(() => {
drawGraph();
}, []);
useEffect(() => {
drawGraph();
}, []);
return (
<div>
<div id="chart">
</div>
<div id="circles">
</div>
<div id="graph">
</div>
</div>
);
return (
<div>
<div id="chart"></div>
<div id="circles"></div>
<div id="graph"></div>
</div>
);
}
export default D3Experiment;
export default D3Experiment;

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import React, { useState } from "react";
export function DataSetChanger({handleSubmit}){
export function DataSetChanger({ handleSubmit }) {
let [value, setValue] = useState(`[
{
"name": "Some element. The name field is necessary",
@ -18,34 +18,35 @@ export function DataSetChanger({handleSubmit}){
"isReferenceValue": true,
"somethirdfield": "c"
}
]`)
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false)
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] = useState(null)
]`);
const [displayingDoneMessage, setDisplayingDoneMessage] = useState(false);
const [displayingDoneMessageTimer, setDisplayingDoneMessageTimer] =
useState(null);
let handleChange = (event) => {
setValue(event.target.value)
}
setValue(event.target.value);
};
let handleSubmitInner = (event) => {
clearTimeout(displayingDoneMessageTimer)
clearTimeout(displayingDoneMessageTimer);
event.preventDefault();
//console.log(event)
console.log("value@handleSubmitInner@DataSetChanger")
console.log("value@handleSubmitInner@DataSetChanger");
//console.log(typeof(value));
console.log(value);
try{
let newData = JSON.parse(value)
try {
let newData = JSON.parse(value);
//console.log(typeof(newData))
//console.log(newData)
handleSubmit(newData)
if(!newData.length || newData.length < 2){
throw Error("Not enough objects")
handleSubmit(newData);
if (!newData.length || newData.length < 2) {
throw Error("Not enough objects");
}
setDisplayingDoneMessage(true)
setDisplayingDoneMessage(true);
let timer = setTimeout(() => setDisplayingDoneMessage(false), 3000);
setDisplayingDoneMessageTimer(timer)
}catch(error){
setDisplayingDoneMessage(false)
setDisplayingDoneMessageTimer(timer);
} catch (error) {
setDisplayingDoneMessage(false);
//alert(error)
//console.log(error)
let substituteText = `Error: ${error.message}
@ -70,34 +71,39 @@ Try something like:
}
]
Your old input was: ${value}`
setValue(substituteText)
Your old input was: ${value}`;
setValue(substituteText);
}
}
};
return (
<form onSubmit={handleSubmitInner} className="inline">
<br/>
<br/>
<textarea
value={value}
onChange={handleChange}
rows="10" cols="50"
<br />
<br />
<textarea
value={value}
onChange={handleChange}
rows="10"
cols="50"
className=""
/>
<br/>
<button
<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}>
onClick={handleSubmitInner}
>
Change dataset
</button>
&nbsp;
<button
className={displayingDoneMessage ? "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5 p-10" : "hidden"}
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>
);
}
}

View File

@ -3,25 +3,28 @@ import ReactMarkdown from "react-markdown";
import gfm from "remark-gfm";
if (!String.prototype.replaceAll) {
String.prototype.replaceAll = function(str, newStr){
String.prototype.replaceAll = function (str, newStr) {
// If a regex pattern
if (
Object.prototype.toString.call(str).toLowerCase() === "[object regexp]"
) {
return this.replace(str, newStr);
}
// If a regex pattern
if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
return this.replace(str, newStr);
}
// If a string
return this.replace(new RegExp(str, 'g'), newStr);
};
// If a string
return this.replace(new RegExp(str, "g"), newStr);
};
}
export function DisplayAsMarkdown({ markdowntext, className }) {
//console.log(markdowntext)
markdowntext = markdowntext.replaceAll("\n", "\n\n");
return (
<ReactMarkdown
plugins={[gfm]}
children={markdowntext}
className={className}
/>
);
}
export function DisplayAsMarkdown({markdowntext, className}){
//console.log(markdowntext)
markdowntext = markdowntext.replaceAll("\n", "\n\n")
return( <ReactMarkdown
plugins={[gfm]}
children={markdowntext}
className={className}
/>)
}

View File

@ -1,31 +1,44 @@
import React from "react";
let capitalizeFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1)
let capitalizeFirstLetter = (string) =>
string.charAt(0).toUpperCase() + string.slice(1);
export function DisplayElement({ element }) {
let otherkeys = Object.keys(element).filter(
(key) =>
key != "name" && key != "url" && key != "id" && key != "isReferenceValue"
);
let othervalues = otherkeys.map((key) => element[key]);
let otherpairs = otherkeys.map((key, i) => ({
key: capitalizeFirstLetter(key),
value: othervalues[i],
}));
if (element.url) {
return (
<div>
{/*<a href={element.url} target="_blank">*/}
<h2>{`${element.name}`}</h2>
{/*</a>*/}
{otherpairs.map((pair) => (
<p key={pair.value}>{`${pair.key}: ${pair.value}`}</p>
))}
<p>
<a href={element.url} target="_blank">
More info
</a>
</p>
</div>
);
} else {
return (
<div>
<h2>{`${element.name}`}</h2>
{otherpairs.map((pair) => (
<p key={pair.value}>{`${pair.key}: ${pair.value}`}</p>
))}
</div>
);
}
}
export function DisplayElement({element}){
let otherkeys = Object.keys(element).filter(key => key!="name" && key!="url" && key != "id" && key != "isReferenceValue")
let othervalues = otherkeys.map(key => element[key])
let otherpairs = otherkeys.map((key,i) => ({key: capitalizeFirstLetter(key), value: othervalues[i]}))
if(element.url){
return(
<div>
{/*<a href={element.url} target="_blank">*/}
<h2>{`${element.name}`}</h2>
{/*</a>*/}
{otherpairs.map(pair => <p key={pair.value}>{`${pair.key}: ${pair.value}`}</p>)}
<p>
<a href={element.url} target="_blank">More info</a>
</p>
</div>
)
}else{
return(
<div>
<h2>{`${element.name}`}</h2>
{otherpairs.map(pair => <p key={pair.value}>{`${pair.key}: ${pair.value}`}</p>)}
</div>
)
}
}

View File

@ -1,14 +1,17 @@
import axios from "axios"
import axios from "axios";
const CONNECTION_IS_ACTIVE = true
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)
}
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);
}
}
// pushToMongo()
// pushToMongo()

View File

@ -1,34 +1,34 @@
/* Imports */
import { Slider, Rail, Handles, Tracks, Ticks } from "react-compound-slider";
import React, { useState } from 'react';
import React, { useState } from "react";
// https://sghall.github.io/react-compound-slider/#/getting-started/tutorial
/* Definitions */
const sliderStyle = { // Give the slider some width
position: 'relative',
width: '40em',
const sliderStyle = {
// Give the slider some width
position: "relative",
width: "40em",
height: 40,
border: '5em',
}
border: "5em",
};
const railStyle = {
position: 'absolute',
width: '100%',
position: "absolute",
width: "100%",
height: 15,
marginTop: 32.5,
borderRadius: 5,
backgroundColor: 'lightgrey',
}
backgroundColor: "lightgrey",
};
/* Support functions */
function Handle({
handle: { id, value, percent },
getHandleProps,
displayFunction,
handleWidth
handleWidth,
}) {
return (
<>
<div className="justify-center text-center text-gray-600 text-xs">
@ -37,50 +37,56 @@ function Handle({
<div
style={{
left: `${percent}%`,
position: 'absolute',
position: "absolute",
marginLeft: -10,
marginTop: 2.5,
zIndex: 2,
width: 30,
height: 30,
cursor: 'pointer',
borderRadius: '0%',
backgroundColor: '#374151',
color: '#374151',
cursor: "pointer",
borderRadius: "0%",
backgroundColor: "#374151",
color: "#374151",
}}
{...getHandleProps(id)}
>
</div>
></div>
</>
)
);
}
function Track({ source, target, getTrackProps }) {
return (
<div
style={{
position: 'absolute',
position: "absolute",
height: 17.5,
zIndex: 1,
marginTop: 8.25,
backgroundColor: ' #3B82F6',
backgroundColor: " #3B82F6",
borderRadius: 5,
cursor: 'pointer',
cursor: "pointer",
left: `${source.percent}%`,
width: `${target.percent - source.percent}%`,
}}
{...getTrackProps() /* this will set up events if you want it to be clickeable (optional) */}
{
...getTrackProps() /* this will set up events if you want it to be clickeable (optional) */
}
/>
)
);
}
/* Body */
// Two functions, essentially identical.
// Two functions, essentially identical.
export function SliderElement({ onChange, value, displayFunction, domain }) {
let toLogDomain = (arr) => [Math.log(arr[0]) / Math.log(10), Math.log(arr[1]) / Math.log(10)]
let toLogDomain = (arr) => [
Math.log(arr[0]) / Math.log(10),
Math.log(arr[1]) / Math.log(10),
];
return (
<Slider
rootStyle={sliderStyle /* inline styles for the outer div. Can also use className prop. */}
rootStyle={
sliderStyle /* inline styles for the outer div. Can also use className prop. */
}
domain={toLogDomain([1 / domain, domain])}
values={[value]}
step={0.0001}
@ -88,14 +94,12 @@ export function SliderElement({ onChange, value, displayFunction, domain }) {
reversed={true}
>
<Rail>
{({ getRailProps }) => (
<div style={railStyle} {...getRailProps()} />
)}
{({ getRailProps }) => <div style={railStyle} {...getRailProps()} />}
</Rail>
<Handles>
{({ handles, getHandleProps }) => (
<div className="slider-handles">
{handles.map(handle => (
{handles.map((handle) => (
<Handle
key={handle.id}
handle={handle}
@ -122,33 +126,54 @@ export function SliderElement({ onChange, value, displayFunction, domain }) {
)}
</Tracks>
</Slider>
)
);
}
export function SubmitSliderButton({ posList, binaryComparisons, sliderValue, reasoning, toComparePair, nextStepSlider, dontPushSubmitButtonAnyMore }) {
export function SubmitSliderButton({
posList,
binaryComparisons,
sliderValue,
reasoning,
toComparePair,
nextStepSlider,
dontPushSubmitButtonAnyMore,
}) {
// This element didn't necessarily have to exist, but it made it easier for debugging purposes
let onClick = (event) => {
if(!dontPushSubmitButtonAnyMore){
if (!dontPushSubmitButtonAnyMore) {
//event.preventDefault();
let obj = { posList, binaryComparisons, sliderValue, reasoning, element1: toComparePair[1], element2: toComparePair[0] }
let obj = {
posList,
binaryComparisons,
sliderValue,
reasoning,
element1: toComparePair[1],
element2: toComparePair[0],
};
//
console.log("input@SubmitSliderButton")
console.log(obj)
console.log("input@SubmitSliderButton");
console.log(obj);
if (!!Number(sliderValue) && sliderValue >= 0) {
nextStepSlider(obj)
nextStepSlider(obj);
} else if (!!Number(sliderValue) && sliderValue < 0) {
alert("Negative numbers not yet allowed")
alert("Negative numbers not yet allowed");
} else {
alert("Your input is not a number")
alert("Your input is not a number");
}
}
}
};
return (<button
className={!dontPushSubmitButtonAnyMore ? "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" : "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5"}
onClick={onClick}>
Submit
</button>)
return (
<button
className={
!dontPushSubmitButtonAnyMore
? "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"
: "bg-transparent text-blue-700 font-semibold py-2 px-4 border border-blue-500 rounded mt-5"
}
onClick={onClick}
>
Submit
</button>
);
}
}

View File

@ -1,53 +1,42 @@
[
{
"source": 3,
"target": 4,
"distance": 5
},
{
"source": 2,
"target": 3,
"distance": 4
},
{
"source": 0,
"target": 1,
"distance": 4
},
{
"source": 0,
"target": 2,
"distance": 5
},
{
"source": 1,
"target": 2,
"distance": 5
}
]
{
"source": 3,
"target": 4,
"distance": 5
},
{
"source": 2,
"target": 3,
"distance": 4
},
{
"source": 0,
"target": 1,
"distance": 4
},
{
"source": 0,
"target": 2,
"distance": 5
},
{
"source": 1,
"target": 2,
"distance": 5
}
]

View File

@ -5,25 +5,28 @@
/* Imports */
import React from "react";
import fs from 'fs';
import path from 'path';
import ComparisonView from '../lib/comparisonView.js'
import fs from "fs";
import path from "path";
import ComparisonView from "../lib/comparisonView.js";
/* Definitions */
const elementsDocument = '../data/listOfMoralGoods.json'
const elementsDocument = "../data/listOfMoralGoods.json";
/* React components */
export async function getStaticProps() {
const directory = path.join(process.cwd(), "pages")
let listOfElementsForView = JSON.parse(fs.readFileSync(path.join(directory, elementsDocument), 'utf8'));
const directory = path.join(process.cwd(), "pages");
let listOfElementsForView = JSON.parse(
fs.readFileSync(path.join(directory, elementsDocument), "utf8")
);
return {
props: {
listOfElementsForView
listOfElementsForView,
},
};
}
// Main react component
export default function Home({ listOfElementsForView }) {
return(<ComparisonView listOfElementsForView={listOfElementsForView}/>)
}
return <ComparisonView listOfElementsForView={listOfElementsForView} />;
}

View File

@ -26,4 +26,4 @@ export async function getStaticProps() {
// Main react component
export default function Home({ listOfElementsForView }) {
return(<ComparisonView listOfElementsForView={listOfElementsForView}/>)
}
}