You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by bb...@apache.org on 2022/08/05 14:42:05 UTC
[airflow] branch main updated: Show dataset-triggered next run details (#25549)
This is an automated email from the ASF dual-hosted git repository.
bbovenzi 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 45e5150714 Show dataset-triggered next run details (#25549)
45e5150714 is described below
commit 45e5150714e0a5a8e82e3fa6d0b337b92cbeb067
Author: Brent Bovenzi <br...@gmail.com>
AuthorDate: Fri Aug 5 15:41:49 2022 +0100
Show dataset-triggered next run details (#25549)
* add dataset-triggered next run details modal
* use correct url
* add tooltip telling users to click
* use macro for modal
* fix datasets url
---
airflow/www/static/css/main.css | 5 ++
airflow/www/static/js/dag.js | 6 ++
airflow/www/static/js/dags.js | 7 +++
airflow/www/static/js/openDatasetModal.js | 67 ++++++++++++++++++++++
airflow/www/templates/airflow/dag.html | 20 ++++++-
airflow/www/templates/airflow/dags.html | 18 +++++-
.../templates/airflow/dataset_next_run_modal.html | 56 ++++++++++++++++++
7 files changed, 175 insertions(+), 4 deletions(-)
diff --git a/airflow/www/static/css/main.css b/airflow/www/static/css/main.css
index e735b7b9bb..2557f521e6 100644
--- a/airflow/www/static/css/main.css
+++ b/airflow/www/static/css/main.css
@@ -473,3 +473,8 @@ details summary {
max-height: 300px;
overflow-y: auto;
}
+
+.next-dataset-triggered:hover {
+ cursor: pointer;
+ background-color: #cbcbcb;
+}
diff --git a/airflow/www/static/js/dag.js b/airflow/www/static/js/dag.js
index c6d7f4223f..d4e5dc2255 100644
--- a/airflow/www/static/js/dag.js
+++ b/airflow/www/static/js/dag.js
@@ -21,6 +21,7 @@
import { getMetaValue } from './utils';
import { approxTimeFromNow, formatDateTime } from './datetime_utils';
+import openDatasetModal from './openDatasetModal';
function updateQueryStringParameter(uri, key, value) {
const re = new RegExp(`([?&])${key}=.*?(&|$)`, 'i');
@@ -399,3 +400,8 @@ $('#next-run').on('mouseover', () => {
return newTitle;
});
});
+
+$('.next-dataset-triggered').on('click', (e) => {
+ const summary = $(e.target).data('summary');
+ openDatasetModal(dagId, summary || '');
+});
diff --git a/airflow/www/static/js/dags.js b/airflow/www/static/js/dags.js
index bdf52b6da9..c6817d038e 100644
--- a/airflow/www/static/js/dags.js
+++ b/airflow/www/static/js/dags.js
@@ -23,6 +23,7 @@
import { getMetaValue } from './utils';
import tiTooltip from './task_instances';
import { approxTimeFromNow, formatDateTime } from './datetime_utils';
+import openDatasetModal from './openDatasetModal';
const DAGS_INDEX = getMetaValue('dags_index');
const ENTER_KEY_CODE = 13;
@@ -537,3 +538,9 @@ $('#auto_refresh').change(() => {
}
startOrStopRefresh();
});
+
+$('.next-dataset-triggered').on('click', (e) => {
+ const dagId = $(e.target).data('dag-id');
+ const summary = $(e.target).data('summary');
+ if (dagId) openDatasetModal(dagId, summary || '');
+});
diff --git a/airflow/www/static/js/openDatasetModal.js b/airflow/www/static/js/openDatasetModal.js
new file mode 100644
index 0000000000..6e6c824e1d
--- /dev/null
+++ b/airflow/www/static/js/openDatasetModal.js
@@ -0,0 +1,67 @@
+/*!
+ * 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.
+ */
+
+/* global $, isoDateToTimeEl, document */
+
+import { getMetaValue } from './utils';
+
+function openDatasetModal(dagId, summary) {
+ const datasetsUrl = getMetaValue('datasets_url');
+ let nextRunUrl = getMetaValue('next_run_datasets_url');
+ $('#datasets_tbody').empty();
+ $('#datasets_error').hide();
+ $('#datasetNextRunModal').modal({});
+ $('#dag_id').text(dagId);
+ $('#next_run_summary').text(summary);
+ $('#datasets-loading-dots').css('display', 'inline-block');
+ if (dagId) {
+ if (nextRunUrl.includes('__DAG_ID__')) {
+ nextRunUrl = nextRunUrl.replace('__DAG_ID__', dagId);
+ }
+ $.get(nextRunUrl)
+ .done(
+ (datasets) => {
+ datasets.forEach((d) => {
+ const row = document.createElement('tr');
+
+ const uriCell = document.createElement('td');
+ const datasetLink = document.createElement('a');
+ datasetLink.href = `${datasetsUrl}?dataset_id=${d.id}`;
+ datasetLink.innerText = d.uri;
+ uriCell.append(datasetLink);
+
+ const timeCell = document.createElement('td');
+ if (d.created_at) timeCell.append(isoDateToTimeEl(d.created_at));
+
+ row.append(uriCell);
+ row.append(timeCell);
+ $('#datasets-loading-dots').hide();
+ $('#datasets_tbody').append(row);
+ });
+ },
+ ).fail((response, textStatus, err) => {
+ $('#datasets-loading-dots').hide();
+ const description = (response.responseJSON && response.responseJSON.error) || 'Something went wrong.';
+ $('#datasets_error_msg').text(`${textStatus}: ${err} ${description}`);
+ $('#datasets_error').show();
+ });
+ }
+}
+
+export default openDatasetModal;
diff --git a/airflow/www/templates/airflow/dag.html b/airflow/www/templates/airflow/dag.html
index 7501eeb295..20a2f06e54 100644
--- a/airflow/www/templates/airflow/dag.html
+++ b/airflow/www/templates/airflow/dag.html
@@ -18,6 +18,7 @@
#}
{% extends base_template %}
+{% from 'airflow/dataset_next_run_modal.html' import dataset_next_run_modal %}
{% from 'appbuilder/dag_docs.html' import dag_docs %}
{% block page_title %}{{ dag.dag_id }} - {{ appbuilder.app_name }}{% endblock %}
@@ -54,8 +55,10 @@
<meta name="success_url" content="{{ url_for('Airflow.success') }}">
<meta name="confirm_url" content="{{ url_for('Airflow.confirm') }}">
<meta name="grid_data_url" content="{{ url_for('Airflow.grid_data') }}">
+ <meta name="next_run_datasets_url" content="{{ url_for('Airflow.next_run_datasets', dag_id=dag.dag_id) }}">
<meta name="run_url" content="{{ url_for('Airflow.run') }}">
<meta name="grid_url" content="{{ url_for('Airflow.grid', dag_id=dag.dag_id) }}">
+ <meta name="datasets_url" content="{{ url_for('Airflow.datasets') }}">
<meta name="grid_url_no_root" content="{{ url_for('Airflow.grid', dag_id=dag.dag_id, num_runs=num_runs_arg, base_date=base_date_arg) }}">
<meta name="dag_details_url" content="{{ url_for('Airflow.dag_details', dag_id=dag.dag_id) }}">
<meta name="graph_url" content="{{ url_for('Airflow.graph', dag_id=dag.dag_id, root=root) }}">
@@ -134,9 +137,18 @@
</p>
{% endif %}
{% if dag_model is defined and dag_model.schedule_interval is defined and dag_model.schedule_interval == 'Dataset' %}
- <p class="label label-default" style="margin-left: 5px">
- Next Run: {{ dag_model.get_dataset_triggered_next_run_info() }}
- </p>
+ <span
+ class="js-tooltip"
+ title="Click to see dataset details."
+ >
+ <p
+ class="label label-default next-dataset-triggered"
+ style="margin-left: 5px;"
+ data-summary="{{ dag_model.get_dataset_triggered_next_run_info() }}"
+ >
+ Next Run: {{ dag_model.get_dataset_triggered_next_run_info() }}
+ </p>
+ </span>
{% endif %}
</h4>
</div>
@@ -431,6 +443,8 @@
</div>
</div>
</div>
+ <!-- Modal for dataset-triggered next run -->
+ {{ dataset_next_run_modal(id='dataset-next-run-modal') }}
{% endblock %}
{% block tail %}
{{ super() }}
diff --git a/airflow/www/templates/airflow/dags.html b/airflow/www/templates/airflow/dags.html
index df4439318c..32ed7e9f8a 100644
--- a/airflow/www/templates/airflow/dags.html
+++ b/airflow/www/templates/airflow/dags.html
@@ -19,6 +19,7 @@
{% extends base_template %}
{% from 'appbuilder/loading_dots.html' import loading_dots %}
+{% from 'airflow/dataset_next_run_modal.html' import dataset_next_run_modal %}
{% from 'airflow/_messages.html' import show_message %}
{%- macro sortable_column(display_name, attribute_name) -%}
@@ -68,6 +69,8 @@
<meta name="dag_stats_url" content="{{ url_for('Airflow.dag_stats') }}">
<meta name="task_stats_url" content="{{ url_for('Airflow.task_stats') }}">
<meta name="grid_url" content="{{ url_for('Airflow.grid', dag_id='__DAG_ID__') }}">
+ <meta name="datasets_url" content="{{ url_for('Airflow.datasets') }}">
+ <meta name="next_run_datasets_url" content="{{ url_for('Airflow.next_run_datasets', dag_id='__DAG_ID__') }}">
{% endblock %}
{% block head_css %}
@@ -290,7 +293,18 @@
</td>
<td class="text-nowrap">
{% if dag.dag_id in dataset_triggered_next_run_info %}
- {{ dataset_triggered_next_run_info[dag.dag_id] }}
+ <span
+ class="js-tooltip"
+ title="Click to see dataset details."
+ >
+ <div
+ class="label label-default next-dataset-triggered"
+ data-dag-id="{{ dag.dag_id }}"
+ data-summary="{{ dataset_triggered_next_run_info[dag.dag_id] }}"
+ >
+ {{ dataset_triggered_next_run_info[dag.dag_id] }}
+ </div>
+ </span>
{% endif %}
{% if dag.next_dagrun is not none %}
<time datetime="{{ dag.next_dagrun }}">{{ dag.next_dagrun }}</time>
@@ -408,6 +422,8 @@
<div class="tooltip-arrow"></div>
<div class="tooltip-inner"></div>
</div>
+ <!-- Modal for dataset-triggered next run -->
+ {{ dataset_next_run_modal(id='dataset-next-run-modal') }}
{% endblock %}
{% block tail %}
diff --git a/airflow/www/templates/airflow/dataset_next_run_modal.html b/airflow/www/templates/airflow/dataset_next_run_modal.html
new file mode 100644
index 0000000000..df5f1466c8
--- /dev/null
+++ b/airflow/www/templates/airflow/dataset_next_run_modal.html
@@ -0,0 +1,56 @@
+{#
+ 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.
+ #}
+
+{% from 'appbuilder/loading_dots.html' import loading_dots %}
+
+{% macro dataset_next_run_modal(id=None, classes=None) %}
+ <div class="modal fade" id="datasetNextRunModal" tabindex="-1" role="dialog" aria-labelledby="datasetNextRunModalLabel" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title" id="datasetNextRunModalLabel">
+ <span class="text-muted">Datasets needed to trigger the next run for</span> <span id="dag_id"></span>
+ </h4>
+ </div>
+ <div class="modal-body">
+ <p id="next_run_summary"></p>
+ {{ loading_dots(id='datasets-loading-dots', classes='refresh-loading') }}
+ <div id="datasets_error" style="display: none; margin-top: 10px;" class="alert alert-danger" role="alert">
+ <span class="material-icons" aria-hidden="true">error</span>
+ <span id="datasets_error_msg">Oops.</span>
+ </div>
+ <table class="table table-striped table-bordered table-hover dataset-events">
+ <thead>
+ <tr>
+ <th>Dataset URI</th>
+ <th>Timestamp</th>
+ </tr>
+ </thead>
+ <tbody id="datasets_tbody">
+ </tbody>
+ </table>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ </div>
+ </div>
+ </div>
+ </div>
+{% endmacro %}