Skip to content

Commit

Permalink
Merge pull request #275 from lucafaggianelli/feature/task-data-view
Browse files Browse the repository at this point in the history
Improve task data visualization
  • Loading branch information
lucafaggianelli authored Nov 3, 2023
2 parents fdaf54b + 1e607a5 commit 6d5f155
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 73 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- Add download task data button in data view dialog (close #46)

### Fixed
- Improve task data visualization (#257)
- Check data file paths before accessing them

## [0.4.1] - 2023-10-11

### Fixed
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"dependencies": {
"@handsontable/react": "^13.1.0",
"@heroicons/react": "^2.0.16",
"@microlink/react-json-view": "^1.23.0",
"@tanstack/react-query": "^4.36.1",
"@tremor/react": "^3.9.2",
"handsontable": "^13.1.0",
Expand Down
91 changes: 75 additions & 16 deletions frontend/src/components/DataViewerDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { Suspense } from 'react'
import { Button, Text } from '@tremor/react'
import { useQuery } from '@tanstack/react-query'
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline'

import { getRunData } from '@/repository'
import { getApiUrl, getRunData, getRunDataUrl } from '@/repository'
import Dialog from './Dialog'

interface Props {
Expand All @@ -13,6 +14,57 @@ interface Props {
}

const HotTable = React.lazy(() => import('./HandsonTable.js'))
const ReactJson = React.lazy(() => import('@microlink/react-json-view'))

const JsonComponent: React.FC<{ data: any }> = ({ data }) => {
const isDark = document.documentElement.classList.contains('dark')

return (
<div className="border dark:border-slate-800 rounded-lg">
<Suspense fallback={<div>Loading table UI...</div>}>
<ReactJson
src={data}
collapseStringsAfterLength={50}
iconStyle="triangle"
theme={isDark ? 'summerfruit' : 'summerfruit:inverted'}
style={{
padding: 24,
borderRadius: 'inherit',
}}
/>
</Suspense>
</div>
)
}

const PrimitiveComponent: React.FC<{ data: any }> = ({ data }) => {
return (
<div className="border dark:border-slate-800 rounded-lg p-6">
{JSON.stringify(data)}
</div>
)
}

const DataViewer: React.FC<{ data: any }> = ({ data }) => {
if (Array.isArray(data) && typeof data[0] === 'object') {
return (
<Suspense fallback={<div>Loading table UI...</div>}>
<HotTable
data={data}
rowHeaders={true}
colHeaders={Object.keys(data[0])}
height="70vh"
width="700px"
licenseKey="non-commercial-and-evaluation"
/>
</Suspense>
)
} else if (typeof data === 'object' && data !== null) {
return <JsonComponent data={data} />
} else {
return <PrimitiveComponent data={data} />
}
}

const DataViewerDialog: React.FC<Props> = ({
runId,
Expand All @@ -32,24 +84,29 @@ const DataViewerDialog: React.FC<Props> = ({
subtitle="View data"
isOpen={open}
footer={
<Button variant="secondary" color="indigo" onClick={() => onClose()}>
Close
</Button>
<>
<Button
variant="secondary"
color="indigo"
onClick={() => onClose()}
>
Close
</Button>

<a
href={`${getApiUrl()}/${getRunDataUrl(runId, taskId)}`}
target="_blank"
className="tremor-Button-root flex-shrink-0 inline-flex justify-center items-center group font-medium outline-none rounded-tremor-default shadow-tremor-input dark:shadow-dark-tremor-input border px-4 py-2 text-sm bg-indigo-500 border-indigo-500 text-white hover:bg-indigo-600 hover:border-indigo-700 no-underline hover:text-inherit"
rel="noopener noreferrer"
>
<ArrowDownTrayIcon className="tremor-Button-icon shrink-0 h-5 w-5 -ml-1 mr-1.5" />
Download
</a>
</>
}
onClose={onClose}
>
{!query.isLoading && !query.isError && (
<Suspense fallback={<div>Loading...</div>}>
<HotTable
data={query.data}
rowHeaders={true}
colHeaders={Object.keys(query.data[0])}
height="70vh"
width="700px"
licenseKey="non-commercial-and-evaluation"
/>
</Suspense>
)}
{query.isLoading && <div>Loading...</div>}

{query.isError &&
(query.error.response.status === 404 ? (
Expand All @@ -59,6 +116,8 @@ const DataViewerDialog: React.FC<Props> = ({
Error fetching task data: {query.error.message}
</Text>
))}

{!query.isLoading && !query.isError && <DataViewer data={query.data} />}
</Dialog>
</>
)
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,16 @@ export const getLogs = (
initialData: [],
})

export const getRunDataUrl = (runId: number, taskId: string) =>
`runs/${runId}/data/${taskId}`

export const getRunData = (
runId: number,
taskId: string
): UseQueryOptions<any, HTTPError> => ({
queryKey: ['getRunData', { runId, taskId }],
queryFn: async () => {
return await get(`runs/${runId}/data/${taskId}`)
return await get(getRunDataUrl(runId, taskId))
},
})

Expand Down
Loading

0 comments on commit 6d5f155

Please sign in to comment.