You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by mo...@apache.org on 2025/03/18 12:08:01 UTC

(airflow) branch main updated: feat: Add dagrun's end_date and duration to OL facet (#47901)

This is an automated email from the ASF dual-hosted git repository.

mobuchowski pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 7b20f331a86 feat: Add dagrun's end_date and duration to OL facet (#47901)
7b20f331a86 is described below

commit 7b20f331a86dfbc5930ca22c755c7998d867bed1
Author: Kacper Muda <mu...@gmail.com>
AuthorDate: Tue Mar 18 13:07:54 2025 +0100

    feat: Add dagrun's end_date and duration to OL facet (#47901)
---
 .../airflow/providers/openlineage/utils/utils.py   | 11 +++++++++
 .../tests/unit/openlineage/utils/test_utils.py     | 28 ++++++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/providers/openlineage/src/airflow/providers/openlineage/utils/utils.py b/providers/openlineage/src/airflow/providers/openlineage/utils/utils.py
index 417db28aed7..b29d4fe0463 100644
--- a/providers/openlineage/src/airflow/providers/openlineage/utils/utils.py
+++ b/providers/openlineage/src/airflow/providers/openlineage/utils/utils.py
@@ -363,8 +363,19 @@ class DagRunInfo(InfoJsonEncodable):
         "run_id",
         "run_type",
         "start_date",
+        "end_date",
     ]
 
+    casts = {"duration": lambda dagrun: DagRunInfo.duration(dagrun)}
+
+    @classmethod
+    def duration(cls, dagrun: DagRun) -> float | None:
+        if not getattr(dagrun, "end_date", None) or not isinstance(dagrun.end_date, datetime.datetime):
+            return None
+        if not getattr(dagrun, "start_date", None) or not isinstance(dagrun.start_date, datetime.datetime):
+            return None
+        return (dagrun.end_date - dagrun.start_date).total_seconds()
+
 
 class TaskInstanceInfo(InfoJsonEncodable):
     """Defines encoding TaskInstance object to JSON."""
diff --git a/providers/openlineage/tests/unit/openlineage/utils/test_utils.py b/providers/openlineage/tests/unit/openlineage/utils/test_utils.py
index e1da5438ad5..f38eeec232b 100644
--- a/providers/openlineage/tests/unit/openlineage/utils/test_utils.py
+++ b/providers/openlineage/tests/unit/openlineage/utils/test_utils.py
@@ -21,6 +21,8 @@ import datetime
 import pathlib
 from unittest.mock import MagicMock, patch
 
+import pytest
+
 from airflow import DAG
 from airflow.decorators import task
 from airflow.models.baseoperator import BaseOperator
@@ -28,6 +30,7 @@ from airflow.models.dagrun import DagRun
 from airflow.models.taskinstance import TaskInstance, TaskInstanceState
 from airflow.providers.openlineage.plugins.facets import AirflowDagRunFacet, AirflowJobFacet
 from airflow.providers.openlineage.utils.utils import (
+    DagRunInfo,
     _get_task_groups_details,
     _get_tasks_details,
     get_airflow_dag_run_facet,
@@ -133,6 +136,7 @@ def test_get_airflow_dag_run_facet():
     dagrun_mock.run_id = "manual_2024-06-01T00:00:00+00:00"
     dagrun_mock.run_type = DagRunType.MANUAL
     dagrun_mock.start_date = datetime.datetime(2024, 6, 1, 1, 2, 4, tzinfo=datetime.timezone.utc)
+    dagrun_mock.end_date = datetime.datetime(2024, 6, 1, 1, 2, 14, 34172, tzinfo=datetime.timezone.utc)
 
     result = get_airflow_dag_run_facet(dagrun_mock)
 
@@ -161,11 +165,35 @@ def test_get_airflow_dag_run_facet():
                 "run_id": "manual_2024-06-01T00:00:00+00:00",
                 "run_type": "manual",
                 "start_date": "2024-06-01T01:02:04+00:00",
+                "end_date": "2024-06-01T01:02:14.034172+00:00",
+                "duration": 10.034172,
             },
         )
     }
 
 
+@pytest.mark.parametrize(
+    ("dag_run_attrs", "expected_duration"),
+    (
+        ({"start_date": None, "end_date": None}, None),
+        ({"start_date": datetime.datetime(2025, 1, 1), "end_date": None}, None),
+        ({"start_date": None, "end_date": datetime.datetime(2025, 1, 1)}, None),
+        ({"start_date": "2024-06-01T01:02:04+00:00", "end_date": "2024-06-01T01:02:14.034172+00:00"}, None),
+        (
+            {
+                "start_date": datetime.datetime(2025, 1, 1, 6, 1, 1, tzinfo=datetime.timezone.utc),
+                "end_date": datetime.datetime(2025, 1, 1, 6, 1, 12, 3456, tzinfo=datetime.timezone.utc),
+            },
+            11.003456,
+        ),
+    ),
+)
+def test_dag_run_duration(dag_run_attrs, expected_duration):
+    dag_run = MagicMock(**dag_run_attrs)
+    result = DagRunInfo.duration(dag_run)
+    assert result == expected_duration
+
+
 def test_get_fully_qualified_class_name_serialized_operator():
     op_module_path = BASH_OPERATOR_PATH
     op_name = "BashOperator"