This is an automated email from the ASF dual-hosted git repository. ephraimanierobi pushed a commit to branch v2-4-test in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 66125651b619a4129c66f36f5d705d399166ff62 Author: Bob Du <[email protected]> AuthorDate: Fri Oct 21 22:39:59 2022 +0800 Fix some bug in web ui dags list page (auto-refresh & jump search null state) (#27141) * Fix some bug in web ui dags list page Signed-off-by: BobDu <[email protected]> * use switch case & fix remove loading dot Signed-off-by: BobDu <[email protected]> * rename drawDagStats & selectors constants Signed-off-by: BobDu <[email protected]> * Further clean up of duplicate code Signed-off-by: BobDu <[email protected]> Signed-off-by: BobDu <[email protected]> (cherry picked from commit ebd34cbd9f62d086bb39727d85fd58a0758edba3) --- airflow/www/static/js/dags.js | 145 ++++++++------------------------ airflow/www/templates/airflow/dags.html | 6 +- 2 files changed, 37 insertions(+), 114 deletions(-) diff --git a/airflow/www/static/js/dags.js b/airflow/www/static/js/dags.js index db8f19b203..3635da841f 100644 --- a/airflow/www/static/js/dags.js +++ b/airflow/www/static/js/dags.js @@ -43,6 +43,9 @@ const gridUrl = getMetaValue('grid_url'); const nextDatasets = {}; let nextDatasetsError; +const DAG_RUN = 'dag-run'; +const TASK_INSTANCE = 'task-instance'; + // auto refresh interval in milliseconds // (x2 the interval in tree/graph view since this page can take longer to refresh ) const refreshIntervalMs = 2000; @@ -188,10 +191,10 @@ d3.selectAll('.js-last-run-tooltip') d3.select(this).attr('data-original-title', tiTooltip(lastRunData)); }); -function drawDagStatsForDag(dagId, states) { - const g = d3.select(`svg#dag-run-${dagId.replace(/\./g, '__dot__')}`) +function drawDagStats(selector, dagId, states) { + const g = d3.select(`svg#${selector}-${dagId.replace(/\./g, '__dot__')}`) .attr('height', diameter + (strokeWidthHover * 2)) - .attr('width', '120px') + .attr('width', (states.length * (diameter + circleMargin)) + circleMargin) .selectAll('g') .data(states) .enter() @@ -203,103 +206,41 @@ function drawDagStatsForDag(dagId, states) { }); g.append('svg:a') - .attr('href', (d) => `${dagRunUrl}?_flt_3_dag_id=${dagId}&_flt_3_state=${d.state}`) - .append('circle') - .attr('id', (d) => `run-${dagId.replace(/\./g, '_')}${d.state || 'none'}`) - .attr('class', 'has-svg-tooltip') - .attr('stroke-width', (d) => { - if (d.count > 0) return strokeWidth; - - return 1; - }) - .attr('stroke', (d) => { - if (d.count > 0) return STATE_COLOR[d.state]; - - return 'gainsboro'; - }) - .attr('fill', '#fff') - .attr('r', diameter / 2) - .attr('title', (d) => d.state) - .on('mouseover', (d) => { - if (d.count > 0) { - d3.select(this).transition().duration(400) - .attr('fill', '#e2e2e2') - .style('stroke-width', strokeWidthHover); + .attr('href', (d) => { + const params = new URLSearchParams(); + params.append('_flt_3_dag_id', dagId); + /* eslint no-unused-expressions: ["error", { "allowTernary": true }] */ + d.state ? params.append('_flt_3_state', d.state) : params.append('_flt_8_state', ''); + switch (selector) { + case DAG_RUN: return `${dagRunUrl}?${params.toString()}`; + case TASK_INSTANCE: return `${taskInstanceUrl}?${params.toString()}`; + default: return ''; } }) - .on('mouseout', (d) => { - if (d.count > 0) { - d3.select(this).transition().duration(400) - .attr('fill', '#fff') - .style('stroke-width', strokeWidth); - } - }) - .style('opacity', 0) - .transition() - .duration(300) - .delay((d, i) => i * 50) - .style('opacity', 1); - d3.select('.js-loading-dag-stats').remove(); - - g.append('text') - .attr('fill', '#51504f') - .attr('text-anchor', 'middle') - .attr('vertical-align', 'middle') - .attr('font-size', 9) - .attr('y', 3) - .style('pointer-events', 'none') - .text((d) => (d.count > 0 ? d.count : '')); -} - -function dagStatsHandler(error, json) { - Object.keys(json).forEach((dagId) => { - const states = json[dagId]; - drawDagStatsForDag(dagId, states); - }); -} - -function drawTaskStatsForDag(dagId, states) { - const g = d3.select(`svg#task-run-${dagId.replace(/\./g, '__dot__')}`) - .attr('height', diameter + (strokeWidthHover * 2)) - .attr('width', (states.length * (diameter + circleMargin)) + circleMargin) - .selectAll('g') - .data(states) - .enter() - .append('g') - .attr('transform', (d, i) => { - const x = (i * (diameter + circleMargin)) + (diameter / 2 + circleMargin); - const y = (diameter / 2) + strokeWidthHover; - return `translate(${x},${y})`; - }); - - g.append('svg:a') - .attr('href', (d) => `${taskInstanceUrl}?_flt_3_dag_id=${dagId}&_flt_3_state=${d.state}`) .append('circle') - .attr('id', (d) => `task-${dagId.replace(/\./g, '_')}${d.state || 'none'}`) + .attr('id', (d) => `${selector}-${dagId.replace(/\./g, '_')}-${d.state || 'none'}`) .attr('class', 'has-svg-tooltip') .attr('stroke-width', (d) => { if (d.count > 0) return strokeWidth; - return 1; }) .attr('stroke', (d) => { if (d.count > 0) return STATE_COLOR[d.state]; - return 'gainsboro'; }) .attr('fill', '#fff') .attr('r', diameter / 2) .attr('title', (d) => d.state || 'none') - .on('mouseover', function mouseOver(d) { + .on('mouseover', (d) => { if (d.count > 0) { - d3.select(this).transition().duration(400) + d3.select(d3.event.currentTarget).transition().duration(400) .attr('fill', '#e2e2e2') .style('stroke-width', strokeWidthHover); } }) - .on('mouseout', function mouseOut(d) { + .on('mouseout', (d) => { if (d.count > 0) { - d3.select(this).transition().duration(400) + d3.select(d3.event.currentTarget).transition().duration(400) .attr('fill', '#fff') .style('stroke-width', strokeWidth); } @@ -310,7 +251,7 @@ function drawTaskStatsForDag(dagId, states) { .delay((d, i) => i * 50) .style('opacity', 1); - d3.select('.js-loading-task-stats').remove(); + d3.select(`.js-loading-${selector}-stats`).remove(); g.append('text') .attr('fill', '#51504f') @@ -322,10 +263,10 @@ function drawTaskStatsForDag(dagId, states) { .text((d) => (d.count > 0 ? d.count : '')); } -function taskStatsHandler(error, json) { +function dagStatsHandler(selector, json) { Object.keys(json).forEach((dagId) => { const states = json[dagId]; - drawTaskStatsForDag(dagId, states); + drawDagStats(selector, dagId, states); }); } @@ -355,14 +296,14 @@ function getDagStats() { .post(params, lastDagRunsHandler); d3.json(dagStatsUrl) .header('X-CSRFToken', csrfToken) - .post(params, dagStatsHandler); + .post(params, (error, json) => dagStatsHandler(DAG_RUN, json)); d3.json(taskStatsUrl) .header('X-CSRFToken', csrfToken) - .post(params, taskStatsHandler); + .post(params, (error, json) => dagStatsHandler(TASK_INSTANCE, json)); } else { // no dags, hide the loading dots - $('.js-loading-task-stats').remove(); - $('.js-loading-dag-stats').remove(); + $(`.js-loading-${DAG_RUN}-stats`).remove(); + $(`.js-loading-${TASK_INSTANCE}-stats`).remove(); } } @@ -381,7 +322,7 @@ function hideSvgTooltip() { $('#svg-tooltip').css('display', 'none'); } -function refreshDagRunsAndTasks(selector, dagId, states) { +function refreshDagStats(selector, dagId, states) { d3.select(`svg#${selector}-${dagId.replace(/\./g, '__dot__')}`) .selectAll('circle') .data(states) @@ -391,19 +332,9 @@ function refreshDagRunsAndTasks(selector, dagId, states) { }) .attr('stroke', (d) => { if (d.count > 0) return STATE_COLOR[d.state]; - return 'gainsboro'; - }) - .attr('fill', '#fff') - .attr('r', diameter / 2) - .attr('title', (d) => d.state) - .on('mouseover', (d) => { - if (d.count > 0) { - d3.select(this).transition().duration(400) - .attr('fill', '#e2e2e2') - .style('stroke-width', strokeWidthHover); - } }); + d3.select(`svg#${selector}-${dagId.replace(/\./g, '__dot__')}`) .selectAll('text') .data(states) @@ -415,13 +346,6 @@ function refreshDagRunsAndTasks(selector, dagId, states) { }); } -function refreshTaskStateHandler(error, ts) { - Object.keys(ts).forEach((dagId) => { - const states = ts[dagId]; - refreshDagRunsAndTasks('task-run', dagId, states); - }); -} - let refreshInterval; function checkActiveRuns(json) { @@ -437,12 +361,11 @@ function checkActiveRuns(json) { } } -function refreshDagRuns(error, json) { - checkActiveRuns(json); +function refreshDagStatsHandler(selector, json) { + if (selector === DAG_RUN) checkActiveRuns(json); Object.keys(json).forEach((dagId) => { const states = json[dagId]; - drawDagStatsForDag(dagId, states); - refreshDagRunsAndTasks('dag-run', dagId, states); + refreshDagStats(selector, dagId, states); }); } @@ -459,10 +382,10 @@ function handleRefresh({ activeDagsOnly = false } = {}) { .post(params, lastDagRunsHandler); d3.json(dagStatsUrl) .header('X-CSRFToken', csrfToken) - .post(params, refreshDagRuns); + .post(params, (error, json) => refreshDagStatsHandler(DAG_RUN, json)); d3.json(taskStatsUrl) .header('X-CSRFToken', csrfToken) - .post(params, refreshTaskStateHandler); + .post(params, (error, json) => refreshDagStatsHandler(TASK_INSTANCE, json)); } setTimeout(() => { $('#loading-dots').css('display', 'none'); diff --git a/airflow/www/templates/airflow/dags.html b/airflow/www/templates/airflow/dags.html index c675b4a05a..91d234d5e0 100644 --- a/airflow/www/templates/airflow/dags.html +++ b/airflow/www/templates/airflow/dags.html @@ -268,7 +268,7 @@ {% endfor %} </td> <td style="padding:0; width:130px;"> - {{ loading_dots(classes='js-loading-dag-stats text-muted') }} + {{ loading_dots(classes='js-loading-dag-run-stats text-muted') }} <svg height="10" width="10" id="dag-run-{{ dag.safe_dag_id }}" style="display: block;"></svg> </td> <td> @@ -331,8 +331,8 @@ {% endif %} </td> <td style="padding:0; width:323px; height:10px;"> - {{ loading_dots(classes='js-loading-task-stats text-muted') }} - <svg height="10" width="10" id='task-run-{{ dag.safe_dag_id }}' style="display: block;"></svg> + {{ loading_dots(classes='js-loading-task-instance-stats text-muted') }} + <svg height="10" width="10" id='task-instance-{{ dag.safe_dag_id }}' style="display: block;"></svg> </td> <td class="text-center"> <div class="btn-group">
