Package Gnumed :: Package timelinelib :: Package db :: Package backends :: Module memory
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.db.backends.memory

  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  """ 
 20  Implementation of timeline database that stores all data in memory. 
 21   
 22  MemoryDB can be used as a base class for other timeline databases that wish to 
 23  store all data in memory and also want to save the data to persistent storage 
 24  whenever it changes in memory. Initially data can be read from persistent 
 25  storage into memory. 
 26   
 27  MemoryDB is not suitable as a base class for timeline databases that need to 
 28  query persistent storage to retrieve data. 
 29  """ 
 30   
 31   
 32  from timelinelib.db.exceptions import TimelineIOError 
 33  from timelinelib.db.objects import Category 
 34  from timelinelib.db.objects import Container 
 35  from timelinelib.db.objects import Event 
 36  from timelinelib.db.observer import Observable 
 37  from timelinelib.db.observer import STATE_CHANGE_ANY 
 38  from timelinelib.db.observer import STATE_CHANGE_CATEGORY 
 39  from timelinelib.db.search import generic_event_search 
 40  from timelinelib.db.utils import IdCounter 
 41   
 42   
43 -class MemoryDB(Observable):
44
45 - def __init__(self):
46 Observable.__init__(self) 47 self.path = "" 48 self.categories = [] 49 self.category_id_counter = IdCounter() 50 self.events = [] 51 self.event_id_counter = IdCounter() 52 self.displayed_period = None 53 self.hidden_categories = [] 54 self.save_disabled = False 55 from timelinelib.time.pytime import PyTimeType 56 self.time_type = PyTimeType()
57
58 - def get_time_type(self):
59 return self.time_type
60
61 - def is_read_only(self):
62 return False
63
64 - def supported_event_data(self):
65 return ["description", "icon", "alert", "hyperlink"]
66
67 - def search(self, search_string):
68 return generic_event_search(self.events, search_string)
69
70 - def get_events(self, time_period):
71 def include_event(event): 72 if not event.inside_period(time_period): 73 return False 74 return True
75 return [e for e in self.events if include_event(e)]
76
77 - def get_all_events(self):
78 return list(self.events)
79
80 - def get_first_event(self):
81 if len(self.events) == 0: 82 return None 83 e = min(self.events, key=lambda e: e.time_period.start_time) 84 return e
85
86 - def get_last_event(self):
87 if len(self.events) == 0: 88 return None 89 e = max(self.events, key=lambda e: e.time_period.end_time) 90 return e
91
92 - def save_event(self, event):
93 if (event.category is not None and 94 event.category not in self.categories): 95 raise TimelineIOError("Event's category not in db.") 96 if event not in self.events: 97 if event.has_id(): 98 raise TimelineIOError("Event with id %s not found in db." % 99 event.id) 100 self.events.append(event) 101 event.set_id(self.event_id_counter.get_next()) 102 if event.is_subevent(): 103 self._register_subevent(event) 104 self._save_if_not_disabled() 105 self._notify(STATE_CHANGE_ANY)
106
107 - def _register_subevent(self, subevent):
108 container_events = [event for event in self.events 109 if event.is_container()] 110 containers = {} 111 for container in container_events: 112 key = container.cid() 113 containers[key] = container 114 try: 115 container = containers[subevent.cid()] 116 container.register_subevent(subevent) 117 except: 118 id = subevent.cid() 119 if id == 0: 120 id = self._get_max_container_id(container_events) + 1 121 subevent.set_cid(id) 122 name = "[%d]Container" % id 123 container = Container(subevent.time_type, 124 subevent.time_period.start_time, 125 subevent.time_period.end_time, name) 126 self.save_event(container) 127 self._register_subevent(subevent) 128 pass
129
130 - def _get_max_container_id(self, container_events):
131 id = 0 132 for event in container_events: 133 if id < event.cid(): 134 id = event.cid() 135 return id
136
137 - def _unregister_subevent(self, subevent):
138 container_events = [event for event in self.events 139 if event.is_container()] 140 containers = {} 141 for container in container_events: 142 containers[container.cid()] = container 143 try: 144 container = containers[subevent.cid()] 145 container.unregister_subevent(subevent) 146 if len(container.events) == 0: 147 self.events.remove(container) 148 except: 149 pass
150
151 - def delete_event(self, event_or_id):
152 if isinstance(event_or_id, Event): 153 event = event_or_id 154 else: 155 event = self.find_event_with_id(event_or_id) 156 if event in self.events: 157 if event.is_subevent(): 158 self._unregister_subevent(event) 159 if event.is_container(): 160 for subevent in event.events: 161 self.events.remove(subevent) 162 self.events.remove(event) 163 event.set_id(None) 164 self._save_if_not_disabled() 165 self._notify(STATE_CHANGE_ANY) 166 else: 167 raise TimelineIOError("Event not in db.")
168
169 - def get_categories(self):
170 return list(self.categories)
171
172 - def get_containers(self):
173 containers = [event for event in self.events 174 if event.is_container()] 175 return containers
176
177 - def save_category(self, category):
178 if (category.parent is not None and 179 category.parent not in self.categories): 180 raise TimelineIOError("Parent category not in db.") 181 self._ensure_no_circular_parent(category) 182 if not category in self.categories: 183 if category.has_id(): 184 raise TimelineIOError("Category with id %s not found in db." % 185 category.id) 186 self.categories.append(category) 187 category.set_id(self.event_id_counter.get_next()) 188 self._save_if_not_disabled() 189 self._notify(STATE_CHANGE_CATEGORY)
190
191 - def delete_category(self, category_or_id):
192 if isinstance(category_or_id, Category): 193 category = category_or_id 194 else: 195 category = self._find_category_with_id(category_or_id) 196 if category in self.categories: 197 if category in self.hidden_categories: 198 self.hidden_categories.remove(category) 199 self.categories.remove(category) 200 category.set_id(None) 201 # Loop to update parent attribute on children 202 for cat in self.categories: 203 if cat.parent == category: 204 cat.parent = category.parent 205 # Loop to update category for events 206 for event in self.events: 207 if event.category == category: 208 event.category = category.parent 209 self._save_if_not_disabled() 210 self._notify(STATE_CHANGE_CATEGORY) 211 else: 212 raise TimelineIOError("Category not in db.")
213
214 - def load_view_properties(self, view_properties):
215 view_properties.displayed_period = self.displayed_period 216 for cat in self.categories: 217 visible = cat not in self.hidden_categories 218 view_properties.set_category_visible(cat, visible)
219
220 - def save_view_properties(self, view_properties):
221 if view_properties.displayed_period is not None: 222 if not view_properties.displayed_period.is_period(): 223 raise TimelineIOError(_("Displayed period must be > 0.")) 224 self.displayed_period = view_properties.displayed_period 225 self.hidden_categories = [] 226 for cat in self.categories: 227 if not view_properties.category_visible(cat): 228 self.hidden_categories.append(cat) 229 self._save_if_not_disabled()
230
231 - def disable_save(self):
232 self.save_disabled = True
233
234 - def enable_save(self, call_save=True):
235 if self.save_disabled == True: 236 self.save_disabled = False 237 if call_save == True: 238 self._save_if_not_disabled()
239
240 - def place_event_after_event(self, event_to_place, target_event):
241 if (event_to_place == target_event): 242 return 243 self.events.remove(event_to_place) 244 new_index = self.events.index(target_event) + 1 245 self.events.insert(new_index, event_to_place)
246
247 - def place_event_before_event(self, event_to_place, target_event):
248 if (event_to_place == target_event): 249 return 250 self.events.remove(event_to_place) 251 new_index = self.events.index(target_event) 252 self.events.insert(new_index, event_to_place)
253
254 - def _ensure_no_circular_parent(self, cat):
255 parent = cat.parent 256 while parent is not None: 257 if parent == cat: 258 raise TimelineIOError("Circular category parent.") 259 else: 260 parent = parent.parent
261
262 - def find_event_with_id(self, id):
263 for e in self.events: 264 if e.id == id: 265 return e 266 return None
267
268 - def _find_category_with_id(self, id):
269 for c in self.categories: 270 if c.id == id: 271 return c 272 return None
273
274 - def _save_if_not_disabled(self):
275 if self.save_disabled == False: 276 self._save()
277
278 - def _get_displayed_period(self):
279 """ 280 Inheritors can call this method to get the displayed period used in 281 load_view_properties and save_view_properties. 282 """ 283 return self.displayed_period
284
285 - def _set_displayed_period(self, period):
286 """ 287 Inheritors can call this method to set the displayed period used in 288 load_view_properties and save_view_properties. 289 """ 290 self.displayed_period = period
291
292 - def _get_hidden_categories(self):
293 """ 294 Inheritors can call this method to get the hidden categories used in 295 load_view_properties and save_view_properties. 296 """ 297 return self.hidden_categories
298
299 - def _set_hidden_categories(self, hidden_categories):
300 """ 301 Inheritors can call this method to set the hidden categories used in 302 load_view_properties and save_view_properties. 303 """ 304 self.hidden_categories = [] 305 for cat in hidden_categories: 306 if cat not in self.categories: 307 raise ValueError("Category '%s' not in db." % cat.name) 308 self.hidden_categories.append(cat)
309
310 - def _save(self):
311 """ 312 Inheritors can override this method to save this db to persistent 313 storage. 314 315 Called whenever this db changes. 316 """ 317 pass
318