@ -0,0 +1,39 @@ |
|||||
|
====================== |
||||
|
Heat Map in Pivot View |
||||
|
====================== |
||||
|
|
||||
|
Added option for heat map in pivot view. |
||||
|
|
||||
|
Installation |
||||
|
============ |
||||
|
|
||||
|
Select the module and install it, there are no other setups needed. |
||||
|
|
||||
|
Usage |
||||
|
===== |
||||
|
|
||||
|
Go to the pivot view, and there will be three new buttons, Heatmap Page, Heatmap Row and Heatmap Colomn. |
||||
|
After setting up the table, click on the button to activate heatmap. |
||||
|
|
||||
|
Known issues / Roadmap |
||||
|
====================== |
||||
|
|
||||
|
* ... |
||||
|
|
||||
|
Bug Tracker |
||||
|
=========== |
||||
|
|
||||
|
Contact odoo@cybrosys.com |
||||
|
|
||||
|
Contributors |
||||
|
------------ |
||||
|
|
||||
|
* Linto CT <linto@cybrosys.in> |
||||
|
* Faslu CA <faslu@cybrosys.in> |
||||
|
|
||||
|
Maintainer |
||||
|
---------- |
||||
|
|
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
|
||||
|
For support and more information, please visit https://www.cybrosys.com. |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: LINTO C T(<https://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
@ -0,0 +1,44 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: LINTO C T(<https://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
|
||||
|
{ |
||||
|
'name': 'Heat Map in Pivot View', |
||||
|
'version': '11.0.1.0.0', |
||||
|
'summary': 'Heat Map in Pivot View', |
||||
|
'category': 'Reporting', |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'depends': ['web'], |
||||
|
'website': 'https://www.cybrosys.com', |
||||
|
'data': [ |
||||
|
'views/templates.xml', |
||||
|
], |
||||
|
'qweb': ["static/src/xml/*.xml"], |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'LGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,101 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h2 class="oe_slogan">Heat Map in Pivot View</h2> |
||||
|
<h3 class="oe_slogan"></h3> |
||||
|
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a> </h4> |
||||
|
</div> |
||||
|
<div class="oe_row oe_spaced" style="padding-left:65px;"> |
||||
|
<h3><p style="margin-left: 42px;"><b>Features:</b></p></h3> |
||||
|
<div> |
||||
|
<span style="color:green;"> ☑ </span> Heat Map for Pivot View<br/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_picture"> |
||||
|
<h3 class="oe_slogan">Overview</h3> |
||||
|
<p class="oe_mt32 text-justify"> |
||||
|
Heat map can be used to color the entries in the pivot view. The coloring will be |
||||
|
in a format where the maximum value will be shown in a dark color and next largest value |
||||
|
will be in a lighter color than the maximum one. |
||||
|
This applies for all the values in the table. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h5 class="oe_slogan"><b>New buttons in the pivot view.</b></h5> |
||||
|
<div class="col-md-10" > |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="pivot_buttons.png" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<br /> |
||||
|
<div class="oe_row oe_spaced" style="float:left;"> |
||||
|
<h5 class="oe_slogan"><b>Clicking on these buttons will enable the |
||||
|
corresponding heat map. |
||||
|
</b></h5> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="oe_demo_footer oe_centeralign" > |
||||
|
<b>Heat Map Column</b> |
||||
|
</div> |
||||
|
<div class=" oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="heat_map_col.png" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="oe_container"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="oe_demo_footer oe_centeralign"> |
||||
|
<b>Heat Map Row</b> |
||||
|
</div> |
||||
|
<div class=" oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="heat_map_row.png" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="col-md-12"> |
||||
|
<div class="oe_demo_footer oe_centeralign"> |
||||
|
<b>Heat Map Page</b> |
||||
|
</div> |
||||
|
<div class=" oe_demo oe_picture oe_screenshot"> |
||||
|
<img src="heat_map_page.png" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2> |
||||
|
<div class="oe_slogan" style="margin-top:10px !important;"> |
||||
|
<div> |
||||
|
<a class="btn btn-primary btn-lg mt8" |
||||
|
style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com"><i |
||||
|
class="fa fa-envelope"></i> Email </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/contact/"><i |
||||
|
class="fa fa-phone"></i> Contact Us </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/odoo-customization-and-installation/"><i |
||||
|
class="fa fa-check-square"></i> Request Customization </a> |
||||
|
</div> |
||||
|
<br> |
||||
|
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
||||
|
<div> |
||||
|
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;"></i></a></td> |
||||
|
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px;padding-left: 8px;"></i></a></td> |
||||
|
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,52 @@ |
|||||
|
odoo.define('pivot_heat_map.controllers', function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var PivotController = require('web.PivotController'); |
||||
|
var PivotRenderer = require('web.PivotRenderer'); |
||||
|
|
||||
|
PivotController.include({ |
||||
|
init: function (parent, model, renderer, params) { |
||||
|
renderer.heat_map = null; |
||||
|
renderer.cells = {}; |
||||
|
this._super(parent, model, renderer, params); |
||||
|
}, |
||||
|
_onButtonClick: function (event) { |
||||
|
var $target = $(event.target); |
||||
|
this._super(event); |
||||
|
/* catching heat map button click and switching between modes*/ |
||||
|
if ($target.hasClass('o_heat_map_col')) { |
||||
|
if (this.renderer.heat_map == 'col' ){ |
||||
|
this.renderer.heat_map = null; |
||||
|
} |
||||
|
else{ |
||||
|
this.renderer.cells = {}; |
||||
|
this.renderer.heat_map = 'col'; |
||||
|
} |
||||
|
|
||||
|
this.renderer._render(); |
||||
|
} |
||||
|
else if ($target.hasClass('o_heat_map_row')) { |
||||
|
if (this.renderer.heat_map == 'row'){ |
||||
|
this.renderer.heat_map = null; |
||||
|
} |
||||
|
else{ |
||||
|
this.renderer.cells = {}; |
||||
|
this.renderer.heat_map = 'row'; |
||||
|
} |
||||
|
|
||||
|
this.renderer._render(); |
||||
|
} |
||||
|
else if ($target.hasClass('o_heat_map_both')) { |
||||
|
if (this.renderer.heat_map == 'both'){ |
||||
|
this.renderer.heat_map = null; |
||||
|
} |
||||
|
else{ |
||||
|
this.renderer.cells = {}; |
||||
|
this.renderer.heat_map = 'both'; |
||||
|
} |
||||
|
|
||||
|
this.renderer._render(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,221 @@ |
|||||
|
odoo.define('pivot_heat_map.renderer', function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var PivotRenderer = require('web.PivotRenderer'); |
||||
|
var field_utils = require('web.field_utils'); |
||||
|
var core = require('web.core'); |
||||
|
|
||||
|
var _t = core._t; |
||||
|
|
||||
|
PivotRenderer.include({ |
||||
|
get_total_col: function(rows, indent, col){ |
||||
|
var total = 0; |
||||
|
_.each(rows, function(rec){ |
||||
|
if(indent == rec.indent && rec.values[col]){ |
||||
|
total += rec.values[col]; |
||||
|
} |
||||
|
}); |
||||
|
return total; |
||||
|
}, |
||||
|
get_total_row: function(values, row, col){ |
||||
|
var total = 0; |
||||
|
var measures = this.state.measures.length; |
||||
|
|
||||
|
for (var i=col;i<values.length-measures;i+=measures){ |
||||
|
total += values[i] ? values[i] : 0; |
||||
|
} |
||||
|
for (var i=col-measures;i>=0;i-=measures){ |
||||
|
total += values[i] ? values[i] : 0; |
||||
|
} |
||||
|
return total; |
||||
|
}, |
||||
|
|
||||
|
get_parent_index: function(indent, row, col){ |
||||
|
var rows = this.state.rows; |
||||
|
for (var i=row-1;i>=0;i--){ |
||||
|
if(rows[i].indent == indent-1){ |
||||
|
return (indent-1)+'-'+i+'-'+col; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
get_bg_color: function(value, row, col, indent){ |
||||
|
var index = indent+'-'+row+'-'+col; |
||||
|
if (value != 'undefined'){ |
||||
|
if(index in this.cells){ |
||||
|
return 'rgb(250,'+this.cells[index]+','+this.cells[index]+')'; |
||||
|
} |
||||
|
} |
||||
|
else{ |
||||
|
this.cells[index] = null; |
||||
|
return null; |
||||
|
} |
||||
|
}, |
||||
|
get_base_value: function(row, col, measures){ |
||||
|
for(var j=row.values.length-measures;j<row.values.length;j++){ |
||||
|
if((j - col)%measures == 0){ |
||||
|
return j; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
update_cell_colors: function(rows){ |
||||
|
var self = this; |
||||
|
var i,j,index,cols,row,value,total,parent_index,base,color_code,cells,measures; |
||||
|
if(rows){ |
||||
|
cols = rows[0].values.length; |
||||
|
cells = this.cells; |
||||
|
measures = this.state.measures.length; |
||||
|
switch(self.heat_map){ |
||||
|
case 'col': { |
||||
|
for(i=0;i<rows.length;i++){ |
||||
|
row = rows[i]; |
||||
|
for(j=0;j<cols;j++){ |
||||
|
value = row.values[j]; |
||||
|
if(value != 'undefined'){ |
||||
|
index = row.indent+'-'+i+'-'+j; |
||||
|
if(!(index in cells)){ |
||||
|
if(row.indent==0){ |
||||
|
/*first row*/ |
||||
|
cells[index] = 90; |
||||
|
continue; |
||||
|
} |
||||
|
/*other rows*/ |
||||
|
total = self.get_total_col(rows, row.indent, j); |
||||
|
parent_index = self.get_parent_index(row.indent, i, j); |
||||
|
base = (parent_index && cells[parent_index]) ? cells[parent_index] : 90; |
||||
|
color_code = Math.floor(base + 165*(total - Math.abs(value))/total); |
||||
|
cells[index] = color_code; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
case 'row': { |
||||
|
for(i=0;i<rows.length;i++){ |
||||
|
row = rows[i]; |
||||
|
for(j=0;j<cols;j++){ |
||||
|
value = row.values[j]; |
||||
|
if(value != 'undefined'){ |
||||
|
index = row.indent+'-'+i+'-'+j; |
||||
|
if(!(index in cells)){ |
||||
|
if(j >= cols-measures){ |
||||
|
cells[index] = 90; |
||||
|
continue; |
||||
|
} |
||||
|
/*other rows*/ |
||||
|
total = self.get_total_row(row.values, i, j); |
||||
|
|
||||
|
color_code = Math.floor(90 + 165*(total - Math.abs(value))/total); |
||||
|
cells[index] = color_code; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
case 'both': { |
||||
|
/*setting first row and last columns as base*/ |
||||
|
for(i=0;i<rows.length;i++){ |
||||
|
row = rows[i]; |
||||
|
for(j=cols-1;j>=0;j--){ |
||||
|
value = row.values[j]; |
||||
|
if(value != 'undefined'){ |
||||
|
index = row.indent+'-'+i+'-'+j; |
||||
|
if(!(index in cells)){ |
||||
|
if((j >= cols-measures) && i==0){ |
||||
|
cells[index] = 90; |
||||
|
continue; |
||||
|
} |
||||
|
else if(j >= cols-measures){ |
||||
|
total = self.get_total_col(rows, row.indent, j); |
||||
|
parent_index = self.get_parent_index(row.indent, i, j); |
||||
|
base = (parent_index && cells[parent_index]) ? cells[parent_index] : 90; |
||||
|
color_code = Math.floor(base + 165*(total - Math.abs(value))/total); |
||||
|
cells[index] = color_code; |
||||
|
} |
||||
|
else{ |
||||
|
total = self.get_total_row(row.values, i, j); |
||||
|
var parent_col = self.get_base_value(row, j, measures); |
||||
|
parent_index = parent_col ? (row.indent+'-'+i+'-'+parent_col):null; |
||||
|
base = parent_index ? cells[parent_index] : 90; |
||||
|
color_code = Math.floor(base + 165*(total - Math.abs(value))/total); |
||||
|
cells[index] = color_code; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
this.cells = cells; |
||||
|
} |
||||
|
}, |
||||
|
_renderRowsHeat: function ($tbody, rows) { |
||||
|
var self = this; |
||||
|
/*setting cell colors*/ |
||||
|
this.update_cell_colors(rows); |
||||
|
var i, j, value, measure, name, $row, $cell, $header; |
||||
|
var nbrMeasures = this.state.measures.length; |
||||
|
var length = rows[0].values.length; |
||||
|
var shouldDisplayTotal = this.state.mainColWidth > 1; |
||||
|
var groupbyLabels = _.map(this.state.rowGroupBys, function (gb) { |
||||
|
return self.state.fields[gb.split(':')[0]].string; |
||||
|
}); |
||||
|
var measureTypes = this.state.measures.map(function (name) { |
||||
|
return self.state.fields[name].type; |
||||
|
}); |
||||
|
for (i = 0; i < rows.length; i++) { |
||||
|
$row = $('<tr>'); |
||||
|
$header = $('<td>') |
||||
|
.text(rows[i].title) |
||||
|
.data('id', rows[i].id) |
||||
|
.css('padding-left', (5 + rows[i].indent * 30) + 'px') |
||||
|
.addClass(rows[i].expanded ? 'o_pivot_header_cell_opened' : 'o_pivot_header_cell_closed'); |
||||
|
if (rows[i].indent > 0) $header.attr('title', groupbyLabels[rows[i].indent - 1]); |
||||
|
$header.appendTo($row); |
||||
|
for (j = 0; j < length; j++) { |
||||
|
value = rows[i].values[j]; |
||||
|
if (value !== undefined) { |
||||
|
name = this.state.measures[j % nbrMeasures]; |
||||
|
measure = this.state.fields[name]; |
||||
|
value = field_utils.format[measureTypes[j % nbrMeasures]](value, measure); |
||||
|
} |
||||
|
/*fetching background color*/ |
||||
|
var bg_color = null; |
||||
|
if (value){ |
||||
|
bg_color = self.get_bg_color(value, i, j, rows[i].indent); |
||||
|
} |
||||
|
|
||||
|
$cell = $('<td>') |
||||
|
.data('id', rows[i].id) |
||||
|
.data('col_id', rows[i].col_ids[Math.floor(j / nbrMeasures)]) |
||||
|
.toggleClass('o_empty', !value) |
||||
|
.text(value) |
||||
|
.addClass('o_pivot_cell_value text-right'); |
||||
|
if(bg_color != null){ |
||||
|
$cell.css({'background-color': bg_color}); |
||||
|
} |
||||
|
if (((j >= length - this.state.measures.length) && shouldDisplayTotal) || i === 0){ |
||||
|
$cell.css('font-weight', 'bold'); |
||||
|
} |
||||
|
$row.append($cell); |
||||
|
|
||||
|
$cell.toggleClass('hidden-xs', j < length - nbrMeasures); |
||||
|
} |
||||
|
$tbody.append($row); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
_renderRows: function ($tbody, rows) { |
||||
|
var self = this; |
||||
|
if(this.heat_map != null){ |
||||
|
self._renderRowsHeat($tbody, rows); |
||||
|
} |
||||
|
else{ |
||||
|
this._super($tbody, rows); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<templates id="pivot_heat_map"> |
||||
|
<t t-extend="PivotView.buttons"> |
||||
|
<t t-jquery=".o_pivot_download" t-operation="after"> |
||||
|
<button class="btn btn-default fa fa-th o_heat_map_both" title="Heat Map Page" /> |
||||
|
<button class="btn btn-default fa fa-bars fa-rotate-90 o_heat_map_col" title="Heat Map Column"/> |
||||
|
<button class="btn btn-default fa fa-bars o_heat_map_row" title="Heat Map Row"/> |
||||
|
</t> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
|
<odoo> |
||||
|
<template id="assets" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script type="text/javascript" src="/pivot_heat_map/static/src/js/pivot_controller_extended.js"></script> |
||||
|
<script type="text/javascript" src="/pivot_heat_map/static/src/js/pivot_renderer_extended.js"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</odoo> |