1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import collections
20 import contextlib
21
22 from timelinelib.calendar.gregorian.timetype import GregorianTimeType
23 from timelinelib.canvas.data.exceptions import TimelineIOError
24 from timelinelib.canvas.data.immutable import ImmutableDB
25 from timelinelib.canvas.data import Category
26 from timelinelib.canvas.data import Container
27 from timelinelib.canvas.data import Era
28 from timelinelib.canvas.data import Eras
29 from timelinelib.canvas.data import Event
30 from timelinelib.canvas.data import Milestone
31 from timelinelib.canvas.data import Subevent
32 from timelinelib.canvas.data import TimePeriod
33 from timelinelib.canvas.data.transactions import Transactions
34 from timelinelib.general.observer import Observable
35
36
37
38 STATE_CHANGE_CATEGORY = 1
39
40 STATE_CHANGE_ANY = 2
44
46 Observable.__init__(self)
47 self._id_counter = 0
48 self._transactions = Transactions(ImmutableDB())
49 self._transactions.listen_for_any(self._transaction_committed)
50 self.path = ""
51 self.displayed_period = None
52 self._hidden_category_ids = []
53 self.time_type = GregorianTimeType()
54 self.saved_now = self.time_type.now()
55 self.readonly = False
56 self._save_callback = None
57 self._should_lock = False
58 self._current_query = None
59
61 return self._create_wrapper(Category, **kwargs)
62
64 return self._create_wrapper(Milestone, **kwargs)
65
67 return self._create_wrapper(Era, **kwargs)
68
70 return self._create_wrapper(Event, **kwargs)
71
73 return self._create_wrapper(Container, **kwargs)
74
76 return self._create_wrapper(Subevent, **kwargs)
77
79 wrapper = wrapper_class(db=self)
80 if hasattr(wrapper, "time_period") and "time_period" not in kwargs:
81 now = self.time_type.now()
82 kwargs["time_period"] = TimePeriod(now, now)
83 if "container" in kwargs:
84 container = kwargs.pop("container")
85 else:
86 container = None
87 for name, value in kwargs.iteritems():
88 setattr(wrapper, name, value)
89 if container is not None:
90 wrapper.container = container
91 return wrapper
92
94 self._id_counter += 1
95 return self._id_counter
96
98 return self._transactions.new(name)
99
101 self._transactions.clear()
102
104 return self._transactions.status
105
108
110 return self._save_callback is not None
111
113 return self._should_lock
114
116 self._should_lock = should_lock
117
119 self._save_callback = callback
120
122 return self.time_type
123
125 self.time_type = time_type
126 if time_type is not None:
127 self.saved_now = time_type.now()
128
131
135
136 - def search(self, search_string):
137 return _generic_event_search(self.get_all_events(), search_string)
138
143
145 return self._get_events(lambda immutable_event: True)
146
158
160 with self._query() as query:
161 return (
162 self._get_milestones(criteria_fn) +
163 sort_events(self.get_containers() + [
164 query.get_event(id_)
165 for id_, immutable_event
166 in self._transactions.value.events
167 if criteria_fn(immutable_event)
168 ])
169 )
170
172 with self._query() as query:
173 return [
174 query.get_milestone(id_)
175 for id_, immutable_milestone
176 in self._transactions.value.milestones
177 if criteria_fn(immutable_milestone)
178 ]
179
181 if len(self._transactions.value.events) == 0:
182 return None
183 else:
184 id_, immutable_event = min(
185 self._transactions.value.events,
186 key=lambda (id_, immutable_event):
187 immutable_event.time_period.start_time
188 )
189 with self._query() as query:
190 return query.get_event(id_)
191
193 if len(self._transactions.value.events) == 0:
194 return None
195 else:
196 id_, immutable_event = max(
197 self._transactions.value.events,
198 key=lambda (id_, immutable_event):
199 immutable_event.time_period.end_time
200 )
201 with self._query() as query:
202 return query.get_event(id_)
203
212
215
226
228 return self._get_eras().get_all()
229
232
234 with self._query() as query:
235 return Eras(
236 now_func=self.time_type.now,
237 eras=[
238 query.get_era(id_)
239 for id_, immutable_era
240 in self._transactions.value.eras
241 ]
242 )
243
250
257
259 with self._query() as query:
260 return [
261 query.get_category(id_)
262 for id_, immutable_category
263 in self._transactions.value.categories
264 ]
265
267 with self._query() as query:
268 return [
269 query.get_container(id_)
270 for id_, immutable_container
271 in self._transactions.value.containers
272 ]
273
280
282 with self._query() as query:
283 for id_, immutable_category in self._transactions.value.categories:
284 if immutable_category.name == name:
285 return query.get_category(id_)
286 return None
287
292
306
308 return self.saved_now
309
313
315 view_properties.displayed_period = self.displayed_period
316 for cat in self.get_categories():
317 visible = cat.id not in self._hidden_category_ids
318 view_properties.set_category_visible(cat, visible)
319
321 if view_properties.displayed_period is not None:
322 if not view_properties.displayed_period.is_period():
323 raise TimelineIOError(_("Displayed period must be > 0."))
324 self.displayed_period = view_properties.displayed_period
325 self._hidden_category_ids = []
326 for cat in self.get_categories():
327 if not view_properties.is_category_visible(cat):
328 self._hidden_category_ids.append(cat.id)
329 self._save()
330
332 self._place_event(
333 lambda index_to_place, index_target: index_to_place < index_target,
334 event_to_place.id,
335 target_event.id
336 )
337
339 self._place_event(
340 lambda index_to_place, index_target: index_to_place > index_target,
341 event_to_place.id,
342 target_event.id
343 )
344
345 - def _place_event(self, validate_index, id_to_place, id_target):
357
363
364 - def _move(self, events, validate_index, id_to_place, id_target):
365 index_to_place = None
366 index_target = None
367 for index, event in enumerate(events):
368 if event.id == id_to_place:
369 index_to_place = index
370 if event.id == id_target:
371 index_target = index
372 if index_to_place is None:
373 return False
374 if index_target is None:
375 return False
376 if validate_index(index_to_place, index_target):
377 events.insert(index_target, events.pop(index_to_place))
378 return True
379 return False
380
382 index = self._get_undo_index()
383 if index is not None:
384 self._transactions.move(index)
385
387 index = self._get_redo_index()
388 if index is not None:
389 self._transactions.move(index)
390
392 return not self.is_read_only() and self._get_undo_index() is not None
393
395 return not self.is_read_only() and self._get_redo_index() is not None
396
398 index, is_in_transaction, history = self._transactions.status
399 if index > 0:
400 return index - 1
401 else:
402 return None
403
405 index, is_in_transaction, history = self._transactions.status
406 if index < len(history) - 1:
407 return index + 1
408 else:
409 return None
410
414
423
427
429 if self._save_callback is not None:
430 self._save_callback()
431
433 """
434 Inheritors can call this method to get the displayed period used in
435 load_view_properties and save_view_properties.
436 """
437 return self.displayed_period
438
440 """
441 Inheritors can call this method to set the displayed period used in
442 load_view_properties and save_view_properties.
443 """
444 self.displayed_period = period
445
447 with self._query() as query:
448 return [
449 query.get_category(id_)
450 for id_
451 in self._hidden_category_ids
452 ]
453
455 self._hidden_category_ids = []
456 for cat in hidden_categories:
457 if cat.id not in self._transactions.value.categories:
458 raise ValueError("Category '%s' not in db." % cat.get_name())
459 self._hidden_category_ids.append(cat.id)
460
467
481
492
498
500 with self.transaction("Compress events"):
501 self._set_events_order_from_rows(self._place_events_on_rows())
502
507
509 rows = collections.defaultdict(lambda: [])
510 for event in self._length_sort():
511 inx = 0
512 while True:
513 if self._fits_on_row(rows[inx], event):
514 event.r = inx
515 rows[inx].append(event)
516 break
517 inx += 1
518 return rows
519
521 all_events = self.get_all_events()
522 reordered_events = [
523 event
524 for event
525 in all_events
526 if not event.is_subevent()
527 ]
528 reordered_events = self._sort_by_length(reordered_events)
529 return reordered_events
530
532 return sorted(events, key=self._event_length, reverse=True)
533
536
538 for ev in row_events:
539 if ev.overlaps(event):
540 return False
541 return True
542
543 @contextlib.contextmanager
545 need_to_create_query = self._current_query is None
546 if need_to_create_query:
547 self._current_query = Query(self, self._transactions.value)
548 try:
549 yield self._current_query
550 finally:
551 if need_to_create_query:
552 self._current_query = None
553
556
558 self._db = db
559 self._immutable_db = immutable_db
560 self._wrappers = {}
561
564
566 return id_ in self._immutable_db.events
567
570
572 if id_ not in self._wrappers:
573 self._wrappers[id_] = self._create_category_wrapper(id_)
574 return self._wrappers[id_]
575
577 if id_ not in self._wrappers:
578 self._wrappers[id_] = self._create_container_wrapper(id_)
579 self._load_subevents(self._wrappers[id_])
580 return self._wrappers[id_]
581
583 if id_ not in self._wrappers:
584 self._wrappers[id_] = self._create_event_wrapper(id_)
585 immutable_event = self._immutable_db.events.get(id_)
586 if immutable_event.container_id is not None:
587
588 self.get_container(immutable_event.container_id)
589 return self._wrappers[id_]
590
592 if id_ not in self._wrappers:
593 self._wrappers[id_] = self._create_milestone_wrapper(id_)
594 return self._wrappers[id_]
595
597 if id_ not in self._wrappers:
598 self._wrappers[id_] = self._create_era_wrapper(id_)
599 return self._wrappers[id_]
600
605
607 immutable_category = self._immutable_db.categories.get(id_)
608 wrapper = Category(
609 db=self._db,
610 id_=id_,
611 immutable_value=immutable_category,
612 )
613 wrapper.parent = self._get_maybe_category(immutable_category.parent_id)
614 return wrapper
615
617 immutable_container = self._immutable_db.containers.get(id_)
618 wrapper = Container(
619 db=self._db,
620 id_=id_,
621 immutable_value=immutable_container,
622 )
623 wrapper.category = self._get_maybe_category(immutable_container.category_id)
624 return wrapper
625
627 immutable_event = self._immutable_db.events.get(id_)
628 if immutable_event.container_id is None:
629 klass = Event
630 else:
631 klass = Subevent
632 wrapper = klass(
633 db=self._db,
634 id_=id_,
635 immutable_value=immutable_event,
636 )
637 wrapper.category = self._get_maybe_category(immutable_event.category_id)
638 return wrapper
639
641 immutable_milestone = self._immutable_db.milestones.get(id_)
642 wrapper = Milestone(
643 db=self._db,
644 id_=id_,
645 immutable_value=immutable_milestone,
646 )
647 wrapper.category = self._get_maybe_category(immutable_milestone.category_id)
648 return wrapper
649
651 immutable_era = self._immutable_db.eras.get(id_)
652 return Era(
653 db=self._db,
654 id_=id_,
655 immutable_value=immutable_era,
656 )
657
663
679
683
694 def mean_time(event):
695 return event.mean_time()
696 matches = [event for event in events if match(event)]
697 matches.sort(key=mean_time)
698 return matches
699