23 from math import floor |
23 from math import floor |
24 |
24 |
25 from logilab.mtconverter import xml_escape |
25 from logilab.mtconverter import xml_escape |
26 |
26 |
27 from cubicweb.utils import make_uid |
27 from cubicweb.utils import make_uid |
28 from cubicweb.selectors import implements, adaptable |
28 from cubicweb.selectors import adaptable |
29 from cubicweb.interfaces import IProgress, IMileStone |
|
30 from cubicweb.schema import display_name |
29 from cubicweb.schema import display_name |
31 from cubicweb.view import EntityView, EntityAdapter, implements_adapter_compat |
30 from cubicweb.view import EntityView |
32 from cubicweb.web.views.tableview import EntityAttributesTableView |
31 from cubicweb.web.views.tableview import EntityAttributesTableView |
33 |
|
34 |
|
35 class IProgressAdapter(EntityAdapter): |
|
36 """something that has a cost, a state and a progression. |
|
37 |
|
38 You should at least override progress_info an in_progress methods on concret |
|
39 implementations. |
|
40 """ |
|
41 __regid__ = 'IProgress' |
|
42 __select__ = implements(IProgress) # XXX for bw compat, should be abstract |
|
43 |
|
44 @property |
|
45 @implements_adapter_compat('IProgress') |
|
46 def cost(self): |
|
47 """the total cost""" |
|
48 return self.progress_info()['estimated'] |
|
49 |
|
50 @property |
|
51 @implements_adapter_compat('IProgress') |
|
52 def revised_cost(self): |
|
53 return self.progress_info().get('estimatedcorrected', self.cost) |
|
54 |
|
55 @property |
|
56 @implements_adapter_compat('IProgress') |
|
57 def done(self): |
|
58 """what is already done""" |
|
59 return self.progress_info()['done'] |
|
60 |
|
61 @property |
|
62 @implements_adapter_compat('IProgress') |
|
63 def todo(self): |
|
64 """what remains to be done""" |
|
65 return self.progress_info()['todo'] |
|
66 |
|
67 @implements_adapter_compat('IProgress') |
|
68 def progress_info(self): |
|
69 """returns a dictionary describing progress/estimated cost of the |
|
70 version. |
|
71 |
|
72 - mandatory keys are (''estimated', 'done', 'todo') |
|
73 |
|
74 - optional keys are ('notestimated', 'notestimatedcorrected', |
|
75 'estimatedcorrected') |
|
76 |
|
77 'noestimated' and 'notestimatedcorrected' should default to 0 |
|
78 'estimatedcorrected' should default to 'estimated' |
|
79 """ |
|
80 raise NotImplementedError |
|
81 |
|
82 @implements_adapter_compat('IProgress') |
|
83 def finished(self): |
|
84 """returns True if status is finished""" |
|
85 return not self.in_progress() |
|
86 |
|
87 @implements_adapter_compat('IProgress') |
|
88 def in_progress(self): |
|
89 """returns True if status is not finished""" |
|
90 raise NotImplementedError |
|
91 |
|
92 @implements_adapter_compat('IProgress') |
|
93 def progress(self): |
|
94 """returns the % progress of the task item""" |
|
95 try: |
|
96 return 100. * self.done / self.revised_cost |
|
97 except ZeroDivisionError: |
|
98 # total cost is 0 : if everything was estimated, task is completed |
|
99 if self.progress_info().get('notestimated'): |
|
100 return 0. |
|
101 return 100 |
|
102 |
|
103 @implements_adapter_compat('IProgress') |
|
104 def progress_class(self): |
|
105 return '' |
|
106 |
|
107 |
|
108 class IMileStoneAdapter(IProgressAdapter): |
|
109 __regid__ = 'IMileStone' |
|
110 __select__ = implements(IMileStone) # XXX for bw compat, should be abstract |
|
111 |
|
112 parent_type = None # specify main task's type |
|
113 |
|
114 @implements_adapter_compat('IMileStone') |
|
115 def get_main_task(self): |
|
116 """returns the main ITask entity""" |
|
117 raise NotImplementedError |
|
118 |
|
119 @implements_adapter_compat('IMileStone') |
|
120 def initial_prevision_date(self): |
|
121 """returns the initial expected end of the milestone""" |
|
122 raise NotImplementedError |
|
123 |
|
124 @implements_adapter_compat('IMileStone') |
|
125 def eta_date(self): |
|
126 """returns expected date of completion based on what remains |
|
127 to be done |
|
128 """ |
|
129 raise NotImplementedError |
|
130 |
|
131 @implements_adapter_compat('IMileStone') |
|
132 def completion_date(self): |
|
133 """returns date on which the subtask has been completed""" |
|
134 raise NotImplementedError |
|
135 |
|
136 @implements_adapter_compat('IMileStone') |
|
137 def contractors(self): |
|
138 """returns the list of persons supposed to work on this task""" |
|
139 raise NotImplementedError |
|
140 |
32 |
141 |
33 |
142 class ProgressTableView(EntityAttributesTableView): |
34 class ProgressTableView(EntityAttributesTableView): |
143 """The progress table view is able to display progress information |
35 """The progress table view is able to display progress information |
144 of any object implement IMileStone. |
36 of any object implement IMileStone. |