Skip to content

Commit

Permalink
Merge pull request #191 from flatironinstitute/compile-model-button
Browse files Browse the repository at this point in the history
Compile Model button and CompileContextProvider
  • Loading branch information
magland authored Aug 1, 2024
2 parents 6fed1ea + 25e4902 commit 6d87ef3
Show file tree
Hide file tree
Showing 13 changed files with 402 additions and 239 deletions.
5 changes: 4 additions & 1 deletion gui/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ProjectContextProvider from "@SpCore/ProjectContextProvider";
import HomePage from "@SpPages/HomePage";
import { Analytics } from "@vercel/analytics/react";
import { BrowserRouter } from "react-router-dom";
import { CompileContextProvider } from "./CompileContext/CompileContextProvider";

const theme = createTheme();

Expand All @@ -13,7 +14,9 @@ function App() {
<ThemeProvider theme={theme}>
<div className="MainWindow">
<ProjectContextProvider>
<HomePage />
<CompileContextProvider>
<HomePage />
</CompileContextProvider>
</ProjectContextProvider>
<Analytics />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import { Cancel, Check } from "@mui/icons-material";
import CloseableDialog, {
useDialogControls,
} from "@SpComponents/CloseableDialog";
import { FunctionComponent, useCallback, useEffect, useState } from "react";
import {
FunctionComponent,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import ConfigureCompilationServerDialog from "./ConfigureCompilationServerDialog";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import { CompileContext } from "@SpCompileContext/CompileContext";

export const publicUrl = "https://trom-stan-wasm-server.magland.org";
export const localUrl = "http://localhost:8083";

export type ServerType = "public" | "local" | "custom";
type ServerType = "public" | "local" | "custom";

type CompilationServerConnectionControlProps = {
// none
Expand All @@ -20,22 +27,8 @@ type CompilationServerConnectionControlProps = {
const CompilationServerConnectionControl: FunctionComponent<
CompilationServerConnectionControlProps
> = () => {
const [stanWasmServerUrl, setStanWasmServerUrl] = useState<string>(
localStorage.getItem("stanWasmServerUrl") || publicUrl,
);

const [serverType, setServerType] = useState<ServerType>(
stanWasmServerUrl === publicUrl
? "public"
: stanWasmServerUrl === localUrl
? "local"
: "custom",
);

const { stanWasmServerUrl } = useContext(CompileContext);
const { isConnected, retryConnection } = useIsConnected(stanWasmServerUrl);
useEffect(() => {
localStorage.setItem("stanWasmServerUrl", stanWasmServerUrl);
}, [stanWasmServerUrl]);

const {
handleOpen: openDialog,
Expand All @@ -47,6 +40,8 @@ const CompilationServerConnectionControl: FunctionComponent<
retryConnection();
}, [retryConnection]);

const serverType = serverTypeForUrl(stanWasmServerUrl);

return (
<>
<IconButton onClick={openDialog} color="inherit" size="small">
Expand All @@ -68,18 +63,18 @@ const CompilationServerConnectionControl: FunctionComponent<
handleClose={closeDialog}
>
<ConfigureCompilationServerDialog
stanWasmServerUrl={stanWasmServerUrl}
setStanWasmServerUrl={setStanWasmServerUrl}
isConnected={isConnected}
onRetry={handleRetry}
choice={serverType}
setChoice={setServerType}
/>
</CloseableDialog>
</>
);
};

export const serverTypeForUrl = (url: string): ServerType => {
return url === publicUrl ? "public" : url === localUrl ? "local" : "custom";
};

const useIsConnected = (stanWasmServerUrl: string) => {
const probeUrl = `${stanWasmServerUrl}/probe`;
const [isConnected, setIsConnected] = useState<boolean>(false);
Expand All @@ -89,6 +84,11 @@ const useIsConnected = (stanWasmServerUrl: string) => {
}, []);
useEffect(() => {
setIsConnected(false);
if (!probeUrl.startsWith("http://") && !probeUrl.startsWith("https://")) {
// important to do this check because otherwise fetch may succeed because
// the server of this web app may respond with success
return;
}
(async () => {
try {
const response = await fetch(probeUrl);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
import { FunctionComponent, useCallback } from "react";
import {
localUrl,
publicUrl,
ServerType,
} from "./CompilationServerConnectionControl";
import FormControl from "@mui/material/FormControl";
import { CompileContext } from "@SpCompileContext/CompileContext";
import { Refresh } from "@mui/icons-material";
import Divider from "@mui/material/Divider";
import FormLabel from "@mui/material/FormLabel";
import RadioGroup from "@mui/material/RadioGroup";
import FormControl from "@mui/material/FormControl";
import FormControlLabel from "@mui/material/FormControlLabel";
import FormLabel from "@mui/material/FormLabel";
import IconButton from "@mui/material/IconButton";
import Radio from "@mui/material/Radio";
import RadioGroup from "@mui/material/RadioGroup";
import TextField from "@mui/material/TextField";
import IconButton from "@mui/material/IconButton";
import { Refresh } from "@mui/icons-material";
import { FunctionComponent, useCallback, useContext } from "react";
import {
localUrl,
publicUrl,
serverTypeForUrl,
} from "./CompilationServerConnectionControl";

type ConfigureCompilationServerDialogProps = {
stanWasmServerUrl: string;
setStanWasmServerUrl: (url: string) => void;
isConnected: boolean;
onRetry: () => void;
choice: ServerType;
setChoice: (choice: ServerType) => void;
};

const ConfigureCompilationServerDialog: FunctionComponent<
ConfigureCompilationServerDialogProps
> = ({
stanWasmServerUrl,
setStanWasmServerUrl,
isConnected,
onRetry,
choice,
setChoice,
}) => {
> = ({ isConnected, onRetry }) => {
const { stanWasmServerUrl, setStanWasmServerUrl } =
useContext(CompileContext);

const serverType = serverTypeForUrl(stanWasmServerUrl);

const makeChoice = useCallback(
(_: unknown, choice: string) => {
if (choice === "public") {
Expand All @@ -44,9 +39,8 @@ const ConfigureCompilationServerDialog: FunctionComponent<
} else {
return;
}
setChoice(choice);
},
[setChoice, setStanWasmServerUrl],
[setStanWasmServerUrl],
);

return (
Expand All @@ -73,7 +67,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
<FormLabel id="compilation-server-selection">
Compilation server
</FormLabel>
<RadioGroup value={choice} onChange={makeChoice}>
<RadioGroup value={serverType} onChange={makeChoice}>
<FormControlLabel
value="public"
control={<Radio />}
Expand All @@ -91,13 +85,13 @@ const ConfigureCompilationServerDialog: FunctionComponent<
/>
</RadioGroup>

{choice === "custom" && (
{serverType === "custom" && (
<div>
<p>
<TextField
variant="standard"
label="Custom server URL"
disabled={choice !== "custom"}
disabled={serverType !== "custom"}
value={stanWasmServerUrl}
onChange={(e) => setStanWasmServerUrl(e.target.value)}
/>
Expand All @@ -106,7 +100,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
)}
</FormControl>

{choice === "local" && (
{serverType === "local" && (
<div>
<p>
To start a local compilation server{" "}
Expand All @@ -119,7 +113,7 @@ const ConfigureCompilationServerDialog: FunctionComponent<
</div>
</div>
)}
{choice === "public" && (
{serverType === "public" && (
<div>
<p>
The public server <span className="details">({publicUrl})</span> is
Expand Down
29 changes: 29 additions & 0 deletions gui/src/app/CompileContext/CompileContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { createContext } from "react";

export type CompileStatus =
| "preparing"
| "compiling"
| "compiled"
| "failed"
| "";

type CompileContextType = {
compileStatus: CompileStatus;
compileMessage: string;
compiledMainJsUrl?: string;
validSyntax: boolean;
compile: () => void;
setValidSyntax: (valid: boolean) => void;
stanWasmServerUrl: string;
setStanWasmServerUrl: (url: string) => void;
};

export const CompileContext = createContext<CompileContextType>({
compileStatus: "",
compileMessage: "",
validSyntax: false,
compile: () => {},
setValidSyntax: () => {},
stanWasmServerUrl: "",
setStanWasmServerUrl: () => {},
});
102 changes: 102 additions & 0 deletions gui/src/app/CompileContext/CompileContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { ProjectContext } from "@SpCore/ProjectContextProvider";
import compileStanProgram from "@SpCompileContext/compileStanProgram";
import {
FunctionComponent,
PropsWithChildren,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import { CompileContext, CompileStatus } from "./CompileContext";

type CompileContextProviderProps = {
// none
};

const initialStanWasmServerUrl =
localStorage.getItem("stanWasmServerUrl") ||
"https://trom-stan-wasm-server.magland.org";

export const CompileContextProvider: FunctionComponent<
PropsWithChildren<CompileContextProviderProps>
> = ({ children }) => {
const { data } = useContext(ProjectContext);
const [compileStatus, setCompileStatus] = useState<CompileStatus>("");
const [
theStanFileContentThasHasBeenCompiled,
setTheStanFileContentThasHasBeenCompiled,
] = useState<string>("");
const [compileMessage, setCompileMessage] = useState<string>("");
const [compiledMainJsUrl, setCompiledMainJsUrl] = useState<
string | undefined
>(undefined);
const [validSyntax, setValidSyntax] = useState<boolean>(false);

useEffect(() => {
// if the compiled content is not the same as the current content,
// then the state should not be compiled or failed
if (data.stanFileContent !== theStanFileContentThasHasBeenCompiled) {
if (compileStatus === "compiled" || compileStatus === "failed") {
setCompileStatus("");
setCompiledMainJsUrl("");
}
}
}, [
data.stanFileContent,
theStanFileContentThasHasBeenCompiled,
compileStatus,
setCompiledMainJsUrl,
]);

const [stanWasmServerUrl, setStanWasmServerUrl] = useState<string>(
initialStanWasmServerUrl,
);
useEffect(() => {
// persist to local storage
localStorage.setItem("stanWasmServerUrl", stanWasmServerUrl);
}, [stanWasmServerUrl]);

const handleCompile = useCallback(async () => {
setCompileStatus("compiling");
await new Promise((resolve) => setTimeout(resolve, 500)); // for effect
const onStatus = (msg: string) => {
setCompileMessage(msg);
};
const { mainJsUrl } = await compileStanProgram(
stanWasmServerUrl,
data.stanFileContent,
onStatus,
);

if (!mainJsUrl) {
setCompileStatus("failed");
return;
}
setCompiledMainJsUrl(mainJsUrl);
setCompileStatus("compiled");
setTheStanFileContentThasHasBeenCompiled(data.stanFileContent);
}, [
data.stanFileContent,
setCompiledMainJsUrl,
setCompileStatus,
stanWasmServerUrl,
]);

return (
<CompileContext.Provider
value={{
compileStatus,
compileMessage,
compiledMainJsUrl,
validSyntax,
compile: handleCompile,
setValidSyntax,
stanWasmServerUrl,
setStanWasmServerUrl,
}}
>
{children}
</CompileContext.Provider>
);
};
Loading

0 comments on commit 6d87ef3

Please sign in to comment.