diff --git a/attributions.md b/attributions.md index 16a41baa47..b7c72912e4 100644 --- a/attributions.md +++ b/attributions.md @@ -12,7 +12,6 @@ - JQuery Time Picker, MIT License, (c) 2013 Trent Richardson, http://trentrichardson.com/examples/timepicker - JQuery Hotkeys Plugin, MIT License, (c) 2010, John Resig - prettydate.js, MIT License, (c) 2011, John Resig -- jquery.flot.downsample, MIT License, (c) 2013, Sveinn Steinarsson - JQuery Resize Event, MIT License, (c) 2010 "Cowboy" Ben Alman - excanvas.js, Apache License Version 2.0, (c) 2006 Google Inc - showdown.js - Javascript Markdown, BSD-style Open Source License, (c) 2007 John Fraser diff --git a/frappe/desk/page/activity/activity.css b/frappe/desk/page/activity/activity.css index b28d74ddc0..7eb70669fc 100644 --- a/frappe/desk/page/activity/activity.css +++ b/frappe/desk/page/activity/activity.css @@ -51,18 +51,6 @@ z-index: 1; } -#page-activity .plot-wrapper { - padding: 20px 15px; - border-bottom: 1px solid #d1d8dd; - text-align: center; -} - -#page-activity .plot { - height: 140px !important; - width: 97% !important; - margin: auto; -} - #page-activity .list-filters { display: none !important; } @@ -71,3 +59,11 @@ color: #ff5858; margin: 0px 5px; } + +.heatmap { + padding-top: 30px; +} + +.heatmap svg { + margin: auto; +} \ No newline at end of file diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js index 33b0c95e49..8fb0c1c9ea 100644 --- a/frappe/desk/page/activity/activity.js +++ b/frappe/desk/page/activity/activity.js @@ -15,60 +15,55 @@ frappe.pages['activity'].on_page_load = function(wrapper) { me.page.set_title(__("Activity")); - frappe.require(['assets/frappe/js/lib/flot/jquery.flot.js', - 'assets/frappe/js/lib/flot/jquery.flot.downsample.js'], function() { - - frappe.model.with_doctype("Communication", function() { - me.page.list = new frappe.ui.Listing({ - hide_refresh: true, - page: me.page, - method: 'frappe.desk.page.activity.activity.get_feed', - parent: $("
").appendTo(me.page.main), - render_row: function(row, data) { - new frappe.activity.Feed(row, data); - }, - show_filters: true, - doctype: "Communication", - get_args: function() { - if (frappe.route_options && frappe.route_options.show_likes) { - delete frappe.route_options.show_likes; - return { - show_likes: true - } - } else { - return {} - } + frappe.model.with_doctype("Communication", function() { + me.page.list = new frappe.ui.Listing({ + hide_refresh: true, + page: me.page, + method: 'frappe.desk.page.activity.activity.get_feed', + parent: $("
").appendTo(me.page.main), + render_row: function(row, data) { + new frappe.activity.Feed(row, data); + }, + show_filters: true, + doctype: "Communication", + get_args: function() { + if (frappe.route_options && frappe.route_options.show_likes) { + delete frappe.route_options.show_likes; + return { + show_likes: true } - }); - - me.page.list.run(); - - me.page.set_primary_action(__("Refresh"), function() { - me.page.list.filter_list.clear_filters(); - me.page.list.run(); - }, "octicon octicon-sync"); - }); - - frappe.activity.render_plot(me.page); - - me.page.main.on("click", ".activity-message", function() { - var link_doctype = $(this).attr("data-link-doctype"), - link_name = $(this).attr("data-link-name"), - doctype = $(this).attr("data-doctype"), - docname = $(this).attr("data-docname"); - - if (doctype && docname) { - if (link_doctype && link_name) { - frappe.route_options = { - scroll_to: { "doctype": doctype, "name": docname } - } - } - - frappe.set_route(["Form", link_doctype || doctype, link_name || docname]); + } else { + return {} } - }); + } + }); + + me.page.list.run(); + + me.page.set_primary_action(__("Refresh"), function() { + me.page.list.filter_list.clear_filters(); + me.page.list.run(); + }, "octicon octicon-sync"); }); + frappe.activity.render_heatmap(me.page); + + me.page.main.on("click", ".activity-message", function() { + var link_doctype = $(this).attr("data-link-doctype"), + link_name = $(this).attr("data-link-name"), + doctype = $(this).attr("data-doctype"), + docname = $(this).attr("data-docname"); + + if (doctype && docname) { + if (link_doctype && link_name) { + frappe.route_options = { + scroll_to: { "doctype": doctype, "name": docname } + } + } + + frappe.set_route(["Form", link_doctype || doctype, link_name || docname]); + } + }); // Build Report Button if(frappe.boot.user.can_get_report.indexOf("Feed")!=-1) { @@ -160,94 +155,39 @@ frappe.activity.Feed = Class.extend({ } }); -frappe.activity.render_plot = function(page) { - page.plot_wrapper = $('
') - .prependTo(page.main) - .find(".plot"); - +frappe.activity.render_heatmap = function(page) { + var me = this; + $('

