Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/journal/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/journal/model.py')
-rw-r--r--src/jarabe/journal/model.py189
1 files changed, 128 insertions, 61 deletions
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index 81ca7d4..320e577 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2008, One Laptop Per Child
+# Copyright (C) 2007-2010, One Laptop per Child
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,10 +16,11 @@
import logging
import os
+import errno
from datetime import datetime
import time
import shutil
-from stat import S_IFMT, S_IFDIR, S_IFREG
+from stat import S_IFLNK, S_IFMT, S_IFDIR, S_IFREG
import re
from operator import itemgetter
@@ -32,18 +33,25 @@ from sugar import dispatch
from sugar import mime
from sugar import util
+
DS_DBUS_SERVICE = 'org.laptop.sugar.DataStore'
DS_DBUS_INTERFACE = 'org.laptop.sugar.DataStore'
DS_DBUS_PATH = '/org/laptop/sugar/DataStore'
# Properties the journal cares about.
-PROPERTIES = ['uid', 'title', 'mtime', 'timestamp', 'creation_time', 'filesize',
- 'keep', 'buddies', 'icon-color', 'mime_type', 'progress',
- 'activity', 'mountpoint', 'activity_id', 'bundle_id']
+PROPERTIES = ['activity', 'activity_id', 'buddies', 'bundle_id',
+ 'creation_time', 'filesize', 'icon-color', 'keep', 'mime_type',
+ 'mountpoint', 'mtime', 'progress', 'timestamp', 'title', 'uid']
MIN_PAGES_TO_CACHE = 3
MAX_PAGES_TO_CACHE = 5
+_datastore = None
+created = dispatch.Signal()
+updated = dispatch.Signal()
+deleted = dispatch.Signal()
+
+
class _Cache(object):
__gtype_name__ = 'model_Cache'
@@ -74,7 +82,7 @@ class BaseResultSet(object):
"""
def __init__(self, query, page_size):
- self._total_count = -1
+ self._total_count = -1
self._position = -1
self._query = query
self._page_size = page_size
@@ -141,7 +149,8 @@ class BaseResultSet(object):
self._cache.append_all(entries)
self._offset = offset
- elif remaining_forward_entries <= 0 and remaining_backwards_entries > 0:
+ elif (remaining_forward_entries <= 0 and
+ remaining_backwards_entries > 0):
# Add one page to the end of cache
logging.debug('appending one more page, offset: %r',
@@ -186,6 +195,7 @@ class BaseResultSet(object):
return self._cache[self._position - self._offset]
+
class DatastoreResultSet(BaseResultSet):
"""Encapsulates the result of a query on the datastore
"""
@@ -213,6 +223,7 @@ class DatastoreResultSet(BaseResultSet):
return entries, total_count
+
class InplaceResultSet(BaseResultSet):
"""Encapsulates the result of a query on a mount point
"""
@@ -220,7 +231,9 @@ class InplaceResultSet(BaseResultSet):
BaseResultSet.__init__(self, query, page_size)
self._mount_point = mount_point
self._file_list = None
- self._pending_directories = 0
+ self._pending_directories = []
+ self._visited_directories = []
+ self._pending_files = []
self._stopped = False
query_text = query.get('query', '')
@@ -247,7 +260,10 @@ class InplaceResultSet(BaseResultSet):
def setup(self):
self._file_list = []
- self._recurse_dir(self._mount_point)
+ self._pending_directories = [self._mount_point]
+ self._visited_directories = []
+ self._pending_files = []
+ gobject.idle_add(self._scan)
def stop(self):
self._stopped = True
@@ -256,10 +272,11 @@ class InplaceResultSet(BaseResultSet):
if self._sort[1:] == 'filesize':
keygetter = itemgetter(3)
else:
- keygetter = itemgetter(2) # timestamp
- self._file_list.sort(lambda a, b: b - a,
+ # timestamp
+ keygetter = itemgetter(2)
+ self._file_list.sort(lambda a, b: cmp(b, a),
key=keygetter,
- reverse=self._sort[0]=='-')
+ reverse=(self._sort[0] == '-'))
self.ready.send(self)
def find(self, query):
@@ -272,7 +289,7 @@ class InplaceResultSet(BaseResultSet):
t = time.time()
offset = int(query.get('offset', 0))
- limit = int(query.get('limit', len(self._file_list)))
+ limit = int(query.get('limit', len(self._file_list)))
total_count = len(self._file_list)
files = self._file_list[offset:offset + limit]
@@ -287,62 +304,101 @@ class InplaceResultSet(BaseResultSet):
return entries, total_count
- def _recurse_dir(self, dir_path):
- self._pending_directories += 1
- gobject.idle_add(self._idle_recurse_dir, dir_path)
+ def _scan(self):
+ if self._stopped:
+ return False
- def _idle_recurse_dir(self, dir_path):
- try:
- self._real_recurse_dir(dir_path)
- finally:
- self._pending_directories -= 1
- if self._pending_directories == 0:
- self.setup_ready()
+ self.progress.send(self)
- def _real_recurse_dir(self, dir_path):
- if self._stopped:
- return
+ if self._pending_files:
+ self._scan_a_file()
+ return True
+
+ if self._pending_directories:
+ self._scan_a_directory()
+ return True
+
+ self.setup_ready()
+ self._visited_directories = []
+ return False
+
+ def _scan_a_file(self):
+ full_path = self._pending_files.pop(0)
try:
- dirs = os.listdir(dir_path)
- except Exception:
- logging.exception('Error reading directory %r', dir_path)
- dirs = []
+ stat = os.lstat(full_path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ logging.exception(
+ 'Error reading metadata of file %r', full_path)
+ return
+
+ if S_IFMT(stat.st_mode) == S_IFLNK:
+ try:
+ link = os.readlink(full_path)
+ except OSError, e:
+ logging.exception(
+ 'Error reading target of link %r', full_path)
+ return
+
+ if not os.path.abspath(link).startswith(self._mount_point):
+ return
- for entry in dirs:
- if entry.startswith('.'):
- continue
- full_path = dir_path + '/' + entry
try:
stat = os.stat(full_path)
- if S_IFMT(stat.st_mode) == S_IFDIR:
- self._recurse_dir(full_path)
- elif S_IFMT(stat.st_mode) == S_IFREG:
- add_to_list = True
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ logging.exception(
+ 'Error reading metadata of linked file %r', full_path)
+ return
+
+ if S_IFMT(stat.st_mode) == S_IFDIR:
+ id_tuple = stat.st_ino, stat.st_dev
+ if not id_tuple in self._visited_directories:
+ self._visited_directories.append(id_tuple)
+ self._pending_directories.append(full_path)
+ return
- if self._regex is not None and \
- not self._regex.match(full_path):
- add_to_list = False
+ if S_IFMT(stat.st_mode) != S_IFREG:
+ return
- if None not in [self._date_start, self._date_end] and \
- (stat.st_mtime < self._date_start or
- stat.st_mtime > self._date_end):
- add_to_list = False
+ if self._regex is not None and \
+ not self._regex.match(full_path):
+ return
- if self._mime_types:
- mime_type = gio.content_type_guess(filename=full_path)
- if mime_type not in self._mime_types:
- add_to_list = False
+ if self._date_start is not None and stat.st_mtime < self._date_start:
+ return
- if add_to_list:
- file_info = (full_path, stat, int(stat.st_mtime), stat.st_size)
- self._file_list.append(file_info)
+ if self._date_end is not None and stat.st_mtime > self._date_end:
+ return
- self.progress.send(self)
+ if self._mime_types:
+ mime_type = gio.content_type_guess(filename=full_path)
+ if mime_type not in self._mime_types:
+ return
+
+ file_info = (full_path, stat, int(stat.st_mtime), stat.st_size)
+ self._file_list.append(file_info)
+
+ return
+
+ def _scan_a_directory(self):
+ dir_path = self._pending_directories.pop(0)
+
+ try:
+ entries = os.listdir(dir_path)
+ except OSError, e:
+ if e.errno != errno.EACCES:
+ logging.exception('Error reading directory %r', dir_path)
+ return
+
+ for entry in entries:
+ if entry.startswith('.'):
+ continue
+ self._pending_files.append(dir_path + '/' + entry)
+ return
- except Exception:
- logging.exception('Error reading file %r', full_path)
def _get_file_metadata(path, stat):
client = gconf.client_get_default()
@@ -356,7 +412,7 @@ def _get_file_metadata(path, stat):
'icon-color': client.get_string('/desktop/sugar/user/color'),
'description': path}
-_datastore = None
+
def _get_datastore():
global _datastore
if _datastore is None:
@@ -370,15 +426,19 @@ def _get_datastore():
return _datastore
+
def _datastore_created_cb(object_id):
created.send(None, object_id=object_id)
+
def _datastore_updated_cb(object_id):
updated.send(None, object_id=object_id)
+
def _datastore_deleted_cb(object_id):
deleted.send(None, object_id=object_id)
+
def find(query_, page_size):
"""Returns a ResultSet
"""
@@ -393,6 +453,7 @@ def find(query_, page_size):
else:
return InplaceResultSet(query, page_size, mount_points[0])
+
def _get_mount_point(path):
dir_path = os.path.dirname(path)
while True:
@@ -401,6 +462,7 @@ def _get_mount_point(path):
else:
dir_path = dir_path.rsplit(os.sep, 1)[0]
+
def get(object_id):
"""Returns the metadata for an object
"""
@@ -413,6 +475,7 @@ def get(object_id):
metadata['mountpoint'] = '/'
return metadata
+
def get_file(object_id):
"""Returns the file for an object
"""
@@ -427,6 +490,7 @@ def get_file(object_id):
else:
return None
+
def get_file_size(object_id):
"""Return the file size for an object
"""
@@ -442,12 +506,14 @@ def get_file_size(object_id):
return 0
+
def get_unique_values(key):
"""Returns a list with the different values a property has taken
"""
empty_dict = dbus.Dictionary({}, signature='ss')
return _get_datastore().get_uniquevaluesfor(key, empty_dict)
+
def delete(object_id):
"""Removes an object from persistent storage
"""
@@ -457,6 +523,7 @@ def delete(object_id):
else:
_get_datastore().delete(object_id)
+
def copy(metadata, mount_point):
"""Copies an object to another mount point
"""
@@ -468,6 +535,7 @@ def copy(metadata, mount_point):
return write(metadata, file_path, transfer_ownership=False)
+
def write(metadata, file_path='', update_mtime=True, transfer_ownership=True):
"""Creates or updates an entry for that id
"""
@@ -502,6 +570,7 @@ def write(metadata, file_path='', update_mtime=True, transfer_ownership=True):
return object_id
+
def _get_file_name(title, mime_type):
file_name = title
@@ -526,11 +595,12 @@ def _get_file_name(title, mime_type):
return file_name
+
def _get_unique_file_name(mount_point, file_name):
if os.path.exists(os.path.join(mount_point, file_name)):
i = 1
+ name, extension = os.path.splitext(file_name)
while len(file_name) <= 255:
- name, extension = os.path.splitext(file_name)
file_name = name + '_' + str(i) + extension
if not os.path.exists(os.path.join(mount_point, file_name)):
break
@@ -538,10 +608,7 @@ def _get_unique_file_name(mount_point, file_name):
return file_name
+
def is_editable(metadata):
mountpoint = metadata.get('mountpoint', '/')
return mountpoint == '/'
-
-created = dispatch.Signal()
-updated = dispatch.Signal()
-deleted = dispatch.Signal()