spree_advanced_reporting 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/Gemfile +5 -0
- data/README.md +23 -0
- data/Rakefile +20 -0
- data/TODO.txt +3 -0
- data/app/assets/images/admin/advanced_reporting/asc.gif +0 -0
- data/app/assets/images/admin/advanced_reporting/barchart.png +0 -0
- data/app/assets/images/admin/advanced_reporting/bg.gif +0 -0
- data/app/assets/images/admin/advanced_reporting/close.png +0 -0
- data/app/assets/images/admin/advanced_reporting/desc.gif +0 -0
- data/app/assets/images/admin/advanced_reporting/menu-current-opposite.png +0 -0
- data/app/assets/images/admin/advanced_reporting/open.png +0 -0
- data/app/assets/images/admin/advanced_reporting/save.png +0 -0
- data/app/assets/images/admin/advanced_reporting/search.png +0 -0
- data/app/assets/images/admin/advanced_reporting/usa.png +0 -0
- data/app/assets/images/admin/advanced_reporting/world.png +0 -0
- data/app/assets/javascripts/admin/advanced_reporting/advanced_reporting.js +46 -0
- data/app/assets/javascripts/admin/advanced_reporting/jquery.tablesorter.min.js +4 -0
- data/app/assets/stylesheets/admin/advanced_reporting/advanced_reporting.css.erb +18 -0
- data/app/assets/stylesheets/pdf.css +15 -0
- data/app/controllers/spree/admin/advanced_report_overview_controller.rb +21 -0
- data/app/controllers/spree/admin/reports_controller_decorator.rb +127 -0
- data/app/helpers/advanced_report_helper.rb +13 -0
- data/app/models/ruport/formatter/html_decorator.rb +29 -0
- data/app/models/ruport/formatter/wicked_pdf_decorator.rb +12 -0
- data/app/views/spree/admin/advanced_report_overview/index.html.erb +117 -0
- data/app/views/spree/admin/reports/_advanced_report_criteria.html.erb +64 -0
- data/app/views/spree/admin/reports/geo_base.html.erb +61 -0
- data/app/views/spree/admin/reports/increment_base.html.erb +63 -0
- data/app/views/spree/admin/reports/outstanding.html.erb +16 -0
- data/app/views/spree/admin/reports/top_base.html.erb +17 -0
- data/config/locales/en.yml +96 -0
- data/config/locales/pt-BR.yml +92 -0
- data/config/routes.rb +37 -0
- data/lib/spree/advanced_report.rb +225 -0
- data/lib/spree/advanced_report/geo_report.rb +2 -0
- data/lib/spree/advanced_report/geo_report/geo_profit.rb +44 -0
- data/lib/spree/advanced_report/geo_report/geo_revenue.rb +44 -0
- data/lib/spree/advanced_report/geo_report/geo_units.rb +42 -0
- data/lib/spree/advanced_report/increment_report.rb +78 -0
- data/lib/spree/advanced_report/increment_report/count.rb +33 -0
- data/lib/spree/advanced_report/increment_report/profit.rb +41 -0
- data/lib/spree/advanced_report/increment_report/revenue.rb +40 -0
- data/lib/spree/advanced_report/increment_report/units.rb +33 -0
- data/lib/spree/advanced_report/top_report.rb +2 -0
- data/lib/spree/advanced_report/top_report/top_customers.rb +32 -0
- data/lib/spree/advanced_report/top_report/top_products.rb +35 -0
- data/lib/spree/advanced_report/transaction_report.rb +79 -0
- data/lib/spree_advanced_reporting.rb +25 -0
- data/spree_advanced_reporting.gemspec +22 -0
- metadata +133 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.DS_Store
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# NOTES:
|
2
|
+
|
3
|
+
This branch is for use with Spree 1.1.0 and later.
|
4
|
+
|
5
|
+
For Spree 1.0.x, use the spree-1-0 branch.
|
6
|
+
|
7
|
+
Forked from what appeared to the be the most up to date for, and made the following general changes:
|
8
|
+
|
9
|
+
1. Removed the route that overrides the main admin overview page
|
10
|
+
2. Fixed a warning about ```ADVANCED_REPORTS``` being redefined
|
11
|
+
3. Improved PDF generation ([custom ruport](http://github.com/iloveitaly/ruport/tree/wicked-pdf) uses wicked_pdf instead of the ancient PDF::Writer)
|
12
|
+
|
13
|
+
## Includes:
|
14
|
+
* Base reports of Revenue, Units, Profit into Daily, Weekly, Monthly, and Yearly increments
|
15
|
+
* Geo reports of Revenue, Units divided into states and countries
|
16
|
+
* Two "top" reports for top products and top customers
|
17
|
+
* The ability to limit reports by order date, "store" (multi-store extension), product, and taxon.
|
18
|
+
* The ability to export data in PDF or CSV format.
|
19
|
+
* Transaction reports
|
20
|
+
|
21
|
+
## Dependencies:
|
22
|
+
* Ruport and Ruport-util
|
23
|
+
* Google Visualization
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/packagetask'
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require 'cucumber/rake/task'
|
7
|
+
require 'spree_core/testing_support/common_rake'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new
|
10
|
+
Cucumber::Rake::Task.new
|
11
|
+
|
12
|
+
task :default => [:spec, :cucumber ]
|
13
|
+
|
14
|
+
spec = eval(File.read('advanced_reporting.gemspec'))
|
15
|
+
|
16
|
+
desc "Generates a dummy app for testing"
|
17
|
+
task :test_app do
|
18
|
+
ENV['LIB_NAME'] = 'advanced_reporting'
|
19
|
+
Rake::Task['common:test_app'].invoke
|
20
|
+
end
|
data/TODO.txt
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,46 @@
|
|
1
|
+
// TODO this would look alot cleaner in coffeescript
|
2
|
+
|
3
|
+
$(function() {
|
4
|
+
$('ul#show_data li').click(function() {
|
5
|
+
$('ul#show_data li').not(this).removeClass('selected');
|
6
|
+
$(this).addClass('selected');
|
7
|
+
var id = 'div#' + $(this).attr('id') + '_data';
|
8
|
+
$('div.advanced_reporting_data').not($(id)).hide();
|
9
|
+
$(id).show();
|
10
|
+
});
|
11
|
+
|
12
|
+
if($('table.tablesorter tbody').length) {
|
13
|
+
$('table.tablesorter').tablesorter();
|
14
|
+
$('table.tablesorter').bind("sortEnd", function() {
|
15
|
+
var section = $(this).parent().attr('id');
|
16
|
+
var even = true;
|
17
|
+
$.each($('div#' + section + ' table tr'), function(i, j) {
|
18
|
+
$(j).removeClass('even').removeClass('odd');
|
19
|
+
$(j).addClass(even ? 'even' : 'odd');
|
20
|
+
even = !even;
|
21
|
+
});
|
22
|
+
});
|
23
|
+
}
|
24
|
+
|
25
|
+
if($('input#product_id').length > 0) {
|
26
|
+
$('select#advanced_reporting_product_id').val($('input#product_id').val());
|
27
|
+
}
|
28
|
+
if($('input#taxon_id').length > 0) {
|
29
|
+
$('select#advanced_reporting_taxon_id').val($('input#taxon_id').val());
|
30
|
+
}
|
31
|
+
$('div#advanced_report_search form').submit(function() {
|
32
|
+
$('div#advanced_report_search form').attr('action', $('select#report').val());
|
33
|
+
});
|
34
|
+
|
35
|
+
$('select#report').change(function() {
|
36
|
+
var value = $(this).val()
|
37
|
+
$('div#advanced_report > form').action = value
|
38
|
+
|
39
|
+
if(value.match(/\/count$/) || value.match(/\/top_products$/)) {
|
40
|
+
$('select#advanced_reporting_product_id,select#advanced_reporting_taxon_id').val('');
|
41
|
+
$('div#taxon_products').hide();
|
42
|
+
} else {
|
43
|
+
$('div#taxon_products').show();
|
44
|
+
}
|
45
|
+
}).trigger('change')
|
46
|
+
})
|
@@ -0,0 +1,4 @@
|
|
1
|
+
|
2
|
+
(function($){$.extend({tablesorter:new
|
3
|
+
function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
|
4
|
+
var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
|
@@ -0,0 +1,18 @@
|
|
1
|
+
/* without overriding the default spree_core in spree admin you can't include CSS */
|
2
|
+
/* easier to precompile this asset and include manually via stylesheet tag */
|
3
|
+
|
4
|
+
div.canvas { padding-bottom: 20px; }
|
5
|
+
|
6
|
+
table.tablesorter th.header { background-image: url(<%= asset_path 'admin/advanced_reporting/bg.gif'%>); cursor: pointer; font-weight: bold; background-repeat: no-repeat; background-position: center left; padding-left: 20px; border-right: 1px solid #dad9c7; margin-left: -1px; }
|
7
|
+
table.tablesorter th.headerSortUp { background-image: url(<%= asset_path 'admin/advanced_reporting/asc.gif'%>); background-color: #3399FF; }
|
8
|
+
table.tablesorter th.headerSortDown { background-image: url(<%= asset_path 'admin/advanced_reporting/desc.gif'%>); background-color: #3399FF; }
|
9
|
+
|
10
|
+
#sidebar #advanced_report_search h1,
|
11
|
+
#sidebar #advanced_report_search h2,
|
12
|
+
#sidebar #advanced_report_search h3 {
|
13
|
+
margin-left:0;
|
14
|
+
}
|
15
|
+
|
16
|
+
#sidebar #advanced_report_search select {
|
17
|
+
max-width: 100%;
|
18
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
body { font:normal 100%/1.25 Helvetica, Arial, Verdana, sans-serif; }
|
2
|
+
|
3
|
+
.odd { background-color: #e6e6e6; }
|
4
|
+
|
5
|
+
/*
|
6
|
+
* Page breaking seems like a tricky issue...
|
7
|
+
* http://stackoverflow.com/questions/1763639/how-to-deal-with-page-breaks-when-printing-a-large-html-table
|
8
|
+
* http://stackoverflow.com/questions/8786755/wkhtmltopdf-characters-in-single-line-partially-cut-between-pages
|
9
|
+
* http://stackoverflow.com/questions/1539876/controlling-css-page-breaks-when-printing-in-webkit
|
10
|
+
*
|
11
|
+
* as long as vertical-align:bottom is set on all tds, and a recent version of the binary is installed,
|
12
|
+
* things seem to work well enough (not clean breaks, but nothing is cut off)
|
13
|
+
*/
|
14
|
+
|
15
|
+
td { vertical-align: bottom; padding:5px; }
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Spree::Admin::AdvancedReportOverviewController < Spree::Admin::BaseController
|
2
|
+
def index
|
3
|
+
@reports = Spree::Admin::ReportsController::ADVANCED_REPORTS
|
4
|
+
@products = Spree::Product.all
|
5
|
+
@taxons = Spree::Taxon.all
|
6
|
+
if defined?(MultiDomainExtension)
|
7
|
+
@stores = Store.all
|
8
|
+
end
|
9
|
+
@report = Spree::AdvancedReport::IncrementReport::Revenue.new({ :search => {} })
|
10
|
+
@top_products_report = Spree::AdvancedReport::TopReport::TopProducts.new({ :search => {} }, 5)
|
11
|
+
@top_customers_report = Spree::AdvancedReport::TopReport::TopCustomers.new({ :search => {} }, 5)
|
12
|
+
@top_customers_report.ruportdata.remove_column(I18n.t("adv_report.units"))
|
13
|
+
|
14
|
+
# From overview_dashboard, Cleanup eventually
|
15
|
+
orders = Spree::Order.find(:all, :order => "completed_at DESC", :limit => 10, :include => :line_items, :conditions => "completed_at is not null")
|
16
|
+
@last_orders = orders.inject([]) { |arr, o| arr << [o.bill_address.firstname, o.line_items.sum(:quantity), o.total, o.number]; arr }
|
17
|
+
@best_taxons = Spree::Taxon.connection.select_rows("select t.name, count(li.quantity) from spree_line_items li inner join spree_variants v on
|
18
|
+
li.variant_id = v.id inner join spree_products p on v.product_id = p.id inner join spree_products_taxons pt on p.id = pt.product_id
|
19
|
+
inner join spree_taxons t on pt.taxon_id = t.id where t.taxonomy_id = #{Spree::Taxonomy.last.id} group by t.name order by count(li.quantity) desc limit 5;")
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require_dependency 'spree/admin/reports_controller'
|
2
|
+
|
3
|
+
Spree::Admin::ReportsController.class_eval do
|
4
|
+
# until https://github.com/spree/spree/issues/1863 is taken care of
|
5
|
+
# this is a workaround hack to get the report definitions to load
|
6
|
+
|
7
|
+
I18n.load_path << Spree::AdvancedReporting::Engine.config.paths["config/locales"].first
|
8
|
+
I18n.locale = Spree::Config[:default_locale]
|
9
|
+
I18n.reload!
|
10
|
+
|
11
|
+
# TODO there has got to be a more ruby way to do this...
|
12
|
+
ADVANCED_REPORTS ||= {}
|
13
|
+
[ :outstanding, :revenue, :units, :profit, :count, :top_products, :top_customers, :geo_revenue, :geo_units, :geo_profit, :transactions].each do |x|
|
14
|
+
# TODO we should pull the name + description for the report models themselves rather than redefining them as I18n definitions
|
15
|
+
ADVANCED_REPORTS[x]= {name: I18n.t("adv_report.#{x}"), :description => I18n.t("adv_report.#{x}")}
|
16
|
+
end
|
17
|
+
|
18
|
+
Spree::Admin::ReportsController::AVAILABLE_REPORTS.merge!(ADVANCED_REPORTS)
|
19
|
+
|
20
|
+
before_filter :basic_report_setup, :actions => ADVANCED_REPORTS.keys
|
21
|
+
|
22
|
+
def basic_report_setup
|
23
|
+
@reports = ADVANCED_REPORTS
|
24
|
+
@products = Spree::Product.all
|
25
|
+
@taxons = Spree::Taxon.all
|
26
|
+
if defined?(MultiDomainExtension)
|
27
|
+
@stores = Store.all
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def geo_report_render(filename)
|
32
|
+
params[:advanced_reporting] ||= {}
|
33
|
+
params[:advanced_reporting]["report_type"] = params[:advanced_reporting]["report_type"].to_sym if params[:advanced_reporting]["report_type"]
|
34
|
+
params[:advanced_reporting]["report_type"] ||= :state
|
35
|
+
respond_to do |format|
|
36
|
+
format.html { render :template => "spree/admin/reports/geo_base" }
|
37
|
+
format.pdf { send_data @report.ruportdata[params[:advanced_reporting]['report_type']].to_pdf }
|
38
|
+
format.csv { send_data @report.ruportdata[params[:advanced_reporting]['report_type']].to_csv }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def base_report_top_render(filename)
|
43
|
+
respond_to do |format|
|
44
|
+
format.html { render :template => "spree/admin/reports/top_base" }
|
45
|
+
format.pdf { send_data @report.ruportdata.to_pdf }
|
46
|
+
format.csv { send_data view_context.strip_tags(@report.ruportdata.to_csv) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def base_report_render(filename)
|
51
|
+
params[:advanced_reporting] ||= {}
|
52
|
+
params[:advanced_reporting]["report_type"] = params[:advanced_reporting]["report_type"].to_sym if params[:advanced_reporting]["report_type"]
|
53
|
+
params[:advanced_reporting]["report_type"] ||= I18n.t("adv_report.daily").downcase.to_sym
|
54
|
+
respond_to do |format|
|
55
|
+
format.html { render :template => "spree/admin/reports/increment_base" }
|
56
|
+
format.pdf do
|
57
|
+
if params[:advanced_reporting]["report_type"] == :all
|
58
|
+
send_data @report.all_data.to_pdf
|
59
|
+
else
|
60
|
+
send_data @report.ruportdata[params[:advanced_reporting]["report_type"]].to_pdf
|
61
|
+
end
|
62
|
+
end
|
63
|
+
format.csv do
|
64
|
+
if params[:advanced_reporting]["report_type"] == :all
|
65
|
+
send_data @report.all_data.to_csv
|
66
|
+
else
|
67
|
+
send_data @report.ruportdata[params[:advanced_reporting]['report_type']].to_csv
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def outstanding
|
74
|
+
@orders = Spree::Order.complete.where("state != 'canceled'").select{ |o| o.outstanding_balance? }
|
75
|
+
@outstanding_balance = @orders.inject(0){ |outstanding, o| outstanding += o.outstanding_balance }
|
76
|
+
end
|
77
|
+
|
78
|
+
def revenue
|
79
|
+
@report = Spree::AdvancedReport::IncrementReport::Revenue.new(params)
|
80
|
+
base_report_render("revenue")
|
81
|
+
end
|
82
|
+
|
83
|
+
def units
|
84
|
+
@report = Spree::AdvancedReport::IncrementReport::Units.new(params)
|
85
|
+
base_report_render("units")
|
86
|
+
end
|
87
|
+
|
88
|
+
def profit
|
89
|
+
@report = Spree::AdvancedReport::IncrementReport::Profit.new(params)
|
90
|
+
base_report_render("profit")
|
91
|
+
end
|
92
|
+
|
93
|
+
def count
|
94
|
+
@report = Spree::AdvancedReport::IncrementReport::Count.new(params)
|
95
|
+
base_report_render("profit")
|
96
|
+
end
|
97
|
+
|
98
|
+
def top_products
|
99
|
+
@report = Spree::AdvancedReport::TopReport::TopProducts.new(params, 4)
|
100
|
+
base_report_top_render("top_products")
|
101
|
+
end
|
102
|
+
|
103
|
+
def top_customers
|
104
|
+
@report = Spree::AdvancedReport::TopReport::TopCustomers.new(params, 4)
|
105
|
+
base_report_top_render("top_customers")
|
106
|
+
end
|
107
|
+
|
108
|
+
def geo_revenue
|
109
|
+
@report = Spree::AdvancedReport::GeoReport::GeoRevenue.new(params)
|
110
|
+
geo_report_render("geo_revenue")
|
111
|
+
end
|
112
|
+
|
113
|
+
def geo_units
|
114
|
+
@report = Spree::AdvancedReport::GeoReport::GeoUnits.new(params)
|
115
|
+
geo_report_render("geo_units")
|
116
|
+
end
|
117
|
+
|
118
|
+
def geo_profit
|
119
|
+
@report = Spree::AdvancedReport::GeoReport::GeoProfit.new(params)
|
120
|
+
geo_report_render("geo_profit")
|
121
|
+
end
|
122
|
+
|
123
|
+
def transactions
|
124
|
+
@report = Spree::AdvancedReport::TransactionReport.new(params)
|
125
|
+
base_report_top_render("transactions")
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AdvancedReportHelper
|
2
|
+
def order_states_options
|
3
|
+
order_states.inject([]){ |acc, value| acc << [t("order_state.#{value}"), value]; acc}
|
4
|
+
end
|
5
|
+
|
6
|
+
def order_states
|
7
|
+
if Spree::Order.respond_to? :progress_states
|
8
|
+
Spree::Order.progress_states.unshift "complete"
|
9
|
+
else
|
10
|
+
Spree::Order.state_machines[:state].states.map(&:name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Ruport::Formatter::HTML.class_eval do
|
2
|
+
# Renders individual rows for the table.
|
3
|
+
def build_row(data = self.data)
|
4
|
+
@odd = !@odd
|
5
|
+
klass = @odd ? "odd" : "even"
|
6
|
+
output <<
|
7
|
+
"\t\t<tr class=\"#{klass}\">\n\t\t\t<td>" +
|
8
|
+
data.to_a.join("</td>\n\t\t\t<td>") +
|
9
|
+
"</td>\n\t\t</tr>\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def html_table
|
13
|
+
@odd = false
|
14
|
+
"<table class=\"tablesorter\">\n" << yield << "</table>\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_table_header
|
18
|
+
output << "\t<table class=\"tablesorter\">\n"
|
19
|
+
unless data.column_names.empty? || !options.show_table_headers
|
20
|
+
output << "\t\t<thead><tr>\n\t\t\t<th>" +
|
21
|
+
data.column_names.join("</th>\n\t\t\t<th>") +
|
22
|
+
"</th>\n\t\t</tr></thead>\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_group_header
|
27
|
+
output << "\t<h1>#{data.name}</h1>\n"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Ruport::Formatter::WickedPDF.class_eval do
|
2
|
+
def finalize_table
|
3
|
+
# TODO would be great to eliminate this hack
|
4
|
+
|
5
|
+
# I think the reason the helper doesn't work is because of the pdf_from_string call vs the standard render :pdf
|
6
|
+
# <%= wicked_pdf_stylesheet_link_tag "pdf" %>
|
7
|
+
|
8
|
+
output.replace(WickedPdf.new.pdf_from_string("<style>" + Rails.application.assets.find_asset("pdf").body + "</style>" + output,
|
9
|
+
:print_media_type => false
|
10
|
+
))
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
<table width="100%">
|
2
|
+
<tr>
|
3
|
+
<td colspan="3">
|
4
|
+
<% @report.increments.each do |type| -%>
|
5
|
+
<div id="<%= type.to_s %>_data" <%= type.to_s == 'daily' ? '' : 'style=display:none;' %> class="advanced_reporting_data">
|
6
|
+
<h3><%=t "adv_report."+ type.to_s %> <%=t "adv_report."+@report.name.downcase %></h3>
|
7
|
+
<div id="<%= type.to_s %>_chart_revenue"></div>
|
8
|
+
</div>
|
9
|
+
<% end -%>
|
10
|
+
</td>
|
11
|
+
</tr>
|
12
|
+
<tr>
|
13
|
+
<td width="33%">
|
14
|
+
<h3><%=t("adv_report.top_products_by_revenue")%></h3>
|
15
|
+
<div id="top_products_chart"></div>
|
16
|
+
<h3>Top Taxons by Quantity</h3>
|
17
|
+
<div id="top_taxons_chart"></div>
|
18
|
+
</td>
|
19
|
+
<td width="33%">
|
20
|
+
<h3><%=t("adv_report.top_customers_by_revenue")%></h3>
|
21
|
+
<div id="top_customers">
|
22
|
+
<%= raw @top_customers_report.ruportdata.to_html %>
|
23
|
+
</div>
|
24
|
+
</td>
|
25
|
+
<td width="33%">
|
26
|
+
<h3><%=t("adv_report.last_10_orders")%></h3>
|
27
|
+
<div id="recent_orders">
|
28
|
+
<table class="tablesorter">
|
29
|
+
<thead><tr>
|
30
|
+
<th><%=t(:order_number)%></th>
|
31
|
+
<th><%=t(:order)%></th>
|
32
|
+
<th><%=t("adv_report.items_count")%></th>
|
33
|
+
<th><%=t(:total)%></th>
|
34
|
+
</tr></thead>
|
35
|
+
<% @last_orders.each do |order| %>
|
36
|
+
<tr class="odd">
|
37
|
+
<td><%=link_to order[3], admin_order_path(:id => order[3]) %></td>
|
38
|
+
<td><%= order[0] %></td>
|
39
|
+
<td><%= order[1] %></td>
|
40
|
+
<td><%= number_to_currency order[2] %></td>
|
41
|
+
</tr>
|
42
|
+
<% end -%>
|
43
|
+
</table>
|
44
|
+
</div>
|
45
|
+
</td>
|
46
|
+
</tr>
|
47
|
+
</table>
|
48
|
+
|
49
|
+
<% content_for :sidebar do %>
|
50
|
+
<div class="report_details">
|
51
|
+
<h1><%= t("adv_report.dashboard")%></h1>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
<ul id="show_data">
|
55
|
+
<% @report.increments.each do |inc| %>
|
56
|
+
<li <%= inc == :daily ? 'class=selected' : '' %> id="<%= inc %>">
|
57
|
+
<label><%= t("adv_report.#{inc}") %></label><br />
|
58
|
+
<a href="<%= @report.download_url('/admin/reports/revenue', 'csv', inc) %>">CSV</a>
|
59
|
+
<a href="<%= @report.download_url('/admin/reports/revenue', 'pdf', inc) %>">PDF</a>
|
60
|
+
</li>
|
61
|
+
<% end -%>
|
62
|
+
</ul>
|
63
|
+
<%= render :partial => 'spree/admin/reports/advanced_report_criteria', :locals => {} %>
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
<% content_for :head do -%>
|
67
|
+
<script src="http://www.google.com/jsapi"></script>
|
68
|
+
<script >
|
69
|
+
google.load('visualization', '1', {'packages': ['corechart']});
|
70
|
+
</script>
|
71
|
+
<script >
|
72
|
+
google.setOnLoadCallback(drawChart);
|
73
|
+
|
74
|
+
function drawChart() {
|
75
|
+
<% @report.increments.each do |type| -%>
|
76
|
+
var data_<%= type.to_s %> = new google.visualization.DataTable();
|
77
|
+
data_<%= type.to_s %>.addColumn('string', 'Display');
|
78
|
+
data_<%= type.to_s %>.addColumn('number', '<%= @report.name %>');
|
79
|
+
data_<%= type.to_s %>.addRows(<%= @report.ruportdata[type].size %>);
|
80
|
+
<% @report.ruportdata[type].each_with_index do |p, i| %>
|
81
|
+
data_<%= type.to_s %>.setValue(<%= i.to_s %>, 0, "<%= p.data[type.to_s.capitalize] %>");
|
82
|
+
data_<%= type.to_s %>.setValue(<%= i.to_s %>, 1, <%= p.data[@report.column].to_s.gsub(/^\$/, '') %>);
|
83
|
+
<% end -%>
|
84
|
+
var chart_<%= type.to_s %> = new google.visualization.ColumnChart(document.getElementById('<%= type.to_s %>_chart_revenue'));
|
85
|
+
chart_<%= type.to_s %>.draw(data_<%= type.to_s %>, {
|
86
|
+
width: 800,
|
87
|
+
height: 400,
|
88
|
+
legend : 'none',
|
89
|
+
title: '' });
|
90
|
+
<% end -%>
|
91
|
+
|
92
|
+
var product_data = new google.visualization.DataTable();
|
93
|
+
product_data.addColumn('string', 'Product');
|
94
|
+
product_data.addColumn('number', 'Revenue');
|
95
|
+
product_data.addRows(<%= @top_products_report.ruportdata.size %>);
|
96
|
+
<% @top_products_report.ruportdata.each_with_index do |p, i| %>
|
97
|
+
product_data.setValue(<%= i.to_s %>, 0, "<%= p.data["Product Name"].gsub('"', '\"') %>");
|
98
|
+
product_data.setValue(<%= i.to_s %>, 1, <%= p.data["Revenue"].to_s.gsub(/^\$/, '') %>);
|
99
|
+
<% end -%>
|
100
|
+
|
101
|
+
var chart = new google.visualization.PieChart(document.getElementById('top_products_chart'));
|
102
|
+
chart.draw(product_data, {width: "100%", height: 300, title: ''});
|
103
|
+
|
104
|
+
var taxon_data = new google.visualization.DataTable();
|
105
|
+
taxon_data.addColumn('string', 'Taxon');
|
106
|
+
taxon_data.addColumn('number', 'Orders');
|
107
|
+
taxon_data.addRows(<%= @best_taxons.size %>);
|
108
|
+
<% @best_taxons.each_with_index do |p, i| %>
|
109
|
+
taxon_data.setValue(<%= i.to_s %>, 0, "<%= p[0] %>");
|
110
|
+
taxon_data.setValue(<%= i.to_s %>, 1, <%= p[1] %>);
|
111
|
+
<% end -%>
|
112
|
+
|
113
|
+
var taxon_chart = new google.visualization.PieChart(document.getElementById('top_taxons_chart'));
|
114
|
+
taxon_chart.draw(taxon_data, {width: "100%", height: 300, title: ''});
|
115
|
+
}
|
116
|
+
</script>
|
117
|
+
<% end -%>
|