Skip to content

Commit

Permalink
Fix log downloading
Browse files Browse the repository at this point in the history
  • Loading branch information
jmerle committed Apr 2, 2024
1 parent 7385cf1 commit 2aac7d1
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 34 deletions.
30 changes: 16 additions & 14 deletions src/pages/home/AlgorithmDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Accordion, Button, Group, MantineColor, Text } from '@mantine/core';
import axios from 'axios';
import { ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import { ErrorAlert } from '../../components/ErrorAlert';
Expand All @@ -7,16 +8,21 @@ import { useActualColorScheme } from '../../hooks/use-actual-color-scheme.ts';
import { useAsync } from '../../hooks/use-async.ts';
import { AlgorithmSummary } from '../../models.ts';
import { useStore } from '../../store.ts';
import { downloadAlgorithmResults, parseAlgorithmLogs } from '../../utils/algorithm.ts';
import { authenticatedAxios } from '../../utils/axios.ts';
import {
downloadAlgorithmLogs,
downloadAlgorithmResults,
getAlgorithmLogsUrl,
parseAlgorithmLogs,
} from '../../utils/algorithm.ts';
import { formatTimestamp } from '../../utils/format.ts';

export interface AlgorithmDetailProps {
position: number;
algorithm: AlgorithmSummary;
proxy: string;
}

export function AlgorithmDetail({ position, algorithm }: AlgorithmDetailProps): ReactNode {
export function AlgorithmDetail({ position, algorithm, proxy }: AlgorithmDetailProps): ReactNode {
const setAlgorithm = useStore(state => state.setAlgorithm);

const navigate = useNavigate();
Expand All @@ -32,14 +38,17 @@ export function AlgorithmDetail({ position, algorithm }: AlgorithmDetailProps):
break;
}

const downloadLogs = useAsync<void>(async () => {
await downloadAlgorithmLogs(algorithm.id);
});

const downloadResults = useAsync<void>(async () => {
await downloadAlgorithmResults(algorithm.id);
});

const openInVisualizer = useAsync<void>(async () => {
const logsResponse = await authenticatedAxios.get(
`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/submission/logs/${algorithm.id}`,
);
const logsUrl = await getAlgorithmLogsUrl(algorithm.id);
const logsResponse = await axios.get(proxy + logsUrl);

setAlgorithm(parseAlgorithmLogs(logsResponse.data, algorithm));
navigate('/visualizer');
Expand All @@ -60,14 +69,7 @@ export function AlgorithmDetail({ position, algorithm }: AlgorithmDetailProps):
<Accordion.Panel>
{openInVisualizer.error && <ErrorAlert error={openInVisualizer.error} mb="xs" />}
<Group grow mb="xs">
<Button
variant="outline"
component="a"
href={`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/submission/logs/${algorithm.id}`}
download
target="_blank"
rel="noreferrer"
>
<Button variant="outline" onClick={downloadLogs.call} loading={downloadLogs.loading}>
Download logs
</Button>
<Button variant="outline" onClick={downloadResults.call} loading={downloadResults.loading}>
Expand Down
5 changes: 3 additions & 2 deletions src/pages/home/AlgorithmList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import { AlgorithmDetail } from './AlgorithmDetail.tsx';

export interface AlgorithmListProps {
algorithms: AlgorithmSummary[];
proxy: string;
}

export function AlgorithmList({ algorithms }: AlgorithmListProps): ReactNode {
export function AlgorithmList({ algorithms, proxy }: AlgorithmListProps): ReactNode {
if (algorithms.length === 0) {
return <Text mt="md">No algorithms found</Text>;
}

return (
<Accordion variant="contained" defaultValue={algorithms[0].id} mt="md">
{algorithms.map((algorithm, i) => (
<AlgorithmDetail key={i} position={algorithms.length - i} algorithm={algorithm} />
<AlgorithmDetail key={i} position={algorithms.length - i} algorithm={algorithm} proxy={proxy} />
))}
</Accordion>
);
Expand Down
22 changes: 19 additions & 3 deletions src/pages/home/LoadFromProsperity.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, Code, Kbd, PasswordInput, Select, Text } from '@mantine/core';
import { Anchor, Button, Code, Kbd, PasswordInput, Select, Text, TextInput } from '@mantine/core';
import { AxiosResponse } from 'axios';
import { FormEvent, ReactNode, useCallback } from 'react';
import { FormEvent, ReactNode, useCallback, useState } from 'react';
import { ErrorAlert } from '../../components/ErrorAlert.tsx';
import { useAsync } from '../../hooks/use-async.ts';
import { AlgorithmSummary } from '../../models.ts';
Expand Down Expand Up @@ -38,6 +38,8 @@ export function LoadFromProsperity(): ReactNode {
const round = useStore(state => state.round);
const setRound = useStore(state => state.setRound);

const [proxy, setProxy] = useState('https://imc-prosperity-2-visualizer-cors-anywhere.jmerle.dev/');

const loadAlgorithms = useAsync<AlgorithmSummary[]>(async (): Promise<AlgorithmSummary[]> => {
let response: AxiosResponse<AlgorithmSummary[]>;
try {
Expand Down Expand Up @@ -108,6 +110,12 @@ export function LoadFromProsperity(): ReactNode {
download algorithm logs and results. The visualizer communicates directly with the API used by the Prosperity
website and never sends data to other servers.
</Text>
{/* prettier-ignore */}
<Text>
By default the &quot;Open in visualizer&quot; button routes the HTTP request to download the algorithm&apos;s logs through a <Anchor href="https://github.com/Rob--W/cors-anywhere" target="_blank" rel="noreferrer">CORS Anywhere</Anchor> instance hosted by the creator of this visualizer.
This is necessary because the logs need to be downloaded from an AWS S3 endpoint without <Anchor href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin" target="_blank" rel="noreferrer">Access-Control-Allow-Origin</Anchor> headers that allow downloads from this visualizer.
While I promise no log data is persisted server-side, you are free to change the proxy to one hosted by yourself.
</Text>

{loadAlgorithms.error && <ErrorAlert error={loadAlgorithms.error} />}

Expand All @@ -128,12 +136,20 @@ export function LoadFromProsperity(): ReactNode {
mt="xs"
/>

<TextInput
label='"Open in visualizer" CORS Anywhere proxy'
placeholder="Proxy"
value={proxy}
onInput={e => setProxy((e.target as HTMLInputElement).value)}
mt="xs"
/>

<Button fullWidth type="submit" loading={loadAlgorithms.loading} mt="sm">
<div>Load algorithms</div>
</Button>
</form>

{loadAlgorithms.success && <AlgorithmList algorithms={loadAlgorithms.result!} />}
{loadAlgorithms.success && <AlgorithmList algorithms={loadAlgorithms.result!} proxy={proxy} />}
</HomeCard>
);
}
15 changes: 6 additions & 9 deletions src/pages/visualizer/AlgorithmSummaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { ReactNode } from 'react';
import { ScrollableCodeHighlight } from '../../components/ScrollableCodeHighlight.tsx';
import { useAsync } from '../../hooks/use-async.ts';
import { useStore } from '../../store.ts';
import { downloadAlgorithmResults } from '../../utils/algorithm.ts';
import { downloadAlgorithmLogs, downloadAlgorithmResults } from '../../utils/algorithm.ts';
import { formatTimestamp } from '../../utils/format.ts';
import { VisualizerCard } from './VisualizerCard.tsx';

export function AlgorithmSummaryCard(): ReactNode {
const algorithm = useStore(state => state.algorithm)!;
const summary = algorithm.summary!;

const downloadLogs = useAsync<void>(async () => {
await downloadAlgorithmLogs(summary.id);
});

const downloadResults = useAsync<void>(async () => {
await downloadAlgorithmResults(summary.id);
});
Expand Down Expand Up @@ -58,14 +62,7 @@ export function AlgorithmSummaryCard(): ReactNode {
</Grid.Col>
<Grid.Col span={2}>
<Group grow>
<Button
variant="outline"
component="a"
href={`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/submission/logs/${summary.id}`}
download
target="_blank"
rel="noreferrer"
>
<Button variant="outline" onClick={downloadLogs.call} loading={downloadLogs.loading}>
Download logs
</Button>
<Button variant="outline" onClick={downloadResults.call} loading={downloadResults.loading}>
Expand Down
27 changes: 21 additions & 6 deletions src/utils/algorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,20 +243,35 @@ export function parseAlgorithmLogs(logs: string, summary?: AlgorithmSummary): Al
};
}

export async function downloadAlgorithmResults(algorithmId: string): Promise<void> {
const detailsResponse = await authenticatedAxios.get(
`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/results/tutorial/${algorithmId}`,
export async function getAlgorithmLogsUrl(algorithmId: string): Promise<string> {
const urlResponse = await authenticatedAxios.get(
`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/submission/logs/${algorithmId}`,
);

const resultsUrl = detailsResponse.data.algo.summary.activitiesLog;
return urlResponse.data;
}

function downloadFile(url: string): void {
const link = document.createElement('a');
link.href = resultsUrl;
link.download = 'results.csv';
link.href = url;
link.download = new URL(url).pathname.split('/').pop()!;
link.target = '_blank';
link.rel = 'noreferrer';

document.body.appendChild(link);
link.click();
link.remove();
}

export async function downloadAlgorithmLogs(algorithmId: string): Promise<void> {
const logsUrl = await getAlgorithmLogsUrl(algorithmId);
downloadFile(logsUrl);
}

export async function downloadAlgorithmResults(algorithmId: string): Promise<void> {
const detailsResponse = await authenticatedAxios.get(
`https://bz97lt8b1e.execute-api.eu-west-1.amazonaws.com/prod/results/tutorial/${algorithmId}`,
);

downloadFile(detailsResponse.data.algo.summary.activitiesLog);
}

0 comments on commit 2aac7d1

Please sign in to comment.