Skip to content
Closed
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
37 changes: 32 additions & 5 deletions airflow/www/static/js/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,48 @@
* under the License.
*/

import {defaultFormatWithTZ, moment} from './datetime-utils';
import { defaultFormatWithTZ, formatDateStr, moment } from './datetime-utils';


const getSelectedTimeZone = () => {
return $('#clock').data('selected-tz')
};

export const formatInSelectedTz = (dateString) => {
let tz = getSelectedTimeZone();
return formatDateStr(dateString, tz);
};

function displayTime() {
let utcTime = moment().utc().format(defaultFormatWithTZ);
$('#clock')
let $clock = $('#clock');
let tz = $clock.data('selected-tz');
$clock
.attr("data-original-title", function() {
return hostName
})
.html(utcTime);
.text(moment().tz(tz).format(defaultFormatWithTZ));

setTimeout(displayTime, 1000);
}

$(document).ready(function () {
displayTime();
$("#tz-picker li").click(function(e) {
let tz = $(this).text() == 'Local' ? moment.tz.guess() : 'UTC';
$('.tz-aware').each(function() {
$(this).trigger('tz-changed', tz);
})
});

$('#clock').on('tz-changed', function(event, tz) {
$(this).data('selected-tz', tz);
displayTime();
});

// Initialize default time zone and start clock
$('.tz-aware').each(function() {
$(this).trigger('tz-changed', 'UTC');
})

$('span').tooltip();
$.ajaxSetup({
beforeSend: function(xhr, settings) {
Expand Down
12 changes: 5 additions & 7 deletions airflow/www/static/js/datetime-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,18 @@ export const generateTooltipDateTime = (startDate, endDate, dagTZ) => {
return tooltipHTML
};


export const converAndFormatUTC = (datetime, tz) => {
let dateTimeObj = moment.utc(datetime);
if (tz) dateTimeObj = dateTimeObj.tz(tz);
export const formatDateStr = (dateString, tz) => {
let dateTimeObj = moment(dateString).tz(tz);
return dateTimeObj.format(defaultFormatWithTZ)
}
};

export const secondsToString = (seconds) => {
let numdays = Math.floor((seconds % 31536000) / 86400);
let numdays = Math.floor((seconds % 31536000) / 86400);
let numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
let numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
let numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60);
return (numdays > 0 ? numdays + (numdays === 1 ? " day " : " days ") : "") +
(numhours > 0 ? numhours + (numhours === 1 ? " hour " : " hours ") : "") +
(numminutes > 0 ? numminutes + (numminutes === 1 ? " minute " : " minutes ") : "") +
(numseconds > 0 ? numseconds + (numseconds === 1 ? " second" : " seconds") : "");
}
};
86 changes: 72 additions & 14 deletions airflow/www/static/js/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
* under the License.
*/

import { generateTooltipDateTime, converAndFormatUTC, secondsToString } from './datetime-utils';
import { generateTooltipDateTime, moment, formatDateStr, secondsToString } from './datetime-utils';

// Assigning css classes based on state to nodes
// Initiating the tooltips
function update_nodes_states(task_instances) {
export const update_nodes_states = (task_instances, dag_tz) => {
$.each(task_instances, function (task_id, ti) {
$('tspan').filter(function (index) {
return $(this).text() === task_id;
Expand All @@ -33,42 +33,100 @@ function update_nodes_states(task_instances) {
// Tooltip
const task = tasks[task_id];
let tt = "Task_id: " + ti.task_id + "<br>";
tt += "Run: " + converAndFormatUTC(ti.execution_date) + "<br>";
tt += "Run: " + formatDateStr(ti.execution_date, 'UTC') + "<br>";
if (ti.run_id != undefined) {
tt += "run_id: <nobr>" + ti.run_id + "</nobr><br>";
}
tt += "Operator: " + task.task_type + "<br>";
tt += "Duration: " + secondsToString(ti.duration) + "<br>";
tt += "State: " + ti.state + "<br>";
tt += generateTooltipDateTime(ti.start_date, ti.end_date, dagTZ); // dagTZ has been defined in dag.html
tt += generateTooltipDateTime(ti.start_date, ti.end_date, dag_tz);
return tt;
});
});
}

function initRefreshButton() {
export const initRefreshButton = (dagTz) => {
d3.select("#refresh_button").on("click",
function () {
$("#loading").css("display", "block");
$("div#svg_container").css("opacity", "0.2");
$.get(getTaskInstanceURL)
.done(
function (task_instances) {
update_nodes_states(JSON.parse(task_instances));
update_nodes_states(JSON.parse(task_instances), dagTz);
$("#loading").hide();
$("div#svg_container").css("opacity", "1");
$('#error').hide();
}
).fail(function (jqxhr, textStatus, err) {
$('#error_msg').html(textStatus + ': ' + err);
$('#error').show();
$('#loading').hide();
$('#chart_section').hide(1000);
$('#datatable_section').hide(1000);
});
$('#error_msg').html(textStatus + ': ' + err);
$('#error').show();
$('#loading').hide();
$('#chart_section').hide(1000);
$('#datatable_section').hide(1000);
});
}
);
}

initRefreshButton();
update_nodes_states(task_instances);
const localizeExecutionDate = (option, tz) => {
let ts = moment(option.val()).tz(tz);
const prefixes = ["scheduled__", "backfill_", "manual__"];
prefixes.forEach(function(prefix) {
if (option.text().startsWith(prefix)) {
option.text(prefix + ts.format());
}
});
}

const getNaiveDateString = (picker) => {
return moment(picker.getDate()).utc().format('YYYY-MM-DD HH:mm');
}

const getSelectedTz = () => {
return $('#datetimepicker').data('current-tz');
}

const getBaseDateInUtc = () => {
let picker = $('#datetimepicker').data('datetimepicker');
let dateString = getNaiveDateString(picker);
let utcTime = moment.tz(dateString, getSelectedTz()).utc();
return utcTime;
}

$(document).ready(function () {
let queryParams = new URLSearchParams(window.location.search)
let baseDateString = queryParams.get('base_date');
let baseDate = baseDateString ? moment.tz(baseDateString, 'UTC') : moment().utc();
$('#datetimepicker')
.data('current-tz', 'UTC')
.datetimepicker('setDate', baseDate.toDate());

$('select#execution_date').on('tz-changed', function(event, tz) {
$(this).children('option').each(function (i) {
localizeExecutionDate($(this), tz);
});
});

$('#datetimepicker')
.addClass('tz-aware')
.on('tz-changed', function(event, tz) {
let oldTz = $(this).data('current-tz') || 'UTC';
$(this).data('current-tz', tz);

let picker = $(this).data('datetimepicker');
let inTargetTz = moment.tz(getNaiveDateString(picker), oldTz).tz(tz);
let naiveDateStrInTargetTz = inTargetTz.format('YYYY-MM-DD HH:mm');

let actualDate = moment.tz(naiveDateStrInTargetTz, tz);
let adjustedDisplayDate = moment.tz(naiveDateStrInTargetTz, 'UTC');
$(this).data('utc-time', actualDate.utc());

picker.setDate(adjustedDisplayDate);
});

$("form").on('submit', function(event) {
$('#base_date').val(getBaseDateInUtc().format());
});
});
21 changes: 21 additions & 0 deletions airflow/www/static/js/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably call this "base" or "common" or something - "index" is a unmeaningful to work out what might be in it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I picked 'index' only because that's the file name that the webpack docs on Libraries used. I'm guessing there's a way to override that - I'll dig in a bit more and see if I can use base.js instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that important actually.

* 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 { formatInSelectedTz } from './base';
export { initRefreshButton, update_nodes_states } from './graph';
5 changes: 3 additions & 2 deletions airflow/www/templates/airflow/dag.html
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ <h4 class="modal-title" id="dagModalLabel">
{% block tail %}
{{ super() }}
<script src="{{ url_for_asset('bootstrap-toggle.min.js') }}"></script>
<script src="{{ url_for_asset('index.js') }}"></script>
<script>
function updateQueryStringParameter(uri, key, value) {
var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
Expand All @@ -311,7 +312,6 @@ <h4 class="modal-title" id="dagModalLabel">

var id = '';
var dag_id = '{{ dag.dag_id }}';
var dagTZ = '{{ dag.timezone.name }}'; // Being used in datetime-utils.js
var task_id = '';
var exection_date = '';
var subdag_id = '';
Expand Down Expand Up @@ -435,7 +435,8 @@ <h4 class="modal-title" id="dagModalLabel">
subdag_id = sd;
execution_date = d;
$('#task_id').html(t);
$('#execution_date').html(d);
let in_selected_tz = AirflowUI.formatInSelectedTz(d);
$('#execution_date').html(in_selected_tz);
$('#myModal').modal({});
$("#myModal").css("margin-top","0px")
if (subdag_id === undefined)
Expand Down
13 changes: 11 additions & 2 deletions airflow/www/templates/airflow/graph.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
<div class="form-inline">
<form method="get" style="float:left;">
{{ state_token }}
Base date: {{ form.base_date(class_="form-control") }}
Base date: {{ form.base_date(class_="form-control tz-aware") }}
Number of runs: {{ form.num_runs(class_="form-control") }}
Run:
{{ form.execution_date(class_="form-control") | safe }}
{{ form.execution_date(class_="form-control tz-aware") | safe }}
Layout:
{{ form.arrange(class_="form-control") | safe }}
<input type="hidden" name="root" value="{{ root }}">
Expand Down Expand Up @@ -362,6 +362,15 @@

</script>
<script src="{{ url_for_asset('graph.js') }}"></script>
<script src="{{ url_for_asset('index.js') }}"></script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this play with the import of export { initRefreshButton, update_nodes_states } from './graph'; inside index.js? Is the include of graph.js on the previous line the same file?

<script type="text/javascript">
let dagTz = '{{ dag.timezone.name }}';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've started putting thins like this in <meta> tags, rather than blocks of JS (or we have open PRs by @mik-laj to do that. I forget if we've merged them yet)

Copy link
Member

@mik-laj mik-laj Mar 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ashb PR is still waiting for you. :-) #4787

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, @mik-laj's PR looks good to me. I'll be happy to update this once #4787 is merged.


window.onload = function() {
AirflowUI.initRefreshButton(dagTz);
AirflowUI.update_nodes_states(task_instances, dagTz);
}
</script>


{% endblock %}
12 changes: 11 additions & 1 deletion airflow/www/templates/appbuilder/navbar_right.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@
{% endmacro %}

<!-- clock -->
<li><a id="clock"></a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<text id="clock" class="tz-aware"></text><b class="caret"></b>
</a>

<ul id="tz-picker" class="dropdown-menu">
<li>UTC</li>
<li>Local</li>
</ul>
</li>


{% if not current_user.is_anonymous %}
<li class="dropdown">
Expand Down
2 changes: 2 additions & 0 deletions airflow/www/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ const config = {
base: `${STATIC_DIR}/js/base.js`,
graph: `${STATIC_DIR}/js/graph.js`,
ganttChartD3v2: `${STATIC_DIR}/js/gantt-chart-d3v2.js`,
index: `${STATIC_DIR}/js/index.js`,
main: `${STATIC_DIR}/css/main.css`,
airflowDefaultTheme: `${STATIC_DIR}/css/bootstrap-theme.css`,
},
output: {
path: BUILD_DIR,
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
library: 'AirflowUI',
},
resolve: {
extensions: [
Expand Down