diff options
Diffstat (limited to 'src/jarabe/journal/volumestoolbar.py')
-rw-r--r-- | src/jarabe/journal/volumestoolbar.py | 153 |
1 files changed, 151 insertions, 2 deletions
diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py index b21832e..9a49cdf 100644 --- a/src/jarabe/journal/volumestoolbar.py +++ b/src/jarabe/journal/volumestoolbar.py @@ -15,7 +15,13 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import logging +import os from gettext import gettext as _ +import cPickle +import xapian +import json +import tempfile +import shutil import gobject import gio @@ -29,13 +35,126 @@ from sugar.graphics.xocolor import XoColor from jarabe.journal import model from jarabe.view.palettes import VolumePalette +_JOURNAL_0_METADATA_DIR = '.olpc.store' + +def _get_id(document): + """Get the ID for the document in the xapian database.""" + tl = document.termlist() + try: + term = tl.skip_to('Q').term + if len(term) == 0 or term[0] != 'Q': + return None + return term[1:] + except StopIteration: + return None + +def _convert_entries(root): + """Converts the entries written by the datastore version 0. + The metadata and the preview will be written using the new + scheme for writing Journal entries to removable storage + devices. + + - entries that do not have an associated file are not + converted. + - if an entry has no title we set it to Untitled and rename + the file accordingly, taking care of creating a unique + filename + + """ + try: + database = xapian.Database(os.path.join(root, _JOURNAL_0_METADATA_DIR, + 'index')) + except xapian.DatabaseError, e: + logging.error('Convert DS-0 Journal entry. Error reading db: %s', + os.path.join(root, _JOURNAL_0_METADATA_DIR, 'index')) + return + + metadata_dir_path = os.path.join(root, model.JOURNAL_METADATA_DIR) + if not os.path.exists(metadata_dir_path): + os.mkdir(metadata_dir_path) + + for i in range(1, database.get_lastdocid() + 1): + try: + document = database.get_document(i) + except xapian.DocNotFoundError, e: + logging.debug('Convert DS-0 Journal entry. ' \ + 'Error getting document %s: %s', i, e) + continue + + try: + metadata_loaded = cPickle.loads(document.get_data()) + except cPickle.PickleError, e: + logging.debug('Convert DS-0 Journal entry. ' \ + 'Error converting metadata: %s', e) + continue + + if 'activity_id' in metadata_loaded and \ + 'mime_type' in metadata_loaded and \ + 'title' in metadata_loaded: + metadata = {} + + uid = _get_id(document) + if uid is None: + continue + + for key, value in metadata_loaded.items(): + metadata[str(key)] = str(value[0]) + + if 'uid' not in metadata: + metadata['uid'] = uid + + if 'filename' in metadata: + filename = metadata['filename'] + else: + continue + if not os.path.exists(os.path.join(root, filename)): + continue + + if metadata['title'] == '': + metadata['title'] = _('Untitled') + fn = model.get_file_name(metadata['title'], + metadata['mime_type']) + new_filename = model.get_unique_file_name(root, fn) + metadata['filename'] = new_filename + os.rename(os.path.join(root, filename), + os.path.join(root, new_filename)) + filename = new_filename + + preview_path = os.path.join(root, _JOURNAL_0_METADATA_DIR, + 'preview', uid) + if os.path.exists(preview_path): + preview_fname = filename + '.preview' + new_preview_path = os.path.join(root, + model.JOURNAL_METADATA_DIR, + preview_fname) + if not os.path.exists(new_preview_path): + metadata['preview'] = preview_fname + shutil.copy(preview_path, new_preview_path) + + metadata_fname = filename + '.metadata' + metadata_path = os.path.join(root, model.JOURNAL_METADATA_DIR, + metadata_fname) + if not os.path.exists(metadata_path): + (fh, fn) = tempfile.mkstemp(dir=root) + os.write(fh, json.dumps(metadata)) + os.close(fh) + os.rename(fn, metadata_path) + + logging.debug('Convert DS-0 Journal entry. Entry converted: ' \ + 'File=%s Metadata=%s', + os.path.join(root, filename), metadata) + + class VolumesToolbar(gtk.Toolbar): __gtype_name__ = 'VolumesToolbar' __gsignals__ = { 'volume-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str])) + ([str])), + 'volume-error': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([str, str])) } def __init__(self): @@ -78,9 +197,15 @@ class VolumesToolbar(gtk.Toolbar): def _add_button(self, mount): logging.debug('VolumeToolbar._add_button: %r' % mount.get_name()) + if os.path.exists(os.path.join(mount.get_root().get_path(), + _JOURNAL_0_METADATA_DIR)): + logging.debug('Convert DS-0 Journal entries.') + gobject.idle_add(_convert_entries, mount.get_root().get_path()) + button = VolumeButton(mount) button.props.group = self._volume_buttons[0] button.connect('toggled', self._button_toggled_cb) + button.connect('volume-error', self.__volume_error_cb) position = self.get_item_index(self._volume_buttons[-1]) + 1 self.insert(button, position) button.show() @@ -90,6 +215,9 @@ class VolumesToolbar(gtk.Toolbar): if len(self.get_children()) > 1: self.show() + def __volume_error_cb(self, button, strerror, severity): + self.emit('volume-error', strerror, severity) + def _button_toggled_cb(self, button): if button.props.active: self.emit('volume-changed', button.mount_point) @@ -123,6 +251,12 @@ class VolumesToolbar(gtk.Toolbar): button.props.active = True class BaseButton(RadioToolButton): + __gsignals__ = { + 'volume-error': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([str, str])) + } + def __init__(self, mount_point): RadioToolButton.__init__(self) @@ -137,7 +271,22 @@ class BaseButton(RadioToolButton): info, timestamp): object_id = selection_data.data metadata = model.get(object_id) - model.copy(metadata, self.mount_point) + file_path = model.get_file(metadata['uid']) + + if not file_path or not os.path.exists(file_path): + logging.warn('Entries without a file cannot be copied.') + self.emit('volume-error', + _('Entries without a file cannot be copied.'), + _('Warning')) + return + + try: + model.copy(metadata, self.mount_point) + except (IOError, OSError), e: + logging.exception('Error while copying the entry. %s', e.strerror) + self.emit('volume-error', + _('Error while copying the entry. %s') % e.strerror, + _('Error')) class VolumeButton(BaseButton): def __init__(self, mount): |