Package Gnumed :: Package timelinelib :: Package db :: Module strategies |
[frames] | no frames]

# Source Code for Module Gnumed.timelinelib.db.strategies

      1  # Copyright (C) 2009, 2010, 2011  Rickard Lindberg, Roger Lindberg
      2  #
      3  # This file is part of Timeline.
      4  #
      5  # Timeline is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Timeline is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>.
     17
     18
     19  from timelinelib.db.interface import ContainerStrategy
     20
     21
     22 -class DefaultContainerStrategy(ContainerStrategy):
     23
     24 -    def __init__(self, container):
     25          ContainerStrategy.__init__(self, container)
     26
     27 -    def register_subevent(self, subevent):
     28          if subevent not in self.container.events:
     29              self.container.events.append(subevent)
     30              subevent.register_container(self.container)
     31              if len(self.container.events) == 1:
     32                  self._set_time_period()
     33              else:
     34                  self._adjust_time_period(subevent)
     35
     36 -    def unregister_subevent(self, subevent):
     37          if subevent not in self.container.events:
     38              return
     39          self.container.events.remove(subevent)
     40          self._set_time_period()
     41
     42 -    def update(self, subevent):
     43          self.unregister_subevent(subevent)
     44          self.register_subevent(subevent)
     45          self._set_time_period()
     46
     47 -    def _set_time_period(self):
     48          """
     49          The container time period starts where the subevent with the earliest
     50          start time, starts, and it ends where the subevent whith the latest end
     51          time ends.
     52             Subevents   +------+  +--------+    +--+
     53             Container   +--------------------------+
     54          """
     55          if len(self.container.events) == 0:
     56              return
     57          self._set_start_time(self.container.events[0])
     58          self._set_end_time(self.container.events[0])
     59          for event in self.container.events:
     60              if self._container_starts_after_event(event):
     61                  self._set_start_time(event)
     62              if self._container_ends_before_event(event):
     63                  self._set_end_time(event)
     64
     65 -    def _container_starts_after_event(self, subevent):
     66          return (self.container.time_period.start_time >
     67                  subevent.time_period.start_time)
     68
     69 -    def _container_ends_before_event(self, event):
     70          return (self.container.time_period.end_time <
     71                  event.time_period.end_time)
     72
     73 -    def _set_start_time(self, event):
     74          self.container.time_period.start_time = event.time_period.start_time
     75
     76 -    def _set_end_time(self, event):
     77          self.container.time_period.end_time = event.time_period.end_time
     78
     79 -    def _adjust_time_period(self, new_event):
     80          """
     81          If the event to be added to the container overlaps any other
     82          event in the container or if the new event is outside of the
     83          container time period the container time period must be adjusted.
     84          """
     85          event = self._event_totally_overlapping_new_event(new_event)
     86          if event is not None:
     87              self._adjust_when_new_event_is_totally_overlapped(new_event, event)
     88          else:
     89              events = self._events_overlapped_by_new_event(new_event)
     90              if len(events) > 0:
     91                  self._adjust_when_new_event_partially_overlaps_other_events(new_event, events)
     92          self._set_time_period()
     93
     94 -    def _adjust_when_new_event_is_totally_overlapped(self, new_event, event):
     95          # Situation:
     96          #    event:       +--------------------------------------------+
     97          #    new_event:                  +-------------+
     98          #                 |- left_delta -|             |- right_delta -|
     99          #
    100          #      or         +----------+
    101          #      or                                  +-------------------+
    102          #      or         +--------------------------------------------+
    103          #      or                      +
    104          #      or         +
    105          #      or                                                      +
    106          left_delta = new_event.time_period.start_time - event.time_period.start_time
    107          right_delta = event.time_period.end_time - new_event.time_period.end_time
    108          move_left = left_delta > right_delta
    109          if move_left:
    110              self._move_events_left(new_event, event)
    111          else:
    112              self._move_events_right(new_event, event)
    113
    114 -    def _move_events_left(self, new_event, event):
    115          delta = event.time_period.end_time - new_event.time_period.start_time
    116          latest_start_time = event.time_period.start_time
    117          self._move_early_events_left(new_event, latest_start_time, delta)
    118
    119 -    def _move_events_right(self, new_event, event):
    120          delta = new_event.time_period.end_time - event.time_period.start_time
    121          earliest_start_time = event.time_period.start_time
    122          self._move_late_events_right(new_event, earliest_start_time, delta)
    123
    124 -    def _adjust_when_new_event_partially_overlaps_other_events(self, new_event, events):
    125          # Situation:
    126          #                                 V = threshold_time
    127          #                            |-td-|
    128          #    new_event:              +----------------------+
    129          #    events:                 +-------------+
    130          #      or              +-------------+
    131          #      or                        +-------------+
    132          #      or                             +-------------+
    133          #      or                                      +-------------+
    134          threshold_time = self._calc_threshold_time(new_event)
    135          event = self._some_event_in_new_event_threshold_time(new_event,
    136                                                               events,
    137                                                               threshold_time)
    138          if event is not None:
    139              self._adjust_threshold_triggered_events(new_event, event, threshold_time)
    140          earliest_start = self._earliest_start_time_for_event_that_starts_within_new_event(new_event,
    141                                                                                            events,
    142                                                                                            threshold_time)
    143          if earliest_start is not None:
    144              self._adjust_events_starting_in_new_event(new_event, earliest_start)
    145
    146 -    def _calc_threshold_time(self, new_event):
    147          td = new_event.time_span()
    148          td = new_event.time_type.mult_timedelta(td, 0.2)
    149          threshold_time = new_event.time_period.start_time + td
    150          return threshold_time
    151
    152 -    def _some_event_in_new_event_threshold_time(self, new_event, events, end):
    153          start = new_event.time_period.start_time
    154          for event in events:
    155              if event == new_event:
    156                  continue
    157              if event.time_period.end_time >= start and event.time_period.end_time <= end:
    158                  return event
    159              if event.time_period.start_time <= start and event.time_period.end_time > end:
    160                  return event
    161          return None
    162
    163 -    def _adjust_threshold_triggered_events(self, new_event, event, threshold_time):
    164          delta = event.time_period.end_time - new_event.time_period.start_time
    165          self._move_early_events_left(new_event, threshold_time, delta)
    166
    167 -    def _earliest_start_time_for_event_that_starts_within_new_event(self, new_event, events, thr):
    168          start = new_event.time_period.start_time
    169          end = new_event.time_period.end_time
    170          min_start = None
    171          for event in events:
    172              if not event.is_period():
    173                  if event.time_period.start_time < thr:
    174                      continue
    175              if event.time_period.start_time >= start and event.time_period.start_time <= end:
    176                  if min_start == None:
    177                      min_start = event.time_period.start_time
    178                  else:
    179                      if event.time_period.start_time < min_start:
    180                          min_start = event.time_period.start_time
    181          return min_start
    182
    183 -    def _adjust_events_starting_in_new_event(self, new_event, earliest_start):
    184          delta = new_event.time_period.end_time - earliest_start
    185          self._move_late_events_right(new_event, earliest_start, delta)
    186
    187 -    def _event_totally_overlapping_new_event(self, new_event):
    188          for event in self.container.events:
    189              if event == new_event:
    190                  continue
    191              if (self._event_totally_overlaps_new_event(new_event, event)):
    192                  return event
    193          return None
    194
    195 -    def _event_totally_overlaps_new_event(self, new_event, event):
    196          return (event.time_period.start_time <= new_event.time_period.start_time and
    197                  event.time_period.end_time >= new_event.time_period.end_time)
    198
    199 -    def _events_overlapped_by_new_event(self, new_event):
    200          overlapping_events = []
    201          for event in self.container.events:
    202              if event != new_event:
    203                  if (self._starts_within(event, new_event) or
    204                      self._ends_within(event, new_event)):
    205                      overlapping_events.append(event)
    206          return overlapping_events
    207
    208 -    def _starts_within(self, event, new_event):
    209          s1 = event.time_period.start_time >= new_event.time_period.start_time
    210          s2 = event.time_period.start_time <= new_event.time_period.end_time
    211          return (s1 and s2)
    212
    213 -    def _ends_within(self, event, new_event):
    214          s1 = event.time_period.end_time >= new_event.time_period.start_time
    215          s2 = event.time_period.end_time <= new_event.time_period.end_time
    216          return (s1 and s2)
    217
    218 -    def _move_early_events_left(self, new_event, latest_start_time, delta):
    219          delta = -delta
    220          for event in self.container.events:
    221              if event == new_event:
    222                  continue
    223              if event.time_period.start_time <= latest_start_time:
    224                  self._adjust_event_time_period(event, delta)
    225
    226 -    def _move_late_events_right(self, new_event, earliest_start_time, delta):
    227          for event in self.container.events:
    228              if event == new_event:
    229                  continue
    230              if event.time_period.start_time >= earliest_start_time:
    231                  self._adjust_event_time_period(event, delta)
    232
    233 -    def _adjust_event_time_period(self, event, delta):
    234          new_start = event.time_period.start_time + delta
    235          new_end = event.time_period.end_time + delta
    236          event.time_period.start_time = new_start
    237          event.time_period.end_time = new_end
    238