From b88e5b69782ce251f510b5f04eb91fb79a02c4dc Mon Sep 17 00:00:00 2001 From: cirun Date: Mon, 29 Jul 2024 15:56:09 +0200 Subject: [PATCH] LLCAXCHZF-56/changes by review --- ckanext/charts/assets/css/charts.css | 2 +- .../charts/assets/js/charts-render-chartjs.js | 74 ------------ .../js/vendor/chartjs-adapter-moment.js | 111 ++++++++++++++++++ ckanext/charts/assets/webassets.yml | 1 + ckanext/charts/chart_builders/base.py | 6 +- ckanext/charts/chart_builders/chartjs.py | 10 +- ckanext/charts/chart_builders/observable.py | 10 +- ckanext/charts/chart_builders/plotly.py | 8 +- .../form_snippets/chart_more_info_button.html | 92 ++++++--------- ckanext/charts/theme/mixins.scss | 33 +----- 10 files changed, 174 insertions(+), 173 deletions(-) create mode 100644 ckanext/charts/assets/js/vendor/chartjs-adapter-moment.js diff --git a/ckanext/charts/assets/css/charts.css b/ckanext/charts/assets/css/charts.css index 9d43bed..edb041b 100644 --- a/ckanext/charts/assets/css/charts.css +++ b/ckanext/charts/assets/css/charts.css @@ -1 +1 @@ -.date-formats-container__row{display:flex;justify-content:space-between}.date-formats-container__column{flex:1;margin-right:20px}.date-formats-container__column:last-child{margin-right:0}.date-formats-text{font-size:1.2em;color:#333}.date-formats-list{list-style-type:none;padding-left:0}.date-formats-item{margin:5px 0;font-size:1em;color:#555}.row.wrapper.charts-view .dataset-resource-form{position:relative;padding:0}.resource-view-charts_builder_view label.no-dots:after,.resource-view-charts_view label.no-dots:after,#content div.charts-view label.no-dots:after{display:none}.resource-view-charts_builder_view #chart-container,.resource-view-charts_view #chart-container,#content div.charts-view #chart-container{max-height:600px}.resource-view-charts_builder_view select,.resource-view-charts_view select,#content div.charts-view select{border:1px solid #d0d0d0;padding:8px;border-radius:3px;width:100%}.resource-view-charts_builder_view #chart-clear,.resource-view-charts_view #chart-clear,#content div.charts-view #chart-clear{margin-left:auto}.resource-view-charts_builder_view .tab-content,.resource-view-charts_view .tab-content,#content div.charts-view .tab-content{display:block !important}.resource-view-charts_builder_view #chart-indicator,.resource-view-charts_view #chart-indicator,#content div.charts-view #chart-indicator{display:none}.resource-view-charts_builder_view .htmx-request#chart-indicator,.resource-view-charts_view .htmx-request#chart-indicator,#content div.charts-view .htmx-request#chart-indicator{display:inline}.charts-filters .filter-container{margin-bottom:1rem}.charts-filters .filter-container .filter-pair{display:flex;gap:.5rem;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid beige}.charts-filters .filter-container .filter-pair:last-of-type{margin-bottom:0;padding-bottom:0;border-bottom:unset}.charts-filters .filter-container .filter-pair .remove-pair{top:0;height:fit-content}.charts-filters .filter-container .filter-pair .ts-wrapper{flex-basis:45%}.charts-view--form .ts-wrapper .ts-control{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em}.charts-view--form .ts-wrapper.plugin-clear_button .ts-control{background:#fff}.charts-view--form .ts-wrapper.plugin-clear_button .ts-control .clear-button{right:.55rem;font-size:1.25rem}.charts-view--form .ts-wrapper .ts-dropdown{top:unset;margin-top:1px} \ No newline at end of file +.modal-open .module-resource{z-index:unset}.row.wrapper.charts-view .dataset-resource-form{position:relative;padding:0}.resource-view-charts_builder_view label.no-dots:after,.resource-view-charts_view label.no-dots:after,#content div.charts-view label.no-dots:after{display:none}.resource-view-charts_builder_view #chart-container,.resource-view-charts_view #chart-container,#content div.charts-view #chart-container{max-height:600px}.resource-view-charts_builder_view select,.resource-view-charts_view select,#content div.charts-view select{border:1px solid #d0d0d0;padding:8px;border-radius:3px;width:100%}.resource-view-charts_builder_view #chart-clear,.resource-view-charts_view #chart-clear,#content div.charts-view #chart-clear{margin-left:auto}.resource-view-charts_builder_view .tab-content,.resource-view-charts_view .tab-content,#content div.charts-view .tab-content{display:block !important}.resource-view-charts_builder_view #chart-indicator,.resource-view-charts_view #chart-indicator,#content div.charts-view #chart-indicator{display:none}.resource-view-charts_builder_view .htmx-request#chart-indicator,.resource-view-charts_view .htmx-request#chart-indicator,#content div.charts-view .htmx-request#chart-indicator{display:inline}.charts-filters .filter-container{margin-bottom:1rem}.charts-filters .filter-container .filter-pair{display:flex;gap:.5rem;margin-bottom:1rem;padding-bottom:1rem;border-bottom:1px solid beige}.charts-filters .filter-container .filter-pair:last-of-type{margin-bottom:0;padding-bottom:0;border-bottom:unset}.charts-filters .filter-container .filter-pair .remove-pair{top:0;height:fit-content}.charts-filters .filter-container .filter-pair .ts-wrapper{flex-basis:45%}.charts-view--form .ts-wrapper .ts-control{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em}.charts-view--form .ts-wrapper.plugin-clear_button .ts-control{background:#fff}.charts-view--form .ts-wrapper.plugin-clear_button .ts-control .clear-button{right:.55rem;font-size:1.25rem}.charts-view--form .ts-wrapper .ts-dropdown{top:unset;margin-top:1px} \ No newline at end of file diff --git a/ckanext/charts/assets/js/charts-render-chartjs.js b/ckanext/charts/assets/js/charts-render-chartjs.js index 77ac6b7..7f3d075 100644 --- a/ckanext/charts/assets/js/charts-render-chartjs.js +++ b/ckanext/charts/assets/js/charts-render-chartjs.js @@ -23,77 +23,3 @@ ckan.module("charts-render-chartjs", function ($, _) { } }; }); - - -/*! - * chartjs-adapter-moment v1.0.0 - * https://www.chartjs.org - * (c) 2021 chartjs-adapter-moment Contributors - * Released under the MIT license - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('moment'), require('chart.js')) : - typeof define === 'function' && define.amd ? define(['moment', 'chart.js'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.moment, global.Chart)); - }(this, (function (moment, chart_js) { 'use strict'; - - function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - - var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment); - - const FORMATS = { - datetime: 'MMM D, YYYY, h:mm:ss a', - millisecond: 'h:mm:ss.SSS a', - second: 'h:mm:ss a', - minute: 'h:mm a', - hour: 'hA', - day: 'MMM D', - week: 'll', - month: 'MMM YYYY', - quarter: '[Q]Q - YYYY', - year: 'YYYY' - }; - - chart_js._adapters._date.override(typeof moment__default['default'] === 'function' ? { - _id: 'moment', // DEBUG ONLY - - formats: function() { - return FORMATS; - }, - - parse: function(value, format) { - if (typeof value === 'string' && typeof format === 'string') { - value = moment__default['default'](value, format); - } else if (!(value instanceof moment__default['default'])) { - value = moment__default['default'](value); - } - return value.isValid() ? value.valueOf() : null; - }, - - format: function(time, format) { - return moment__default['default'](time).format(format); - }, - - add: function(time, amount, unit) { - return moment__default['default'](time).add(amount, unit).valueOf(); - }, - - diff: function(max, min, unit) { - return moment__default['default'](max).diff(moment__default['default'](min), unit); - }, - - startOf: function(time, unit, weekday) { - time = moment__default['default'](time); - if (unit === 'isoWeek') { - weekday = Math.trunc(Math.min(Math.max(0, weekday), 6)); - return time.isoWeekday(weekday).startOf('day').valueOf(); - } - return time.startOf(unit).valueOf(); - }, - - endOf: function(time, unit) { - return moment__default['default'](time).endOf(unit).valueOf(); - } - } : {}); - -}))); diff --git a/ckanext/charts/assets/js/vendor/chartjs-adapter-moment.js b/ckanext/charts/assets/js/vendor/chartjs-adapter-moment.js new file mode 100644 index 0000000..9ea8681 --- /dev/null +++ b/ckanext/charts/assets/js/vendor/chartjs-adapter-moment.js @@ -0,0 +1,111 @@ +/* + +Purpose: +To handle and display date and time data in Chart.js scatter and bubble charts with type: 'time' on the x-axis. + +Why We Use It: +1. Date Parsing and Formatting: +- Enables robust parsing and formatting of date strings using the moment library. + +2. Chart.js Compatibility: +- Integrates with Chart.js for time-based data handling. +- Necessary for using type: 'time' in chart configurations. + +3. Advanced Date Manipulation: +- Provides functions for date manipulation like adding/subtracting time and calculating date differences. + +Example Usage: +To use type: 'time' for the x-axis in scatter and bubble charts: + +const config = { + type: 'scatter', + data: {...}, + options: { + scales: { + x: { + type: 'time', + time: { + unit: 'day' + } + } + } + } +}; + +The moment adapter ensures accurate processing and display of date and time data in Chart.js. + +*/ + + +/*! + * chartjs-adapter-moment v1.0.0 + * https://www.chartjs.org + * (c) 2021 chartjs-adapter-moment Contributors + * Released under the MIT license + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('moment'), require('chart.js')) : + typeof define === 'function' && define.amd ? define(['moment', 'chart.js'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.moment, global.Chart)); + }(this, (function (moment, chart_js) { 'use strict'; + + function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + + var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment); + + const FORMATS = { + datetime: 'MMM D, YYYY, h:mm:ss a', + millisecond: 'h:mm:ss.SSS a', + second: 'h:mm:ss a', + minute: 'h:mm a', + hour: 'hA', + day: 'MMM D', + week: 'll', + month: 'MMM YYYY', + quarter: '[Q]Q - YYYY', + year: 'YYYY' + }; + + chart_js._adapters._date.override(typeof moment__default['default'] === 'function' ? { + _id: 'moment', // DEBUG ONLY + + formats: function() { + return FORMATS; + }, + + parse: function(value, format) { + if (typeof value === 'string' && typeof format === 'string') { + value = moment__default['default'](value, format); + } else if (!(value instanceof moment__default['default'])) { + value = moment__default['default'](value); + } + return value.isValid() ? value.valueOf() : null; + }, + + format: function(time, format) { + return moment__default['default'](time).format(format); + }, + + add: function(time, amount, unit) { + return moment__default['default'](time).add(amount, unit).valueOf(); + }, + + diff: function(max, min, unit) { + return moment__default['default'](max).diff(moment__default['default'](min), unit); + }, + + startOf: function(time, unit, weekday) { + time = moment__default['default'](time); + if (unit === 'isoWeek') { + weekday = Math.trunc(Math.min(Math.max(0, weekday), 6)); + return time.isoWeekday(weekday).startOf('day').valueOf(); + } + return time.startOf(unit).valueOf(); + }, + + endOf: function(time, unit) { + return moment__default['default'](time).endOf(unit).valueOf(); + } + } : {}); + +}))); diff --git a/ckanext/charts/assets/webassets.yml b/ckanext/charts/assets/webassets.yml index 3f954aa..b7c46c5 100644 --- a/ckanext/charts/assets/webassets.yml +++ b/ckanext/charts/assets/webassets.yml @@ -3,6 +3,7 @@ chartjs: output: ckanext-charts/%(version)s-chartjs.js contents: - js/vendor/chartjs.min.js + - js/vendor/chartjs-adapter-moment.js - js/charts-render-chartjs.js extra: preload: diff --git a/ckanext/charts/chart_builders/base.py b/ckanext/charts/chart_builders/base.py index 533fe2c..be4008b 100644 --- a/ckanext/charts/chart_builders/base.py +++ b/ckanext/charts/chart_builders/base.py @@ -607,7 +607,11 @@ def height_field(self) -> dict[str, Any]: "group": "Data", } - def more_info_button(self) -> dict[str, Any]: + def more_info_button_field(self) -> dict[str, Any]: + """ + Adds a "More info" button to the Data tab in the form, which triggers a pop-up. + This pop-up provides users with information about supported date formats. + """ return { "field_name": "more_info", "label": "More info", diff --git a/ckanext/charts/chart_builders/chartjs.py b/ckanext/charts/chart_builders/chartjs.py index e788cdd..20d07fc 100644 --- a/ckanext/charts/chart_builders/chartjs.py +++ b/ckanext/charts/chart_builders/chartjs.py @@ -90,7 +90,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_multi_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.limit_field(), self.filter_field(columns), ] @@ -149,7 +149,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_multi_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.limit_field(), @@ -206,7 +206,7 @@ def get_form_fields(self): self.type_field(chart_types), self.values_field(columns), self.names_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.limit_field(), self.filter_field(columns), ] @@ -295,7 +295,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.limit_field(), @@ -421,7 +421,7 @@ def get_form_fields(self): columns, help_text="Select 3 or more different categorical variables (dimensions)", ), - self.more_info_button(), + self.more_info_button_field(), self.limit_field(), self.filter_field(columns), ] diff --git a/ckanext/charts/chart_builders/observable.py b/ckanext/charts/chart_builders/observable.py index 7edb29c..45f4507 100644 --- a/ckanext/charts/chart_builders/observable.py +++ b/ckanext/charts/chart_builders/observable.py @@ -54,7 +54,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.fill_field(columns), @@ -109,7 +109,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.limit_field(), @@ -183,7 +183,7 @@ def get_form_fields(self): self.type_field(chart_types), self.values_field(columns), self.names_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.opacity_field(), self.inner_radius_field(), self.stroke_width_field(), @@ -224,7 +224,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.color_field(columns), @@ -263,7 +263,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.color_field(columns), diff --git a/ckanext/charts/chart_builders/plotly.py b/ckanext/charts/chart_builders/plotly.py index 0bda1ee..91d876e 100644 --- a/ckanext/charts/chart_builders/plotly.py +++ b/ckanext/charts/chart_builders/plotly.py @@ -106,7 +106,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.log_x_field(), self.log_y_field(), self.sort_x_field(), @@ -138,7 +138,7 @@ def get_form_fields(self): self.type_field(chart_types), self.values_field(columns), self.names_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.opacity_field(), self.limit_field(), self.filter_field(columns), @@ -181,7 +181,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.plotly_y_multi_axis_field(columns, 2), - self.more_info_button(), + self.more_info_button_field(), self.sort_x_field(), self.sort_y_field(), self.limit_field(), @@ -224,7 +224,7 @@ def get_form_fields(self): self.type_field(chart_types), self.x_axis_field(columns), self.y_axis_field(columns), - self.more_info_button(), + self.more_info_button_field(), self.log_x_field(), self.log_y_field(), self.sort_x_field(), diff --git a/ckanext/charts/templates/scheming/form_snippets/chart_more_info_button.html b/ckanext/charts/templates/scheming/form_snippets/chart_more_info_button.html index 102d8e3..992583f 100644 --- a/ckanext/charts/templates/scheming/form_snippets/chart_more_info_button.html +++ b/ckanext/charts/templates/scheming/form_snippets/chart_more_info_button.html @@ -1,76 +1,62 @@
+{% set datetime_mapping = { + "YYYY-MM-DD": "2023-07-24", + "YYYY/MM/DD": "2023/07/24", + "MM-DD-YYYY": "07-24-2023", + "MM/DD/YYYY": "07/24/2023", + "DD-MM-YYYY": "24-07-2023", + "DD/MM/YYYY": "24/07/2023", + "dd/MMM/yyyy": "25/Nov/2023", + "YYYYMMDD": "20231125", + "YYYY-MM-DDTHH:MM:SS": "2023-07-24T14:30:00", + "YYYY-MM-DD HH:mm:ss": "2023-11-25 12:34:56", + "YYYY-MM-DDTHH:mm:ssZ": "2023-11-25T12:34:56Z", + "YYYY-MM-DD HH:mm:ss.SSS": "2023-11-25 12:34:56.789", + "MM/dd/yyyy hh:mm:ss a": "9/28/2023 2:23:15 PM", + "MM/dd/yyyy hh:mm:ss a:SSS": "8/5/2023 3:31:18 AM:234", + "MMdd_HH:mm:ss.SSS": "0423_11:42:35.883", + "dd MMM yyyy HH:mm:ss*SSS": "23 Apr 2023 10:32:35*311", + "dd MMM yyyy HH:mm:ss": "23 Apr 2023 11:42:35", + "yyMMdd HH:mm:ss": "220423 11:42:35", + "yy-MM-dd HH:mm:ss": "23-04-19 12:00:17" +} %}