-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement card view for tasks view of a dag (#44604)
* Initial commit for tasks tab. * Remove unused columns for card view. * Fetch latest dag run and then fetch corresponding task instances. * Make font to be consistent. * Pass dagRunsLimit to limit the number of dagruns. * Add recent task instance plot to card. * Fix merge conflicts and use Status component. * Make task name bold Change Last Run to Last Instance in header name Use start date and wrap it with Time tag Show try number only when greater than 1 Use pluralize for task * Refactor tooltip to a separate component. Fix variable name casing. * Fix duration when null. * Use children prop to simplify TaskInstanceTooltip wrapping. * Omit content to reuse tooltip type.
- Loading branch information
Showing
6 changed files
with
362 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/*! | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
import { Box, Text } from "@chakra-ui/react"; | ||
|
||
import type { TaskInstanceResponse } from "openapi/requests/types.gen"; | ||
import Time from "src/components/Time"; | ||
import { Tooltip, type TooltipProps } from "src/components/ui"; | ||
|
||
type Props = { | ||
readonly taskInstance: TaskInstanceResponse; | ||
} & Omit<TooltipProps, "content">; | ||
|
||
const TaskInstanceTooltip = ({ children, taskInstance }: Props) => ( | ||
<Tooltip | ||
content={ | ||
<Box> | ||
<Text>Run ID: {taskInstance.dag_run_id}</Text> | ||
<Text>Logical Date: {taskInstance.logical_date}</Text> | ||
<Text> | ||
Start Date: <Time datetime={taskInstance.start_date} /> | ||
</Text> | ||
<Text> | ||
End Date: <Time datetime={taskInstance.end_date} /> | ||
</Text> | ||
{taskInstance.try_number > 1 && ( | ||
<Text>Try Number: {taskInstance.try_number}</Text> | ||
)} | ||
<Text>Duration: {taskInstance.duration?.toFixed(2) ?? 0}s</Text> | ||
<Text>State: {taskInstance.state}</Text> | ||
</Box> | ||
} | ||
key={taskInstance.dag_run_id} | ||
positioning={{ | ||
offset: { | ||
crossAxis: 5, | ||
mainAxis: 5, | ||
}, | ||
placement: "bottom-start", | ||
}} | ||
showArrow | ||
> | ||
{children} | ||
</Tooltip> | ||
); | ||
|
||
export default TaskInstanceTooltip; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/*! | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
import { | ||
Heading, | ||
VStack, | ||
HStack, | ||
Box, | ||
SimpleGrid, | ||
Text, | ||
} from "@chakra-ui/react"; | ||
|
||
import type { | ||
TaskResponse, | ||
TaskInstanceResponse, | ||
} from "openapi/requests/types.gen"; | ||
import TaskInstanceTooltip from "src/components/TaskInstanceTooltip"; | ||
import Time from "src/components/Time"; | ||
import { Status } from "src/components/ui"; | ||
|
||
import { TaskRecentRuns } from "./TaskRecentRuns.tsx"; | ||
|
||
type Props = { | ||
readonly task: TaskResponse; | ||
readonly taskInstances: Array<TaskInstanceResponse>; | ||
}; | ||
|
||
export const TaskCard = ({ task, taskInstances }: Props) => ( | ||
<Box | ||
borderColor="border.emphasized" | ||
borderRadius={8} | ||
borderWidth={1} | ||
overflow="hidden" | ||
> | ||
<Text bg="bg.info" color="fg.info" fontWeight="bold" p={2}> | ||
{task.task_display_name ?? task.task_id} | ||
{task.is_mapped ? "[]" : undefined} | ||
</Text> | ||
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}> | ||
<VStack align="flex-start" gap={1}> | ||
<Heading color="fg.muted" fontSize="xs"> | ||
Operator | ||
</Heading> | ||
<Text fontSize="sm">{task.operator_name}</Text> | ||
</VStack> | ||
<VStack align="flex-start" gap={1}> | ||
<Heading color="fg.muted" fontSize="xs"> | ||
Trigger Rule | ||
</Heading> | ||
<Text fontSize="sm">{task.trigger_rule}</Text> | ||
</VStack> | ||
<VStack align="flex-start" gap={1}> | ||
<Heading color="fg.muted" fontSize="xs"> | ||
Last Instance | ||
</Heading> | ||
{taskInstances[0] ? ( | ||
<TaskInstanceTooltip taskInstance={taskInstances[0]}> | ||
<HStack fontSize="sm"> | ||
<Time datetime={taskInstances[0].start_date} /> | ||
{taskInstances[0].state === null ? undefined : ( | ||
<Status state={taskInstances[0].state}> | ||
{taskInstances[0].state} | ||
</Status> | ||
)} | ||
</HStack> | ||
</TaskInstanceTooltip> | ||
) : undefined} | ||
</VStack> | ||
{/* TODO: Handled mapped tasks to not plot each map index as a task instance */} | ||
{!task.is_mapped && <TaskRecentRuns taskInstances={taskInstances} />} | ||
</SimpleGrid> | ||
</Box> | ||
); |
76 changes: 76 additions & 0 deletions
76
airflow/ui/src/pages/DagsList/Dag/Tasks/TaskRecentRuns.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/*! | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
import { Box } from "@chakra-ui/react"; | ||
import { Flex } from "@chakra-ui/react"; | ||
import dayjs from "dayjs"; | ||
import duration from "dayjs/plugin/duration"; | ||
|
||
import type { TaskInstanceResponse } from "openapi/requests/types.gen"; | ||
import TaskInstanceTooltip from "src/components/TaskInstanceTooltip"; | ||
import { stateColor } from "src/utils/stateColor"; | ||
|
||
dayjs.extend(duration); | ||
|
||
const BAR_HEIGHT = 60; | ||
|
||
export const TaskRecentRuns = ({ | ||
taskInstances, | ||
}: { | ||
readonly taskInstances: Array<TaskInstanceResponse>; | ||
}) => { | ||
if (!taskInstances.length) { | ||
return undefined; | ||
} | ||
|
||
const taskInstancesWithDuration = taskInstances.map((taskInstance) => ({ | ||
...taskInstance, | ||
duration: | ||
dayjs | ||
.duration(dayjs(taskInstance.end_date).diff(taskInstance.start_date)) | ||
.asSeconds() || 0, | ||
})); | ||
|
||
const max = Math.max.apply( | ||
undefined, | ||
taskInstancesWithDuration.map((taskInstance) => taskInstance.duration), | ||
); | ||
|
||
return ( | ||
<Flex alignItems="flex-end" flexDirection="row-reverse"> | ||
{taskInstancesWithDuration.map((taskInstance) => | ||
taskInstance.state === null ? undefined : ( | ||
<TaskInstanceTooltip | ||
key={taskInstance.dag_run_id} | ||
taskInstance={taskInstance} | ||
> | ||
<Box p={1}> | ||
<Box | ||
bg={stateColor[taskInstance.state]} | ||
borderRadius="4px" | ||
height={`${(taskInstance.duration / max) * BAR_HEIGHT}px`} | ||
minHeight={1} | ||
width="4px" | ||
/> | ||
</Box> | ||
</TaskInstanceTooltip> | ||
), | ||
)} | ||
</Flex> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/*! | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
import { Heading, Skeleton, Box } from "@chakra-ui/react"; | ||
import { useParams } from "react-router-dom"; | ||
|
||
import { | ||
useTaskServiceGetTasks, | ||
useTaskInstanceServiceGetTaskInstances, | ||
useDagsServiceRecentDagRuns, | ||
} from "openapi/queries"; | ||
import type { | ||
TaskResponse, | ||
TaskInstanceResponse, | ||
} from "openapi/requests/types.gen"; | ||
import { DataTable } from "src/components/DataTable"; | ||
import type { CardDef } from "src/components/DataTable/types"; | ||
import { ErrorAlert } from "src/components/ErrorAlert"; | ||
import { pluralize } from "src/utils"; | ||
|
||
import { TaskCard } from "./TaskCard"; | ||
|
||
const cardDef = ( | ||
taskInstances?: Array<TaskInstanceResponse>, | ||
): CardDef<TaskResponse> => ({ | ||
card: ({ row }) => ( | ||
<TaskCard | ||
task={row} | ||
taskInstances={ | ||
taskInstances | ||
? taskInstances.filter( | ||
(instance: TaskInstanceResponse) => | ||
instance.task_id === row.task_id, | ||
) | ||
: [] | ||
} | ||
/> | ||
), | ||
meta: { | ||
customSkeleton: <Skeleton height="120px" width="100%" />, | ||
}, | ||
}); | ||
|
||
export const Tasks = () => { | ||
const { dagId } = useParams(); | ||
const { | ||
data, | ||
error: tasksError, | ||
isFetching, | ||
isLoading, | ||
} = useTaskServiceGetTasks({ | ||
dagId: dagId ?? "", | ||
}); | ||
|
||
// TODO: Replace dagIdPattern with dagId once supported for better matching | ||
const { data: runsData } = useDagsServiceRecentDagRuns( | ||
{ dagIdPattern: dagId ?? "", dagRunsLimit: 14 }, | ||
undefined, | ||
{ | ||
enabled: Boolean(dagId), | ||
}, | ||
); | ||
|
||
const runs = | ||
runsData?.dags.find((dagWithRuns) => dagWithRuns.dag_id === dagId) | ||
?.latest_dag_runs ?? []; | ||
|
||
// TODO: Revisit this endpoint since only 100 task instances are returned and | ||
// only duration is calculated with other attributes unused. | ||
const { data: taskInstancesResponse } = | ||
useTaskInstanceServiceGetTaskInstances( | ||
{ | ||
dagId: dagId ?? "", | ||
dagRunId: "~", | ||
logicalDateGte: runs.at(-1)?.logical_date ?? "", | ||
}, | ||
undefined, | ||
{ enabled: Boolean(runs[0]?.dag_run_id) }, | ||
); | ||
|
||
return ( | ||
<Box> | ||
<ErrorAlert error={tasksError} /> | ||
<Heading my={1} size="md"> | ||
{pluralize("Task", data ? data.total_entries : 0)} | ||
</Heading> | ||
<DataTable | ||
cardDef={cardDef(taskInstancesResponse?.task_instances.reverse())} | ||
columns={[]} | ||
data={data ? data.tasks : []} | ||
displayMode="card" | ||
isFetching={isFetching} | ||
isLoading={isLoading} | ||
modelName="Task" | ||
total={data ? data.total_entries : 0} // Todo : Disable pagination? | ||
/> | ||
</Box> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/*! | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
export { Tasks } from "./Tasks"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters