Skip to content

Commit

Permalink
LLCAXCHZF-56/feat: Add more_info button, include moment.js adapter, a…
Browse files Browse the repository at this point in the history
…nd configure date axis for scatter and bubble charts
  • Loading branch information
TomeCirun committed Jul 25, 2024
1 parent a971650 commit 0c74b4d
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 4 deletions.
2 changes: 1 addition & 1 deletion ckanext/charts/assets/css/charts.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions ckanext/charts/assets/js/charts-render-chartjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,77 @@ 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();
}
} : {});

})));
8 changes: 8 additions & 0 deletions ckanext/charts/chart_builders/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,14 @@ def height_field(self) -> dict[str, Any]:
"group": "Data",
}

def more_info_button(self) -> dict[str, Any]:
return {
"field_name": "more_info",
"label": "More info",
"form_snippet": "chart_more_info_button.html",
"group": "Data",
}

def size_field(self, choices: list[dict[str, str]]) -> dict[str, Any]:
field = self.column_field(choices)
field.update({"field_name": "size", "label": "Size", "group": "Structure"})
Expand Down
34 changes: 31 additions & 3 deletions ckanext/charts/chart_builders/chartjs.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +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.limit_field(),
self.filter_field(columns),
]
Expand Down Expand Up @@ -148,6 +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.sort_x_field(),
self.sort_y_field(),
self.limit_field(),
Expand Down Expand Up @@ -204,6 +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.limit_field(),
self.filter_field(columns),
]
Expand Down Expand Up @@ -245,8 +248,32 @@ def to_json(self) -> str:
"data": dataset_data,
}
]
return json.dumps(self._configure_date_axis(data))

def _configure_date_axis(self, data: dict[str, Any]) -> dict[str, Any]:
"""
Configure date settings for the x-axis if it uses 'date_time'.
"""
x_axis = data["options"]["x"]
scales = data["options"].get("scales", {})

if x_axis == "date_time":
x_scale = scales.get("x", {})
x_scale.update(
{
"type": "time",
"time": {
"unit": "day",
"displayFormats": {"day": "YYYY-MM-DD"},
},
}
)
scales["x"] = x_scale

return json.dumps(data)
if scales:
data["options"]["scales"] = scales

return data


class ChartJSScatterForm(BaseChartForm):
Expand All @@ -268,6 +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.sort_x_field(),
self.sort_y_field(),
self.limit_field(),
Expand Down Expand Up @@ -303,8 +331,7 @@ def to_json(self) -> str:
data["data"]["datasets"] = [
{"label": self.settings["y"], "data": dataset_data},
]

return json.dumps(data)
return json.dumps(self._configure_date_axis(data))

def _calculate_bubble_radius(self, data_series: pd.Series, max_size: int) -> int:
"""Calculate bubble radius based on the size column"""
Expand Down Expand Up @@ -394,6 +421,7 @@ def get_form_fields(self):
columns,
help_text="Select 3 or more different categorical variables (dimensions)",
),
self.more_info_button(),
self.limit_field(),
self.filter_field(columns),
]
5 changes: 5 additions & 0 deletions ckanext/charts/chart_builders/observable.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +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.sort_x_field(),
self.sort_y_field(),
self.fill_field(columns),
Expand Down Expand Up @@ -108,6 +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.sort_x_field(),
self.sort_y_field(),
self.limit_field(),
Expand Down Expand Up @@ -181,6 +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.opacity_field(),
self.inner_radius_field(),
self.stroke_width_field(),
Expand Down Expand Up @@ -221,6 +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.sort_x_field(),
self.sort_y_field(),
self.color_field(columns),
Expand Down Expand Up @@ -259,6 +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.sort_x_field(),
self.sort_y_field(),
self.color_field(columns),
Expand Down
4 changes: 4 additions & 0 deletions ckanext/charts/chart_builders/plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +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.log_x_field(),
self.log_y_field(),
self.sort_x_field(),
Expand Down Expand Up @@ -137,6 +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.opacity_field(),
self.limit_field(),
self.filter_field(columns),
Expand Down Expand Up @@ -179,6 +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.sort_x_field(),
self.sort_y_field(),
self.limit_field(),
Expand Down Expand Up @@ -221,6 +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.log_x_field(),
self.log_y_field(),
self.sort_x_field(),
Expand Down
4 changes: 4 additions & 0 deletions ckanext/charts/fetchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def fetch_data(self) -> pd.DataFrame:
# Apply numeric conversion only to non-datetime columns
df[non_datetime_cols] = df[non_datetime_cols].apply(pd.to_numeric, errors='ignore').fillna(0)

