| 1 | import json | 
|---|
| 2 | from copy import deepcopy | 
|---|
| 3 | from flowtimer.Phase import Phase | 
|---|
| 4 |  | 
|---|
| 5 |  | 
|---|
| 6 | class RecurringPhaseSequence: | 
|---|
| 7 |  | 
|---|
| 8 | @classmethod | 
|---|
| 9 | def from_json(cls, a_json_string): | 
|---|
| 10 | def custom_object_hook(d): | 
|---|
| 11 | if 'title' in d and 'duration' in d: | 
|---|
| 12 | return Phase(d['title'], d['duration']) | 
|---|
| 13 | if 'phase_list' in d and 'repetitions' in d: | 
|---|
| 14 | return RecurringPhaseSequence(d['phase_list'], d['repetitions']) | 
|---|
| 15 | return d | 
|---|
| 16 | return json.loads(a_json_string, object_hook=custom_object_hook) | 
|---|
| 17 |  | 
|---|
| 18 | @classmethod | 
|---|
| 19 | def default_json_string(cls): | 
|---|
| 20 | return json.dumps({"phase_list": [{"title": "Huddle", "duration": 10}, | 
|---|
| 21 | {"title": "Tasking", "duration": 5}, | 
|---|
| 22 | {"title": "Work", "duration": 45}, | 
|---|
| 23 | {"title": "Break", "duration": 15}], | 
|---|
| 24 | "repetitions": 3}) | 
|---|
| 25 |  | 
|---|
| 26 | @classmethod | 
|---|
| 27 | def default(cls): | 
|---|
| 28 | return cls.from_json(cls.default_json_string()) | 
|---|
| 29 |  | 
|---|
| 30 | def __init__(self, phase_list, repetitions): | 
|---|
| 31 | assert repetitions > 0 | 
|---|
| 32 | assert phase_list is not [] | 
|---|
| 33 | self.state = "initial" | 
|---|
| 34 | self.phase_list = phase_list | 
|---|
| 35 | self.current_phase = phase_list[0] | 
|---|
| 36 | self.initial_repetitions = repetitions | 
|---|
| 37 | self.passes_left = repetitions | 
|---|
| 38 |  | 
|---|
| 39 | def to_json(self): | 
|---|
| 40 | return json.dumps(self.__dict__, default=lambda each: each.to_json()) | 
|---|
| 41 |  | 
|---|
| 42 | def upcoming_phases_in_pass(self): | 
|---|
| 43 | index = self.phase_list.index(self.current_phase) | 
|---|
| 44 | if index < len(self.phase_list) - 1: | 
|---|
| 45 | return self.phase_list[index+1:] | 
|---|
| 46 | return [] | 
|---|
| 47 |  | 
|---|
| 48 | def ticks_left(self): | 
|---|
| 49 | return (self.passes_left * sum([each.initial_ticks for each in self.phase_list]) + | 
|---|
| 50 | self.current_phase.ticks_left + | 
|---|
| 51 | sum([each.ticks_left for each in self.upcoming_phases_in_pass()])) | 
|---|
| 52 |  | 
|---|
| 53 | def finished(self): | 
|---|
| 54 | return (self.passes_left < 1) and (not self.upcoming_phases_in_pass()) | 
|---|
| 55 |  | 
|---|
| 56 | def abort(self): | 
|---|
| 57 | self.current_phase.abort() | 
|---|
| 58 | self.state = "finished" | 
|---|
| 59 |  | 
|---|
| 60 | def tick(self, ticks): | 
|---|
| 61 | if not self.finished(): | 
|---|
| 62 | result = self.current_phase.tick(ticks) | 
|---|
| 63 | print("Current phase", self.current_phase) | 
|---|
| 64 | print("Passes left:", self.passes_left) | 
|---|
| 65 | if self.current_phase.finished(): | 
|---|
| 66 | if self.upcoming_phases_in_pass(): | 
|---|
| 67 | self.current_phase.reset() | 
|---|
| 68 | self.current_phase = self.upcoming_phases_in_pass()[0] | 
|---|
| 69 | self.current_phase.start() | 
|---|
| 70 | print("New phase:", self.current_phase) | 
|---|
| 71 | self.tick(abs(result)) | 
|---|
| 72 | return True | 
|---|
| 73 | self.passes_left -= 1 | 
|---|
| 74 | if self.finished(): | 
|---|
| 75 | self.state = "finished" | 
|---|
| 76 | else: | 
|---|
| 77 | self.current_phase.reset() | 
|---|
| 78 | self.current_phase = self.phase_list[0] | 
|---|
| 79 | return True | 
|---|
| 80 |  | 
|---|
| 81 | def unrolled(self): | 
|---|
| 82 | return [deepcopy(seq) for seq in [each for each in self.repetitions * self.phase_list]] | 
|---|