Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permutations #27

Merged
merged 32 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
80c8da8
Replace experiment.config and experiment.output with reference and pe…
sverhoeven Sep 11, 2024
2ff95ab
Add rough permutation components
sverhoeven Sep 11, 2024
e33012f
Merge remote-tracking branch 'origin/layout-tweaks' into 24-permutations
sverhoeven Sep 11, 2024
6640918
Move ObjectField and inflate() to own files + allow any prop to MyTex…
sverhoeven Sep 11, 2024
5b71643
Add difference icon
sverhoeven Sep 11, 2024
5059536
Show permutation runs in analysis components
sverhoeven Sep 11, 2024
640ccc7
Make clearer that when you add an experiment you a editing the refere…
sverhoeven Sep 11, 2024
ffa73c1
Implement add/edit/delete permutation and rough difference view of re…
sverhoeven Sep 11, 2024
e94ae0d
inflate does not contain jsx
sverhoeven Sep 11, 2024
75d1279
Styling + typos
sverhoeven Sep 11, 2024
29c70d4
Merge configs deeply
sverhoeven Sep 12, 2024
9974bc7
Redraw timeseries when runs change
sverhoeven Sep 12, 2024
209ca99
Remove name from config
sverhoeven Sep 12, 2024
2da2be7
Fix permutation deletion
sverhoeven Sep 12, 2024
4e9bfbb
Add promote permutation button
sverhoeven Sep 12, 2024
537b4d1
Add todo
sverhoeven Sep 12, 2024
5125378
Download experiment configuration json file and zip with config.json …
sverhoeven Sep 12, 2024
686fbfb
Another todo
sverhoeven Sep 12, 2024
628b29d
Upload experiment + Download experiment config shaped to what upload …
sverhoeven Sep 12, 2024
7c6a162
Oeps add missing file
sverhoeven Sep 12, 2024
81b089c
Merge remote-tracking branch 'origin/main' into 24-permutations
Peter9192 Sep 17, 2024
a0ce684
Update apps/class-solid/src/components/Experiment.tsx
sverhoeven Sep 17, 2024
0efb054
Moved todos to issues
sverhoeven Sep 19, 2024
f794cf2
Use experiment name in analyis
sverhoeven Sep 20, 2024
d6c7e6d
Use experiment name in download
sverhoeven Sep 20, 2024
6ebeb25
Also duplicate permutations + dont run after perm deletion
sverhoeven Sep 20, 2024
3d96a58
Convert permutations from record to array with name
sverhoeven Sep 23, 2024
2f75130
Add duplicate perm button
sverhoeven Sep 23, 2024
e40366c
Add swap button + move perm actions to dropdown
sverhoeven Sep 23, 2024
67d6fd9
Edit experiment name and description in dialog
sverhoeven Sep 23, 2024
06f857d
Dont rerender on download
sverhoeven Sep 23, 2024
d6a07be
Merge remote-tracking branch 'origin/main' into 24-permutations
sverhoeven Sep 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions apps/class-solid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@solid-primitives/refs": "^1.0.8",
"@solidjs/router": "^0.14.1",
"@solidjs/start": "^1.0.4",
"@zip.js/zip.js": "^2.7.52",
"autoprefixer": "^10.4.19",
"chart.js": "^4.4.3",
"class-variance-authority": "^0.7.0",
Expand All @@ -25,15 +26,16 @@
"tailwind-merge": "^2.4.0",
"tailwindcss": "^3.4.4",
"tailwindcss-animate": "^1.0.7",
"vinxi": "^0.3.14"
"vinxi": "^0.3.14",
"zod": "^3.23.8"
},
"engines": {
"node": ">=18"
},
"devDependencies": {
"@playwright/test": "^1.45.3",
"@types/node": "^20.13.1",
"typescript": "^5.3.3",
"serve": "^14.2.3"
"serve": "^14.2.3",
"typescript": "^5.3.3"
}
}
64 changes: 47 additions & 17 deletions apps/class-solid/src/components/Analysis.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { For, Match, Switch, createUniqueId } from "solid-js";
import { For, Match, Switch, createMemo, createUniqueId } from "solid-js";
import { analyses, experiments, setAnalyses } from "~/lib/store";
import type { Experiment } from "~/lib/store";
import { MdiCog, MdiContentCopy, MdiDelete, MdiDownload } from "./icons";
Expand Down Expand Up @@ -43,34 +43,64 @@ function deleteAnalysis(analysis: Analysis) {
* It isn't reactive; would require intercepting the signal to call chart.update()
*/
export function TimeSeriesPlot() {
const chartData = {
labels:
experiments[0].output === undefined ? undefined : experiments[0].output.t,
datasets: experiments
.filter((e) => e.output)
.map((e) => {
return {
label: e.id,
data: e.output === undefined ? [null] : e.output.h,
fill: false,
};
}),
};
const chartData = createMemo(() => {
sverhoeven marked this conversation as resolved.
Show resolved Hide resolved
return {
labels:
experiments[0].reference.output === undefined
? undefined
: experiments[0].reference.output.t,
datasets: experiments
.filter((e) => e.reference.output)
.flatMap((e) => {
const permutationRuns = e.permutations.map((perm) => {
return {
label: `${e.name}/${perm.name}`,
data: perm.output === undefined ? [null] : perm.output.h,
fill: false,
};
});
return [
{
label: e.name,
data:
e.reference.output === undefined
? [null]
: e.reference.output.h,
fill: false,
},
...permutationRuns,
];
}),
};
});

return <LineChart data={chartData} />;
return <LineChart data={chartData()} />;
}

/** Simply show the final height for each experiment that has output */
function FinalHeights() {
return (
<For each={experiments}>
{(experiment, i) => {
const h = experiment.output?.h[experiment.output.h.length - 1] || 0;
const h =
experiment.reference.output?.h[
experiment.reference.output.h.length - 1
] || 0;
return (
<div class="mb-2">
<p>
{experiment.id}: {h.toFixed()} m
{experiment.name}: {h.toFixed()} m
</p>
<For each={experiment.permutations}>
{(perm) => {
const h = perm.output?.h[perm.output.h.length - 1] || 0;
return (
<p>
{experiment.name}/{perm.name}: {h.toFixed()} m
</p>
);
}}
</For>
</div>
);
}}
Expand Down
55 changes: 0 additions & 55 deletions apps/class-solid/src/components/EditableText.tsx

This file was deleted.

121 changes: 73 additions & 48 deletions apps/class-solid/src/components/Experiment.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { Show, createMemo, createSignal, onCleanup } from "solid-js";
import {
Show,
createEffect,
createMemo,
createSignal,
onCleanup,
} from "solid-js";

import { Button, buttonVariants } from "~/components/ui/button";
import { createArchive, toConfigBlob } from "~/lib/download";
import {
type Experiment,
deleteExperiment,
duplicateExperiment,
modifyExperiment,
setExperimentDescription,
setExperimentName,
} from "~/lib/store";
import { EditableText } from "./EditableText";
import { ExperimentConfigForm } from "./ExperimentConfigForm";
import { PermutationsList } from "./PermutationsList";
import { MdiCog, MdiContentCopy, MdiDelete, MdiDownload } from "./icons";
import {
Card,
Expand All @@ -22,36 +28,42 @@ import {
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "./ui/dropdown-menu";

export function ExperimentSettingsDialog(experiment: Experiment) {
const [open, setOpen] = createSignal(experiment.output === undefined);
const [open, setOpen] = createSignal(
experiment.reference.output === undefined,
);

return (
<Dialog open={open()} onOpenChange={setOpen}>
<DialogTrigger variant="outline" as={Button<"button">}>
<DialogTrigger variant="outline" as={Button<"button">} title="Edit">
<MdiCog />
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Experiment {experiment.id}</DialogTitle>
<DialogDescription>{experiment.description}</DialogDescription>
<DialogTitle>Experiment</DialogTitle>
</DialogHeader>
<ExperimentConfigForm
id={experiment.id}
config={experiment.config}
onSubmit={async (newConfig) => {
id="experiment-form"
experiment={experiment}
onSubmit={(newConfig, name, description) => {
setOpen(false);
modifyExperiment(experiment.id, newConfig);
modifyExperiment(experiment.id, newConfig, name, description);
}}
/>
<DialogFooter>
<Button type="submit" form={experiment.id}>
<Button type="submit" form="experiment-form">
Run
</Button>
</DialogFooter>
Expand Down Expand Up @@ -89,72 +101,85 @@ function RunningIndicator() {
);
}

function DownloadExperiment(props: { experiment: Experiment }) {
function DownloadExperimentConfiguration(props: { experiment: Experiment }) {
const downloadUrl = createMemo(() => {
// Drop id and running
const data = {
name: props.experiment.name,
description: props.experiment.description,
config: props.experiment.config,
output: props.experiment.output,
};
return URL.createObjectURL(
new Blob([JSON.stringify(data, undefined, 2)], {
type: "application/json",
}),
);
return URL.createObjectURL(toConfigBlob(props.experiment));
});

onCleanup(() => {
URL.revokeObjectURL(downloadUrl());
});

const filename = `class-${props.experiment.id}.json`;
const filename = `class-${props.experiment.name}.json`;
return (
<a href={downloadUrl()} download={filename} type="application/json">
Configuration
</a>
);
}

function DownloadExperimentArchive(props: { experiment: Experiment }) {
const [url, setUrl] = createSignal<string>("");
createEffect(async () => {
const archive = await createArchive(props.experiment);
const objectUrl = URL.createObjectURL(archive);
setUrl(objectUrl);
onCleanup(() => URL.revokeObjectURL(objectUrl));
});

const filename = `class-${props.experiment.id}.zip`;
sverhoeven marked this conversation as resolved.
Show resolved Hide resolved
return (
<a
class={buttonVariants({ variant: "outline" })}
href={downloadUrl()}
download={filename}
type="application/json"
>
<MdiDownload />
<a href={url()} download={filename} type="application/json">
Config + output
</a>
);
}

function DownloadExperiment(props: { experiment: Experiment }) {
return (
<DropdownMenu>
<DropdownMenuTrigger
class={buttonVariants({ variant: "outline" })}
title="Download"
>
<MdiDownload />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<DownloadExperimentConfiguration experiment={props.experiment} />
</DropdownMenuItem>
<DropdownMenuItem>
<DownloadExperimentArchive experiment={props.experiment} />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

export function ExperimentCard(experiment: Experiment) {
return (
<Card class="w-[380px]">
<CardHeader>
<CardTitle>
<EditableText
text={experiment.name}
onChange={(name) => setExperimentName(experiment.id, name)}
/>
</CardTitle>
<CardDescription>{experiment.id}</CardDescription>
<CardTitle>{experiment.name}</CardTitle>
<CardDescription>{experiment.description}</CardDescription>
</CardHeader>
<CardContent>
<EditableText
text={experiment.description}
onChange={(description) =>
setExperimentDescription(experiment.id, description)
}
/>
<PermutationsList experiment={experiment} />
</CardContent>
<CardFooter>
<Show when={!experiment.running} fallback={<RunningIndicator />}>
<DownloadExperiment experiment={experiment} />
<ExperimentSettingsDialog {...experiment} />
<Button
variant="outline"
title="Duplicate experiment"
onClick={() => duplicateExperiment(experiment.id)}
>
<MdiContentCopy />
</Button>
<Button
variant="outline"
title="Delete experiment"
onClick={() => deleteExperiment(experiment.id)}
>
<MdiDelete />
Expand Down
Loading