if "date_time" in df.columns:
# Convert the 'date_time' column to string format in ISO 8601
df['date_time'] = df['date_time'].dt.strftime("%Y-%m-%dT%H:%M:%S")

except (ProgrammingError, UndefinedTable) as e:
raise exception.DataFetchError(
f"An error occurred during fetching data from DataStore: {e}",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<div class="form-group control-full control-large">
<div class="controls">
<button class="btn btn-primary" type="button" data-bs-toggle="modal" data-bs-target="#moreInfoModal">
More info
</button>
</div>
</div>


<div class="modal fade" id="moreInfoModal" tabindex="-1" role="dialog" aria-labelledby="moreInfoModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="moreInfoModalLabel">Supported Date Formats</h5>
<button type="button" class="btn-close close" data-bs-dismiss="modal" aria-label="Close">
</button>
</div>
<div class="modal-body">
<div class="date-formats-container">
<div class="date-formats-container__row">
<div class="date-formats-container__column">
<p class="date-formats-text"><strong>Timestamp Format</strong></p>
<ul class="date-formats-list">
<li class="date-formats-item">YYYY-MM-DD</li>
<li class="date-formats-item">YYYY/MM/DD</li>
<li class="date-formats-item">MM-DD-YYYY</li>
<li class="date-formats-item">MM/DD/YYYY</li>
<li class="date-formats-item">DD-MM-YYYY</li>
<li class="date-formats-item">DD/MM/YYYY</li>
<li class="date-formats-item">dd/MMM/yyyy</li>
<li class="date-formats-item">YYYYMMDD</li>
<li class="date-formats-item">YYYY-MM-DDTHH:MM:SS</li>
<li class="date-formats-item">YYYY-MM-DD HH:mm:ss</li>
<li class="date-formats-item">YYYY-MM-DDTHH:mm:ssZ</li>
<li class="date-formats-item">YYYY-MM-DD HH:mm:ss.SSS</li>
<li class="date-formats-item">MM/dd/yyyy hh:mm:ss a</li>
<li class="date-formats-item">MM/dd/yyyy hh:mm:ss a:SSS</li>
<li class="date-formats-item">MMdd_HH:mm:ss.SSS</li>
<li class="date-formats-item">dd MMM yyyy HH:mm:ss*SSS</li>
<li class="date-formats-item">dd MMM yyyy HH:mm:ss</li>
<li class="date-formats-item">yyMMdd HH:mm:ss</li>
<li class="date-formats-item">yy-MM-dd HH:mm:ss</li>
</ul>
</div>
<div class="date-formats-container__column">
<p class="date-formats-text"><strong>Example</strong></p>
<ul class="date-formats-list">
<li class="date-formats-item">2023-07-24</li>
<li class="date-formats-item">2023/07/24</li>
<li class="date-formats-item">07-24-2023</li>
<li class="date-formats-item">07/24/2023</li>
<li class="date-formats-item">24-07-2023</li>
<li class="date-formats-item">24/07/2023</li>
<li class="date-formats-item">25/Nov/2023</li>
<li class="date-formats-item">20231125</li>
<li class="date-formats-item">2023-07-24T14:30:00</li>
<li class="date-formats-item">2023-11-25 12:34:56</li>
<li class="date-formats-item">2023-11-25T12:34:56Z</li>
<li class="date-formats-item">2023-11-25 12:34:56.789</li>
<li class="date-formats-item">9/28/2023 2:23:15 PM</li>
<li class="date-formats-item">8/5/2023 3:31:18 AM:234</li>
<li class="date-formats-item">0423_11:42:35.883</li>
<li class="date-formats-item">23 Apr 2023 10:32:35*311</li>
<li class="date-formats-item">23 Apr 2023 11:42:35</li>
<li class="date-formats-item">220423 11:42:35</li>
<li class="date-formats-item">23-04-19 12:00:17</li>
</ul>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
32 changes: 32 additions & 0 deletions ckanext/charts/theme/mixins.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.date-formats-container {

&__row {
display: flex;
justify-content: space-between;
}

&__column {
flex: 1;
margin-right: 20px;

&: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;
}

0 comments on commit 0c74b4d

Please sign in to comment.