Attachments you submit will be routed for moderation. If you have an account, please log in first.

Ticket #1636: 0001-Journal-entry-sharing.patch

File 0001-Journal-entry-sharing.patch, 10.7 KB (added by erikos, 2 years ago)

Sharing/Backup Journal entries using a storage device (branch master)

  • src/jarabe/journal/model.py

    From f6dd32244133525a0966662f1b13deef0367a412 Mon Sep 17 00:00:00 2001
    From: Simon Schampijer <simon@schampijer.de>
    Date: Tue, 8 Feb 2011 12:18:59 -0500
    Subject: [PATCH] Journal entry sharing
    
    http://wiki.sugarlabs.org/go/Features/Journal_Entry_Sharing
    ---
     src/jarabe/journal/model.py |  182 +++++++++++++++++++++++++++++++++++++++----
     1 files changed, 166 insertions(+), 16 deletions(-)
    
    diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
    index 320e577..4228768 100644
    a b  
    2020from datetime import datetime 
    2121import time 
    2222import shutil 
     23import tempfile 
    2324from stat import S_IFLNK, S_IFMT, S_IFDIR, S_IFREG 
    2425import re 
    2526from operator import itemgetter 
     27import json 
     28from gettext import gettext as _ 
    2629 
    2730import gobject 
    2831import dbus 
    29 import gconf 
    3032import gio 
    3133 
    3234from sugar import dispatch 
     
    4648MIN_PAGES_TO_CACHE = 3 
    4749MAX_PAGES_TO_CACHE = 5 
    4850 
     51JOURNAL_METADATA_DIR = '.Sugar-Metadata' 
     52 
    4953_datastore = None 
    5054created = dispatch.Signal() 
    5155updated = dispatch.Signal() 
     
    295299        files = self._file_list[offset:offset + limit] 
    296300 
    297301        entries = [] 
    298         for file_path, stat, mtime_, size_ in files: 
    299             metadata = _get_file_metadata(file_path, stat) 
     302        for file_path, stat, mtime_, metadata, size_ in files: 
     303            if metadata is None: 
     304                metadata = _get_file_metadata(file_path, stat) 
    300305            metadata['mountpoint'] = self._mount_point 
    301306            entries.append(metadata) 
    302307 
     
    324329 
    325330    def _scan_a_file(self): 
    326331        full_path = self._pending_files.pop(0) 
     332        metadata = None 
    327333 
    328334        try: 
    329335            stat = os.lstat(full_path) 
     
    365371 
    366372        if self._regex is not None and \ 
    367373                not self._regex.match(full_path): 
    368             return 
     374            filename = os.path.basename(full_path) 
     375            dir_path = os.path.dirname(full_path) 
     376            metadata = _get_file_metadata_from_json( \ 
     377                dir_path, filename, preview=False) 
     378            add_to_list = False 
     379            if metadata is not None: 
     380                for f in ['fulltext', 'title', 
     381                          'description', 'tags']: 
     382                    if f in metadata and \ 
     383                            self._regex.match(metadata[f]): 
     384                        add_to_list = True 
     385                        break 
     386            if not add_to_list: 
     387                return 
    369388 
    370389        if self._date_start is not None and stat.st_mtime < self._date_start: 
    371390            return 
     
    378397            if mime_type not in self._mime_types: 
    379398                return 
    380399 
    381         file_info = (full_path, stat, int(stat.st_mtime), stat.st_size) 
     400        file_info = (full_path, stat, int(stat.st_mtime), metadata, 
     401                     stat.st_size) 
    382402        self._file_list.append(file_info) 
    383403 
    384404        return 
     
    401421 
    402422 
    403423def _get_file_metadata(path, stat): 
    404     client = gconf.client_get_default() 
     424    """Returns the metadata from the corresponding file 
     425    on the external device or does create the metadata 
     426    based on the file properties. 
     427 
     428    """ 
     429    filename = os.path.basename(path) 
     430    dir_path = os.path.dirname(path) 
     431    metadata = _get_file_metadata_from_json(dir_path, filename, preview=True) 
     432    if metadata: 
     433        return metadata 
     434 
    405435    return {'uid': path, 
    406436            'title': os.path.basename(path), 
    407437            'timestamp': stat.st_mtime, 
     
    409439            'mime_type': gio.content_type_guess(filename=path), 
    410440            'activity': '', 
    411441            'activity_id': '', 
    412             'icon-color': client.get_string('/desktop/sugar/user/color'), 
     442            'icon-color': '#000000,#ffffff', 
    413443            'description': path} 
    414444 
    415445 
     446def _get_file_metadata_from_json(dir_path, filename, preview=False): 
     447    """Returns the metadata from the json file and the preview 
     448    stored on the external device. 
     449 
     450    """ 
     451    metadata = None 
     452    metadata_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, 
     453                                 filename + '.metadata') 
     454    if os.path.exists(metadata_path): 
     455        try: 
     456            metadata = json.load(open(metadata_path)) 
     457        except ValueError: 
     458            logging.debug("Could not read metadata for file %r on" \ 
     459                              "external device.", filename) 
     460        else: 
     461            metadata['uid'] = os.path.join(dir_path, filename) 
     462    if preview: 
     463        preview_path = os.path.join(dir_path, JOURNAL_METADATA_DIR, 
     464                                    filename + '.preview') 
     465        if os.path.exists(preview_path): 
     466            try: 
     467                metadata['preview'] = dbus.ByteArray(open(preview_path).read()) 
     468            except: 
     469                logging.debug("Could not read preview for file %r on" \ 
     470                                  "external device.", filename) 
     471    else: 
     472        if metadata and 'preview' in metadata: 
     473            del(metadata['preview']) 
     474    return metadata 
     475 
     476 
    416477def _get_datastore(): 
    417478    global _datastore 
    418479    if _datastore is None: 
     
    519580    """ 
    520581    if os.path.exists(object_id): 
    521582        os.unlink(object_id) 
     583        dir_path = os.path.dirname(object_id) 
     584        filename = os.path.basename(object_id) 
     585        old_files = [os.path.join(dir_path, JOURNAL_METADATA_DIR, 
     586                                  filename + '.metadata'), 
     587                     os.path.join(dir_path, JOURNAL_METADATA_DIR, 
     588                                  filename + '.preview')] 
     589        for old_file in old_files: 
     590            if os.path.exists(old_file): 
     591                try: 
     592                    os.unlink(old_file) 
     593                except: 
     594                    pass 
    522595        deleted.send(None, object_id=object_id) 
    523596    else: 
    524597        _get_datastore().delete(object_id) 
     
    556629                                                 file_path, 
    557630                                                 transfer_ownership) 
    558631    else: 
    559         if not os.path.exists(file_path): 
    560             raise ValueError('Entries without a file cannot be copied to ' 
    561                              'removable devices') 
     632        object_id = _write_entry_on_external_device(metadata, file_path) 
     633 
     634    return object_id 
    562635 
    563         file_name = _get_file_name(metadata['title'], metadata['mime_type']) 
    564         file_name = _get_unique_file_name(metadata['mountpoint'], file_name) 
    565636 
     637def _write_entry_on_external_device(metadata, file_path): 
     638    """This creates and updates an entry copied from the 
     639    DS to external storage device. Besides copying the 
     640    associated file a hidden file for the preview and one 
     641    for the metadata are stored. We make sure that the 
     642    metadata and preview file are in the same directory 
     643    as the data file. 
     644 
     645    This function handles renames of an entry on the 
     646    external device and avoids name collisions. Renames are 
     647    handled failsafe. 
     648 
     649    """ 
     650    if 'uid' in metadata and os.path.exists(metadata['uid']): 
     651        file_path = metadata['uid'] 
     652 
     653    if not file_path or not os.path.exists(file_path): 
     654        raise ValueError('Entries without a file cannot be copied to ' 
     655                         'removable devices') 
     656 
     657    if metadata['title'] == '': 
     658        metadata['title'] = _('Untitled') 
     659    file_name = get_file_name(metadata['title'], metadata['mime_type']) 
     660 
     661    destination_path = os.path.join(metadata['mountpoint'], file_name) 
     662    if destination_path != file_path: 
     663        file_name = get_unique_file_name(metadata['mountpoint'], file_name) 
    566664        destination_path = os.path.join(metadata['mountpoint'], file_name) 
     665        clean_name, extension_ = os.path.splitext(file_name) 
     666        metadata['title'] = clean_name 
     667 
     668    metadata_copy = metadata.copy() 
     669    del metadata_copy['mountpoint'] 
     670    if 'uid' in metadata_copy: 
     671        del metadata_copy['uid'] 
     672 
     673    metadata_dir_path = os.path.join(metadata['mountpoint'], 
     674                                     JOURNAL_METADATA_DIR) 
     675    if not os.path.exists(metadata_dir_path): 
     676        os.mkdir(metadata_dir_path) 
     677 
     678    if 'preview' in metadata_copy: 
     679        preview = metadata_copy['preview'] 
     680        preview_fname = file_name + '.preview' 
     681        preview_path = os.path.join(metadata['mountpoint'], 
     682                                    JOURNAL_METADATA_DIR, preview_fname) 
     683        metadata_copy['preview'] = preview_fname 
     684 
     685        (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) 
     686        os.write(fh, preview) 
     687        os.close(fh) 
     688        os.rename(fn, preview_path) 
     689 
     690    metadata_path = os.path.join(metadata['mountpoint'], 
     691                                 JOURNAL_METADATA_DIR, 
     692                                 file_name + '.metadata') 
     693    (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint']) 
     694    os.write(fh, json.dumps(metadata_copy)) 
     695    os.close(fh) 
     696    os.rename(fn, metadata_path) 
     697 
     698    if os.path.dirname(destination_path) == os.path.dirname(file_path): 
     699        old_file_path = file_path 
     700        if old_file_path != destination_path: 
     701            os.rename(file_path, destination_path) 
     702            old_fname = os.path.basename(file_path) 
     703            old_files = [os.path.join(metadata['mountpoint'], 
     704                                      JOURNAL_METADATA_DIR, 
     705                                      old_fname + '.metadata'), 
     706                         os.path.join(metadata['mountpoint'], 
     707                                      JOURNAL_METADATA_DIR, 
     708                                      old_fname + '.preview')] 
     709            for ofile in old_files: 
     710                if os.path.exists(ofile): 
     711                    try: 
     712                        os.unlink(ofile) 
     713                    except: 
     714                        pass 
     715    else: 
    567716        shutil.copy(file_path, destination_path) 
    568         object_id = destination_path 
    569         created.send(None, object_id=object_id) 
     717 
     718    object_id = destination_path 
     719    created.send(None, object_id=object_id) 
    570720 
    571721    return object_id 
    572722 
    573723 
    574 def _get_file_name(title, mime_type): 
     724def get_file_name(title, mime_type): 
    575725    file_name = title 
    576726 
    577727    extension = mime.get_primary_extension(mime_type) 
     
    596746    return file_name 
    597747 
    598748 
    599 def _get_unique_file_name(mount_point, file_name): 
     749def get_unique_file_name(mount_point, file_name): 
    600750    if os.path.exists(os.path.join(mount_point, file_name)): 
    601751        i = 1 
    602752        name, extension = os.path.splitext(file_name)