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

UI fixes #136

Merged
merged 3 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 15 additions & 9 deletions frontend/src/components/LogViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'
import {
Badge,
Bold,
Color,
Flex,
Grid,
Expand All @@ -19,7 +20,11 @@ import useWebSocket from 'react-use-websocket'

import { getLogs, getWebsocketUrl } from '@/repository'
import { LogEntry, LogLevel, Pipeline, WebSocketMessage } from '@/types'
import { formatTimestamp, getTasksColors } from '@/utils'
import {
formatNumber,
formatTimestamp,
getTasksColors,
} from '@/utils'
import TracebackInfoDialog from './TracebackInfoDialog'

interface Props {
Expand Down Expand Up @@ -154,14 +159,15 @@ const LogViewer: React.FC<Props> = ({ pipeline, runId }) => {
return (
<TableRow key={log.id}>
<TableCell>
<span className="font-mono text-xs text-slate-500">
{formatTimestamp(log.timestamp)}
</span>
{duration >= 0 && (
<span className="font-mono text-xs text-slate-500 ml-2">
+{duration} ms
</span>
)}
<Text className="font-mono text-xs">
<span>{formatTimestamp(log.timestamp)}</span>

{duration >= 0 && (
<span className="text-slate-400 ml-2">
+{formatNumber(duration)} ms
</span>
)}
</Text>
</TableCell>
<TableCell>
<Badge size="xs" color={LOG_LEVELS_COLORS[log.level]}>
Expand Down
29 changes: 25 additions & 4 deletions frontend/src/components/RunsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Text,
Title,
} from '@tremor/react'
import { formatDistanceToNow, differenceInDays } from 'date-fns'
import { useCallback, useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import useWebSocket from 'react-use-websocket'
Expand Down Expand Up @@ -96,11 +97,12 @@ const RunsList: React.FC<Props> = ({ pipelineId, runs: _runs, triggerId }) => {
<Card>
<Title>Runs</Title>

<Table>
<TableHead>
<Table className="overflow-auto max-h-[50vh]">
<TableHead className="sticky top-0 bg-white shadow">
<TableRow>
<TableHeaderCell className="text-right">#</TableHeaderCell>
<TableHeaderCell>Status</TableHeaderCell>
{!pipelineId && <TableHeaderCell>Pipeline</TableHeaderCell>}
{!triggerId && <TableHeaderCell>Trigger</TableHeaderCell>}
<TableHeaderCell>Started at</TableHeaderCell>
<TableHeaderCell className="text-right">Duration</TableHeaderCell>
Expand All @@ -121,6 +123,18 @@ const RunsList: React.FC<Props> = ({ pipelineId, runs: _runs, triggerId }) => {
<TableCell>
<StatusBadge status={run.status} />
</TableCell>
{!pipelineId && (
<TableCell>
<Link
to={`/pipelines/${run.pipeline_id}`}
className="link--arrow"
title="View pipeline details"
onClick={(event) => event.stopPropagation()}
>
{run.pipeline_id}
</Link>
</TableCell>
)}
{!triggerId && (
<TableCell>
<Link
Expand All @@ -133,8 +147,15 @@ const RunsList: React.FC<Props> = ({ pipelineId, runs: _runs, triggerId }) => {
</Link>
</TableCell>
)}
<TableCell>
<Text>{formatDateTime(run.start_time)}</Text>
<TableCell title={formatDateTime(run.start_time)}>
<Text>
{differenceInDays(new Date(), run.start_time) <= 1
? formatDistanceToNow(run.start_time, {
addSuffix: true,
includeSeconds: true,
})
: formatDateTime(run.start_time)}
</Text>
</TableCell>
<TableCell className="text-right">
{run.status !== 'running' ? (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Card, CategoryBar, Flex, Grid, Metric, Text, Title } from '@tremor/react'
import {
Bold,
Card,
CategoryBar,
Flex,
Grid,
Metric,
Text,
Title,
} from '@tremor/react'
import { addMilliseconds, isSameDay } from 'date-fns'
import { useParams } from 'react-router-dom'
import useWebSocket from 'react-use-websocket'
import { useEffect } from 'react'
Expand All @@ -12,7 +22,11 @@ import RunsTasksList from '@/components/Tasks'
import { MANUAL_TRIGGER } from '@/constants'
import { getPipeline, getRun, getWebsocketUrl } from '@/repository'
import { Pipeline, Trigger, WebSocketMessage } from '@/types'
import { TASKS_COLORS, getTasksColors } from '@/utils'
import {
TASKS_COLORS,
formatDate,
formatTimestamp,
} from '@/utils'

const RunViewPage = () => {
const { lastJsonMessage } = useWebSocket(getWebsocketUrl().toString())
Expand Down Expand Up @@ -66,8 +80,15 @@ const RunViewPage = () => {
return <div>Trigger not found</div>
}

const totalTasksDuration = (run.tasks_run || []).reduce((tot, cur) => tot + cur.duration, 0)
const tasksRunDurations = (run.tasks_run || []).map(tr => tr.duration / totalTasksDuration * 100)
const totalTasksDuration = (run.tasks_run || []).reduce(
(tot, cur) => tot + cur.duration,
0
)
const tasksRunDurations = (run.tasks_run || []).map((tr) =>
totalTasksDuration ? (tr.duration / totalTasksDuration) * 100 : 0
)

const runEndTime = addMilliseconds(run.start_time, run.duration)

return (
<PageLayout
Expand Down Expand Up @@ -97,6 +118,24 @@ const RunViewPage = () => {
showLabels={false}
className="mt-3"
/>

<Flex alignItems="start" className="mt-2">
<div>
<Text>
<Bold>{formatTimestamp(run.start_time)}</Bold>
</Text>
<Text className="mt-1">{formatDate(run.start_time)}</Text>
</div>

<div className="text-right">
<Text>
<Bold>{formatTimestamp(runEndTime)}</Bold>
</Text>
{!isSameDay(run.start_time, runEndTime) && (
<Text>{formatDate(runEndTime)}</Text>
)}
</div>
</Flex>
</Card>
</Grid>

Expand Down
4 changes: 4 additions & 0 deletions frontend/src/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export const getRun = async (runId: number): Promise<PipelineRun> => {
export const getLogs = async (runId: number): Promise<LogEntry[]> => {
const rawLogs = await client.get<string>(`/runs/${runId}/logs`)

if (!rawLogs) {
return []
}

// Logs data is in JSONL format (1 JSON object per line)
return rawLogs.split('\n').map((line, i) => {
const parsed = JSON.parse(line)
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ export const formatDateTime = (date: Date) =>

export const formatTimestamp = (date: Date) =>
format(date, 'HH:mm:ss.SSS')

export const formatDate = (date: Date) =>
format(date, 'd MMM yyyy')

const numberFormatter = new Intl.NumberFormat()

export const formatNumber = (value: number) =>
numberFormatter.format(value)
2 changes: 1 addition & 1 deletion src/plombery/database/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Config:

class PipelineRun(PipelineRunBase):
id: int
duration: int
duration: float
tasks_run: List[TaskRun]


Expand Down
4 changes: 2 additions & 2 deletions src/plombery/schemas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import List, Optional
from enum import Enum

from pydantic import BaseModel, Field, NonNegativeInt
from pydantic import BaseModel, Field, NonNegativeFloat


class PipelineRunStatus(str, Enum):
Expand All @@ -12,7 +12,7 @@ class PipelineRunStatus(str, Enum):


class TaskRun(BaseModel):
duration: Optional[NonNegativeInt]
duration: Optional[NonNegativeFloat]
"""Task duration in milliseconds"""
has_output: bool = False
"""True if the task generated an output"""
Expand Down