Skip to content

Commit

Permalink
Fix timezone handling for analytics dates, and make date ranges relative
Browse files Browse the repository at this point in the history
- This fixes the default dates in the admin UI so they correspond with
  the analytics timezone. This prevents issues with loading the app and
  the default date ranges possibly not reflecting the current time
  according to the analytics timezone.

  For example, if the analytics timezone was New York (-5), but you
  loaded the app in Denver (-7) at 23:00, the end date was previously
  the current date in Denver, not the following day in New York.
- Remove defunct client-side "tz" parameters. This got removed before
  the v0.12 release (7c87634).
- Make the date range options like "Last 30 Days" relative so that
  shared or bookmarked URLs can always reflect the last 30 days, rather
  than when the page was first loaded.

  This had been on our radar for a while, but with the changes to make
  the default date ranges time-zone aware, this became a bit more
  relevant (to deal with Ember not really supporting dynamic defaults
  for URLs).

  See 18F/api.data.gov#73
- Add more tests around all the date handling with regards to timezones,
  the current date, and URL handling.
  • Loading branch information
GUI committed Feb 18, 2017
1 parent 047013f commit 90ed2b6
Show file tree
Hide file tree
Showing 46 changed files with 652 additions and 218 deletions.
2 changes: 0 additions & 2 deletions src/api-umbrella/admin-ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ module.exports = {
'ace': true,
'bootbox': true,
'inflection': true,
'jstz': true,
'marked': true,
'moment': true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default Ember.Component.extend({
if(data.terminal) {
return '<i class="fa fa-file-o fa-space-right"></i>' + _.escape(name);
} else {
let params = _.clone(this.get('queryParamValues'));
let params = _.clone(this.get('presentQueryParamValues'));
params.prefix = data.descendent_prefix;
let link = '#/stats/drilldown?' + $.param(params);

Expand Down Expand Up @@ -53,7 +53,7 @@ export default Ember.Component.extend({
table.draw();
}),

downloadUrl: Ember.computed('allQueryParamValues', function() {
return '/api-umbrella/v1/analytics/drilldown.csv?api_key=' + this.get('session.data.authenticated.api_key') + '&' + $.param(this.get('allQueryParamValues'));
downloadUrl: Ember.computed('backendQueryParamValues', function() {
return '/api-umbrella/v1/analytics/drilldown.csv?api_key=' + this.get('session.data.authenticated.api_key') + '&' + $.param(this.get('backendQueryParamValues'));
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Ember from 'ember';
export default Ember.Component.extend({
setLinks: Ember.on('init', Ember.observer('facets', function() {
_.each(this.get('facets'), function(bucket) {
let params = _.clone(this.get('queryParamValues'));
let params = _.clone(this.get('presentQueryParamValues'));
params.search = _.compact([params.search, this.get('field') + ':"' + bucket.key + '"']).join(' AND ');
bucket.link = '#/stats/logs?' + $.param(params);
}.bind(this));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default Ember.Component.extend({
// exceed URL length limits in IE (and apparently Capybara too).
type: 'POST',
data: function(data) {
return _.extend({}, data, this.get('allQueryParamValues'));
return _.extend({}, data, this.get('backendQueryParamValues'));
}.bind(this),
},
drawCallback: _.bind(function() {
Expand Down Expand Up @@ -70,7 +70,7 @@ export default Ember.Component.extend({
defaultContent: '-',
render: function(email, type, data) {
if(type === 'display' && email && email !== '-') {
let params = _.clone(this.get('queryParamValues'));
let params = _.clone(this.get('presentQueryParamValues'));
params.search = _.compact([params.search, 'user_id:"' + data.user_id + '"']).join(' AND ');
let link = '#/stats/logs?' + $.param(params);

Expand Down Expand Up @@ -174,11 +174,11 @@ export default Ember.Component.extend({
});
},

refreshData: Ember.observer('allQueryParamValues', function() {
refreshData: Ember.observer('backendQueryParamValues', function() {
this.$().find('table').DataTable().draw();
}),

downloadUrl: Ember.computed('allQueryParamValues', function() {
return '/admin/stats/logs.csv?' + $.param(this.get('allQueryParamValues'));
downloadUrl: Ember.computed('backendQueryParamValues', function() {
return '/admin/stats/logs.csv?' + $.param(this.get('backendQueryParamValues'));
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default Ember.Component.extend({
},

handleRegionClick(event) {
let queryParams = _.clone(this.get('queryParamValues'));
let queryParams = _.clone(this.get('presentQueryParamValues'));
queryParams.region = event.name;
this.get('routing').transitionTo('stats.map', undefined, queryParams);
},
Expand All @@ -30,7 +30,7 @@ export default Ember.Component.extend({
let currentRegion = this.get('allQueryParamValues.region').split('-');
let currentCountry = currentRegion[0];
currentRegion = currentRegion[1];
let queryParams = _.clone(this.get('queryParamValues'));
let queryParams = _.clone(this.get('presentQueryParamValues'));
queryParams.query = JSON.stringify({
condition: 'AND',
rules: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default Ember.Component.extend({
render: _.bind(function(name, type, data) {
if(type === 'display' && name && name !== '-') {
let link;
let params = _.clone(this.get('queryParamValues'));
let params = _.clone(this.get('presentQueryParamValues'));
if(this.get('regionField') === 'request_ip_city') {
delete params.region;
params.search = 'request_ip_city:"' + data.id + '"';
Expand Down Expand Up @@ -54,7 +54,7 @@ export default Ember.Component.extend({
table.draw();
}),

downloadUrl: Ember.computed('allQueryParamValues', function() {
return '/admin/stats/map.csv?' + $.param(this.get('allQueryParamValues'));
downloadUrl: Ember.computed('backendQueryParamValues', function() {
return '/admin/stats/map.csv?' + $.param(this.get('backendQueryParamValues'));
}),
});
117 changes: 74 additions & 43 deletions src/api-umbrella/admin-ui/app/components/stats/query-form.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,36 @@
import Ember from 'ember';
import I18n from 'npm:i18n-js';
import moment from 'npm:moment-timezone';
import 'npm:bootstrap-daterangepicker';

export default Ember.Component.extend({
session: Ember.inject.service('session'),

enableInterval: false,

datePickerRanges: {
'Today': [
moment().startOf('day'),
moment().endOf('day'),
],
'Yesterday': [
moment().subtract(1, 'days'),
moment().subtract(1, 'days').endOf('day'),
],
'Last 7 Days': [
moment().subtract(6, 'days'),
moment().endOf('day'),
],
'Last 30 Days': [
moment().subtract(29, 'days').startOf('day'),
moment().endOf('day'),
],
'This Month': [
moment().startOf('month'),
moment().endOf('month'),
],
'Last Month': [
moment().subtract(1, 'month').startOf('month'),
moment().subtract(1, 'month').endOf('month'),
],
},

didInsertElement() {
this.updateDateRange();
let rangeOptions = {};
let rangeKeys = {};
_.forEach(this.get('dateRanges'), function(range, key) {
rangeOptions[range.label] = [
range.start_at,
range.end_at,
];
rangeKeys[range.label] = key;
});
this.set('rangeOptions', rangeOptions);
this.set('rangeKeys', rangeKeys);

$('#reportrange').daterangepicker({
ranges: this.datePickerRanges,
startDate: moment(this.get('start_at'), 'YYYY-MM-DD'),
endDate: moment(this.get('end_at'), 'YYYY-MM-DD'),
}, _.bind(this.handleDateRangeChange, this));
let $dateRangePicker = $('#reportrange');
$dateRangePicker.daterangepicker({
ranges: rangeOptions,
});
$dateRangePicker.on('showCalendar.daterangepicker', this.handleDateRangeCalendarShow.bind(this));
$dateRangePicker.on('hideCalendar.daterangepicker', this.handleDateRangeCalendarHide.bind(this));
$dateRangePicker.on('apply.daterangepicker', this.handleDateRangeApply.bind(this));

this.dateRangePicker = $dateRangePicker.data('daterangepicker');
this.updateDateRange();

let stringOperators = [
'begins_with',
Expand Down Expand Up @@ -293,18 +283,59 @@ export default Ember.Component.extend({
}
}.observes('query'),

updateDateRange: function() {
let start = moment(this.get('start_at'));
let end = moment(this.get('end_at'));
updateDateRange: Ember.observer('allQueryParamValues.start_at', 'allQueryParamValues.end_at', function() {
let start = moment(this.get('allQueryParamValues.start_at'), 'YYYY-MM-DD');
let end = moment(this.get('allQueryParamValues.end_at'), 'YYYY-MM-DD');

$('#reportrange span.text').html(start.format('MMM D, YYYY') + ' - ' + end.format('MMM D, YYYY'));
}.observes('start_at', 'end_at'),
this.dateRangePicker.hideCalendars();
this.dateRangePicker.setStartDate(start);
this.dateRangePicker.setEndDate(end);
$('#reportrange span.text').html(start.format('ll') + ' - ' + end.format('ll'));
}),

handleDateRangeChange(start, end) {
this.setProperties({
'start_at': start.format('YYYY-MM-DD'),
'end_at': end.format('YYYY-MM-DD'),
});
handleDateRangeCalendarShow() {
this.set('calendarShown', true);
},

handleDateRangeCalendarHide() {
this.set('calendarShown', false);
},

handleDateRangeApply(event, picker) {
// If the user selects a predefined date range (like "Last 7 Days"), then
// don't set explicit dates in the URL query params. This allows for the
// URLs that are bookmarked or shared to use relative dates (eg, you'll
// always see the last 7 days regardless of when the URL was first
// bookmarked).
//
// If the user selects a custom date range, then explicit dates will be set
// in the URL (so the data is fixed in time).
//
// Note that if the user picks "Custom Range" and happens to select dates
// that correspond with the one of the predefined ranges, then the
// Bootstrap Date Picker sets the "chosenLabel" as if the user picked the
// predefined range. To workaround this issue (so any dates picked when
// "Custom Range" is open are treated the same), we check to see if the
// "Custom Range" calendars are visible or not.
let rangeOptions = this.get('rangeOptions');
if(rangeOptions[picker.chosenLabel] && !this.get('calendarShown')) {
let rangeKeys = this.get('rangeKeys');
this.setProperties({
start_at: '',
end_at: '',
date_range: rangeKeys[picker.chosenLabel],
});
} else {
this.setProperties({
start_at: picker.startDate.format('YYYY-MM-DD'),
end_at: picker.endDate.format('YYYY-MM-DD'),
// In this case the "date_range" param isn't being used ("start_at" and
// "end_at" take precedence), so reset it back to the default value
// (defined in app/controllers/stats/base.js), so it's hidden from the
// URL.
date_range: '30d',
});
}
},

actions: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default Ember.Component.extend({
ajax: {
url: '/admin/stats/users.json',
data: function(data) {
return _.extend({}, data, this.get('allQueryParamValues'));
return _.extend({}, data, this.get('backendQueryParamValues'));
}.bind(this),
},
order: [[4, 'desc']],
Expand All @@ -21,7 +21,7 @@ export default Ember.Component.extend({
defaultContent: '-',
render: function(email, type, data) {
if(type === 'display' && email && email !== '-') {
let params = _.clone(this.get('queryParamValues'));
let params = _.clone(this.get('presentQueryParamValues'));
params.search = 'user_id:"' + data.id + '"';
let link = '#/stats/logs?' + $.param(params);

Expand Down Expand Up @@ -79,11 +79,11 @@ export default Ember.Component.extend({
});
},

refreshData: Ember.observer('allQueryParamValues', function() {
refreshData: Ember.observer('backendQueryParamValues', function() {
this.$().find('table').DataTable().draw();
}),

downloadUrl: Ember.computed('allQueryParamValues', function() {
return '/admin/stats/users.csv?' + $.param(this.get('allQueryParamValues'));
downloadUrl: Ember.computed('backendQueryParamValues', function() {
return '/admin/stats/users.csv?' + $.param(this.get('backendQueryParamValues'));
}),
});
6 changes: 3 additions & 3 deletions src/api-umbrella/admin-ui/app/controllers/stats/base.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import Ember from 'ember';

export default Ember.Controller.extend({
tz: jstz.determine().name(),
search: '',
interval: 'day',
prefix: '0/',
region: 'world',
start_at: moment().subtract(29, 'days').format('YYYY-MM-DD'),
end_at: moment().format('YYYY-MM-DD'),
date_range: '30d',
start_at: '',
end_at: '',
query: JSON.stringify({
condition: 'AND',
rules: [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Base from './base';

export default Base.extend({
queryParams: [
'tz',
'start_at',
'end_at',
'interval',
Expand Down
2 changes: 1 addition & 1 deletion src/api-umbrella/admin-ui/app/controllers/stats/logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Base from './base';

export default Base.extend({
queryParams: [
'tz',
'date_range',
'start_at',
'end_at',
'interval',
Expand Down
2 changes: 0 additions & 2 deletions src/api-umbrella/admin-ui/app/controllers/stats/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import Base from './base';

export default Base.extend({
queryParams: [
'tz',
'start_at',
'end_at',
'query',
'search',
'region',
'beta_analytics',
],

});
1 change: 0 additions & 1 deletion src/api-umbrella/admin-ui/app/controllers/stats/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Base from './base';

export default Base.extend({
queryParams: [
'tz',
'start_at',
'end_at',
'query',
Expand Down
1 change: 1 addition & 0 deletions src/api-umbrella/admin-ui/app/helpers/format-date.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Ember from 'ember';
import moment from 'npm:moment-timezone';

export function formatDate(params) {
let date = params[0];
Expand Down
1 change: 1 addition & 0 deletions src/api-umbrella/admin-ui/app/models/api/rate-limit.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Ember from 'ember';
import DS from 'ember-data';
import moment from 'npm:moment-timezone';

export default DS.Model.extend({
duration: DS.attr('number'),
Expand Down
Loading

0 comments on commit 90ed2b6

Please sign in to comment.