Commit 3a8a21f4 authored by Dominik Schwabe's avatar Dominik Schwabe
Browse files

changed checkboxes to badges; color palette for highlighting

parent 7fddc960
......@@ -145,7 +145,7 @@ router.post(
async (req, res, next) => {
try {
await Feedbacks.insert(req.body);
return res.status(200).end();
return res.json({sucess: true});
} catch (err) {
return next(err);
}
......
......@@ -9,6 +9,7 @@
"version": "1.6.0",
"dependencies": {
"dexie-react-hooks": "^1.0.6",
"fuse.js": "^6.4.6",
"is-url": "^1.2.4",
"parse-github-url": "^1.0.2",
"react": "17.0.2",
......@@ -9065,6 +9066,14 @@
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
},
"node_modules/fuse.js": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz",
"integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw==",
"engines": {
"node": ">=10"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
......@@ -29547,6 +29556,11 @@
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
},
"fuse.js": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.4.6.tgz",
"integrity": "sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw=="
},
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
......@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"dexie-react-hooks": "^1.0.6",
"fuse.js": "^6.4.6",
"is-url": "^1.2.4",
"parse-github-url": "^1.0.2",
"react": "17.0.2",
......
......@@ -12,7 +12,7 @@ const summarizeRequest = (text, summarizers, ratio) =>
const feedbackRequest = (summarizer, summary, reference, url, feedback) => {
let json = { summarizer, summary, reference, feedback };
if (url !== null) json = { url, ...json };
return post("/api/feedback", json);
return post("/api/feedback", json).catch(e => alert(e));
};
export {
......
import gh from "parse-github-url";
import React, { useContext } from "react";
import { FaCode, FaLink } from "react-icons/fa";
import { MetricsContext } from "../contexts/MetricsContext";
import { SummarizersContext } from "../contexts/SummarizersContext";
......@@ -8,47 +9,98 @@ import { CenterLoading } from "./utils/Loading";
const extractGithubUser = (url) => gh(url).repo || url;
const AboutTable = ({ content, type }) => (
<table className="uk-table uk-table-striped">
<thead>
<tr>
<th>{type}</th>
<th>Code</th>
<th>Embedding Model</th>
</tr>
</thead>
<tbody>
{Object.entries(content).map(([metric, { readable, homepage, sourcecode, model }]) => (
<tr key={metric}>
<td>
{homepage ? (
<a href={homepage}>{extractGithubUser(readable)}</a>
) : (
<span>{readable}</span>
)}
</td>
<td>
{sourcecode ? (
<a href={sourcecode}>{extractGithubUser(sourcecode)}</a>
) : (
<span>{sourcecode}</span>
)}
</td>
<td>{model}</td>
</tr>
))}
</tbody>
</table>
const isLink = (text) => text.match(/^https?:\/\//);
const Link = ({ Icon, link, title }) =>
link && (
<a className="nostyle" href={link} title={title}>
<Icon />
</a>
);
const withIcon = (Icon, title) => (props) => <Link Icon={Icon} title={title} {...props} />;
const SourceCode = withIcon(FaCode, "sourcecode");
const HomePage = withIcon(FaLink, "homepage");
const PluginCard = ({ plugin, inline = true }) => {
const { readable, sourcecode, homepage } = plugin;
return (
<div
style={{
border: "1px solid black",
borderRadius: "5px",
padding: "0",
display: inline ? "inline-block" : "block",
}}
>
<div
className="uk-flex uk-flex-between"
style={{
alignItems: "center",
borderBottom: "1px solid black",
padding: "3px",
paddingBottom: "0",
backgroundColor: "#B02F2C",
color: "white",
}}
>
<div>{readable}</div>
<div
className="margin-between-10"
style={{ marginLeft: "30px", marginBottom: "4px", paddingRight: "4px" }}
>
<SourceCode link={sourcecode} />
<HomePage link={homepage} />
</div>
</div>
<div style={{ padding: "5px" }}>
{["type", "model"]
.filter((propKey) => plugin[propKey])
.map((propKey) => {
const propValue = plugin[propKey];
return (
<div key={propKey}>
<span style={{ fontWeight: "bold", marginRight: "10px" }}>{propKey}:</span>
{isLink(propValue) ? <a href={propValue}>{propKey}</a> : propValue}
</div>
);
})}
</div>
</div>
);
};
const AboutTable = ({ plugins }) => (
<div className="margin-between-20 uk-flex" style={{ flexWrap: "wrap" }}>
{Object.entries(plugins).map(([key, plugin]) => (
<PluginCard key={key} plugin={plugin} />
))}
</div>
);
const About = () => {
const { summarizers, loading: summarizersLoading, reload: summarizersReload } = useContext(
SummarizersContext
);
const {
summarizers,
loading: summarizersLoading,
reload: summarizersReload,
} = useContext(SummarizersContext);
const { metrics, loading: metricsLoading, reload: metricsReload } = useContext(MetricsContext);
return (
<article className="uk-container">
<div style={{ marginBottom: "1.5em" }} className="uk-text-meta">
Evaluate a single hypothesis against the reference or upload hypothesis and reference files.
Results can be saved and exported as LaTeX and CSV.
</div>
<div>
<h4 style={{ display: "inline-flex" }}>Sourcecode</h4>
<a
className="uk-margin-left"
href="https://git.informatik.uni-leipzig.de/ds40bamo/comparefile"
>
https://git.informatik.uni-leipzig.de/ds40bamo/comparefile
</a>
</div>
<div>
<h4>Summarization</h4>
<div className="uk-margin-left">
......@@ -62,14 +114,14 @@ const About = () => {
Retry
</Button>
) : (
<AboutTable type="Summarizer" content={summarizers} />
<AboutTable plugins={summarizers} />
)}
</>
)}
</>
</div>
</div>
<div>
<div className="uk-margin">
<h4>Evaluation</h4>
<div className="uk-margin-left">
<>
......@@ -82,28 +134,15 @@ const About = () => {
Retry
</Button>
) : (
<AboutTable type="Metric" content={metrics} />
<AboutTable plugins={metrics} />
)}
</>
)}
</>
<div style={{ marginTop: "1em", marginBottom: "1.5em" }} className="uk-text-meta">
Evaluate a single hypothesis against the reference or upload hypothesis and reference
files. Results can be saved and exported as LaTeX and CSV.
</div>
</div>
</div>
<div>
<h4>Code</h4>
<a
className="uk-margin-left"
href="https://git.informatik.uni-leipzig.de/ds40bamo/comparefile"
>
https://git.informatik.uni-leipzig.de/ds40bamo/comparefile
</a>
</div>
</article>
);
};
export { About };
export { About, PluginCard };
......@@ -48,7 +48,7 @@ const CompareTable = ({ comparisons }) => {
>
<div>
<input
className="uk-input"
className="uk-input align-center"
type="text"
placeholder="jump to page"
onKeyDown={(e) => e.keyCode === 13 && setPage(e.currentTarget.value)}
......@@ -56,7 +56,7 @@ const CompareTable = ({ comparisons }) => {
</div>
<div>
<input
className="uk-input"
className="uk-input align-center"
type="text"
placeholder="examples per page"
onKeyDown={(e) => e.keyCode === 13 && setSize(e.currentTarget.value)}
......
import React from "react";
import { textToColor } from "../utils/color";
import { BadgeButton } from "./utils/Button";
const typeToColor = (type) => {
switch (type) {
case "lexical":
return "blue";
case "semantic":
return "green";
case "extractive":
return "blue";
case "abstractive":
return "green";
default:
return textToColor(type)[0];
}
};
const Model = ({ info, onClick, isSet }) => (
<BadgeButton
onClick={onClick}
style={{
border: "3px solid",
borderRadius: "8px",
borderColor: typeToColor(info.type),
padding: "14px",
color: "black",
backgroundColor: isSet ? "#ffcccb" : "white",
}}
>
{info.readable}
</BadgeButton>
);
export { Model };
import React, { useContext } from "react";
import { FaCogs } from "react-icons/fa";
import React, { useContext, useMemo, useState } from "react";
import { MetricsContext } from "../contexts/MetricsContext";
import { PluginCard } from "./About";
import { Model } from "./Model";
import { DismissableBadge } from "./utils/Badge";
import { Card, CardBody, CardHeader, CardTitle } from "./utils/Card";
import { Checkboxes } from "./utils/Checkboxes";
import { LiveSearch, useFilter } from "./utils/FuzzySearch";
const getChosenMetrics = (settings) =>
Object.entries(settings)
.filter((e) => e[1])
.map((e) => e[0]);
const Settings = () => {
const { metrics, settings, toggleSetting, metricTypes } = useContext(MetricsContext);
const metricKeys = useMemo(() => Object.keys(metrics).sort(), [metrics]);
const { query, setQuery, filteredKeys } = useFilter(metricKeys);
const [selectedMetric, setSelectedMetric] = useState(null);
const selectMetric = (key) => {
toggleSetting(key);
if (selectedMetric === key) setSelectedMetric(null);
};
const unselectMetric = (key) => {
toggleSetting(key);
if (selectedMetric === key) setSelectedMetric(null);
};
const chosenMetrics = getChosenMetrics(settings);
return (
<Card>
<CardHeader>
<CardTitle>
<FaCogs /> Choose Metrics
<CardTitle style={{ display: "flex", alignItems: "center", marginRight: "10px" }}>
<span style={{ marginRight: "10px" }}>Metrics</span>
<LiveSearch query={query} setQuery={setQuery} />
</CardTitle>
</CardHeader>
<CardBody>
<div className="uk-flex" style={{ marginTop: "-25px" }}>
{Object.keys(metricTypes).length ? (
Object.entries(metricTypes).map(([key, value]) => (
<div key={key} style={{ flex: "1" }} className="margin-right">
<h4
className="underline-border uk-text-left colored-header"
style={{ textTransform: "capitalize" }}
>
{key}
</h4>
<Checkboxes
options={value.map((metric) => [
metric,
metrics[metric].readable,
settings[metric],
])}
toggleOption={toggleSetting}
/>
</div>
<div className="margin-between-10">
{Object.values(metrics).length ? (
filteredKeys.map((key) => (
<Model
key={key}
info={metrics[key]}
onClick={() => selectMetric(key)}
isSet={settings[key]}
/>
))
) : (
<div>no metrics configured</div>
)}
</div>
<div className="colored-header" style={{ marginTop: "30px" }}>
Selected Metrics
</div>
<div className="margin-between-5" style={{ marginLeft: "20px", marginBottom: "10px" }}>
{chosenMetrics.map((model) => {
const name = metrics[model].readable;
return (
<DismissableBadge onClick={() => unselectMetric(model)} key={model}>
<a
href="/#"
className="nostyle"
onClick={(e) => {
e.preventDefault();
setSelectedMetric(model);
}}
>
{name}
</a>
</DismissableBadge>
);
})}
</div>
{selectedMetric && <PluginCard plugin={metrics[selectedMetric]} inline={false} />}
</CardBody>
</Card>
);
......
......@@ -13,10 +13,13 @@ import { feedbackRequest, summarizeRequest } from "../api";
import { SummarizersContext } from "../contexts/SummarizersContext";
import { useMarkups } from "../hooks/markup";
import { displayError, displayMessage } from "../utils/message";
import { Badge } from "./utils/Badge";
import { PluginCard } from "./About";
import { Model } from "./Model";
import { Badge, DismissableBadge } from "./utils/Badge";
import { Button } from "./utils/Button";
import { Checkboxes } from "./utils/Checkboxes";
import { Bars, EyeClosed, EyeOpen, ThumbsDown, ThumbsUp } from "./utils/Icons";
import { LiveSearch, useFilter } from "./utils/FuzzySearch";
import { CenterLoading } from "./utils/Loading";
import { Markup, useMarkupScroll } from "./utils/Markup";
......@@ -51,7 +54,7 @@ const Feedback = ({ summary }) => {
const Header = ({ text, fontSize, backgroundColor = "#B02F2C", children, style }) => (
<div
className="uk-flex uk-flex-between uk-flex-middle"
className="uk-flex uk-flex-between uk-flex-middle margin-between-20"
style={{
paddingLeft: "20px",
paddingRight: "10px",
......@@ -69,7 +72,7 @@ const Header = ({ text, fontSize, backgroundColor = "#B02F2C", children, style }
</div>
);
const getSetModels = (models) =>
const getChosenModels = (models) =>
Object.entries(models)
.filter((e) => e[1])
.map((e) => e[0]);
......@@ -87,8 +90,19 @@ const InputDocument = ({ summarize, isComputing }) => {
const [documentText, setDocumentText] = useState("");
const { summarizers, summarizerTypes, settings, toggleSetting } = useContext(SummarizersContext);
const [percentage, setPercentage] = useState("15");
const summarizerKeys = useMemo(() => Object.keys(summarizers).sort(), [summarizers]);
const { query, setQuery, filteredKeys } = useFilter(summarizerKeys);
const [selectedSummarizer, setSelectedSummarizer] = useState(null);
const selectSummarizer = (key) => {
toggleSetting(key);
if (selectedSummarizer === key) setSelectedSummarizer(null);
};
const unselectSummarizer = (key) => {
toggleSetting(key);
if (selectedSummarizer === key) setSelectedSummarizer(null);
};
const anyModelSet = () => Object.values(settings).some((isSet) => isSet);
const chosenModels = getChosenModels(settings);
const insertSampleText = () => {
setDocumentText(sampleText);
......@@ -98,7 +112,6 @@ const InputDocument = ({ summarize, isComputing }) => {
return (
<div className="uk-container uk-container-expand uk-margin-medium-top@s uk-margin-large-top@l">
<div className="uk-flex uk-flex-between" style={{ minHeight: "60vh" }}>
{/* Start Document container */}
<div className="uk-flex uk-flex-column" style={{ flexBasis: "60%" }}>
<Header text="Document" fontSize="14pt">
<Button variant="primary" onClick={insertSampleText}>
......@@ -114,45 +127,56 @@ const InputDocument = ({ summarize, isComputing }) => {
style={{ height: "100%", padding: "20px", resize: "none", overflow: "auto" }}
/>
</div>
{/* End Document container */}
<div style={{ minWidth: "20px" }} />
{/* Start model lists container */}
<div className="uk-flex uk-flex-column" style={{ flexBasis: "40%" }}>
<Header text="Models" fontSize="14pt" />
<Header text="Models" fontSize="14pt">
<LiveSearch query={query} setQuery={setQuery} />
</Header>
<div
className="uk-card uk-card-default uk-card-body uk-flex-stretch"
style={{ height: "100%" }}
>
{/* Start model checkbox lists */}
<div className="uk-flex" style={{ marginTop: "-25px" }}>
{Object.keys(summarizerTypes).length ? (
Object.entries(summarizerTypes).map(([key, value]) => (
<div key={key} style={{ flex: "1" }} className="margin-right">
<h4
className="underline-border uk-text-left colored-header"
style={{ textTransform: "capitalize" }}
>
{key}
</h4>
<Checkboxes
options={value.map((summarizer) => [
summarizer,
summarizers[summarizer].readable,
settings[summarizer],
])}
toggleOption={toggleSetting}
/>
</div>
<div className="margin-between-10">
{Object.values(summarizers).length ? (
filteredKeys.map((key) => (
<Model
key={key}
info={summarizers[key]}
onClick={() => selectSummarizer(key)}
isSet={settings[key]}
/>
))
) : (
<div>no summarizers configured</div>
)}
</div>
{/* End model checkbox lists */}
{/* Start summary options container */}
<div className="colored-header" style={{ marginTop: "30px" }}>
Selected Summarizers
</div>
<div className="margin-between-5" style={{ marginLeft: "20px", marginBottom: "10px" }}>
{chosenModels.map((model) => {
const name = summarizers[model].readable;
return (
<DismissableBadge onClick={() => unselectSummarizer(model)} key={model}>
<a
href="/#"
className="nostyle"
onClick={(e) => {
e.preventDefault();
setSelectedSummarizer(model);
}}
>
{name}
</a>
</DismissableBadge>
);
})}
</div>
{selectedSummarizer && (
<PluginCard plugin={summarizers[selectedSummarizer]} inline={false} />
)}
<div>
<div
className="uk-flex uk-flex-row"
......@@ -191,18 +215,16 @@ const InputDocument = ({ summarize, isComputing }) => {
) : (
<button
className="uk-button uk-button-primary"
disabled={!documentText || !anyModelSet()}
onClick={() => summarize(documentText, getSetModels(settings), percentage)}
disabled={!documentText || !chosenModels.length}
onClick={() => summarize(documentText, chosenModels, percentage)}
>
Summarize
</button>
)}
</div>
</div>
{/* End summary options container */}
</div>
</div>
{/* End model lists container */}
</div>
</div>
);
......@@ -491,7 +513,7 @@ const Summarize = () => {
setComputing(true);
try {
const response = await summarizeRequest(requestText, models, ratio);
const { summaries, original, url } = response
const { summaries, original, url } = response;
if (Object.values(summaries).every((summarySentences) => !summarySentences.length)) {
throw new Error("No summaries could be generated. The input is probably too short.");
}
......
......@@ -16,7 +16,7 @@ const AddModel = ({ style, file, setFile, lines, linesAreSame, addModel }) => {
className="uk-flex uk-flex-column"
style={{ padding: "10px", border: "1px solid", ...style }}
>