@ -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> |