2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-04-16 13:34:26 +00:00
#
2009-10-14 11:15:34 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 11:15:34 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 19:18:56 +00:00
# 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
2009-10-14 11:15:34 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 11:15:34 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-04-16 13:34:26 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
import time
import netsvc
2010-05-05 10:11:11 +00:00
from osv import fields , osv , orm
2006-12-07 13:41:40 +00:00
from mx import DateTime
import re
class scrum_project ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_inherit = ' project.project '
_columns = {
' product_owner_id ' : fields . many2one ( ' res.users ' , ' Product Owner ' ) ,
' sprint_size ' : fields . integer ( ' Sprint Days ' ) ,
2010-02-23 07:38:48 +00:00
' scrum ' : fields . integer ( ' Is a Scrum Project ' ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
' product_owner_id ' : lambda self , cr , uid , context = { } : uid ,
2010-05-05 10:11:11 +00:00
' sprint_size ' : 15 ,
' scrum ' : 1
2008-07-22 15:11:28 +00:00
}
2006-12-07 13:41:40 +00:00
scrum_project ( )
class scrum_sprint ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' scrum.sprint '
_description = ' Scrum Sprint '
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def _calc_progress ( self , cr , uid , ids , name , args , context ) :
res = { }
for sprint in self . browse ( cr , uid , ids ) :
tot = 0.0
prog = 0.0
for bl in sprint . backlog_ids :
tot + = bl . planned_hours
prog + = bl . planned_hours * bl . progress / 100.0
res . setdefault ( sprint . id , 0.0 )
if tot > 0 :
res [ sprint . id ] = round ( prog / tot * 100 )
return res
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def _calc_effective ( self , cr , uid , ids , name , args , context ) :
res = { }
for sprint in self . browse ( cr , uid , ids ) :
res . setdefault ( sprint . id , 0.0 )
for bl in sprint . backlog_ids :
res [ sprint . id ] + = bl . effective_hours
return res
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def _calc_planned ( self , cr , uid , ids , name , args , context ) :
res = { }
for sprint in self . browse ( cr , uid , ids ) :
res . setdefault ( sprint . id , 0.0 )
for bl in sprint . backlog_ids :
res [ sprint . id ] + = bl . planned_hours
return res
2010-05-28 12:04:47 +00:00
2010-02-25 14:53:04 +00:00
def _calc_expected ( self , cr , uid , ids , name , args , context ) :
res = { }
for sprint in self . browse ( cr , uid , ids ) :
res . setdefault ( sprint . id , 0.0 )
for bl in sprint . backlog_ids :
res [ sprint . id ] + = bl . expected_hours
2010-04-16 13:34:26 +00:00
return res
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_cancel ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_draft ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' draft ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_open ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' open ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_close ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' done ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_pending ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' pending ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
_columns = {
2008-09-10 09:31:28 +00:00
' name ' : fields . char ( ' Sprint Name ' , required = True , size = 64 ) ,
2008-07-22 15:11:28 +00:00
' date_start ' : fields . date ( ' Starting Date ' , required = True ) ,
' date_stop ' : fields . date ( ' Ending Date ' , required = True ) ,
2010-05-10 07:25:11 +00:00
' project_id ' : fields . many2one ( ' project.project ' , ' Project ' , required = True , domain = [ ( ' scrum ' , ' = ' , 1 ) ] , help = " If you have [?] in the project name, it means there are no analytic account linked to this project. " ) ,
2008-07-22 15:11:28 +00:00
' product_owner_id ' : fields . many2one ( ' res.users ' , ' Product Owner ' , required = True ) ,
2010-05-05 10:34:09 +00:00
' scrum_master_id ' : fields . many2one ( ' res.users ' , ' Scrum Manager ' , required = True ) ,
2010-02-23 07:38:48 +00:00
' meeting_ids ' : fields . one2many ( ' scrum.meeting ' , ' sprint_id ' , ' Daily Scrum ' ) ,
2008-07-22 15:11:28 +00:00
' review ' : fields . text ( ' Sprint Review ' ) ,
' retrospective ' : fields . text ( ' Sprint Retrospective ' ) ,
' backlog_ids ' : fields . one2many ( ' scrum.product.backlog ' , ' sprint_id ' , ' Sprint Backlog ' ) ,
2010-05-05 10:11:11 +00:00
' progress ' : fields . function ( _calc_progress , method = True , string = ' Progress (0-100) ' , help = " Computed as: Time Spent / Total Time. " ) ,
' effective_hours ' : fields . function ( _calc_effective , method = True , string = ' Effective hours ' , help = " Computed using the sum of the task work done. " ) ,
' planned_hours ' : fields . function ( _calc_planned , method = True , string = ' Planned Hours ' , help = ' Estimated time to do the task, usually set by the project manager when the task is in draft state. ' ) ,
' expected_hours ' : fields . function ( _calc_expected , method = True , string = ' Expected Hours ' , help = ' Estimated time to do the task. ' ) ,
2010-02-24 16:28:53 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' open ' , ' Open ' ) , ( ' pending ' , ' Pending ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' done ' , ' Done ' ) ] , ' State ' , required = True ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-05-05 10:11:11 +00:00
' state ' : ' draft ' ,
' date_start ' : time . strftime ( ' % Y- % m- %d ' ) ,
2008-07-22 15:11:28 +00:00
}
2010-05-05 10:11:11 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
""" Overrides orm copy method
@param self : The object pointer
@param cr : the current row , from the database cursor ,
@param uid : the current user ’ s ID for security checks ,
@param ids : List of case ’ s IDs
@param context : A standard dictionary for contextual values
"""
if context is None :
context = { }
if default is None :
default = { }
2010-05-14 10:18:29 +00:00
default . update ( { ' backlog_ids ' : [ ] , ' meeting_ids ' : [ ] } )
return super ( scrum_sprint , self ) . copy ( cr , uid , id , default = default , context = context )
2010-05-05 10:11:11 +00:00
2008-07-22 15:11:28 +00:00
def onchange_project_id ( self , cr , uid , ids , project_id ) :
v = { }
if project_id :
2010-02-23 07:38:48 +00:00
proj = self . pool . get ( ' project.project ' ) . browse ( cr , uid , [ project_id ] ) [ 0 ]
v [ ' product_owner_id ' ] = proj . product_owner_id and proj . product_owner_id . id or False
v [ ' scrum_master_id ' ] = proj . user_id and proj . user_id . id or False
2008-07-22 15:11:28 +00:00
v [ ' date_stop ' ] = ( DateTime . now ( ) + DateTime . RelativeDateTime ( days = int ( proj . sprint_size or 14 ) ) ) . strftime ( ' % Y- % m- %d ' )
return { ' value ' : v }
2010-04-16 13:34:26 +00:00
2006-12-07 13:41:40 +00:00
scrum_sprint ( )
class scrum_product_backlog ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' scrum.product.backlog '
_description = ' Product Backlog '
2006-12-07 13:41:40 +00:00
2009-12-09 11:43:34 +00:00
def name_search ( self , cr , uid , name , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
2008-07-22 15:11:28 +00:00
if not args :
args = [ ]
if not context :
context = { }
match = re . match ( ' ^S \ (([0-9]+) \ )$ ' , name )
if match :
ids = self . search ( cr , uid , [ ( ' sprint_id ' , ' = ' , int ( match . group ( 1 ) ) ) ] , limit = limit , context = context )
return self . name_get ( cr , uid , ids , context = context )
return super ( scrum_product_backlog , self ) . name_search ( cr , uid , name , args , operator , context , limit = limit )
2006-12-07 13:41:40 +00:00
2008-07-22 15:11:28 +00:00
def _calc_progress ( self , cr , uid , ids , name , args , context ) :
res = { }
for bl in self . browse ( cr , uid , ids ) :
tot = 0.0
prog = 0.0
for task in bl . tasks_id :
tot + = task . planned_hours
prog + = task . planned_hours * task . progress / 100.0
res . setdefault ( bl . id , 0.0 )
if tot > 0 :
res [ bl . id ] = round ( prog / tot * 100 )
return res
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def _calc_effective ( self , cr , uid , ids , name , args , context ) :
res = { }
for bl in self . browse ( cr , uid , ids ) :
res . setdefault ( bl . id , 0.0 )
for task in bl . tasks_id :
res [ bl . id ] + = task . effective_hours
return res
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def _calc_planned ( self , cr , uid , ids , name , args , context ) :
res = { }
for bl in self . browse ( cr , uid , ids ) :
res . setdefault ( bl . id , 0.0 )
for task in bl . tasks_id :
res [ bl . id ] + = task . planned_hours
return res
2010-02-24 16:28:53 +00:00
def button_cancel ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' cancel ' } , context = context )
2010-04-16 13:34:26 +00:00
for backlog in self . browse ( cr , uid , ids , context = context ) :
self . pool . get ( ' project.task ' ) . write ( cr , uid , [ i . id for i in backlog . tasks_id ] , { ' state ' : ' cancelled ' } )
2010-02-24 16:28:53 +00:00
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_draft ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' draft ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_open ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' open ' } , context = context )
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_close ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' done ' } , context = context )
2010-05-05 10:11:11 +00:00
for backlog in self . browse ( cr , uid , ids , context = context ) :
self . pool . get ( ' project.task ' ) . write ( cr , uid , [ i . id for i in backlog . tasks_id ] , { ' state ' : ' done ' } )
2010-02-24 16:28:53 +00:00
return True
2010-05-28 12:04:47 +00:00
2010-02-24 16:28:53 +00:00
def button_pending ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' pending ' } , context = context )
return True
2008-07-22 15:11:28 +00:00
_columns = {
2008-09-10 09:31:28 +00:00
' name ' : fields . char ( ' Feature ' , size = 64 , required = True ) ,
2008-07-22 15:11:28 +00:00
' note ' : fields . text ( ' Note ' ) ,
2010-05-03 08:30:24 +00:00
' active ' : fields . boolean ( ' Active ' , help = " If Active field is set to true, it will allow you to hide the product backlog without removing it. " ) ,
2010-05-05 10:11:11 +00:00
' project_id ' : fields . many2one ( ' project.project ' , ' Project ' , required = True , domain = [ ( ' scrum ' , ' = ' , 1 ) ] , help = " If you have [?] in the project name, it means there are no analytic account linked to this project. " ) ,
2010-02-23 07:38:48 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Responsible ' ) ,
2008-07-22 15:11:28 +00:00
' sprint_id ' : fields . many2one ( ' scrum.sprint ' , ' Sprint ' ) ,
2009-12-21 13:14:12 +00:00
' sequence ' : fields . integer ( ' Sequence ' , help = " Gives the sequence order when displaying a list of product backlog. " ) ,
2010-02-23 07:38:48 +00:00
' tasks_id ' : fields . one2many ( ' project.task ' , ' product_backlog_id ' , ' Tasks Details ' ) ,
2010-02-24 16:28:53 +00:00
' state ' : fields . selection ( [ ( ' draft ' , ' Draft ' ) , ( ' open ' , ' Open ' ) , ( ' pending ' , ' Pending ' ) , ( ' done ' , ' Done ' ) , ( ' cancel ' , ' Cancelled ' ) ] , ' State ' , required = True ) ,
2010-05-05 10:11:11 +00:00
' progress ' : fields . function ( _calc_progress , method = True , string = ' Progress ' , help = " Computed as: Time Spent / Total Time. " ) ,
2010-05-12 12:40:48 +00:00
' effective_hours ' : fields . function ( _calc_effective , method = True , string = ' Effective hours ' , help = " Computed using the sum of the task work done (Time spent on tasks) " ) ,
2010-05-05 10:11:11 +00:00
' planned_hours ' : fields . function ( _calc_planned , method = True , string = ' Planned Hours ' , help = ' Estimated time to do the task, usually set by the project manager when the task is in draft state. ' ) ,
2010-05-12 12:40:48 +00:00
' expected_hours ' : fields . float ( ' Expected Hours ' , help = ' Estimated total time to do the Backlog ' ) ,
2010-05-28 12:04:47 +00:00
' create_date ' : fields . datetime ( " Creation Date " , readonly = True ) ,
2008-07-22 15:11:28 +00:00
}
_defaults = {
2010-05-05 10:11:11 +00:00
' state ' : ' draft ' ,
' active ' : 1 ,
2010-05-06 12:55:54 +00:00
' user_id ' : lambda self , cr , uid , context : uid ,
2008-07-22 15:11:28 +00:00
}
2010-02-23 07:38:48 +00:00
_order = " sequence "
2006-12-07 13:41:40 +00:00
scrum_product_backlog ( )
class scrum_task ( osv . osv ) :
2010-02-26 13:42:56 +00:00
_name = ' project.task '
2008-07-22 15:11:28 +00:00
_inherit = ' project.task '
2010-05-28 12:04:47 +00:00
2010-02-28 10:57:24 +00:00
def _get_task ( self , cr , uid , ids , context = { } ) :
result = { }
for line in self . pool . get ( ' scrum.product.backlog ' ) . browse ( cr , uid , ids , context = context ) :
for task in line . tasks_id :
result [ task . id ] = True
return result . keys ( )
2008-07-22 15:11:28 +00:00
_columns = {
' product_backlog_id ' : fields . many2one ( ' scrum.product.backlog ' , ' Product Backlog ' ) ,
2010-02-28 10:57:24 +00:00
' sprint_id ' : fields . related ( ' product_backlog_id ' , ' sprint_id ' , type = ' many2one ' , relation = ' scrum.sprint ' , string = ' Sprint ' ,
store = {
' project.task ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' product_backlog_id ' ] , 10 ) ,
' scrum.product.backlog ' : ( _get_task , [ ' sprint_id ' ] , 10 )
} ) ,
2008-07-22 15:11:28 +00:00
}
2010-05-28 12:04:47 +00:00
2008-07-22 15:11:28 +00:00
def onchange_backlog_id ( self , cr , uid , backlog_id ) :
if not backlog_id :
return { }
project_id = self . pool . get ( ' scrum.product.backlog ' ) . browse ( cr , uid , backlog_id ) . project_id . id
return { ' value ' : { ' project_id ' : project_id } }
2006-12-07 13:41:40 +00:00
scrum_task ( )
class scrum_meeting ( osv . osv ) :
2008-07-22 15:11:28 +00:00
_name = ' scrum.meeting '
_description = ' Scrum Meeting '
2010-05-03 07:20:35 +00:00
_order = ' date desc '
2008-07-22 15:11:28 +00:00
_columns = {
2010-05-05 10:11:11 +00:00
' name ' : fields . char ( ' Meeting Name ' , size = 64 ) ,
2008-07-22 15:11:28 +00:00
' date ' : fields . date ( ' Meeting Date ' , required = True ) ,
' sprint_id ' : fields . many2one ( ' scrum.sprint ' , ' Sprint ' , required = True ) ,
2010-04-16 13:34:26 +00:00
' project_id ' : fields . many2one ( ' project.project ' , ' Project ' ) ,
2008-07-22 15:11:28 +00:00
' question_yesterday ' : fields . text ( ' Tasks since yesterday ' ) ,
' question_today ' : fields . text ( ' Tasks for today ' ) ,
' question_blocks ' : fields . text ( ' Blocks encountered ' ) ,
' question_backlog ' : fields . text ( ' Backlog Accurate ' ) ,
2010-04-16 13:34:26 +00:00
' task_ids ' : fields . many2many ( ' project.task ' , ' meeting_task_rel ' , ' metting_id ' , ' task_id ' , ' Tasks ' )
2008-07-22 15:11:28 +00:00
}
#
2010-02-23 07:38:48 +00:00
# TODO: Find the right sprint thanks to users and date
2008-07-22 15:11:28 +00:00
#
_defaults = {
2010-05-05 10:11:11 +00:00
' date ' : time . strftime ( ' % Y- % m- %d ' ) ,
2008-07-22 15:11:28 +00:00
}
2006-12-07 13:41:40 +00:00
scrum_meeting ( )