cleanups: charts
This commit is contained in:
parent
12a236186f
commit
3ae7a68cb2
|
@ -1,3 +1,4 @@
|
||||||
|
import { format } from "date-fns";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
VictoryAxis, VictoryChart, VictoryGroup, VictoryLabel, VictoryLegend, VictoryLine,
|
VictoryAxis, VictoryChart, VictoryGroup, VictoryLabel, VictoryLegend, VictoryLine,
|
||||||
|
@ -10,17 +11,18 @@ interface Props {
|
||||||
question: QuestionWithHistoryFragment;
|
question: QuestionWithHistoryFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
let formatOptionName = (name) => {
|
let formatOptionName = (name: string) => {
|
||||||
return name.length > 20 ? name.slice(0, 17) + "..." : name;
|
return name.length > 20 ? name.slice(0, 17) + "..." : name;
|
||||||
};
|
};
|
||||||
|
|
||||||
let getLength = (str) => {
|
let getLength = (str: string): number => {
|
||||||
let capitalLetterLengthMultiplier = 1.25;
|
// TODO - measure with temporary DOM element instead?
|
||||||
let smallLetterMultiplier = 0.8;
|
const capitalLetterLengthMultiplier = 1.25;
|
||||||
let numUpper = (str.match(/[A-Z]/g) || []).length;
|
const smallLetterMultiplier = 0.8;
|
||||||
let numSmallLetters = (str.match(/[fijlrt]/g) || []).length;
|
const numUpper = (str.match(/[A-Z]/g) || []).length;
|
||||||
let numSpaces = (str.match(/[\s]/g) || []).length;
|
const numSmallLetters = (str.match(/[fijlrt]/g) || []).length;
|
||||||
let length =
|
const numSpaces = (str.match(/[\s]/g) || []).length;
|
||||||
|
const length =
|
||||||
str.length +
|
str.length +
|
||||||
-numUpper -
|
-numUpper -
|
||||||
numSmallLetters +
|
numSmallLetters +
|
||||||
|
@ -29,78 +31,58 @@ let getLength = (str) => {
|
||||||
return length;
|
return length;
|
||||||
};
|
};
|
||||||
|
|
||||||
let timestampToString = (x) => {
|
type DataSet = { date: Date; probability: number; name: string }[];
|
||||||
// for real timestamps
|
|
||||||
// console.log(x);
|
|
||||||
let date = new Date(Date.parse(x));
|
|
||||||
let dayOfMonth = date.getDate();
|
|
||||||
let month = date.getMonth() + 1;
|
|
||||||
let year = date.getFullYear();
|
|
||||||
let dateString = `${("0" + dayOfMonth).slice(-2)}/${("0" + month).slice(
|
|
||||||
-2
|
|
||||||
)}/${year.toString().slice(-2)}`;
|
|
||||||
// console.log(dateString);
|
|
||||||
return dateString;
|
|
||||||
};
|
|
||||||
|
|
||||||
let dataAsXy = (data) =>
|
const dataAsXy = (data: DataSet) =>
|
||||||
data.map((datum) => ({
|
data.map((datum) => ({
|
||||||
x: timestampToString(datum.date), //getDate(datum.date * (1000 * 60 * 60 * 24)),
|
x: format(datum.date, "yyyy-MM-dd"),
|
||||||
y: datum.probability,
|
y: datum.probability,
|
||||||
name: datum.name,
|
name: datum.name,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const colors = ["dodgerblue", "crimson", "seagreen", "darkviolet", "turquoise"];
|
const colors = ["dodgerblue", "crimson", "seagreen", "darkviolet", "turquoise"];
|
||||||
const getVictoryGroup = (data, i) => {
|
// can't be replaced with React component, VictoryChar requires VictoryGroup elements to be immediate children
|
||||||
|
const getVictoryGroup = ({ data, i }: { data: DataSet; i: number }) => {
|
||||||
return (
|
return (
|
||||||
<VictoryGroup color={colors[i] || "darkgray"} data={dataAsXy(data)}>
|
<VictoryGroup color={colors[i] || "darkgray"} data={dataAsXy(data)} key={i}>
|
||||||
<VictoryScatter
|
<VictoryScatter
|
||||||
name={`scatter-${i}`}
|
name={`scatter-${i}`}
|
||||||
//style={{ labels: { display: "none" } }}
|
|
||||||
size={({ active }) => (active ? 3.75 : 3)}
|
size={({ active }) => (active ? 3.75 : 3)}
|
||||||
//labels={() => null}
|
|
||||||
//labelComponent={<span></span>}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VictoryLine
|
<VictoryLine name={`line-${i}`} />
|
||||||
name={`line-${i}`}
|
|
||||||
//style={{ labels: { display: "none" } }}
|
|
||||||
//labels={() => null}
|
|
||||||
//labelComponent={<span></span>}
|
|
||||||
/>
|
|
||||||
</VictoryGroup>
|
</VictoryGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HistoryChart: React.FC<Props> = ({ question }) => {
|
export const HistoryChart: React.FC<Props> = ({ question }) => {
|
||||||
let dataSetsNames = [];
|
let dataSetsNames: string[] = [];
|
||||||
question.history.forEach((item) => {
|
question.history.forEach((item) => {
|
||||||
let optionNames = item.options.map((option) => option.name);
|
let optionNames = item.options.map((option) => option.name);
|
||||||
dataSetsNames.push(...optionNames);
|
dataSetsNames.push(...optionNames);
|
||||||
});
|
});
|
||||||
dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5
|
dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5
|
||||||
let isBinary =
|
const isBinary =
|
||||||
(dataSetsNames[0] == "Yes" && dataSetsNames[1] == "No") ||
|
(dataSetsNames[0] === "Yes" && dataSetsNames[1] === "No") ||
|
||||||
(dataSetsNames[0] == "No" && dataSetsNames[1] == "Yes");
|
(dataSetsNames[0] === "No" && dataSetsNames[1] === "Yes");
|
||||||
if (isBinary) {
|
if (isBinary) {
|
||||||
dataSetsNames = ["Yes"];
|
dataSetsNames = ["Yes"];
|
||||||
}
|
}
|
||||||
let dataSets = [];
|
let dataSets: DataSet[] = [];
|
||||||
let maxProbability = 0;
|
let maxProbability = 0;
|
||||||
let longestNameLength = 0;
|
let longestNameLength = 0;
|
||||||
|
|
||||||
for (let name of dataSetsNames) {
|
for (const name of dataSetsNames) {
|
||||||
let newDataset = [];
|
let newDataset: DataSet = [];
|
||||||
let previousDate = -Infinity;
|
let previousDate = -Infinity;
|
||||||
for (let item of question.history) {
|
for (let item of question.history) {
|
||||||
let relevantItemsArray = item.options.filter((x) => x.name == name);
|
const relevantItemsArray = item.options.filter((x) => x.name === name);
|
||||||
let date = new Date(item.timestamp * 1000);
|
const date = new Date(item.timestamp * 1000);
|
||||||
if (
|
if (
|
||||||
relevantItemsArray.length == 1 &&
|
relevantItemsArray.length == 1 &&
|
||||||
item.timestamp - previousDate > 12 * 60 * 60
|
item.timestamp - previousDate > 12 * 60 * 60
|
||||||
) {
|
) {
|
||||||
let relevantItem = relevantItemsArray[0];
|
let relevantItem = relevantItemsArray[0];
|
||||||
// if (relevantItem.type == "PROBABILITY") {
|
|
||||||
let result = {
|
let result = {
|
||||||
date,
|
date,
|
||||||
probability: relevantItem.probability,
|
probability: relevantItem.probability,
|
||||||
|
@ -110,34 +92,33 @@ export const HistoryChart: React.FC<Props> = ({ question }) => {
|
||||||
relevantItem.probability > maxProbability
|
relevantItem.probability > maxProbability
|
||||||
? relevantItem.probability
|
? relevantItem.probability
|
||||||
: maxProbability;
|
: maxProbability;
|
||||||
let length = getLength(relevantItem.name);
|
let length = getLength(formatOptionName(relevantItem.name));
|
||||||
longestNameLength =
|
longestNameLength =
|
||||||
length > longestNameLength ? length : longestNameLength;
|
length > longestNameLength ? length : longestNameLength;
|
||||||
newDataset.push(result);
|
newDataset.push(result);
|
||||||
// }
|
|
||||||
previousDate = item.timestamp;
|
previousDate = item.timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataSets.push(newDataset);
|
dataSets.push(newDataset);
|
||||||
}
|
}
|
||||||
|
|
||||||
let letterLength = 7;
|
const letterLength = 7;
|
||||||
let labelLegendStart = 45;
|
const labelLegendStart = 45;
|
||||||
|
|
||||||
let domainMax =
|
const domainMax =
|
||||||
maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1;
|
maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1;
|
||||||
let dataSetsLength = dataSets.length;
|
const dataSetsLength = dataSets.length;
|
||||||
let goldenRatio = (1 + Math.sqrt(5)) / 2;
|
const goldenRatio = (1 + Math.sqrt(5)) / 2;
|
||||||
let width = 750;
|
const width = 750;
|
||||||
let height = width / goldenRatio;
|
const height = width / goldenRatio;
|
||||||
let padding = {
|
const padding = {
|
||||||
top: 20,
|
top: 20,
|
||||||
bottom: 50,
|
bottom: 50,
|
||||||
left: 60,
|
left: 60,
|
||||||
right: labelLegendStart + letterLength * longestNameLength,
|
right: labelLegendStart + letterLength * longestNameLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
let legendData = Array.from(Array(dataSetsLength).keys()).map((i) => ({
|
const legendData = Array.from(Array(dataSetsLength).keys()).map((i) => ({
|
||||||
name: formatOptionName(dataSetsNames[i]),
|
name: formatOptionName(dataSetsNames[i]),
|
||||||
symbol: { fill: colors[i] },
|
symbol: { fill: colors[i] },
|
||||||
}));
|
}));
|
||||||
|
@ -154,6 +135,7 @@ export const HistoryChart: React.FC<Props> = ({ question }) => {
|
||||||
labels={({ datum }) => `Not shown`}
|
labels={({ datum }) => `Not shown`}
|
||||||
labelComponent={
|
labelComponent={
|
||||||
<VictoryTooltip
|
<VictoryTooltip
|
||||||
|
constrainToVisibleArea
|
||||||
pointerLength={0}
|
pointerLength={0}
|
||||||
dy={-12}
|
dy={-12}
|
||||||
text={({ datum }) =>
|
text={({ datum }) =>
|
||||||
|
@ -192,7 +174,9 @@ export const HistoryChart: React.FC<Props> = ({ question }) => {
|
||||||
data={legendData}
|
data={legendData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{dataSets.slice(0, 5).map((dataset, i) => getVictoryGroup(dataset, i))}
|
{dataSets
|
||||||
|
.slice(0, 5)
|
||||||
|
.map((dataset, i) => getVictoryGroup({ data: dataset, i }))}
|
||||||
<VictoryAxis
|
<VictoryAxis
|
||||||
// tickValues specifies both the number of ticks and where
|
// tickValues specifies both the number of ticks and where
|
||||||
// they are placed on the axis
|
// they are placed on the axis
|
||||||
|
@ -205,7 +189,6 @@ export const HistoryChart: React.FC<Props> = ({ question }) => {
|
||||||
//axisLabelComponent={
|
//axisLabelComponent={
|
||||||
// <VictoryLabel dy={40} style={{ fontSize: 10, fill: "gray" }} />
|
// <VictoryLabel dy={40} style={{ fontSize: 10, fill: "gray" }} />
|
||||||
//}
|
//}
|
||||||
// label="Date (dd/mm/yy)"
|
|
||||||
tickLabelComponent={
|
tickLabelComponent={
|
||||||
<VictoryLabel
|
<VictoryLabel
|
||||||
dy={10}
|
dy={10}
|
||||||
|
|
|
@ -40,7 +40,7 @@ export const getServerSideProps: GetServerSideProps<Props> = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
const Section: React.FC<{ title: string }> = ({ title, children }) => (
|
const Section: React.FC<{ title: string }> = ({ title, children }) => (
|
||||||
<div className="space-y-4 flex flex-col items-center">
|
<div className="space-y-2 flex flex-col items-center">
|
||||||
<h2 className="text-xl text-gray-900">{title}</h2>
|
<h2 className="text-xl text-gray-900">{title}</h2>
|
||||||
<div>{children}</div>
|
<div>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,7 @@ const Section: React.FC<{ title: string }> = ({ title, children }) => (
|
||||||
const QuestionCardContents: React.FC<{
|
const QuestionCardContents: React.FC<{
|
||||||
question: QuestionWithHistoryFragment;
|
question: QuestionWithHistoryFragment;
|
||||||
}> = ({ question }) => (
|
}> = ({ question }) => (
|
||||||
<div className="flex flex-col space-y-8 items-center pt-5">
|
<div className="flex flex-col space-y-4 items-center">
|
||||||
<h1 className="sm:text-4xl text-2xl text-center">
|
<h1 className="sm:text-4xl text-2xl text-center">
|
||||||
<a
|
<a
|
||||||
className="text-black no-underline hover:text-gray-600"
|
className="text-black no-underline hover:text-gray-600"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user