Added documentation to action.py and project.py
[matthijs/upstream/mobilegtd.git] / src / model / project.py
1 from action import Action
2 from info import Info
3 from model import *
4 from datetime import date
5 import action
6 from observable import *
7 from filtered_list import FilteredList,StatusFilteredList
8 import datetime
9 from log.logging import logger
10 import sys
11
12 class ProjectStatus(Status):
13     pass
14
15 #    def __eq__(self,other):
16 #        return False
17 class Inactive(ProjectStatus):
18     def __init__(self):
19         super(Inactive,self).__init__(u'review',4,'!')
20
21     def update(self,project):
22 #        if project.has_active_actions():
23 #            #print repr(active)
24 #            return active
25         return self
26
27
28
29 class Active(ProjectStatus):
30     
31     def __init__(self):
32         super(Active,self).__init__(u'active',1)
33         
34     def update(self,project):
35         if not project.has_active_actions():
36             #print repr(inactive)
37             return inactive
38         return self
39
40     
41 class Tickled(ProjectStatus):
42     def __init__(self,date=date.tomorrow()):
43         super(Tickled,self).__init__(u'tickled',3,'/')
44         self.date = date
45     
46     def update(self,project):
47         if self.date <= date.now():
48             return active
49         else:
50             return self
51
52     def __str__(self):
53         return super(Tickled,self).__str__()+" for %s"%self.date
54     
55     def __repr__(self):
56         return self.__str__()
57
58 unprocessed = ProjectStatus(u'unprocessed',0)
59 active = Active()
60 done = ProjectStatus(u'done',2,'+')
61 tickled = Tickled()
62 inactive = Inactive()
63 someday = ProjectStatus(u'someday',5,'~')
64 info = ProjectStatus(u'info',0)
65
66
67
68 class Project(ObservableItem,ItemWithStatus):
69     """Projects in the GTD terminology are things that need more than one
70     step to completion.
71     In MobileGTD a project consists of a name, a set of actions and a set of infos.
72     A Project also has a state. This can be one of the following:
73     - unprocessed: New, has not yet been processed in any way by MobileGTD
74     - active: It is planned to work on the project before the next Review cycle
75     - done: The project has been finished
76     - tickled: The project has been postponed to a certain date
77     - inactive: The project has been paused
78     """
79     observers = []
80     def __init__(self,name,status = inactive):
81         assert type(name) == unicode
82         logger.log(u'Creating project %s (%s)'%(name,status))
83         ItemWithStatus.__init__(self,status)
84         self.name=name
85         self.actions=StatusFilteredList([])
86         self.infos=FilteredList([])
87         self.update_methods = {'status':self.action_changed_status,
88                                'description':self.action_changed_content,
89                                'info':self.action_changed_content,
90                                'context':self.action_changed_content,
91                                'text':self.info_changed}
92         super(Project,self).__init__()
93         for o in Project.observers:
94             o.notify(self.__class__,'new_project',self,None)
95         logger.log(u'Now, its project %s (%s)'%(name,status))
96
97
98     def add_action(self,a):
99         a.project = self
100         a.observers.append(self)
101         self.actions.append(a)
102         self.notify_observers('add_action',a)
103         if a.status == action.unprocessed:
104             a.status = action.active
105         
106     def remove_action(self,a):
107         a.status = action.done
108         a.observers.remove(self)
109         self.actions.remove(a)
110         self.notify_observers('remove_action',a)
111
112     def add_info(self,info,position=None):
113         info.observers.append(self)
114         self.infos.append(info)
115         self.notify_observers('add_info', info)
116
117     def remove_info(self,info):
118         info.observers.remove(self)
119         self.infos.remove(info)
120         self.notify_observers('remove_info', info)
121
122     def activate(self):
123         self.status = active
124         for a in self.actions_with_status(action.inactive):
125             a.status = action.active
126
127     def deactivate(self):
128         self.status = inactive
129         for a in self.actions_with_status(action.active):
130             a.status = action.inactive
131
132     def actions_with_status(self,status):
133         return self.actions.with_property(lambda a:a.status == status)
134
135     def active_actions(self):
136         return self.actions_with_status(action.active)
137
138     def has_active_actions(self):
139         return len(self.active_actions()) > 0
140     
141     def notify(self,action,attribute,new=None,old=None):
142         self.update_methods[attribute](action,new)
143     
144     def info_changed(self,info,text):
145         self.notify_observers('changed_info', info)
146
147     def action_changed_content(self,action,content):
148         self.notify_observers('changed_action',action)   
149     
150     def action_changed_status(self,a,status):
151         self.notify_observers('changed_action', new=a, old=None)
152         
153     def last_modification_date(self):
154         return datetime.date.now()
155         
156     def __eq__(self, other):
157         return self.name == other.name and self.status == other.status
158
159     def __ne__(self,project):
160         return not self.__eq__(project)
161
162     def __str__(self):
163         return self.name
164
165     def status_symbol_and_name(self):
166         return self.status_symbol()+self.name
167     def __repr__(self):
168         return u'Project %s (@%s, %s actions, %s infos)'%("Moeject",self.status.name.capitalize(),len(self.actions),len(self.infos))