').prependTo(page.main); + frappe.call({ - method: "frappe.desk.page.activity.activity.get_months_activity", + method: "frappe.desk.page.activity.activity.get_heatmap_data", callback: function(r) { - var plot_data = [{ - data: $.map(r.message, function(v, i) { - var d = dateutil.str_to_obj(v[0]); - return [[d.getTime(), v[1]]]; - }) - }]; - - var plot_options = frappe.activity.get_plot_options(); - - page.plot = $.plot(page.plot_wrapper.empty(), plot_data, plot_options); - - frappe.activity.setup_plot_hover(page); - } - }); -}; - -frappe.activity.get_plot_options = function(data) { - return { - grid: { - hoverable: true, - clickable: true, - borderWidth: 1, - borderColor: "#d1d8dd" - }, - xaxis: { - mode: "time", - timeformat: "%d-%b", - minTickSize: [1, "day"], - monthNames: [__("Jan"), __("Feb"), __("Mar"), __("Apr"), __("May"), __("Jun"), - __("Jul"), __("Aug"), __("Sep"), __("Oct"), __("Nov"), __("Dec")], - tickLength: 0 - }, - yaxis: {tickLength: 0}, - series: { - downsample: { threshold: 1000 }, - bars: { - show: true, - fill: true, - barWidth: 43200000, - align: "center", - fillColor: "#FCF8E3" - } - }, - colors: ["#ffa00a"] - } -}; - -frappe.activity.setup_plot_hover = function(page) { - var tooltip_id = frappe.dom.set_unique_id(); - - function showTooltip(x, y, contents) { - $('
' + contents + '
').css( { - position: 'absolute', - display: 'none', - top: y - 30, - left: x - 10, - border: '1px solid #ffa00a', - padding: '2px', - 'background-color': '#ffa00a', - color: "#FCF8E3" - }).appendTo("body").fadeIn(200); - } - - previousPoint = null; - page.plot_wrapper.bind("plothover", function (event, pos, item) { - if (item) { - if (previousPoint != item.dataIndex) { - previousPoint = item.dataIndex; - - $("#" + tooltip_id).remove(); - - var date = dateutil.obj_to_user(new Date(item.datapoint[0])); - var tooltip_text = __("{0} on {1}", ["" + (item.datapoint[1] || 0) + "", date]); - - showTooltip(item.pageX, item.pageY, tooltip_text); + if(r.message) { + heatmap = new CalHeatMap(); + heatmap.init({ + itemSelector: ".heatmap", + domain: "month", + subDomain: "day", + start: moment().subtract(1, 'year').add(1, 'month').toDate(), + cellSize: 9, + cellPadding: 2, + domainGutter: 2, + range: 12, + domainLabelFormat: function(date) { + return moment(date).format("MMM").toUpperCase(); + }, + displayLegend: false, + legend: [5, 10, 15, 20], + tooltip: true, + subDomainTitleFormat: { + empty: "{date}", + filled: "{count} Communications on {date}" + }, + subDomainDateFormat: "%d-%b" + }); + + heatmap.update(r.message); } } - else { - $("#" + tooltip_id).remove(); - previousPoint = null; - } - }); -} + }) +} \ No newline at end of file diff --git a/frappe/desk/page/activity/activity.py b/frappe/desk/page/activity/activity.py index 1ce15c5cef..a4be06d7a7 100644 --- a/frappe/desk/page/activity/activity.py +++ b/frappe/desk/page/activity/activity.py @@ -48,4 +48,13 @@ def get_months_activity(): and date(creation) > subdate(curdate(), interval 1 month) group by date(creation) order by creation asc""", as_list=1) - + +@frappe.whitelist() +def get_heatmap_data(): + return dict(frappe.db.sql("""select unix_timestamp(date(creation)), count(name) + from `tabCommunication` + where + communication_type in ("Communication", "Comment") + and date(creation) > subdate(curdate(), interval 1 year) + group by date(creation) + order by creation asc""")) \ No newline at end of file diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 69602ace6a..09bebed2b3 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -70,7 +70,7 @@ def run(report_name, filters=()): frappe.msgprint(_("Must have report permission to access this report."), raise_exception=True) - columns, result, message = [], [], None + columns, result, message, chart = [], [], None, {} if report.report_type=="Query Report": if not report.query: frappe.msgprint(_("Must specify a Query to run"), raise_exception=True) @@ -86,10 +86,13 @@ def run(report_name, filters=()): if report.is_standard=="Yes": method_name = get_report_module_dotted_path(module, report.name) + ".execute" res = frappe.get_attr(method_name)(frappe._dict(filters)) + columns, result = res[0], res[1] if len(res) > 2: message = res[2] - + if len(res) > 3: + chart = res[3] + if report.apply_user_permissions and result: result = get_filtered_data(report.ref_doctype, columns, result) @@ -99,7 +102,8 @@ def run(report_name, filters=()): return { "result": result, "columns": columns, - "message": message + "message": message, + "chart": chart } def get_report_module_dotted_path(module, report_name): diff --git a/frappe/public/build.json b/frappe/public/build.json index ed307e2bfd..ac336acdf7 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -143,7 +143,9 @@ "public/js/frappe/query_string.js", "public/html/error_object.html", - "public/html/error_snapshot.html" + "public/html/error_snapshot.html", + + "public/js/frappe/ui/charts.js" ], "css/module.min.css": [ "public/css/module.css" diff --git a/frappe/public/js/frappe/form/dashboard.js b/frappe/public/js/frappe/form/dashboard.js index 2de7322a14..1b02060b77 100644 --- a/frappe/public/js/frappe/form/dashboard.js +++ b/frappe/public/js/frappe/form/dashboard.js @@ -10,7 +10,7 @@ frappe.ui.form.Dashboard = Class.extend({ this.headline = this.wrapper.find('.form-headline'); this.progress_area = this.wrapper.find(".progress-area"); this.heatmap_area = this.wrapper.find('.form-heatmap'); - this.graph_area = this.wrapper.find('.form-graph'); + this.chart_area = this.wrapper.find('.form-chart'); this.stats_area = this.wrapper.find('.form-stats'); this.links_area = this.wrapper.find('.form-links'); this.transactions_area = this.links_area.find('.transactions'); @@ -26,9 +26,6 @@ frappe.ui.form.Dashboard = Class.extend({ // clear links this.links_area.addClass('hidden'); this.transactions_area.empty(); - - //clear graphs - this.graph_area.empty().addClass('hidden'); // clear stats this.stats_area.empty().addClass('hidden'); @@ -257,45 +254,25 @@ frappe.ui.form.Dashboard = Class.extend({ }, //graphs - add_graph: function(data) { - if(!data) data = {}; - var chart = c3.generate({ - bindto: '.form-graph', - data: data, - axis: { - x: { - type: 'timeseries', - tick: { - format: '%d-%m-%Y' - } - }, - y: { - min: 0, - padding: {bottom: 10} - } - }, - legend: { - show: false - }, + setup_chart: function(opts) { + var me = this; + + $.extend(opts, { + wrapper: me.wrapper, + bind_to: ".form-chart", padding: { right: 30, bottom: 30 } - }); - - this.chart = chart; - this.graph_area.removeClass('hidden'); - this.show(); - this.set_chart_size(); + + this.chart = new frappe.ui.Chart(opts); + if(this.chart) { + this.show(); + this.chart.set_chart_size(me.wrapper.width() - 60); + } }, - show: function() { this.wrapper.removeClass('hidden'); - }, - - set_chart_size: function() { - var width = this.wrapper.width() - 80; - this.chart.resize({ width: width }); } }); diff --git a/frappe/public/js/frappe/form/templates/form_dashboard.html b/frappe/public/js/frappe/form/templates/form_dashboard.html index 4c8d6eae40..14ba0f7c77 100644 --- a/frappe/public/js/frappe/form/templates/form_dashboard.html +++ b/frappe/public/js/frappe/form/templates/form_dashboard.html @@ -7,7 +7,7 @@
- +