From dd91ea89744336cf5b8c3f60acf31a057ae697eb Mon Sep 17 00:00:00 2001 From: Marton Kiss Date: Wed, 18 Feb 2015 15:51:12 +0100 Subject: [PATCH] Add charts to membership reports Add a custom chartjs module to integrate chart.js with Drupal. Extend the membership report by a bar chart, and the membership history report by a line chart. Change-Id: Ifb134b632e593c88d81b44e97cb1a2c73626f0a8 --- drupal-org.make | 5 ++ groups.install | 10 +++ modules/custom/chartjs/chartjs.info | 9 +++ modules/custom/chartjs/chartjs.install | 40 +++++++++++ modules/custom/chartjs/chartjs.module | 56 +++++++++++++++ modules/custom/chartjs/chartjs_elements.inc | 70 +++++++++++++++++++ modules/custom/chartjs/js/chart_elements.js | 21 ++++++ .../groups/groups_reports/groups_reports.info | 3 +- .../groups_reports/groups_reports.install | 2 +- .../groups_reports/groups_reports.module | 54 +++++++++++--- .../custom-sass/partials/_reports.scss | 26 +++++++ 11 files changed, 283 insertions(+), 13 deletions(-) create mode 100644 modules/custom/chartjs/chartjs.info create mode 100644 modules/custom/chartjs/chartjs.install create mode 100644 modules/custom/chartjs/chartjs.module create mode 100644 modules/custom/chartjs/chartjs_elements.inc create mode 100644 modules/custom/chartjs/js/chart_elements.js diff --git a/drupal-org.make b/drupal-org.make index 6b9bb39..2b5697d 100644 --- a/drupal-org.make +++ b/drupal-org.make @@ -688,3 +688,8 @@ libraries[feeds_jsonpath_parser][download][type] = get libraries[feeds_jsonpath_parser][download][url] = http://jsonpath.googlecode.com/files/jsonpath-0.8.1.php libraries[feeds_jsonpath_parser][destination] = modules/contrib libraries[feeds_jsonpath_parser][install_path] = profiles/groups + +libraries[chartjs][download][type] = "get" +libraries[chartjs][type] = "libraries" +libraries[chartjs][download][url] = "https://github.com/nnnick/Chart.js/archive/v1.0.1.tar.gz" +libraries[chartjs][destination] = "libraries" \ No newline at end of file diff --git a/groups.install b/groups.install index 4c5d962..e2d6ac0 100644 --- a/groups.install +++ b/groups.install @@ -313,6 +313,16 @@ function groups_update_7115() { drupal_flush_all_caches(); } +/** + * Enable chartjs module. + */ +function groups_update_7116() { + if (!module_exists('chartjs')) { + module_enable(array('chartjs')); + } + drupal_flush_all_caches(); +} + /** * Set language negotiation to URL based. */ diff --git a/modules/custom/chartjs/chartjs.info b/modules/custom/chartjs/chartjs.info new file mode 100644 index 0000000..1a00db6 --- /dev/null +++ b/modules/custom/chartjs/chartjs.info @@ -0,0 +1,9 @@ +name = Chart.js +description = Charting module for Drupal based on Chart.js javascript library. +core = 7.x + +dependencies[] = libraries (>=2.0) + +version = "7.x-1.0" +core = "7.x" +project = "chartjs" diff --git a/modules/custom/chartjs/chartjs.install b/modules/custom/chartjs/chartjs.install new file mode 100644 index 0000000..3eff5c4 --- /dev/null +++ b/modules/custom/chartjs/chartjs.install @@ -0,0 +1,40 @@ + 'Chart.js', + ); + foreach ($libraries as $lib => $label) { + $requirements['chartjs_' . $lib] = array( + 'title' => t('Chart.js: @library library', array('@library' => $label)), + 'value' => t('The @library library is not present', array('@library' => $label)), + 'severity' => REQUIREMENT_ERROR, + ); + if (function_exists('libraries_detect')) { + if (($library = libraries_detect($lib)) && !empty($library['installed'])) { + $requirements['chartjs_' . $lib]['value'] = t('@version (@variant)', array( + '@version' => $library['version'], + '@variant' => TRUE + )); + $requirements['chartjs_' . $lib]['severity'] = REQUIREMENT_OK; + } + elseif (!empty($library['error'])) { + $requirements['chartjs_' . $lib]['description'] = $library['error message']; + } + } + } + } + + return $requirements; +} \ No newline at end of file diff --git a/modules/custom/chartjs/chartjs.module b/modules/custom/chartjs/chartjs.module new file mode 100644 index 0000000..4f019af --- /dev/null +++ b/modules/custom/chartjs/chartjs.module @@ -0,0 +1,56 @@ + 'Chart.js', + 'vendor url' => 'http://www.chartjs.org/', + 'download url' => 'https://github.com/nnnick/Chart.js/archive/v1.0.1.tar.gz', + // 'version callback' => 'short_circuit_version', + 'version arguments' => array( + 'file' => 'Chart.js', + 'pattern' => '/Version: (\d+\.+\d+\.+\d+)/', + 'lines' => 4, + ), + 'files' => array( + 'js' => array( + 'Chart.js', + ), + ), + ); + return $libraries; +} + +/** + * Implements hook_element_info() + */ +function chartjs_element_info() { + require_once 'chartjs_elements.inc'; + return _chartjs_element_info(); +} + +/** + * Implements hook_theme() + */ +function chartjs_theme($existing, $type, $theme, $path) { + require_once 'chartjs_elements.inc'; + return _chartjs_theme($existing, $type, $theme, $path); +} + +/** + * Construct a new dataset with defaults + */ +function chartjs_create_dataset($data = array()) { + $dataSet = new stdClass(); + $dataSet->label = 'Dataset label'; + $dataSet->fillColor = 'rgba(128,197,229,1)'; + $dataSet->strokeColor = 'rgba(128,197,229,1)'; + $dataSet->highlightFill = 'rgba(0,153,218,1)'; + $dataSet->highlightStroke = 'rgba(0,153,218,1)'; + $dataSet->data = $data; + return $dataSet; +} \ No newline at end of file diff --git a/modules/custom/chartjs/chartjs_elements.inc b/modules/custom/chartjs/chartjs_elements.inc new file mode 100644 index 0000000..5ccfaec --- /dev/null +++ b/modules/custom/chartjs/chartjs_elements.inc @@ -0,0 +1,70 @@ + TRUE, + '#theme' => 'chartjs_chart', + '#theme_wrappers' => array('form_element'), + '#pre_render' => array('_chartjs_chart_pre_render'), + ); + return $types; +} + +/** + * Define chartjs theme callbacks. + */ +function _chartjs_theme($existing, $type, $theme, $path) { + return array( + 'chartjs_chart' => array( + 'render element' => 'element', + 'file' => 'chartjs_elements.inc', + ), + ); +} + +/** + * Implement chart pre render callback. + */ +function _chartjs_chart_pre_render($element) { + libraries_load('chartjs'); + $name = isset($element['#name']) ? $element['#name'] : "chart"; + $chart_type = isset($element['#chart_type']) ? $element['#chart_type'] : "bar"; + $element['#attached']['js'] = array( + drupal_get_path('module', 'chartjs').'/js/chart_elements.js', + array( + 'data' => array( + 'chart_'.$name => array( + 'labels' => $element['#labels'], + 'datasets' => $element['#datasets'], + ), + 'chart_'.$name.'_options' => $element['#options'], + 'chart_'.$name.'_type' => $chart_type, + ), + 'type' => 'setting', + ) + ); + return $element; +} + +/** + * Implements chart theme callback. + */ +function theme_chartjs_chart($variables) { + $name = isset($variables['element']['#name']) ? $variables['element']['#name'] : 'chart'; + $output = '
'; + $output .= sprintf('
', $name); + return $output; +} \ No newline at end of file diff --git a/modules/custom/chartjs/js/chart_elements.js b/modules/custom/chartjs/js/chart_elements.js new file mode 100644 index 0000000..1cb4e3b --- /dev/null +++ b/modules/custom/chartjs/js/chart_elements.js @@ -0,0 +1,21 @@ +(function ($) { + Drupal.behaviors.chartJS = { + attach: function (context, settings) { + $('.chart-container canvas').each( function (index, data) { + var element_id = $(this).attr('id'); + var ctx = document.getElementById(element_id).getContext("2d"); + var data = settings['chart_' + element_id]; + var options = settings['chart_' + element_id + '_options']; + switch (settings['chart_' + element_id + '_type']) { + case 'bar': + var myChart = new Chart(ctx).Bar(data, options); + break; + case 'line': + var myChart = new Chart(ctx).Line(data, options); + break; + } + }); + } + }; + +})(jQuery); \ No newline at end of file diff --git a/modules/groups/groups_reports/groups_reports.info b/modules/groups/groups_reports/groups_reports.info index 76afc5c..14cad65 100644 --- a/modules/groups/groups_reports/groups_reports.info +++ b/modules/groups/groups_reports/groups_reports.info @@ -4,4 +4,5 @@ core = 7.x package = Groups - Building Blocks version = 7.x-1.0 project = groups_reports -dependencies[] = elysia_cron \ No newline at end of file +dependencies[] = elysia_cron +dependencies[] = chartjs \ No newline at end of file diff --git a/modules/groups/groups_reports/groups_reports.install b/modules/groups/groups_reports/groups_reports.install index 4fd3536..fef23b2 100644 --- a/modules/groups/groups_reports/groups_reports.install +++ b/modules/groups/groups_reports/groups_reports.install @@ -43,4 +43,4 @@ function groups_reports_schema() { 'primary key' => array('nid', 'timestamp'), ); return $schema; -} \ No newline at end of file +} diff --git a/modules/groups/groups_reports/groups_reports.module b/modules/groups/groups_reports/groups_reports.module index 94b1683..58fc2ec 100644 --- a/modules/groups/groups_reports/groups_reports.module +++ b/modules/groups/groups_reports/groups_reports.module @@ -82,6 +82,10 @@ function groups_reports_cronapi($op, $job = NULL) { */ function groups_reports_groups_membership_report() { module_load_include('inc', 'field_group_location', 'field_group_lookup'); + $chart_data = array( + 'labels' => array(), + 'datasets' => array(chartjs_create_dataset()), + ); $continents = _continent_get_predefined_list(); $report = groups_report_get_regional_membership_report(time()); $total = 0; @@ -91,19 +95,20 @@ function groups_reports_groups_membership_report() { 'title' => t('@title', array('!url' => '#'.$key, '@title' => $value)), 'count' => isset($report['totals'][$key]) ? $report['totals'][$key] : 0, ); + $chart_data['labels'][] = $value; + $chart_data['datasets'][0]->data[] = $summary[$key]['count']; $total += $summary[$key]['count']; } - $summary['XX'] = array( - 'title' => 'Total', - 'count' => ''.$total.'', + $build['chart'] = array( + '#type' => 'chart', + '#labels' => $chart_data['labels'], + '#datasets' => $chart_data['datasets'], + '#options' => array('responsive' => true, 'maintainAspectRatio' => false), ); - $build['summary_table'] = array( - '#theme' => 'table', - '#header' => array(t('Continent'), t('Total members')), - '#rows' => $summary, - '#sticky' => FALSE, - '#attributes' => array('id' => 'group-report-summary'), - '#empty' => t('No membership data available.'), + $build['totals'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#markup' => t('Community members worldwide @total people', array('@total' => $total)), ); foreach ($report['rows'] as $row) { $data[$row['field_group_location_continent']][] = array( @@ -136,10 +141,37 @@ function groups_reports_groups_membership_report() { */ function groups_reports_groups_membership_history_report() { $report = groups_report_get_members_report(time()); - $report = array_reverse($report); + $chart_data = array( + 'labels' => array(), + 'datasets' => array(chartjs_create_dataset()), + ); + $chart_data['datasets'][0]->strokeColor = '#0099da'; + $chart_data['datasets'][0]->fillColor = '#d7edfb'; + $chart_data['datasets'][0]->pointColor = '#0099da'; + $chart_data['datasets'][0]->pointStrokeColor = '#0099da'; + $chart_data['datasets'][0]->pointHighlightFill = '#000000'; + $chart_data['datasets'][0]->pointHighlightStroke = '#000000'; foreach ($report as &$item) { $item['timestamp'] = format_date($item['timestamp'], 'custom', 'm/d/Y', 'GMT'); + $chart_data['labels'][] = $item['timestamp']; + $chart_data['datasets'][0]->data[] = $item['count']; } + $report = array_reverse($report); + $build['chart'] = array( + '#type' => 'chart', + '#chart_type' => 'line', + '#name' => 'membership-history-chart', + '#height' => '300px', + '#labels' => $chart_data['labels'], + '#datasets' => $chart_data['datasets'], + '#options' => array( + 'responsive' => true, + 'maintainAspectRatio' => false, + 'datasetFill' => true, + 'bezierCurve' => false, + 'datasetStrokeWidth' => 3, + ), + ); $header = array(t('Date'), t('Members')); $build['summary_table'] = array( '#theme' => 'table', diff --git a/themes/openstack_bootstrap/custom-sass/partials/_reports.scss b/themes/openstack_bootstrap/custom-sass/partials/_reports.scss index d467470..c53f395 100644 --- a/themes/openstack_bootstrap/custom-sass/partials/_reports.scss +++ b/themes/openstack_bootstrap/custom-sass/partials/_reports.scss @@ -12,4 +12,30 @@ th, td { width: 50%; } +} + +.totals-container { + background: #EDF2F7; + padding: 10px; + .totals_label { + color: #2A4E68; + font-size: 30px; + font-weight: 300; + display: inline-block; + padding-right: 10px; + margin-right: 10px; + border-right: 4px solid #ffffff; + } + .total { + font-size: 30px; + font-weight: bold; + } + .suffix { + color: #2A4E68; + font-weight: 300; + } +} + +#membership-history-chart { + height: 400px; } \ No newline at end of file