Skip to content

Commit

Permalink
feature(runs): added UI to list s3 resources
Browse files Browse the repository at this point in the history
  • Loading branch information
AugustDev committed Oct 14, 2023
1 parent c3cd0cb commit 4396f12
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/app/runs/[id]/components/DataViewer/DataViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const DataViewer = ({ data }: DataViewerProps) => {
<JsonViewer
displayDataTypes={false}
displaySize={false}
// maxDisplayLength={0}
maxDisplayLength={300}
value={data}
enableClipboard={false}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/app/runs/[id]/components/Main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Configuration,
DataViewer,
General,
MentionedResources,
MetricsOverview,
Processes,
Status,
Expand Down Expand Up @@ -107,6 +108,10 @@ export const MainRun = (props: PageProps) => {
name: "Configuration",
content: <Configuration files={workflow?.configFiles || []} configText={workflow?.configText || ""} />,
},
{
name: "Resources",
content: <MentionedResources data={workflow?.params} />,
},
]
return (
<>
Expand Down
126 changes: 126 additions & 0 deletions src/app/runs/[id]/components/MentionedResources/MentionedResources.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"use client"

import { useMemo } from "react"

type MentionedResourcesProps = {
data?: any
}

type MatchedResourceLink = {
key: string
url: string
urlName: string
}

export const MentionedResources = ({ data }: MentionedResourcesProps) => {
const findS3Entries = (dict: any) => {
let result: { [key: string]: string } = {}

for (let key in dict) {
if (typeof dict[key] === "string" && dict[key].includes("s3://")) {
result[key] = dict[key]
}
if (typeof dict[key] === "object" && dict[key] !== null) {
let nestedResults = findS3Entries(dict[key])
for (let nestedKey in nestedResults) {
result[nestedKey] = nestedResults[nestedKey]
}
}
}

return result
}

const resolveLinks = (dict: any) => {
let result: MatchedResourceLink[] = []

for (let key in dict) {
if (dict[key].includes("s3://")) {
const urlName = dict[key].split("s3://")[1]
const url = `https://s3.console.aws.amazon.com/s3/buckets/${urlName}`
result.push({
key: key,
url: cleanUrl(url),
urlName: dict[key],
})
}
}
return result
}

const priorityKeywords = ["output"]

const hasPriority = (key: string): boolean => {
return priorityKeywords.some((keyword) => key.includes(keyword))
}

const sortLinks = (links: MatchedResourceLink[]): MatchedResourceLink[] => {
return links.sort((a, b) => {
if (hasPriority(a.key) && !hasPriority(b.key)) {
return -1
} else if (!hasPriority(a.key) && hasPriority(b.key)) {
return 1
}
return 0
})
}

const cleanUrl = (url: string): string => {
// Remove duplicate slashes
url = url.replace(/([^:]\/)\/+/g, "$1")

// for S3 directories
if (url.includes("s3")) {
const extensionPattern = /\.[0-9a-z]+$/i

// If the URL doesn't have a file extension
if (!extensionPattern.test(url)) {
// Ensure there's a slash at the end
if (!url.endsWith("/")) {
url += "/"
}
}
}

console.log(url)
return url
}

const matches = useMemo(() => (data ? findS3Entries(data) : {}), [data])
const resolvedLinks = useMemo(() => (matches ? resolveLinks(matches) : []), [matches])
const sortedLinks = useMemo(() => (resolvedLinks ? sortLinks(resolvedLinks) : []), [resolvedLinks])

return (
<div className="mx-auto">
<p className="mb-4 text-gray-600 font-sm">
Below are paths used in Nextflow resources and links to resources (S3) if possible.
</p>
<table className="min-w-full divide-y divide-gray-200">
<tbody className="bg-white divide-y divide-gray-200">
{sortedLinks.map((link) => (
<tr key={link.key}>
<td className="py-2">{link.key}</td>
<td className="py-2">
<a href={link.url} className="text-blue-500 underline text-sm" target="_blank">
{link.urlName}
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
// <div>
// <p>Mentioned resources extracts paths used in Nextflow configuration and constructs URLs if possible.</p>
// <ul>
// {Object.entries(matches).map(([key, value]) => (
// <li key={key}>
// <p>
// {key}- {JSON.stringify(value)}
// </p>
// </li>
// ))}
// </ul>
// </div>
)
}
1 change: 1 addition & 0 deletions src/app/runs/[id]/components/MentionedResources/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MentionedResources } from "./MentionedResources"
46 changes: 23 additions & 23 deletions src/app/runs/[id]/components/TasksTable/TasksTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,37 @@ export const TasksTable = ({ tasks, className, onTaskClick }: TasksTableProps) =
<div className="font-semibold text-left">Process</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-left">Tag</div>
<div className="font-semibold text-center">Duration</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-left">Task Id</div>
<div className="font-semibold text-center">Realtime</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Hash</div>
<div className="font-semibold text-center">% CPU</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Exit</div>
<div className="font-semibold text-center">% Memory</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Container</div>
<div className="font-semibold text-left">Tag</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Native Id</div>
<div className="font-semibold text-left">Task Id</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Submitted</div>
<div className="font-semibold text-center">Hash</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Duration</div>
<div className="font-semibold text-center">Exit</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Realtime</div>
<div className="font-semibold text-center">Container</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">% CPU</div>
<div className="font-semibold text-center">Native Id</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">% Memory</div>
<div className="font-semibold text-center">Submitted</div>
</th>
<th className="p-2 whitespace-nowrap">
<div className="font-semibold text-center">Peak RSS</div>
Expand Down Expand Up @@ -87,38 +87,38 @@ export const TasksTable = ({ tasks, className, onTaskClick }: TasksTableProps) =
<div className="text-left">{task.data.process}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.tag}</div>
<div className="text-left">{formatDuration(task.data.duration, "ms")}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.id}</div>
<div className="text-left">{formatDuration(task.data.realtime, "ms")}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.hash}</div>
<div className="text-left">{task.data.pcpu}</div>
</td>

<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.exit}</div>
<div className="text-center">{task.data.pmem}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.container}</div>
<div className="text-left">{task.data.tag}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.nativeId}</div>
<div className="text-left">{task.id}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{fullDateTime(task.data.submit)}</div>
<div className="text-left">{task.data.hash}</div>
</td>

<td className="p-2 whitespace-nowrap">
<div className="text-left">{formatDuration(task.data.duration, "ms")}</div>
<div className="text-left">{task.data.exit}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{formatDuration(task.data.realtime, "ms")}</div>
<div className="text-left">{task.data.container}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-left">{task.data.pcpu}</div>
<div className="text-left">{task.data.nativeId}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-center">{task.data.pmem}</div>
<div className="text-left">{fullDateTime(task.data.submit)}</div>
</td>
<td className="p-2 whitespace-nowrap">
<div className="text-center">{bytes(task.data.peakRss ?? 0)}</div>
Expand Down
1 change: 1 addition & 0 deletions src/app/runs/[id]/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from "./TasksTable"
export * from "./TaskDetails"
export * from "./PlotBox"
export * from "./Metrics"
export * from "./MentionedResources"

0 comments on commit 4396f12

Please sign in to comment.