From ec27165a8db420ea1620b87353cb565fd7e5a03d Mon Sep 17 00:00:00 2001 From: Sam Betts Date: Mon, 30 Mar 2015 16:49:24 +0100 Subject: [PATCH] Fix date pickers in metering modal The metering forms were using the date picker functionality from the d3 line chart library, this used to work when it was loaded as a whole page as the d3 libary would run intending to setup any d3 graphs however now it is being loaded as a modal the elements that would be affected by the d3 code do not exist at page load time. In order to get the date picker events to be added to the elements on the form when the modal loads, the JS in the forms template has been extended in this patch to support adding those events. This patch also changes the data attributes on the form elements so that they do not conflict with any d3 line chart elements. Alongside the javascript this patch fixes bug to do with parsing the dates in metering utils and adds UTs to prevent regression. Change-Id: I4e239daa03b2f54e434254bac48ba0cceb037b5d Closes-Bug: 1427756 (cherry picked from commit 0d010f601cecacac01c26be572ae0d6444df9669) --- .../static/horizon/js/horizon.d3linechart.js | 10 ++---- .../static/horizon/js/horizon.datepickers.js | 13 ++++++++ horizon/static/horizon/js/horizon.metering.js | 32 +++++++++++++++++++ horizon/templates/horizon/_scripts.html | 2 ++ .../dashboards/admin/metering/forms.py | 6 ++-- .../metering/templates/metering/_daily.html | 25 ++------------- .../metering/templates/metering/stats.html | 28 ++-------------- .../static/dashboard/scss/horizon.scss | 5 +++ openstack_dashboard/test/tests/utils.py | 25 +++++++++++++++ openstack_dashboard/utils/metering.py | 4 +-- 10 files changed, 88 insertions(+), 62 deletions(-) create mode 100644 horizon/static/horizon/js/horizon.datepickers.js create mode 100644 horizon/static/horizon/js/horizon.metering.js diff --git a/horizon/static/horizon/js/horizon.d3linechart.js b/horizon/static/horizon/js/horizon.d3linechart.js index ef12a1d9a7..3b02274b89 100644 --- a/horizon/static/horizon/js/horizon.d3linechart.js +++ b/horizon/static/horizon/js/horizon.d3linechart.js @@ -755,15 +755,9 @@ horizon.d3_line_chart = { * A helper function for catching changeDate event of form datepickers * connected to charts. */ - bind_datepicker_change = function(settings) { + var bind_datepicker_change = function(settings) { var now = new Date(); - - $(datepicker_selector).each(function() { - var el = $(this); - el.datepicker({format: 'yyyy-mm-dd', - setDate: new Date(), - showButtonPanel: true}); - }); + horizon.datepickers.add(datepicker_selector); delegate_event_and_refresh_charts(datepicker_selector, 'changeDate', settings); }; diff --git a/horizon/static/horizon/js/horizon.datepickers.js b/horizon/static/horizon/js/horizon.datepickers.js new file mode 100644 index 0000000000..a7563fcf6f --- /dev/null +++ b/horizon/static/horizon/js/horizon.datepickers.js @@ -0,0 +1,13 @@ +horizon.datepickers = { + add: function(selector) { + $(selector).each(function () { + var el = $(this); + el.datepicker({ + format: 'yyyy-mm-dd', + setDate: new Date(), + showButtonPanel: true, + language: horizon.datepickerLocale + }); + }); + } +}; diff --git a/horizon/static/horizon/js/horizon.metering.js b/horizon/static/horizon/js/horizon.metering.js new file mode 100644 index 0000000000..c4c9a4d412 --- /dev/null +++ b/horizon/static/horizon/js/horizon.metering.js @@ -0,0 +1,32 @@ +horizon.metering = { + init_create_usage_report_form: function() { + horizon.datepickers.add('input[data-date-picker="True"]'); + horizon.metering.add_change_event_to_period_dropdown(); + horizon.metering.show_or_hide_date_fields(); + }, + init_stats_page: function() { + if (typeof horizon.d3_line_chart !== 'undefined') { + horizon.d3_line_chart.init("div[data-chart-type='line_chart']", + {'auto_resize': true}); + } + horizon.metering.add_change_event_to_period_dropdown(); + horizon.metering.show_or_hide_date_fields(); + }, + show_or_hide_date_fields: function() { + $("#date_from .controls input, #date_to .controls input").val(''); + if ($("#id_period").find("option:selected").val() === "other"){ + $("#id_date_from, #id_date_to").parent().parent().show(); + return true; + } else { + $("#id_date_from, #id_date_to").parent().parent().hide(); + return false; + } + }, + add_change_event_to_period_dropdown: function() { + $("#id_period").change(function(evt) { + if (horizon.metering.show_or_hide_date_fields()) { + evt.stopPropagation(); + } + }); + } +}; diff --git a/horizon/templates/horizon/_scripts.html b/horizon/templates/horizon/_scripts.html index 18dbe1155c..363f9ac468 100644 --- a/horizon/templates/horizon/_scripts.html +++ b/horizon/templates/horizon/_scripts.html @@ -75,6 +75,7 @@ + @@ -91,6 +92,7 @@ + diff --git a/openstack_dashboard/dashboards/admin/metering/forms.py b/openstack_dashboard/dashboards/admin/metering/forms.py index 31fe953cbe..d75ee028cf 100644 --- a/openstack_dashboard/dashboards/admin/metering/forms.py +++ b/openstack_dashboard/dashboards/admin/metering/forms.py @@ -34,12 +34,10 @@ class UsageReportForm(forms.SelfHandlingForm): choices=PERIOD_CHOICES) date_from = forms.DateField(label=_("From"), required=False, widget=forms.TextInput( - attrs={'data-line-chart-command': - 'date_picker_change'})) + attrs={'data-date-picker': True})) date_to = forms.DateField(label=_("To"), required=False, widget=forms.TextInput( - attrs={'data-line-chart-command': - 'date_picker_change'})) + attrs={'data-date-picker': True})) def clean_date_from(self): period = self.cleaned_data['period'] diff --git a/openstack_dashboard/dashboards/admin/metering/templates/metering/_daily.html b/openstack_dashboard/dashboards/admin/metering/templates/metering/_daily.html index a19572c049..fbdcb315e9 100644 --- a/openstack_dashboard/dashboards/admin/metering/templates/metering/_daily.html +++ b/openstack_dashboard/dashboards/admin/metering/templates/metering/_daily.html @@ -27,31 +27,12 @@ {% block modal-js %} {% endblock %} diff --git a/openstack_dashboard/dashboards/admin/metering/templates/metering/stats.html b/openstack_dashboard/dashboards/admin/metering/templates/metering/stats.html index cf17a33ff3..47eea47b77 100644 --- a/openstack_dashboard/dashboards/admin/metering/templates/metering/stats.html +++ b/openstack_dashboard/dashboards/admin/metering/templates/metering/stats.html @@ -166,35 +166,11 @@ diff --git a/openstack_dashboard/static/dashboard/scss/horizon.scss b/openstack_dashboard/static/dashboard/scss/horizon.scss index 9fbf60b832..a078ff2378 100644 --- a/openstack_dashboard/static/dashboard/scss/horizon.scss +++ b/openstack_dashboard/static/dashboard/scss/horizon.scss @@ -623,6 +623,11 @@ td.loading { /* Forms */ + +.datepicker { + z-index: $zindex-popover !important; +} + .datepicker input{ @extend .form-control; } diff --git a/openstack_dashboard/test/tests/utils.py b/openstack_dashboard/test/tests/utils.py index 6581ecc940..5ab2cd6b60 100644 --- a/openstack_dashboard/test/tests/utils.py +++ b/openstack_dashboard/test/tests/utils.py @@ -13,10 +13,12 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import uuid from openstack_dashboard.test import helpers as test from openstack_dashboard.utils import filters +from openstack_dashboard.utils import metering class UtilsFilterTests(test.TestCase): @@ -38,3 +40,26 @@ class UtilsFilterTests(test.TestCase): def test_reject_random_string(self): val = '55WbJTpJDf' self.assertRaises(ValueError, filters.get_int_or_uuid, val) + + +class UtilsMeteringTests(test.TestCase): + + def test_calc_date_args_strings(self): + date_from, date_to = metering.calc_date_args( + "2012-04-11", "2012-04-12", "other") + self.assertTrue(type(date_from) is datetime.datetime) + self.assertTrue(type(date_to) is datetime.datetime) + self.assertEqual(str(date_from.tzinfo), "UTC") + self.assertEqual(str(date_to.tzinfo), "UTC") + + def test_calc_date_args_datetime_dates(self): + date_from, date_to = metering.calc_date_args( + datetime.date(2012, 4, 11), datetime.date(2012, 4, 12), "other") + self.assertTrue(type(date_from) is datetime.datetime) + self.assertTrue(type(date_to) is datetime.datetime) + self.assertEqual(str(date_from.tzinfo), "UTC") + self.assertEqual(str(date_to.tzinfo), "UTC") + + def test_calc_date_args_invalid(self): + self.assertRaises( + ValueError, metering.calc_date_args, object, object, "other") diff --git a/openstack_dashboard/utils/metering.py b/openstack_dashboard/utils/metering.py index 9cd42fe61a..b6eb50f566 100644 --- a/openstack_dashboard/utils/metering.py +++ b/openstack_dashboard/utils/metering.py @@ -61,7 +61,7 @@ def calc_date_args(date_from, date_to, date_options): try: if date_from: date_from = pytz.utc.localize( - datetime.datetime.strptime(date_from, "%Y-%m-%d")) + datetime.datetime.strptime(str(date_from), "%Y-%m-%d")) else: # TODO(lsmola) there should be probably the date # of the first sample as default, so it correctly @@ -70,7 +70,7 @@ def calc_date_args(date_from, date_to, date_options): pass if date_to: date_to = pytz.utc.localize( - datetime.datetime.strptime(date_to, "%Y-%m-%d")) + datetime.datetime.strptime(str(date_to), "%Y-%m-%d")) # It returns the beginning of the day, I want the end of # the day, so I add one day without a second. date_to = (date_to + datetime.timedelta(days=1) -