reducer.py :  » Project-Management » Task-Coach » TaskCoach-1.0.3 » taskcoachlib » domain » effort » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Project Management » Task Coach 
Task Coach » TaskCoach 1.0.3 » taskcoachlib » domain » effort » reducer.py
'''
Task Coach - Your friendly task manager
Copyright (C) 2004-2009 Frank Niessink <frank@niessink.com>

Task Coach is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Task Coach 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
'''

from taskcoachlib import patterns
from taskcoachlib.domain import date,task
import effortlist, composite


class EffortAggregator(patterns.SetDecorator, 
                       effortlist.EffortUICommandNamesMixin):
    ''' This class observes an TaskList and aggregates the individual effort
        records to CompositeEfforts, e.g. per day or per week. '''
    def __init__(self, *args, **kwargs):
        self.__composites = {}
        aggregation = kwargs.pop('aggregation')
        assert aggregation in ('day', 'week', 'month')
        aggregation = aggregation.capitalize()
        self.startOfPeriod = getattr(date.DateTime, 'startOf%s'%aggregation)
        self.endOfPeriod = getattr(date.DateTime, 'endOf%s'%aggregation)
        super(EffortAggregator, self).__init__(*args, **kwargs)
        patterns.Publisher().registerObserver(self.onCompositeEmpty,
            eventType='effort.composite.empty')
        patterns.Publisher().registerObserver(self.onEffortAddedToTask, 
            eventType='task.effort.add')
        patterns.Publisher().registerObserver(self.onChildAddedToTask,
            eventType=task.Task.addChildEventType())
        patterns.Publisher().registerObserver(self.onEffortStartChanged, 
            eventType='effort.start')
        
    def extend(self, efforts, event=None): # pylint: disable-msg=W0221
        notify = event is None
        event = event or patterns.Event()
        for effort in efforts:
            effort.task().addEffort(effort, event)
        if notify:
            event.send()
            
    def removeItems(self, efforts, event=None): # pylint: disable-msg=W0221
        notify = event is None
        event = event or patterns.Event()
        for effort in efforts:
            effort.task().removeEffort(effort, event)
        if notify:
            event.send()
            
    def extendSelf(self, tasks, event=None):
        ''' extendSelf is called when an item is added to the observed
            list. The default behavior of extendSelf is to add the item
            to the observing list (i.e. this list) unchanged. We override 
            the default behavior to first get the efforts from thetask import 
            and then group the efforts by time period. '''
        newComposites = []
        for task in tasks: # pylint: disable-msg=W0621
            newComposites.extend(self.createComposites(task, task.efforts()))
        super(EffortAggregator, self).extendSelf(newComposites, event)

    def removeItemsFromSelf(self, tasks, event=None):
        ''' removeItemsFromSelf is called when an item is removed from the import 
            observed list. The default behavior of removeItemsFromSelf is to 
            remove the item from theobservinglisti.e.thislist import 
            unchanged. We override the default behavior to remove the 
            tasks' efforts from the CompositeEfforts they are part of. '''
        notify = event is None
        event = event or patterns.Event()
        for task in tasks: # pylint: disable-msg=W0621
            self.removeComposites(task, task.efforts(), event)
        if notify:
            event.send()

    def onEffortAddedToTask(self, event):
        newComposites = []
        for task in event.sources(): # pylint: disable-msg=W0621
            if task in self.observable():
                efforts = event.values(task)
                newComposites.extend(self.createComposites(task, efforts))
        super(EffortAggregator, self).extendSelf(newComposites)
        
    def onChildAddedToTask(self, event):
        newComposites = []
        for task in event.sources(): # pylint: disable-msg=W0621
            if task in self.observable():
                child = event.value(task)
                newComposites.extend(self.createComposites(task,
                    child.efforts(recursive=True)))
        super(EffortAggregator, self).extendSelf(newComposites)

    def onCompositeEmpty(self, event):
        # pylint: disable-msg=W0621
        composites = [composite for composite in event.sources() if \
                      composite in self]
        keys = [self.keyForComposite(composite) for composite in composites]
        # A composite may already have been removed, e.g. when a
        # parent and child task have effort in the same period
        keys = [key for key in keys if key in self.__composites]
        for key in keys:
            del self.__composites[key]
        super(EffortAggregator, self).removeItemsFromSelf(composites)
        
    def onEffortStartChanged(self, event):
        newComposites = []
        for effort in event.sources():
            key = self.keyForEffort(effort)
            task = effort.task() # pylint: disable-msg=W0621
            if (task in self.observable()) and (key not in self.__composites):
                newComposites.extend(self.createComposites(task, [effort]))
        super(EffortAggregator, self).extendSelf(newComposites)
            
    def createComposites(self, task, efforts): # pylint: disable-msg=W0621
        newComposites = []
        taskAndAncestors = [task] + task.ancestors()
        for effort in efforts:
            for task in taskAndAncestors:
                newComposites.extend(self.createComposite(effort, task))
            newComposites.extend(self.createCompositeForPeriod(effort))
        return newComposites

    def createComposite(self, anEffort, task): # pylint: disable-msg=W0621
        key = self.keyForEffort(anEffort, task)
        if key in self.__composites:
            return []
        newComposite = composite.CompositeEffort(*key) # pylint: disable-msg=W0142
        self.__composites[key] = newComposite
        return [newComposite]
    
    def createCompositeForPeriod(self, anEffort):
        key = self.keyForPeriod(anEffort)
        if key in self.__composites:
            return []
        newCompositePerPeriod = composite.CompositeEffortPerPeriod(key[0], key[1], self.observable())
        self.__composites[key] = newCompositePerPeriod
        return [newCompositePerPeriod]

    def removeComposites(self, task, efforts, event): # pylint: disable-msg=W0621
        taskAndAncestors = [task] + task.ancestors()
        for effort in efforts:
            for task in taskAndAncestors:
                self.removeComposite(effort, task, event)

    def removeComposite(self, anEffort, task, event): # pylint: disable-msg=W0613,W0621
        key = self.keyForEffort(anEffort, task)
        if key not in self.__composites:
            # A composite may already have been removed, e.g. when a
            # parent and child task have effort in the same period
            return
        compositeToRemove = self.__composites.pop(key)
        # FIXME: Can't pass event to removeItemsFromSelf, because 
        # otherwise 
        super(EffortAggregator, self).removeItemsFromSelf([compositeToRemove])#, event)

    def maxDateTime(self):
        stopTimes = [effort.getStop() for compositeEffort in self for effort
            in compositeEffort if effort.getStop() is not None]
        if stopTimes:
            return max(stopTimes)
        else:
            return None

    @staticmethod
    def keyForComposite(compositeEffort):
        if compositeEffort.task().__class__.__name__ == 'Total':
            return (compositeEffort.getStart(), compositeEffort.getStop())
        else:
            return (compositeEffort.task(), compositeEffort.getStart(), 
                    compositeEffort.getStop())
    
    def keyForEffort(self, effort, task=None): # pylint: disable-msg=W0621
        task = task or effort.task()
        effortStart = effort.getStart()
        return (task, self.startOfPeriod(effortStart), 
            self.endOfPeriod(effortStart))
        
    def keyForPeriod(self, effort):
        key = self.keyForEffort(effort)
        return key[1], key[2]
    
    @classmethod
    def sortEventType(class_):
        return 'this event type is not used' 

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.