From 56dfb4eeb977eb9493b83d0c2f2de22a561bc8c3 Mon Sep 17 00:00:00 2001 From: sus Date: Tue, 22 Jul 2014 17:54:41 +0530 Subject: [PATCH] [IMP] project: onboardin / usability improvements - project kanban view: dashboard-like view, add sparklines for open tasks and closed issues - project: cleaned demo data, less stages - project: description is now an html field --- addons/project/__openerp__.py | 3 +- addons/project/project.py | 48 +++++++++++++- addons/project/project_data.xml | 33 ++-------- addons/project/project_demo.xml | 62 +++++++++---------- addons/project/project_view.xml | 14 +++-- addons/project/static/src/css/project.css | 15 +++++ addons/project_issue/project_issue.py | 21 ++++++- addons/project_issue/project_issue_demo.xml | 32 +++++----- addons/project_issue/project_issue_view.xml | 5 +- .../sale_service/demo/sale_service_demo.xml | 2 +- .../test/project_task_procurement.yml | 2 +- addons/web_kanban/static/src/css/kanban.css | 2 +- addons/web_kanban/static/src/css/kanban.sass | 2 +- 13 files changed, 152 insertions(+), 89 deletions(-) diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py index e4e62ec8027..0b2308d62ec 100644 --- a/addons/project/__openerp__.py +++ b/addons/project/__openerp__.py @@ -44,7 +44,8 @@ 'board', 'mail', 'resource', - 'web_kanban' + 'web_kanban', + 'web_kanban_sparkline' ], 'description': """ Track multi-level projects, tasks, work done on tasks diff --git a/addons/project/project.py b/addons/project/project.py index e278cddd59f..d17980fd33b 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -19,8 +19,11 @@ # ############################################################################## +import calendar from datetime import datetime, date +from dateutil import relativedelta from lxml import etree +import json import time from openerp import SUPERUSER_ID @@ -65,6 +68,7 @@ class project(osv.osv): _inherits = {'account.analytic.account': "analytic_account_id", "mail.alias": "alias_id"} _inherit = ['mail.thread', 'ir.needaction_mixin'] + _period_number = 5 def _auto_init(self, cr, context=None): """ Installation hook: aliases, project.project """ @@ -224,6 +228,46 @@ class project(osv.osv): 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id) } + def __get_bar_values(self, cr, uid, obj, domain, read_fields, value_field, groupby_field, context=None): + """ Generic method to generate data for bar chart values using SparklineBarWidget. + This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field). + + :param obj: the target model (i.e. crm_lead) + :param domain: the domain applied to the read_group + :param list read_fields: the list of fields to read in the read_group + :param str value_field: the field used to compute the value of the bar slice + :param str groupby_field: the fields used to group + + :return list section_result: a list of dicts: [ + { 'value': (int) bar_column_value, + 'tootip': (str) bar_column_tooltip, + } + ] + """ + month_begin = date.today().replace(day=1) + section_result = [{ + 'value': 0, + 'tooltip': (month_begin + relativedelta.relativedelta(months=-i)).strftime('%B'), + } for i in range(self._period_number - 1, -1, -1)] + group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context) + pattern = tools.DEFAULT_SERVER_DATE_FORMAT if obj.fields_get(cr, uid, groupby_field)[groupby_field]['type'] == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT + for group in group_obj: + group_begin_date = datetime.strptime(group['__domain'][0][2], pattern) + month_delta = relativedelta.relativedelta(month_begin, group_begin_date) + section_result[self._period_number - (month_delta.months + 1)] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field, 0)} + return section_result + + def _get_project_task_data(self, cr, uid, ids, field_name, arg, context=None): + obj = self.pool['project.task'] + month_begin = date.today().replace(day=1) + date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + res = {} + for id in ids: + created_domain = [('project_id', '=', id), ('create_date', '>=', date_begin ), ('create_date', '<=', date_end ), ('stage_id.fold', '=', False)] + res[id] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, [ 'create_date'], 'create_date_count', 'create_date', context=context)) + return res + # Lambda indirection method to avoid passing a copy of the overridable method when declaring the field _alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs) _visibility_selection = lambda self, *args, **kwargs: self._get_visibility_selection(*args, **kwargs) @@ -288,6 +332,8 @@ class project(osv.osv): ('pending','Pending'), ('close','Closed')], 'Status', required=True, copy=False), + 'monthly_tasks': fields.function(_get_project_task_data, type='char', readonly=True, + string='Project Task By Month'), 'doc_count': fields.function( _get_attached_docs, string="Number of documents attached", type='integer' ) @@ -737,7 +783,7 @@ class task(osv.osv): _columns = { 'active': fields.function(_is_template, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."), 'name': fields.char('Task Summary', track_visibility='onchange', size=128, required=True, select=True), - 'description': fields.text('Description'), + 'description': fields.html('Description'), 'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True), 'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."), 'stage_id': fields.many2one('project.task.type', 'Stage', track_visibility='onchange', select=True, diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index f98ee9aaea6..12a028151bc 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -27,44 +27,23 @@ - + 1 - Analysis + To Do - + 10 - Specification + In Progress - - 11 - Design - - - - 12 - Development - - - - 13 - Testing - - - - 14 - Merge - - - - + 20 Done - + 30 Cancelled diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml index 695aa80af2a..e15586dc420 100644 --- a/addons/project/project_demo.xml +++ b/addons/project/project_demo.xml @@ -40,7 +40,7 @@ - + @@ -56,7 +56,7 @@ - + @@ -70,7 +70,7 @@ - + @@ -85,7 +85,7 @@ - + @@ -101,7 +101,7 @@ - + @@ -113,7 +113,7 @@ 0 Prepare Requirements Document - + 3 @@ -123,7 +123,7 @@ 0 Make SRS - + @@ -133,7 +133,7 @@ Budget Planning - + 4 @@ -146,7 +146,7 @@ Use the account_budget module 5 - + @@ -160,7 +160,7 @@ done 0 - + @@ -171,7 +171,7 @@ 2 Integrate Modules - + @@ -180,7 +180,7 @@ 2 Internal testing + Software Install - + @@ -190,7 +190,7 @@ New portal system 0 - + @@ -201,7 +201,7 @@ 2 Document history management - + @@ -211,7 +211,7 @@ Social network integration blocked - + @@ -223,7 +223,7 @@ - + @@ -233,7 +233,7 @@ 2 Improve+clean code and functionality - + 6 @@ -244,7 +244,7 @@ 2 Design Use Cases - + @@ -254,7 +254,7 @@ 2 Dataflow Design - + @@ -263,7 +263,7 @@ 2 User Interface design - + 2011-02-06 @@ -277,7 +277,7 @@ 2 Set target for all deparments - + @@ -287,7 +287,7 @@ 2 Integration of core components - + @@ -297,13 +297,13 @@ 2 Deploy and review on live system - + 24.0 24.0 - + Basic outline @@ -315,7 +315,7 @@ 42.0 42.0 - + Create new components @@ -324,7 +324,7 @@ 14.0 14.0 - + Useablity review 12.0 12.0 - + 0 @@ -345,7 +345,7 @@ 15 15.0 15.0 - + Data Export flow @@ -355,7 +355,7 @@ 17 8.0 8.0 - + 2 @@ -371,7 +371,7 @@ Data importation + Doc - + @@ -381,7 +381,7 @@ Unit Testing - + diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 650a9b30015..3444f179b90 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -257,7 +257,7 @@ -
+
í
    @@ -272,8 +272,11 @@
- - Tasks + + Tasks + + Created Tasks +
@@ -414,7 +417,7 @@ - + @@ -523,7 +526,7 @@
-
+
oe_kanban_text_red
@@ -606,6 +609,7 @@ + diff --git a/addons/project/static/src/css/project.css b/addons/project/static/src/css/project.css index 616c8625cd4..3572c45a9fe 100644 --- a/addons/project/static/src/css/project.css +++ b/addons/project/static/src/css/project.css @@ -70,3 +70,18 @@ position: relative; } +.openerp .oe_kanban_view .oe_kanban_project_task_spark { + /* Customize width and height of kanban according bootstrap3 */ + width: 265px; + min-height: 120px; + /* End of customize */ + cursor: default; +} + +.openerp .oe_kanban_view .oe_sparkline_bar { + height: 20px; + width: 30px; + display: inline; + +} + diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 747485d64ce..76e1766c181 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -19,8 +19,11 @@ # ############################################################################## -from datetime import datetime - +import calendar +from datetime import datetime,date +from dateutil import relativedelta +import json +import time from openerp import api from openerp import SUPERUSER_ID from openerp import tools @@ -480,13 +483,25 @@ class project(osv.Model): project_id: Issue.search_count(cr,uid, [('project_id', '=', project_id), ('stage_id.fold', '=', False)], context=context) for project_id in ids } + def _get_project_issue_data(self, cr, uid, ids, field_name, arg, context=None): + obj = self.pool['project.issue'] + month_begin = date.today().replace(day=1) + date_begin = (month_begin - relativedelta.relativedelta(months=self._period_number - 1)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + date_end = month_begin.replace(day=calendar.monthrange(month_begin.year, month_begin.month)[1]).strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + res = {} + for id in ids: + created_domain = [('project_id', '=', id), ('date_closed', '>=', date_begin ), ('date_closed', '<=', date_end )] + res[id] = json.dumps(self.__get_bar_values(cr, uid, obj, created_domain, [ 'date_closed'], 'date_closed_count', 'date_closed', context=context)) + return res _columns = { 'project_escalation_id': fields.many2one('project.project', 'Project Escalation', help='If any issue is escalated from the current Project, it will be listed under the project selected here.', states={'close': [('readonly', True)], 'cancelled': [('readonly', True)]}), 'issue_count': fields.function(_issue_count, type='integer', string="Issues",), 'issue_ids': fields.one2many('project.issue', 'project_id', - domain=[('stage_id.fold', '=', False)]) + domain=[('date_closed', '!=', False)]), + 'monthly_issues': fields.function(_get_project_issue_data, type='char', readonly=True, + string='Project Issue By Month') } def _check_escalation(self, cr, uid, ids, context=None): diff --git a/addons/project_issue/project_issue_demo.xml b/addons/project_issue/project_issue_demo.xml index 7da36f1d4c6..d55dd648aa9 100644 --- a/addons/project_issue/project_issue_demo.xml +++ b/addons/project_issue/project_issue_demo.xml @@ -44,7 +44,7 @@ Bug in Accounts module agr@agrolait.com - + @@ -59,7 +59,7 @@ Program not giving proper output - + @@ -71,7 +71,7 @@ Output incorrect - + @@ -87,7 +87,7 @@ Problem loading page - + @@ -100,7 +100,7 @@ Page not Found - + @@ -113,7 +113,7 @@ Programming Error - + @@ -129,7 +129,7 @@ Logical Error in Program - + @@ -142,7 +142,7 @@ Constraint Error - + @@ -154,7 +154,7 @@ Error in Program - + @@ -167,7 +167,7 @@ Patches Error in Program - + @@ -180,7 +180,7 @@ New Features To Be Added - + @@ -194,7 +194,7 @@ Add menus to the module info@opensides.be - + @@ -208,7 +208,7 @@ Include Attendance sheet in Project contact@tecsas.fr - + @@ -221,7 +221,7 @@ Create new object - + @@ -234,7 +234,7 @@ Improve Reports in HRMS - + @@ -247,7 +247,7 @@ Improve Reports in PMS - + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 2dd50de04a9..236aea7a7eb 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -331,9 +331,12 @@ - Issues + + Closed Issues + diff --git a/addons/sale_service/demo/sale_service_demo.xml b/addons/sale_service/demo/sale_service_demo.xml index e4622866891..7f9401430fc 100644 --- a/addons/sale_service/demo/sale_service_demo.xml +++ b/addons/sale_service/demo/sale_service_demo.xml @@ -12,7 +12,7 @@ 5.0 - + diff --git a/addons/sale_service/test/project_task_procurement.yml b/addons/sale_service/test/project_task_procurement.yml index ed996465673..f3105370ba0 100644 --- a/addons/sale_service/test/project_task_procurement.yml +++ b/addons/sale_service/test/project_task_procurement.yml @@ -61,7 +61,7 @@ !python {model: project.task}: | task_ids = self.search(cr, uid, [('sale_line_id', '=', ref('service_line'))]) assert task_ids, "Task is not generated for Service Order Line." - self.write(cr, uid, task_ids, {'stage_id': ref('project.project_tt_deployment')}, context=context) + self.write(cr, uid, task_ids, {'stage_id': ref('project.project_stage_2')}, context=context) - I check procurement of Service Order Line after closed task. - diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 50a9e54d7be..782a9fba26a 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -9,7 +9,7 @@ .openerp .oe_kanban_view .oe_view_nocontent { position: relative; max-width: none; - height: 100%; + height: auto; } .openerp .oe_kanban_view .oe_view_nocontent .oe_view_nocontent_content { margin-left: 90px; diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index d5f2bfcdbd8..98247d7602f 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -56,7 +56,7 @@ .oe_view_nocontent position: relative max-width: none - height: 100% + height: auto .oe_view_nocontent_content margin-left: 90px margin-top: 5px