Browse Source

Merge 29abe434f5 into d736ee4460

pull/1304/merge
Dinar 5 years ago
committed by GitHub
parent
commit
9804d53ec7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      web_widget_heatmap/__init__.py
  2. 30
      web_widget_heatmap/__manifest__.py
  3. 1
      web_widget_heatmap/readme/CONTRIBUTORS.rst
  4. 3
      web_widget_heatmap/readme/CREDITS.rst
  5. 3
      web_widget_heatmap/readme/DESCRIPTION.rst
  6. 35
      web_widget_heatmap/readme/USAGE.rst
  7. BIN
      web_widget_heatmap/static/description/icon.png
  8. 75
      web_widget_heatmap/static/description/index.html
  9. 145
      web_widget_heatmap/static/lib/css/cal-heatmap.css
  10. 3487
      web_widget_heatmap/static/lib/js/cal-heatmap.js
  11. 12
      web_widget_heatmap/static/src/css/heatmap_widget.css
  12. 194
      web_widget_heatmap/static/src/js/heatmap_widget.js
  13. 14
      web_widget_heatmap/static/src/xml/base.xml
  14. 13
      web_widget_heatmap/views/web_widget_heatmap_template.xml

1
web_widget_heatmap/__init__.py

@ -0,0 +1 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

30
web_widget_heatmap/__manifest__.py

@ -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"
]
}

1
web_widget_heatmap/readme/CONTRIBUTORS.rst

@ -0,0 +1 @@
* Dinar Gabbasov <gabbasov@it-projects.info>

3
web_widget_heatmap/readme/CREDITS.rst

@ -0,0 +1,3 @@
The development of this module has been financially supported by:
* Georg A. G. Notter <georg.notter@agenterp.com>

3
web_widget_heatmap/readme/DESCRIPTION.rst

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

35
web_widget_heatmap/readme/USAGE.rst

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

BIN
web_widget_heatmap/static/description/icon.png

After

Width: 100  |  Height: 100  |  Size: 3.0 KiB

75
web_widget_heatmap/static/description/index.html

@ -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">
...
&lt;field name=&quot;arch&quot; type=&quot;xml&quot;&gt;
&lt;form string=&quot;View name&quot;&gt;
...
&lt;field name=&quot;sale_order_ids&quot; widget=&quot;heatmap&quot;/&gt;
...
&lt;/form&gt;
&lt;/field&gt;
...
</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 &lt;<a href="mailto:gabbasov@it-projects.info">gabbasov@it-projects.info</a>&gt;</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>

145
web_widget_heatmap/static/lib/css/cal-heatmap.css

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

12
web_widget_heatmap/static/src/css/heatmap_widget.css

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

194
web_widget_heatmap/static/src/js/heatmap_widget.js

@ -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;
});

14
web_widget_heatmap/static/src/xml/base.xml

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

13
web_widget_heatmap/views/web_widget_heatmap_template.xml

@ -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>
Loading…
Cancel
Save