Initial N800 backup support
- From: Jaime Frutos Morales <acidborg gmail com>
- To: Conduit <conduit-list gnome org>
- Subject: Initial N800 backup support
- Date: Fri, 03 Aug 2007 00:13:22 +0200
Hi again,
Here is my initial patch to add N800 backup support to Conduit. It's
quite "hacky" and it isn't finished yet, but the sync N800 -> PC folder
works.
The folder created in the PC has an strange name (self.folderGroupName +
"file:") and I don't know how to fix this. Besides, the PC folder ->
N800 sync doesn't work.
Any help on the folder name fixing would be appreciated :-)
--
Jaime Frutos Morales
Computer Science Engineer
Blog: http://textoplano.livejournal.com
Index: conduit/dataproviders/N800Module.py
===================================================================
--- conduit/dataproviders/N800Module.py (revisi�0)
+++ conduit/dataproviders/N800Module.py (revisi�0)
@@ -0,0 +1,426 @@
+"""
+Provides a number of dataproviders which are associated with
+a N800 device.
+
+Copyright: Jaime Frutos Morales , 2007
+License: GPLv2
+"""
+import os
+import os.path
+import gtk
+import gobject
+import gnomevfs
+import traceback
+import threading
+import time
+from gettext import gettext as _
+
+import conduit
+from conduit import log,logd,logw
+import conduit.DataProvider as DataProvider
+import conduit.Module as Module
+from conduit.datatypes import DataType
+from conduit.datatypes import File
+import conduit.datatypes.Text as Text
+import conduit.Exceptions as Exceptions
+import conduit.Utils as Utils
+import conduit.Settings as Settings
+import conduit.DB as DB
+
+MODULES = {
+ "N800Factory" : { "type": "dataprovider-factory" },
+}
+
+
+class N800Factory(Module.DataProviderFactory):
+ def __init__(self, **kwargs):
+ Module.DataProviderFactory.__init__(self, **kwargs)
+
+ if kwargs.has_key("hal"):
+ self.hal = kwargs["hal"]
+ self.hal.connect("n800-added", self._n800_added)
+ self.hal.connect("n800-removed", self._n800_removed)
+
+ self.n800s = {}
+
+ def probe(self):
+ """ Probe for N800 devices that are already attached """
+ for device_type, udi, mount, name in self.hal.get_all_n800s():
+ self._n800_added(None, udi, mount, name)
+
+ def _n800_added(self, hal, udi, mount, name):
+ """ New N800 has been discovered """
+ cat = DataProvider.DataProviderCategory(
+ "Nokia N800",
+ "n800",
+ mount)
+
+ keys = []
+ for klass in [N800BackupTwoWay]:
+ key = self.emit_added(
+ klass, # Dataprovider class
+ (mount,udi,), # Init args
+ cat) # Category..
+ keys.append(key)
+
+ self.n800s[udi] = keys
+
+ def _n800_removed(self, hal, udi, mount, name):
+ for key in self.n800s[udi]:
+ self.emit_removed(key)
+
+ del self.n800s[udi]
+
+TYPE_FILE = 0
+TYPE_FOLDER = 1
+TYPE_EMPTY_FOLDER = 2
+TYPE_SINGLE_FILE = 3
+
+#Indexes of data in the list store
+URI_IDX = 0 #URI of the file/folder
+TYPE_IDX = 1 #TYPE_FILE/FOLDER/etc
+CONTAINS_NUM_ITEMS_IDX = 2 #(folder only) How many items in the folder
+SCAN_COMPLETE_IDX = 3 #(folder only) HAs the folder been recursively scanned
+GROUP_NAME_IDX = 4 #(folder only) The visible identifier for the folder
+CONTAINS_ITEMS_IDX = 5 #(folder only) All the items contained within the folder
+
+CONFIG_FILE_NAME = ".conduit.conf"
+
+def _save_config_file_for_dir(uri, groupName):
+ tempFile = Utils.new_tempfile(groupName)
+ tempFile.force_new_filename(CONFIG_FILE_NAME)
+ tempFile.transfer(uri, True)
+
+def _get_config_file_for_dir(uri):
+ config = os.path.join(uri,CONFIG_FILE_NAME)
+ return gnomevfs.read_entire_file(config)
+
+class _FolderScanner(threading.Thread, gobject.GObject):
+ __gsignals__ = {
+ "scan-progress": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [
+ gobject.TYPE_INT]),
+ "scan-completed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [])
+ }
+
+ def __init__(self, baseURI, includeHidden):
+ threading.Thread.__init__(self)
+ gobject.GObject.__init__(self)
+ self.baseURI = baseURI
+ self.includeHidden = includeHidden
+ self.dirs = [baseURI]
+ self.cancelled = False
+ self.URIs = []
+ self.setName("FolderScanner Thread: %s" % baseURI)
+
+ def run(self):
+ delta = 0
+
+ startTime = time.time()
+ t = 1
+ last_estimated = estimated = 0
+ while len(self.dirs)>0:
+ if self.cancelled:
+ return
+ dir = self.dirs.pop(0)
+ try:hdir = gnomevfs.DirectoryHandle(gnomevfs.URI(dir))
+ except:
+ logw("Folder %s Not found" % dir)
+ continue
+ try: fileinfo = hdir.next()
+ except StopIteration: continue;
+ while fileinfo:
+ if fileinfo.name in [".","..",CONFIG_FILE_NAME]:
+ pass
+ else:
+ if fileinfo.type == gnomevfs.FILE_TYPE_DIRECTORY:
+ #Include hidden directories
+ if fileinfo.name[0] != "." or self.includeHidden:
+ self.dirs.append(dir+"/"+fileinfo.name)
+ t += 1
+ elif fileinfo.type == gnomevfs.FILE_TYPE_REGULAR:
+ try:
+ uri = gnomevfs.make_uri_canonical(dir+"/"+fileinfo.name)
+ #Include hidden files
+ if fileinfo.name[0] != "." or self.includeHidden:
+ self.URIs.append(uri)
+ except UnicodeDecodeError:
+ raise "UnicodeDecodeError",uri
+ else:
+ logd("Unsupported file type: %s (%s)" % (fileinfo.name, fileinfo.type))
+ try: fileinfo = hdir.next()
+ except StopIteration: break;
+ #Calculate the estimated complete percentags
+ estimated = 1.0-float(len(self.dirs))/float(t)
+ estimated *= 100
+ #Enly emit progress signals every 10% (+/- 1%) change to save CPU
+ if delta+10 - estimated <= 1:
+ logd("Folder scan %s%% complete" % estimated)
+ self.emit("scan-progress", len(self.URIs))
+ delta += 10
+ last_estimated = estimated
+
+ i = 0
+ total = len(self.URIs)
+ endTime = time.time()
+ logd("%s files loaded in %s seconds" % (total, (endTime - startTime)))
+ self.emit("scan-completed")
+
+ def cancel(self):
+ self.cancelled = True
+
+ def get_uris(self):
+ return self.URIs
+
+
+class _ScannerThreadManager:
+ MAX_CONCURRENT_SCAN_THREADS = 2
+ def __init__(self):
+ self.scanThreads = {}
+ self.pendingScanThreadsURIs = []
+
+ def make_thread(self, folderURI, includeHidden, progressCb, completedCb, rowref):
+ running = len(self.scanThreads) - len(self.pendingScanThreadsURIs)
+
+ if folderURI not in self.scanThreads:
+ thread = _FolderScanner(folderURI, includeHidden)
+ thread.connect("scan-progress",progressCb, rowref)
+ thread.connect("scan-completed",completedCb, rowref)
+ thread.connect("scan-completed", self._register_thread_completed, folderURI)
+ self.scanThreads[folderURI] = thread
+ if running < _ScannerThreadManager.MAX_CONCURRENT_SCAN_THREADS:
+ logd("Starting thread %s" % folderURI)
+ self.scanThreads[folderURI].start()
+ else:
+ self.pendingScanThreadsURIs.append(folderURI)
+
+ def _register_thread_completed(self, sender, folderURI):
+ #delete the old thread
+ del(self.scanThreads[folderURI])
+ running = len(self.scanThreads) - len(self.pendingScanThreadsURIs)
+
+ logd("Thread %s completed. %s running, %s pending" % (folderURI, running, len(self.pendingScanThreadsURIs)))
+
+ if running < _ScannerThreadManager.MAX_CONCURRENT_SCAN_THREADS:
+ try:
+ uri = self.pendingScanThreadsURIs.pop()
+ logd("Starting pending thread %s" % uri)
+ self.scanThreads[uri].start()
+ except IndexError: pass
+
+ def join_all_threads(self):
+ joinedThreads = 0
+ while(joinedThreads < len(self.scanThreads)):
+ for thread in self.scanThreads.values():
+ try:
+ thread.join()
+ joinedThreads += 1
+ except AssertionError:
+ #deal with not started threads
+ time.sleep(1)
+
+ def cancel_all_threads(self):
+ for thread in self.scanThreads.values():
+ if thread.isAlive():
+ logd("Cancelling thread %s" % thread)
+ thread.cancel()
+ thread.join() #May block
+
+
+class FolderTwoWay(DataProvider.TwoWay):
+ """
+ TwoWay dataprovider for synchronizing a folder
+ """
+
+ _name_ = _("Folder")
+ _description_ = _("Synchronize folders")
+ _category_ = DataProvider.CATEGORY_FILES
+ _module_type_ = "twoway"
+ _in_type_ = "file"
+ _out_type_ = "file"
+ _icon_ = "folder"
+
+ DEFAULT_FOLDER = os.path.expanduser("~")
+ DEFAULT_GROUP = "Home"
+ DEFAULT_HIDDEN = False
+
+ def __init__(self, *args):
+ DataProvider.TwoWay.__init__(self)
+ self.need_configuration(True)
+
+ self.folder = FolderTwoWay.DEFAULT_FOLDER
+ self.folderGroupName = FolderTwoWay.DEFAULT_GROUP
+ self.includeHidden = FolderTwoWay.DEFAULT_HIDDEN
+ self.files = []
+
+ self._monitor_folder_id = None
+
+ def __del__(self):
+ if self._monitor_folder_id != None:
+ gnomevfs.monitor_cancel(self._monitor_folder_id)
+ self._monitor_folder_id = None
+
+ def initialize(self):
+ return True
+
+ def configure(self, window):
+ pass
+ def refresh(self):
+ DataProvider.TwoWay.refresh(self)
+ #scan the folder
+ scanThread = _FolderScanner(self.folder, self.includeHidden)
+ scanThread.start()
+ scanThread.join()
+
+ self.files = scanThread.get_uris()
+
+
+ def put(self, vfsFile, overwrite, LUID=None):
+ """
+ Puts vfsFile at the correct location. There are two scenarios
+ 1) File came from a foreign DP like tomboy
+ 2) File came from another file dp
+
+ Behaviour:
+ 1) The foreign DP should have encoded enough information (such as
+ the filename) so that we can go ahead and put the file in the dir
+ 2) First we see if the file has a group attribute. If so, and the
+ group matches the groupName here then we put the files into the
+ directory. If not we put the file in the orphan dir. We try and
+ retain the relative path for the files in the specifed group
+ and recreate that in the group dir
+ """
+ DataProvider.TwoWay.put(self, vfsFile, overwrite, LUID)
+ newURI = ""
+ if LUID != None:
+ newURI = LUID
+ elif vfsFile.basePath == "":
+ #came from another type of dataprovider such as tomboy
+ #where relative path makes no sense. Could also come from
+ #the FileSource dp when the user has selected a single file
+ logd("FolderTwoWay: No basepath. Going to empty dir")
+ newURI = self.folder+"/"+vfsFile.get_filename()
+ else:
+ pathFromBase = vfsFile._get_text_uri().replace(vfsFile.basePath,"")
+ #Look for corresponding groups
+ if self.folderGroupName == vfsFile.group:
+ logd("FolderTwoWay: Found corresponding group")
+ #put in the folder
+ newURI = self.folder+pathFromBase
+ else:
+ logd("FolderTwoWay: Recreating group %s --- %s --- %s" % (vfsFile._get_text_uri(),vfsFile.basePath,vfsFile.group))
+ #unknown. Store in the dir but recreate the group
+ newURI = self.folder+"/"+vfsFile.group+pathFromBase
+
+ destFile = File.File(URI=newURI)
+ comp = vfsFile.compare(destFile)
+ if overwrite or comp == DataType.COMPARISON_NEWER:
+ vfsFile.transfer(newURI, True)
+
+ return gnomevfs.make_uri_canonical(newURI)
+
+ def delete(self, LUID):
+ f = File.File(URI=LUID)
+ if f.exists():
+ f.delete()
+
+ def get(self, uid):
+ DataProvider.TwoWay.get(self, uid)
+ f = File.File(
+ URI=uid,
+ basepath=self.folder,
+ group=self.folderGroupName
+ )
+ f.set_open_URI(uid)
+ f.set_UID(uid)
+ return f
+
+ def get_all(self):
+ DataProvider.TwoWay.get_all(self)
+ return self.files
+
+ def finish(self):
+ DataProvider.TwoWay.finish(self)
+ self.files = []
+
+ def set_configuration(self, config):
+ self.folder = config.get("folder", FolderTwoWay.DEFAULT_FOLDER)
+ self.folderGroupName = config.get("folderGroupName", FolderTwoWay.DEFAULT_GROUP)
+ self.includeHidden = config.get("includeHidden", FolderTwoWay.DEFAULT_HIDDEN)
+
+ self.set_configured(True)
+ self._monitor_folder()
+
+ def get_configuration(self):
+ _save_config_file_for_dir(self.folder, self.folderGroupName)
+ return {
+ "folder" : self.folder,
+ "folderGroupName" : self.folderGroupName,
+ "includeHidden" : self.includeHidden
+ }
+
+ def get_UID(self):
+ return "%s:%s" % (self.folder, self.folderGroupName)
+
+ def _on_scan_folder_progress(self, folderScanner, numItems, rowref):
+ path = self.items.get_path(rowref)
+ self.items[path][CONTAINS_NUM_ITEMS_IDX] = numItems
+
+ def _on_scan_folder_completed(self, folderScanner, rowref):
+ logd("Folder scan complete %s" % folderScanner)
+ path = self.items.get_path(rowref)
+ self.items[path][SCAN_COMPLETE_IDX] = True
+ self.items[path][CONTAINS_ITEMS_IDX] = folderScanner.get_uris()
+ #If the user has not yet given the folder a descriptive name then
+ #check of the folder contains a .conduit file in which that name is
+ #stored (i.e. the case when the user starts the sync from a
+ #saved configuration)
+ if self.items[path][GROUP_NAME_IDX] == "":
+ try:
+ configString = _get_config_file_for_dir(folderScanner.baseURI)
+ self.items[path][GROUP_NAME_IDX] = configString
+ except gnomevfs.NotFoundError: pass
+
+ def _monitor_folder(self):
+ if self._monitor_folder_id != None:
+ gnomevfs.monitor_cancel(self._monitor_folder_id)
+ self._monitor_folder_id = None
+
+ try:
+ self._monitor_folder_id = gnomevfs.monitor_add(self.folder, gnomevfs.MONITOR_DIRECTORY, self._monitor_folder_cb)
+ except gnomevfs.NotSupportedError:
+ # silently fail if we are looking at a folder that doesn't support directory monitoring
+ pass
+
+ def _monitor_folder_cb(self, monitor_uri, event_uri, event, data=None):
+ if event in (gnomevfs.MONITOR_EVENT_CREATED, gnomevfs.MONITOR_EVENT_CHANGED, gnomevfs.MONITOR_EVENT_DELETED):
+ self.emit_change_detected()
+
+
+class N800BackupTwoWay(FolderTwoWay):
+
+ _name_ = _("Backup files")
+ _description_ = _("Synchronize your N800 Backup files")
+
+ def __init__(self, *args):
+ FolderTwoWay.__init__(self, *args)
+
+ self.mountPoint = args[0]
+ self.folder = os.path.join(self.mountPoint, 'backups')
+ self.folderGroupName = "N800-b"
+ self.includeHidden = True
+ # Check whether the folder exists and create it
+ if not os.path.exists(self.folder):
+ os.mkdir(self.folder)
+ self.need_configuration(False)
+ self.set_configured(True)
+
+ def set_configuration(self, config):
+ pass
+
+ def get_configuration(self):
+ pass
+
+ def configure(self, window):
+ pass
+
+
Index: conduit/dataproviders/Makefile.am
===================================================================
--- conduit/dataproviders/Makefile.am (revisi�726)
+++ conduit/dataproviders/Makefile.am (copia de trabajo)
@@ -16,7 +16,8 @@
__init__.py \
iPodModule.py \
TomboyModule.py \
- ConverterModule.py
+ ConverterModule.py \
+ N800Module.py
clean-local:
rm -rf *.pyc *.pyo
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]