27 from logilab.mtconverter import TransformError |
27 from logilab.mtconverter import TransformError |
28 from logilab.common.decorators import cached |
28 from logilab.common.decorators import cached |
29 |
29 |
30 from cubicweb.view import EntityAdapter, implements_adapter_compat |
30 from cubicweb.view import EntityAdapter, implements_adapter_compat |
31 from cubicweb.selectors import implements, relation_possible |
31 from cubicweb.selectors import implements, relation_possible |
32 from cubicweb.interfaces import IDownloadable, ITree |
32 from cubicweb.interfaces import IDownloadable, ITree, IProgress, IMileStone |
33 |
33 |
34 |
34 |
35 class IEmailableAdapter(EntityAdapter): |
35 class IEmailableAdapter(EntityAdapter): |
36 __regid__ = 'IEmailable' |
36 __regid__ = 'IEmailable' |
37 __select__ = relation_possible('primary_email') | relation_possible('use_email') |
37 __select__ = relation_possible('primary_email') | relation_possible('use_email') |
325 except AttributeError: |
325 except AttributeError: |
326 break |
326 break |
327 path.reverse() |
327 path.reverse() |
328 return path |
328 return path |
329 |
329 |
|
330 |
|
331 class IProgressAdapter(EntityAdapter): |
|
332 """something that has a cost, a state and a progression. |
|
333 |
|
334 You should at least override progress_info an in_progress methods on concret |
|
335 implementations. |
|
336 """ |
|
337 __regid__ = 'IProgress' |
|
338 __select__ = implements(IProgress) # XXX for bw compat, should be abstract |
|
339 |
|
340 @property |
|
341 @implements_adapter_compat('IProgress') |
|
342 def cost(self): |
|
343 """the total cost""" |
|
344 return self.progress_info()['estimated'] |
|
345 |
|
346 @property |
|
347 @implements_adapter_compat('IProgress') |
|
348 def revised_cost(self): |
|
349 return self.progress_info().get('estimatedcorrected', self.cost) |
|
350 |
|
351 @property |
|
352 @implements_adapter_compat('IProgress') |
|
353 def done(self): |
|
354 """what is already done""" |
|
355 return self.progress_info()['done'] |
|
356 |
|
357 @property |
|
358 @implements_adapter_compat('IProgress') |
|
359 def todo(self): |
|
360 """what remains to be done""" |
|
361 return self.progress_info()['todo'] |
|
362 |
|
363 @implements_adapter_compat('IProgress') |
|
364 def progress_info(self): |
|
365 """returns a dictionary describing progress/estimated cost of the |
|
366 version. |
|
367 |
|
368 - mandatory keys are (''estimated', 'done', 'todo') |
|
369 |
|
370 - optional keys are ('notestimated', 'notestimatedcorrected', |
|
371 'estimatedcorrected') |
|
372 |
|
373 'noestimated' and 'notestimatedcorrected' should default to 0 |
|
374 'estimatedcorrected' should default to 'estimated' |
|
375 """ |
|
376 raise NotImplementedError |
|
377 |
|
378 @implements_adapter_compat('IProgress') |
|
379 def finished(self): |
|
380 """returns True if status is finished""" |
|
381 return not self.in_progress() |
|
382 |
|
383 @implements_adapter_compat('IProgress') |
|
384 def in_progress(self): |
|
385 """returns True if status is not finished""" |
|
386 raise NotImplementedError |
|
387 |
|
388 @implements_adapter_compat('IProgress') |
|
389 def progress(self): |
|
390 """returns the % progress of the task item""" |
|
391 try: |
|
392 return 100. * self.done / self.revised_cost |
|
393 except ZeroDivisionError: |
|
394 # total cost is 0 : if everything was estimated, task is completed |
|
395 if self.progress_info().get('notestimated'): |
|
396 return 0. |
|
397 return 100 |
|
398 |
|
399 @implements_adapter_compat('IProgress') |
|
400 def progress_class(self): |
|
401 return '' |
|
402 |
|
403 |
|
404 class IMileStoneAdapter(IProgressAdapter): |
|
405 __regid__ = 'IMileStone' |
|
406 __select__ = implements(IMileStone) # XXX for bw compat, should be abstract |
|
407 |
|
408 parent_type = None # specify main task's type |
|
409 |
|
410 @implements_adapter_compat('IMileStone') |
|
411 def get_main_task(self): |
|
412 """returns the main ITask entity""" |
|
413 raise NotImplementedError |
|
414 |
|
415 @implements_adapter_compat('IMileStone') |
|
416 def initial_prevision_date(self): |
|
417 """returns the initial expected end of the milestone""" |
|
418 raise NotImplementedError |
|
419 |
|
420 @implements_adapter_compat('IMileStone') |
|
421 def eta_date(self): |
|
422 """returns expected date of completion based on what remains |
|
423 to be done |
|
424 """ |
|
425 raise NotImplementedError |
|
426 |
|
427 @implements_adapter_compat('IMileStone') |
|
428 def completion_date(self): |
|
429 """returns date on which the subtask has been completed""" |
|
430 raise NotImplementedError |
|
431 |
|
432 @implements_adapter_compat('IMileStone') |
|
433 def contractors(self): |
|
434 """returns the list of persons supposed to work on this task""" |
|
435 raise NotImplementedError |