def save_event(self, event): if (event.category is not None and event.category not in self.categories): raise TimelineIOError("Event's category not in db.") if event not in self.events: if event.has_id(): raise TimelineIOError("Event with id %s not found in db." % event.id) self.events.append(event) event.set_id(self.event_id_counter.get_next()) if event.is_subevent(): self._register_subevent(event) self._save_if_not_disabled() self._notify(STATE_CHANGE_ANY)
def _ensure_no_circular_parent(self, cat): parent = cat.parent while parent is not None: if parent == cat: raise TimelineIOError("Circular category parent.") else: parent = parent.parent
def db_open(path, timetype=None): """ Create timeline database that can read and write timeline data from and to persistent storage identified by path. Throw a TimelineIOError exception if not able to read from the given path. Valid values for path: - special string ":tutorial:" - string with suffix .timeline - string with suffix .ics - string denoting a directory """ if path == ":tutorial:": return open_tutorial_timeline(path) elif os.path.isdir(path): return open_directory_timeline(path) elif path.endswith(".timeline"): return db_open_timeline(path, timetype) elif path.endswith(".ics"): return db_open_ics(path) else: msg_template = (_("Unable to open timeline '%s'.") + "\n\n" + _("Unknown format.")) raise TimelineIOError(msg_template % path)
def _load(self): """ Load timeline data from the file that this timeline points to. This should only be done once when this class is created. The data is stored internally until we do a save. If a read error occurs a TimelineIOError will be raised. """ if not os.path.exists(self.path): # Nothing to load. Will create a new timeline on save. return try: # _parse_version will create the rest of the schema dynamically partial_schema = Tag("timeline", SINGLE, None, [Tag("version", SINGLE, self._parse_version)]) tmp_dict = { "partial_schema": partial_schema, "category_map": {}, "hidden_categories": [], } self.disable_save() parse(self.path, partial_schema, tmp_dict) self.enable_save(call_save=False) except Exception, e: msg = _("Unable to read timeline data from '%s'.") whole_msg = (msg + "\n\n%s") % (abspath(self.path), ex_msg(e)) raise TimelineIOError(whole_msg)
def _load(db, dir_path): """ Load timeline data from the given directory. Each filename inside the directory (at any level) becomes an event where the text is the filename name and the time is the modification time for the filename. For each sub-directory a category is created and all events (files) belong the category (directory) in which they are. """ if not os.path.exists(dir_path): # Nothing to load return if not os.path.isdir(dir_path): # Nothing to load return try: db.disable_save() color_ranges = {} # Used to color categories color_ranges[dir_path] = (0.0, 1.0, 1.0) all_cats = [] parents = {} for (dirpath, dirnames, filenames) in os.walk(dir_path): # Assign color ranges (rstart, rend, b) = color_ranges[dirpath] step = (rend - rstart) / (len(dirnames) + 1) next_start = rstart + step new_b = b - 0.2 if new_b < 0: new_b = 0 for dirname in dirnames: next_end = next_start + step color_ranges[os.path.join(dirpath, dirname)] = (next_start, next_end, new_b) next_start = next_end # Create the stuff p = parents.get(os.path.normpath(os.path.join(dirpath, "..")), None) cat = Category(dirpath, (233, 233, 233), None, parent=p) parents[os.path.normpath(dirpath)] = cat all_cats.append(cat) db.save_category(cat) for filename in filenames: path_inner = os.path.join(dirpath, filename) evt = _event_from_path(db, path_inner) db.save_event(evt) # Hide all categories but the first db.set_hidden_categories(all_cats[1:]) # Set colors and change names for cat in db.get_categories(): cat.color = _color_from_range(color_ranges[cat.name]) cat.name = os.path.basename(cat.name) db.save_category(cat) except Exception, e: msg = _("Unable to read from filename '%s'.") % dir_path whole_msg = "%s\n\n%s" % (msg, e) raise TimelineIOError(whole_msg)
def convert_to_datetime(d): if isinstance(d, datetime): return Gregorian(d.year, d.month, d.day, d.hour, d.minute, d.second).to_time() elif isinstance(d, date): return gregorian.from_date(d.year, d.month, d.day).to_time() else: raise TimelineIOError("Unknown date.")
def db_open_timeline(path, timetype=None): if (os.path.exists(path) and file_starts_with(path, "# Written by Timeline ")): raise TimelineIOError( _("You are trying to open an old file with a new version of timeline. Please install version 0.21.1 of timeline to convert it to the new format." )) else: return db_open_newtype_timeline(path, timetype)
def save_view_properties(self, view_properties): if view_properties.displayed_period is not None: if not view_properties.displayed_period.is_period(): raise TimelineIOError(_("Displayed period must be > 0.")) self.displayed_period = view_properties.displayed_period self.hidden_categories = [] for cat in self.categories: if not view_properties.is_category_visible(cat): self.hidden_categories.append(cat) self._save_if_not_disabled()
def read_first_line(path): try: f = open(path) try: line = f.readline() return line finally: f.close() except IOError: raise TimelineIOError("Unable to read data from '%s'." % path)
def db_open_ics(path): try: import icalendar except ImportError: raise TimelineIOError( _("Could not find iCalendar Python package. It is required for working with ICS files." )) else: from timelinelib.dataimport.ics import import_db_from_ics return import_db_from_ics(path)
def import_timeline(self, path): try: ics_file = open(path, "rb") try: file_contents = ics_file.read() try: cal = Calendar.from_ical(file_contents) for event in cal.walk("VEVENT"): event["timeline_id"] = self.event_id_counter.get_next() self.cals.append(cal) except Exception, pe: msg1 = _("Unable to read timeline data from '%s'.") msg2 = "\n\n" + ex_msg(pe) raise TimelineIOError((msg1 % abspath(path)) + msg2) finally: ics_file.close() except IOError, e: msg = _("Unable to read from file '%s'.") whole_msg = (msg + "\n\n%s") % (abspath(self.path), e) raise TimelineIOError(whole_msg)
def save_category(self, category): if (category.parent is not None and category.parent not in self.categories): raise TimelineIOError("Parent category not in db.") self._ensure_no_circular_parent(category) if not category in self.categories: if self.importing: if not self._category_name_exists(category): self._append_category(category) else: self._append_category(category) self._save_if_not_disabled() self._notify(STATE_CHANGE_CATEGORY)
def delete_event(self, event_or_id): if isinstance(event_or_id, Event): event = event_or_id else: event = self.find_event_with_id(event_or_id) if event in self.events: if event.is_subevent(): self._unregister_subevent(event) if event.is_container(): for subevent in event.events: self.events.remove(subevent) self.events.remove(event) event.set_id(None) self._save_if_not_disabled() self._notify(STATE_CHANGE_ANY) else: raise TimelineIOError("Event not in db.")
def _load(self): try: # _parse_version will create the rest of the schema dynamically partial_schema = Tag("timeline", SINGLE, None, [Tag("version", SINGLE, self._parse_version)]) tmp_dict = { "partial_schema": partial_schema, "category_map": {}, "hidden_categories": [], } self.db.disable_save() parse(self.path, partial_schema, tmp_dict) self.db.enable_save(call_save=False) except Exception, e: msg = _("Unable to read timeline data from '%s'.") whole_msg = (msg + "\n\n%s") % (abspath(self.path), ex_msg(e)) raise TimelineIOError(whole_msg)
def db_open_ics(path, import_timeline=False): global current_timeline try: import icalendar except ImportError: raise TimelineIOError( _("Could not find iCalendar Python package. It is required for working with ICS files. See the Timeline website or the doc/installing.rst file for instructions how to install it." )) else: from timelinelib.db.backends.ics import IcsTimeline if import_timeline and current_timeline: extension = current_timeline.path.rsplit(".", 1)[1] if extension != "ics": display_warning_message( _("Only %s files can be imported") % extension) return current_timeline current_timeline.import_timeline(path) else: current_timeline = IcsTimeline(path) return current_timeline
def delete_category(self, category_or_id): if isinstance(category_or_id, Category): category = category_or_id else: category = self._find_category_with_id(category_or_id) if category in self.categories: if category in self.hidden_categories: self.hidden_categories.remove(category) self.categories.remove(category) category.set_id(None) # Loop to update parent attribute on children for cat in self.categories: if cat.parent == category: cat.parent = category.parent # Loop to update category for events for event in self.events: if event.category == category: event.category = category.parent self._save_if_not_disabled() self._notify(STATE_CHANGE_CATEGORY) else: raise TimelineIOError("Category not in db.")
def _append_category(self, category): if category.has_id(): raise TimelineIOError("Category with id %s not found in db." % category.id) self.categories.append(category) category.set_id(self.event_id_counter.get_next())
def raise_error(specific_msg, cause_exception): err_general = _("Unable to save timeline data to '%s'. File left unmodified.") % path err_template = "%s\n\n%%s\n\n%%s" % err_general raise TimelineIOError(err_template % (specific_msg, cause_exception))
def test_handles_open_timeline_failure(self): error = TimelineIOError("") self.given_opening_fails_with_error(error) self.when_timeline_is_opened() self.main_frame.handle_db_error.assert_called_with(error)