diga
5 years ago
No known key found for this signature in database
GPG Key ID: 47D77E95CE2E6B8A
14 changed files with 4013 additions and 0 deletions
-
1web_widget_heatmap/__init__.py
-
30web_widget_heatmap/__manifest__.py
-
1web_widget_heatmap/readme/CONTRIBUTORS.rst
-
3web_widget_heatmap/readme/CREDITS.rst
-
3web_widget_heatmap/readme/DESCRIPTION.rst
-
35web_widget_heatmap/readme/USAGE.rst
-
BINweb_widget_heatmap/static/description/icon.png
-
75web_widget_heatmap/static/description/index.html
-
145web_widget_heatmap/static/lib/css/cal-heatmap.css
-
3487web_widget_heatmap/static/lib/js/cal-heatmap.js
-
12web_widget_heatmap/static/src/css/heatmap_widget.css
-
194web_widget_heatmap/static/src/js/heatmap_widget.js
-
14web_widget_heatmap/static/src/xml/base.xml
-
13web_widget_heatmap/views/web_widget_heatmap_template.xml
@ -0,0 +1 @@ |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
@ -0,0 +1,30 @@ |
|||
# Copyright 2019 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar> |
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). |
|||
{ |
|||
"name": "Web Widget HeatMap", |
|||
"summary": "Displaying your records in calendar HeatMap", |
|||
"version": "11.0.1.0.0", |
|||
"development_status": "Stable", |
|||
"category": "Extra Tools", |
|||
"website": "https://github.com/OCA/web/tree/11.0/web_widget_heatmap", |
|||
"author": "Dinar Gabbasov, Odoo Community Association (OCA)", |
|||
"maintainers": ["GabbasovDinar"], |
|||
"license": "AGPL-3", |
|||
"application": False, |
|||
"installable": True, |
|||
"preloadable": True, |
|||
"external_dependencies": { |
|||
"python": [], |
|||
"bin": [], |
|||
}, |
|||
"depends": [ |
|||
"web", |
|||
], |
|||
"data": [ |
|||
"views/web_widget_heatmap_template.xml", |
|||
], |
|||
"demo": [], |
|||
"qweb": [ |
|||
"static/src/xml/base.xml" |
|||
] |
|||
} |
@ -0,0 +1 @@ |
|||
* Dinar Gabbasov <gabbasov@it-projects.info> |
@ -0,0 +1,3 @@ |
|||
The development of this module has been financially supported by: |
|||
|
|||
* Georg A. G. Notter <georg.notter@agenterp.com> |
@ -0,0 +1,3 @@ |
|||
This module aims to add a heatmap calendar to Odoo. |
|||
|
|||
It's a `cal-heatmap <https://cal-heatmap.com/>`_ lib integration. |
@ -0,0 +1,35 @@ |
|||
To use this module, you need to declare a One2many or Many2many field:: |
|||
|
|||
sale_order_ids = fields.One2many( |
|||
"sale.order", |
|||
"partner_id", |
|||
string="Sales Order", |
|||
) |
|||
|
|||
In the view declaration, put widget="heatmap" attribute in the field tag:: |
|||
|
|||
... |
|||
<field name="arch" type="xml"> |
|||
<form string="View name"> |
|||
... |
|||
<field name="sale_order_ids" widget="heatmap"/> |
|||
... |
|||
</form> |
|||
</field> |
|||
... |
|||
|
|||
Widget Options:: |
|||
|
|||
cellSize - Size of each subDomain cell, in pixel. |
|||
... |
|||
<field name="arch" type="xml"> |
|||
<form string="View name"> |
|||
... |
|||
<field name="sale_order_ids" widget="heatmap" options="{'cellSize': 15}"/> |
|||
... |
|||
</form> |
|||
</field> |
|||
... |
|||
|
|||
|
|||
* All widget options you can find in the official `documentation <https://cal-heatmap.com/#options>`_ of the cal-heatmap lib. |
After Width: 100 | Height: 100 | Size: 3.0 KiB |
@ -0,0 +1,75 @@ |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Web Widget HeatMap</h2> |
|||
<p>This module aims to add a heatmap calendar to Odoo.</p> |
|||
<p>It's a <a class="reference external" href="https://cal-heatmap.com/">cal-heatmap</a> lib integration.</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Installation</h2> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32">Install this module in a usual way</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Usage</h2> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<p class="oe_mt32">You need to declare a One2many or Many2many field: |
|||
<pre class="literal-block"> |
|||
sale_order_ids = fields.One2many("sale.order", "partner_id", "Sales Order") |
|||
</pre> |
|||
<p>In the view declaration, put widget="heatmap" attribute in the field tag:</p> |
|||
<pre class="literal-block"> |
|||
... |
|||
<field name="arch" type="xml"> |
|||
<form string="View name"> |
|||
... |
|||
<field name="sale_order_ids" widget="heatmap"/> |
|||
... |
|||
</form> |
|||
</field> |
|||
... |
|||
</pre> |
|||
</p> |
|||
<p class="oe_mt32">For further information, please visit: |
|||
<ul> |
|||
<li><a href="https://www.odoo.com/forum/help-1">https://www.odoo.com/forum/help-1</a></li> |
|||
</ul> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row"> |
|||
<div class="oe_span12"> |
|||
<h2 class="oe_slogan">Credits</h2> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<h3>Contributors</h3> |
|||
<ul> |
|||
<li>Dinar Gabbasov <<a href="mailto:gabbasov@it-projects.info">gabbasov@it-projects.info</a>></li> |
|||
</ul> |
|||
</div> |
|||
<div class="oe_span12"> |
|||
<h3>Maintainer</h3> |
|||
<p> |
|||
This module is maintained by the OCA.<br/> |
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.<br/> |
|||
To contribute to this module, please visit <a href="http://odoo-community.org">http://odoo-community.org</a>.<br/> |
|||
<a href="http://odoo-community.org"><img class="oe_picture oe_centered" src="http://odoo-community.org/logo.png"></a> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</section> |
@ -0,0 +1,145 @@ |
|||
/* Cal-HeatMap CSS */ |
|||
|
|||
.cal-heatmap-container { |
|||
display: block; |
|||
} |
|||
|
|||
.cal-heatmap-container .graph |
|||
{ |
|||
font-family: "Lucida Grande", Lucida, Verdana, sans-serif; |
|||
} |
|||
|
|||
.cal-heatmap-container .graph-label |
|||
{ |
|||
fill: #999; |
|||
font-size: 10px |
|||
} |
|||
|
|||
.cal-heatmap-container .graph, .cal-heatmap-container .graph-legend rect { |
|||
shape-rendering: crispedges |
|||
} |
|||
|
|||
.cal-heatmap-container .graph-rect |
|||
{ |
|||
fill: #ededed |
|||
} |
|||
|
|||
.cal-heatmap-container .graph-subdomain-group rect:hover |
|||
{ |
|||
stroke: #000; |
|||
stroke-width: 1px |
|||
} |
|||
|
|||
.cal-heatmap-container .subdomain-text { |
|||
font-size: 8px; |
|||
fill: #999; |
|||
pointer-events: none |
|||
} |
|||
|
|||
.cal-heatmap-container .hover_cursor:hover { |
|||
cursor: pointer |
|||
} |
|||
|
|||
.cal-heatmap-container .qi { |
|||
background-color: #999; |
|||
fill: #999 |
|||
} |
|||
|
|||
/* |
|||
Remove comment to apply this style to date with value equal to 0 |
|||
.q0 |
|||
{ |
|||
background-color: #fff; |
|||
fill: #fff; |
|||
stroke: #ededed |
|||
} |
|||
*/ |
|||
|
|||
.cal-heatmap-container .q1 |
|||
{ |
|||
background-color: #dae289; |
|||
fill: #dae289 |
|||
} |
|||
|
|||
.cal-heatmap-container .q2 |
|||
{ |
|||
background-color: #cedb9c; |
|||
fill: #9cc069 |
|||
} |
|||
|
|||
.cal-heatmap-container .q3 |
|||
{ |
|||
background-color: #b5cf6b; |
|||
fill: #669d45 |
|||
} |
|||
|
|||
.cal-heatmap-container .q4 |
|||
{ |
|||
background-color: #637939; |
|||
fill: #637939 |
|||
} |
|||
|
|||
.cal-heatmap-container .q5 |
|||
{ |
|||
background-color: #3b6427; |
|||
fill: #3b6427 |
|||
} |
|||
|
|||
.cal-heatmap-container rect.highlight |
|||
{ |
|||
stroke:#444; |
|||
stroke-width:1 |
|||
} |
|||
|
|||
.cal-heatmap-container text.highlight |
|||
{ |
|||
fill: #444 |
|||
} |
|||
|
|||
.cal-heatmap-container rect.highlight-now |
|||
{ |
|||
stroke: red |
|||
} |
|||
|
|||
.cal-heatmap-container text.highlight-now |
|||
{ |
|||
fill: red; |
|||
font-weight: 800 |
|||
} |
|||
|
|||
.cal-heatmap-container .domain-background { |
|||
fill: none; |
|||
shape-rendering: crispedges |
|||
} |
|||
|
|||
.ch-tooltip { |
|||
padding: 10px; |
|||
background: #222; |
|||
color: #bbb; |
|||
font-size: 12px; |
|||
line-height: 1.4; |
|||
width: 140px; |
|||
position: absolute; |
|||
z-index: 99999; |
|||
text-align: center; |
|||
border-radius: 2px; |
|||
box-shadow: 2px 2px 2px rgba(0,0,0,0.2); |
|||
display: none; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.ch-tooltip::after{ |
|||
position: absolute; |
|||
width: 0; |
|||
height: 0; |
|||
border-color: transparent; |
|||
border-style: solid; |
|||
content: ""; |
|||
padding: 0; |
|||
display: block; |
|||
bottom: -6px; |
|||
left: 50%; |
|||
margin-left: -6px; |
|||
border-width: 6px 6px 0; |
|||
border-top-color: #222; |
|||
} |
3487
web_widget_heatmap/static/lib/js/cal-heatmap.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,12 @@ |
|||
/* Copyright 2019 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar> |
|||
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ |
|||
.heatmap_widget { |
|||
width: 100%; |
|||
padding: 10px 0px; |
|||
} |
|||
.o_field_heatmap { |
|||
width: 100%; |
|||
} |
|||
.o_field_heatmap .cal-heatmap-container { |
|||
margin: 0 auto; |
|||
} |
@ -0,0 +1,194 @@ |
|||
/* |
|||
Copyright 2019 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar>
|
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
*/ |
|||
|
|||
odoo.define('web_widget_heatmap.widget', function (require) { |
|||
"use strict"; |
|||
|
|||
var AbstractField = require('web.AbstractField'); |
|||
var registry = require('web.field_registry'); |
|||
|
|||
|
|||
var HeatMapWidget = AbstractField.extend({ |
|||
|
|||
fieldsToFetch: { |
|||
display_name: {type: 'char'}, |
|||
create_date: {type: 'datetime'}, |
|||
}, |
|||
|
|||
template: 'HeatMapWidget', |
|||
|
|||
supportedFieldTypes: ['one2many', 'many2many'], |
|||
|
|||
description: "", |
|||
|
|||
cssLibs: [ |
|||
'/web/static/lib/nvd3/nv.d3.css', |
|||
'/web_widget_heatmap/static/lib/css/cal-heatmap.css' |
|||
], |
|||
|
|||
jsLibs: [ |
|||
'/web/static/lib/nvd3/d3.v3.js', |
|||
'/web/static/lib/nvd3/nv.d3.js', |
|||
'/web/static/src/js/libs/nvd3.js', |
|||
'/web_widget_heatmap/static/lib/js/cal-heatmap.js' |
|||
], |
|||
|
|||
/** |
|||
* Calculate and get the maximum date (up to which date the grid will be built). |
|||
* Display of control buttons depends on the maximum date. |
|||
* @param {Object} options |
|||
* @returns {Date} max_date |
|||
*/ |
|||
get_max_date: function(options) { |
|||
var max_date = new Date(options.start_date); |
|||
var range = this.nodeOptions ? this.nodeOptions.range : options.range; |
|||
var domain = this.nodeOptions ? this.nodeOptions.domain : options.domain; |
|||
if (domain === 'hour') { |
|||
max_date.setHours(max_date.getHours() + range); |
|||
} else if (domain === 'day') { |
|||
max_date.setDate(max_date.getDate() + range); |
|||
} else if (domain === 'week') { |
|||
max_date.setDate(max_date.getDate() + (range * 7)); |
|||
} else if (domain === 'month') { |
|||
max_date.setMonth(max_date.getMonth() + range); |
|||
} else if (domain === 'year') { |
|||
max_date.setFullYear(max_date.getFullYear() + range); |
|||
} |
|||
if (max_date > options.end_date) { |
|||
return max_date; |
|||
} |
|||
return options.end_date; |
|||
}, |
|||
|
|||
/** |
|||
* Generate widget options by field data |
|||
* @param {Array} elements |
|||
* @returns {Object} widget options |
|||
*/ |
|||
generate_element_options: function(elements) { |
|||
var start_date = elements.length ? elements[0].create_date.toDate() : new Date(); |
|||
var timestamps = elements.map(function(el) { |
|||
return el.create_date.unix(); |
|||
}); |
|||
var domain = "day"; |
|||
var range = 16; |
|||
var end_date = elements.length ? elements[elements.length - 1].create_date.toDate() : null; |
|||
var max_date = this.get_max_date({ |
|||
start_date: start_date, |
|||
end_date: end_date, |
|||
domain: domain, |
|||
range: range |
|||
}); |
|||
var controls = false; |
|||
if (max_date < end_date) { |
|||
controls = true; |
|||
} |
|||
return { |
|||
start: start_date, |
|||
data: _.chain(timestamps).countBy().value(), |
|||
dataType: 'json', |
|||
minDate: start_date, |
|||
maxDate: max_date, |
|||
controls: controls, |
|||
range: range, |
|||
domain: domain, |
|||
subDomain: "hour", |
|||
domainGutter: 0, |
|||
highlight: "now", |
|||
onClick: this.onClickHeatMap.bind(this), |
|||
label: { |
|||
position: "top" |
|||
} |
|||
}; |
|||
}, |
|||
|
|||
/** |
|||
* Render the view |
|||
* @private |
|||
*/ |
|||
_render: function () { |
|||
var self = this; |
|||
var elements = this.value ? _.pluck(this.value.data, 'data') : []; |
|||
var options = this.generate_element_options(elements); |
|||
this.controls = options.controls; |
|||
this.renderElement(); |
|||
var nodeOptions = this.nodeOptions || {}; |
|||
this.heatmap_options = _.extend(options, {itemSelector: this.$el.find('.o_field_heatmap')[0]}); |
|||
_.each(nodeOptions, function(value, key) { |
|||
self.heatmap_options[key] = value; |
|||
}); |
|||
this.heatmap = new CalHeatMap(); |
|||
this.heatmap.init(this.heatmap_options); |
|||
}, |
|||
|
|||
/** |
|||
* Renders the element and add new events. |
|||
*/ |
|||
renderElement: function() { |
|||
this._super(); |
|||
this.$el.find('.next').click(this.nextHeatMap.bind(this)); |
|||
this.$el.find('.previous').click(this.previousHeatMap.bind(this)); |
|||
}, |
|||
|
|||
/** |
|||
* Show next heatmap grid |
|||
*/ |
|||
nextHeatMap: function() { |
|||
this.heatmap.next(); |
|||
}, |
|||
|
|||
/** |
|||
* Show previous heatmap grid |
|||
*/ |
|||
previousHeatMap: function() { |
|||
this.heatmap.previous(); |
|||
}, |
|||
|
|||
/** |
|||
* Find the records that match the selected date and show in the tree view |
|||
* @param {Date} date |
|||
* @returns {jQuery.Deferred} Action loaded |
|||
*/ |
|||
onClickHeatMap: function(date) { |
|||
date = moment(date); |
|||
var options = this.heatmap_options; |
|||
var elements = this.value ? _.pluck(this.value.data, 'data') : []; |
|||
if (elements && elements.length) { |
|||
// TODO: make faster
|
|||
var current_elements_ids = elements.filter(function(el) { |
|||
var duration = moment.duration(date.diff(el.create_date)); |
|||
if (options.domain === 'hour') { |
|||
return duration.years() === 0 && duration.months() === 0 && duration.weeks() === 0 && duration.days() === 0 && duration.hours() === 0; |
|||
} else if (options.domain === 'day') { |
|||
return duration.years() === 0 && duration.months() === 0 && duration.weeks() === 0 && duration.days() === 0; |
|||
} else if (options.domain === 'week') { |
|||
return duration.years() === 0 && duration.months() === 0 && duration.weeks() === 0; |
|||
} else if (options.domain === 'month') { |
|||
return duration.years() === 0 && duration.months() === 0; |
|||
} else if (options.domain === 'year') { |
|||
return duration.years() === 0; |
|||
} |
|||
return false; |
|||
}).map(function(el) { |
|||
return el.id; |
|||
}); |
|||
var action = { |
|||
name: date.format('YYYY-MM-DD HH:mm'), |
|||
type: 'ir.actions.act_window', |
|||
res_model: this.value.model, |
|||
view_mode: 'list,form', |
|||
views: [[false, 'list'], [false, 'form']], |
|||
view_type: 'list', |
|||
domain: [['id', 'in', current_elements_ids]], |
|||
}; |
|||
return this.do_action(action); |
|||
} |
|||
}, |
|||
}); |
|||
|
|||
registry.add('heatmap', HeatMapWidget); |
|||
|
|||
return HeatMapWidget; |
|||
}); |
@ -0,0 +1,14 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- Copyright 2019 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar> |
|||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> |
|||
<templates id="template" xml:space="preserve"> |
|||
<t t-name="HeatMapWidget"> |
|||
<div class="heatmap_widget"> |
|||
<span class="controls" t-if="widget.controls"> |
|||
<button class="btn btn-sm btn-default previous fa fa-chevron-left"></button> |
|||
<button class="btn btn-sm btn-default next fa fa-chevron-right"></button> |
|||
</span> |
|||
<span class="o_field_heatmap"></span> |
|||
</div> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0"?> |
|||
<!-- Copyright 2019 Dinar Gabbasov <https://it-projects.info/team/GabbasovDinar> |
|||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> |
|||
<odoo> |
|||
|
|||
<template id="assets_backend" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/web_widget_heatmap/static/src/js/heatmap_widget.js"></script> |
|||
<link rel="stylesheet" type="text/css" href="/web_widget_heatmap/static/src/css/heatmap_widget.css"/> |
|||
</xpath> |
|||
</template> |
|||
|
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue