Beispiel #1
0
    def __init__(self, id, path, config_file_name, tags_file, vocabulary_file, navigation_dir_list, storage_dir_list, describing_nav_dir_list, categorising_nav_dir_list, expiry_dir_list, expiry_prefix):
        """
        constructor
        """
        QtCore.QObject.__init__(self)

        #self.__log = logging.getLogger("TagStoreLogger")#None##FIXXME
        self.__log = None

        self.__file_system = FileSystemWrapper(self.__log)
        self.__watcher = QtCore.QFileSystemWatcher(self)
        self.__watcher.connect(self.__watcher,QtCore.SIGNAL("directoryChanged(QString)"), self.__directory_changed)
        self.__tag_wrapper = None
        self.__sync_tag_wrapper = None
        self.__store_config_wrapper = None
        self.__pending_changes = PendingChanges()
        
        self.__sync_tags_file_name = TsConstants.DEFAULT_STORE_SYNC_TAGS_FILENAME
        
        self.__tagline_config = None
        self.__paths_to_maintain = []
        
        self.__id = unicode(id)
        self.__path = unicode(path)
        self.__config_file_name = unicode(config_file_name)
        self.__tags_file_name = unicode(tags_file)
        self.__vocabulary_file_name = unicode(vocabulary_file)
        
        self.__storage_dir_list = storage_dir_list
        self.__describing_nav_dir_list = describing_nav_dir_list
        self.__categorising_nav_dir_list = categorising_nav_dir_list
        self.__expiry_dir_list = expiry_dir_list
        self.__expiry_prefix = unicode(expiry_prefix)
        
        self.__storage_dir_name = self.trUtf8("storage")
        self.__navigation_dir_name = self.trUtf8("navigation")
        self.__describing_nav_dir_name = self.trUtf8("descriptions")
        self.__categorising_nav_dir_name = self.trUtf8("categories")
        self.__expiry_dir_name = self.trUtf8("expired_items")
        #self.__parent_path = None
        #self.__name = None
        #self.__config_path = None
        #self.__watcher_path = None
        #self.__describing_nav_path = None
        #self.__config_path = self.__path + "/" + self.__config_file_name
        #self.__watcher_path = self.__path + "/" + self.__storage_dir_name
        #self.__describing_nav_path = self.__path + "/" + self.__describing_nav_dir_name

        self.__create_wrappers()
        
        if self.__path.find(":/") == -1:
            self.__path = self.__path.replace(":", ":/")
        self.__name = unicode(self.__path.split("/")[-1])
        self.__parent_path = unicode(self.__path[:len(self.__path)-len(self.__name)-1])
        
        self.__tagcloud = TagCloud()
        self.__recommender = Recommender(self.get_store_path())
Beispiel #2
0
class Store(QtCore.QObject):

    __pyqtSignals__ = ("removed(PyQt_PyObject)",
                       "renamed(PyQt_PyObject, QString)",
                       "file_renamed(PyQt_PyObject, QString, QString)",
                       "file_removed(PyQt_PyObject, QString)",
                       "pending_operations_changed(PyQt_PyObject)")

    def __init__(self, id, path, config_file_name, tags_file, vocabulary_file, navigation_dir_list, storage_dir_list, describing_nav_dir_list, categorising_nav_dir_list, expiry_dir_list, expiry_prefix):
        """
        constructor
        """
        QtCore.QObject.__init__(self)

        #self.__log = logging.getLogger("TagStoreLogger")#None##FIXXME
        self.__log = None

        self.__file_system = FileSystemWrapper(self.__log)
        self.__watcher = QtCore.QFileSystemWatcher(self)
        self.__watcher.connect(self.__watcher,QtCore.SIGNAL("directoryChanged(QString)"), self.__directory_changed)
        self.__tag_wrapper = None
        self.__sync_tag_wrapper = None
        self.__store_config_wrapper = None
        self.__pending_changes = PendingChanges()
        
        self.__sync_tags_file_name = TsConstants.DEFAULT_STORE_SYNC_TAGS_FILENAME
        
        self.__tagline_config = None
        self.__paths_to_maintain = []
        
        self.__id = unicode(id)
        self.__path = unicode(path)
        self.__config_file_name = unicode(config_file_name)
        self.__tags_file_name = unicode(tags_file)
        self.__vocabulary_file_name = unicode(vocabulary_file)
        
        self.__storage_dir_list = storage_dir_list
        self.__describing_nav_dir_list = describing_nav_dir_list
        self.__categorising_nav_dir_list = categorising_nav_dir_list
        self.__expiry_dir_list = expiry_dir_list
        self.__expiry_prefix = unicode(expiry_prefix)
        
        self.__storage_dir_name = self.trUtf8("storage")
        self.__navigation_dir_name = self.trUtf8("navigation")
        self.__describing_nav_dir_name = self.trUtf8("descriptions")
        self.__categorising_nav_dir_name = self.trUtf8("categories")
        self.__expiry_dir_name = self.trUtf8("expired_items")
        #self.__parent_path = None
        #self.__name = None
        #self.__config_path = None
        #self.__watcher_path = None
        #self.__describing_nav_path = None
        #self.__config_path = self.__path + "/" + self.__config_file_name
        #self.__watcher_path = self.__path + "/" + self.__storage_dir_name
        #self.__describing_nav_path = self.__path + "/" + self.__describing_nav_dir_name

        self.__create_wrappers()
        
        if self.__path.find(":/") == -1:
            self.__path = self.__path.replace(":", ":/")
        self.__name = unicode(self.__path.split("/")[-1])
        self.__parent_path = unicode(self.__path[:len(self.__path)-len(self.__name)-1])
        
        self.__tagcloud = TagCloud()
        self.__recommender = Recommender(self.get_store_path())
        
    def __create_wrappers(self):
        if self.__file_system.path_exists(self.__path + "/" + self.__tags_file_name):
            self.__tag_wrapper = TagWrapper(self.__path + "/" + self.__tags_file_name)
        if self.__file_system.path_exists(self.__path + "/" + self.__config_file_name):
            self.__store_config_wrapper = ConfigWrapper(self.__path + "/" + self.__config_file_name)

    def init(self):
        """
        init is called after event listeners were added to the store instance
        """
        ## throw exception if store directory does not exist
        if not self.__file_system.path_exists(self.__path):
            ## look for renamed or removed store folder
            self.__handle_renamed_removed_store()
        if not self.__file_system.path_exists(self.__path):
            #print self.__path
            raise StoreInitError, self.trUtf8("The specified store directory does not exist! %s" % self.__path)
            return
        
        
        ## look for store/describing_nav/categorising_nav/expire directories names (all languages) if they do not exist
        if not self.__file_system.path_exists(self.__path + "/" + self.__storage_dir_name):
            for dir in self.__storage_dir_list:
                if self.__file_system.path_exists(self.__path + "/" + dir):
                    self.__storage_dir_name = unicode(dir)
        if not self.__file_system.path_exists(self.__path + "/" + self.__describing_nav_dir_name):
            for dir in self.__describing_nav_dir_list:
                if self.__file_system.path_exists(self.__path + "/" + dir):
                    self.__describing_nav_dir_name = unicode(dir)
        if not self.__file_system.path_exists(self.__path + "/" + self.__categorising_nav_dir_name):
            for dir in self.__categorising_nav_dir_list:
                if self.__file_system.path_exists(self.__path + "/" + dir):
                    self.__categorising_nav_dir_name = unicode(dir)
        if not self.__file_system.path_exists(self.__path + "/" + self.__expiry_dir_name):
            for dir in self.__expiry_dir_list:
                if self.__file_system.path_exists(self.__path + "/" + dir):
                    self.__expiry_dir_name = unicode(dir)
        if not self.__file_system.path_exists(self.__path + "/" + self.__navigation_dir_name):
            for dir in self.__expiry_dir_list:
                if self.__file_system.path_exists(self.__path + "/" + dir):
                    self.__navigation_dir_name = unicode(dir)
        
        
        
        ## built stores directories and config file if they currently not exist (new store)
        self.__file_system.create_dir(self.__path + "/" + self.__storage_dir_name)
        self.__file_system.create_dir(self.__path + "/" + self.__expiry_dir_name)
        self.__file_system.create_dir(self.__path + "/" + self.__config_file_name.split("/")[0])
        self.__file_system.create_dir(self.__path + "/" + self.__navigation_dir_name)
        ## create config/vocabulary files if they don't exist
        if not self.__file_system.path_exists(self.__path + "/" + self.__config_file_name):
            ConfigWrapper.create_store_config_file(self.__path + "/" + self.__config_file_name)
            ## now create a new config_wrapper instance
            self.__store_config_wrapper = ConfigWrapper(self.__path + "/" + self.__config_file_name)
        if not self.__file_system.path_exists(self.__path + "/" + self.__tags_file_name):
            TagWrapper.create_tags_file(self.__path + "/" + self.__tags_file_name)
            ## now create a new tag_wrapper instance
            self.__tag_wrapper = TagWrapper(self.__path + "/" + self.__tags_file_name)
        if not self.__file_system.path_exists(self.__path + "/" + self.__vocabulary_file_name):
            self.__vocabulary_wrapper = VocabularyWrapper.create_vocabulary_file(self.__path + "/" + self.__vocabulary_file_name)


        ## 0 ... show just the describing tagline -> create the NAVIGATION dir 
        ## 3 ... show just the categorizing tagline - only restricted vocabulary is allowed -> create the CATEGORIES dir
        ## ELSE: two taglines with dirs: CATEGORIES/DESCRIPTIONS
        self.__tagline_config = self.__store_config_wrapper.get_show_category_line()
        
        # clear the old list (if there is already one)
        self.__paths_to_maintain = []
        
        if self.__tagline_config == 0:
            #self.__file_system.create_dir(self.__path + "/" + self.__navigation_dir_name)
            self.__paths_to_maintain.append(self.__path + "/" + self.__navigation_dir_name)
        elif self.__tagline_config == 3:
            #self.__file_system.create_dir(self.__path + "/" + self.__categorising_nav_dir_name)
            self.__paths_to_maintain.append(self.__path + "/" + self.__categorising_nav_dir_name)
        else:
            #self.__file_system.create_dir(self.__path + "/" + self.__categorising_nav_dir_name)
            self.__paths_to_maintain.append(self.__path + "/" + self.__categorising_nav_dir_name)
            #self.__file_system.create_dir(self.__path + "/" + self.__describing_nav_dir_name)
            self.__paths_to_maintain.append(self.__path + "/" + self.__describing_nav_dir_name)

        for path in self.__paths_to_maintain:
            self.__file_system.create_dir(path)

        self.__init_store()

    def init_sync_log(self, target_store_name):
        """
        initializes the sync log
        """
        # construct sync tags file path
        target_store_name = target_store_name.replace(":", "")
        
        self.__sync_tags_file_path = self.__path + "/" + TsConstants.DEFAULT_STORE_CONFIG_DIR + "/" + target_store_name + self.__sync_tags_file_name
        
        self.__log.info("init sync log path:%s" % self.__sync_tags_file_path)
        if not self.__file_system.path_exists(self.__sync_tags_file_path):
            # create default sync tags file
            TagWrapper.create_tags_file(self.__sync_tags_file_path)
            
        ## now create a new sync tag_wrapper instance
        self.__sync_tag_wrapper = TagWrapper(self.__sync_tags_file_path)

    def sync_item_exists(self, item_name):
        """
        checks an item exists in the sync log
        """
        return self.__sync_tag_wrapper.file_exists(item_name)

    def get_sync_items(self):
        """
        returns a list of all item names in the sync log 
        """
        return self.__sync_tag_wrapper.get_files()

    def get_sync_file_timestamp(self, file_name):
        """
        returns the timestamp value in the sync log
        """
        return self.__sync_tag_wrapper.get_file_timestamp(file_name)
        
    def get_describing_sync_tags_for_item(self, item_name):
        """
        returns all describing tags associated with the given item in the sync log
        """
        return self.__sync_tag_wrapper.get_file_tags(item_name)
        
    def get_categorizing_sync_tags_for_item(self, item_name):
        """
        returns all categorizing tags associated with the given item in the sync log
        """
        return self.__sync_tag_wrapper.get_file_categories(item_name)

        
    def __init_store(self):
        """
        initializes the store paths, config reader, file system watcher without instantiation of a new object
        """
        self.__name = self.__path.split("/")[-1]
        self.__parent_path = self.__path[:len(self.__path)-len(self.__name)]
        self.__tags_file_path = self.__path + "/" + self.__tags_file_name
        self.__sync_tags_file_path = self.__path + "/" + TsConstants.DEFAULT_STORE_CONFIG_DIR + "/" + self.__sync_tags_file_name
        self.__config_path = self.__path + "/" + self.__config_file_name
        self.__vocabulary_path = self.__path + "/" + self.__vocabulary_file_name #TsConstants.STORE_CONFIG_DIR + "/" + TsConstants.STORE_VOCABULARY_FILENAME
        self.__watcher_path = self.__path + "/" + self.__storage_dir_name
        self.__navigation_path = self.__path + "/" + self.__navigation_dir_name
        self.__describing_nav_path = self.__path + "/" + self.__describing_nav_dir_name
        self.__categorising_nav_path = self.__path + "/" + self.__categorising_nav_dir_name
        config_file_name = unicode(self.__config_path.split("/")[-1])
        self.__temp_progress_path = unicode(self.__config_path[:len(self.__config_path)-len(config_file_name)-1])
        
        self.__tag_wrapper = TagWrapper(self.__tags_file_path)
        self.__sync_tag_wrapper = TagWrapper(self.__sync_tags_file_path)
        
        ## update store id to avoid inconsistency
        config_wrapper = ConfigWrapper(self.__path + "/" + self.__config_file_name)#self.__tags_file_name)
        config_wrapper.set_store_id(self.__id)
        self.__vocabulary_wrapper = VocabularyWrapper(self.__vocabulary_path)
        self.connect(self.__vocabulary_wrapper, QtCore.SIGNAL("changed"), self.__handle_vocabulary_changed)
        self.__store_config_wrapper = ConfigWrapper(self.__config_path)
        self.connect(self.__store_config_wrapper, QtCore.SIGNAL("changed()"), self.__handle_store_config_changed)
        
        if len(self.__name) == 0:
            self.__name = self.__path[:self.__path.rfind("/")]
        
        if not self.__is_android_store():
            # no activity is required on android tag stores
            self.__watcher.addPath(self.__parent_path)
            self.__watcher.addPath(self.__watcher_path)
        
        ## all necessary files and dirs should have been created now - so init the logger
        self.__log = LogHelper.get_store_logger(self.__path, logging.INFO) 
        self.__log.info("parent_path: '%s'" % self.__name)

        ## handle offline changes
        self.__handle_unfinished_operation()
        self.__handle_file_expiry()
        self.__handle_file_changes(self.__watcher_path)
        
    
    def __handle_store_config_changed(self):
        self.emit(QtCore.SIGNAL("store_config_changed"), self)

    def __handle_vocabulary_changed(self):
        self.emit(QtCore.SIGNAL("vocabulary_changed"), self)
        
#    def handle_offline_changes(self):
#        """
#        called after store and event-handler are created to handle (offline) modifications
#        """
#        self.__handle_file_changes(self.__watcher_path)

    def add_ignored_extensions(self, ignored_list):
        self.__file_system.add_ignored_extensions(ignored_list)

    def set_path(self, path, config_file=None, tags_file=None, vocabulary_file=None):
        """
        resets the stores path and config path (called if application config changes)
        """
        if self.__path == unicode(path) and (config_file is None or self.__config_file_name == unicode(config_file)):
            exit
        ## update changes
        self.__watcher.removePaths([self.__parent_path, self.__watcher_path])
        self.__path = unicode(path)
        if config_file is not None:
            self.__config_file_name = unicode(config_file)
        if tags_file is not None:
            self.__tags_file_name = unicode(tags_file)
        if vocabulary_file is not None:
            self.__vocabulary_file_name = unicode(vocabulary_file)
        self.__init_store()
        
    def __handle_renamed_removed_store(self):
        """
        searches the parents directory for renamed or removed stores
        """
        #print self.__parent_path
        #print self.__config_file_name
        #print ".."
        config_paths = self.__file_system.find_files(self.__parent_path, self.__config_file_name)
        #print "config paths: " + ",".join(config_paths)
        new_name = ""
        for path in config_paths:
            reader = ConfigWrapper(path)
            #print "found: " + path
            #print self.__id + ", " + reader.get_store_id()
            
            if self.__id == reader.get_store_id():
                new_name = path.split("/")[-3]
                #print "new name: " + new_name

        if new_name == "":      ## removed
            ## delete describing_nav directors
            #self.remove()
            self.emit(QtCore.SIGNAL("removed(PyQt_PyObject)"), self)
        else:                   ## renamed
            self.__path = self.__parent_path + "/" + new_name
            self.__navigation_path = self.__path + "/" + self.__navigation_dir_name
            self.__describing_nav_path = self.__path + "/" + self.__describing_nav_dir_name
            self.__categorising_nav_path = self.__path + "/" + self.__categorising_nav_dir_name
            ## update all links in windows: absolute links only
            #if self.__file_system.get_os() == EOS.Windows:
                #print "rebuild"
                #self.rebuild()
            #print "emit: " + self.__parent_path + "/" + new_name
            self.emit(QtCore.SIGNAL("renamed(PyQt_PyObject, QString)"), self, self.__parent_path + "/" + new_name)
    
    def __directory_changed(self, path):
        """
        handles directory changes of the stores directory and its parent directory 
        and finds out if the store itself was renamed/removed
        """
        if path == self.__parent_path:
            if not self.__file_system.path_exists(self.__path):
                ## store itself was changed: renamed, moved or deleted
                self.__watcher.removePath(self.__parent_path)
                self.__handle_renamed_removed_store()
        else:
            ## files or directories in the store directory have been changed
            self.__handle_file_changes(self.__watcher_path)

    def __handle_unfinished_operation(self):
        """
        looks for a opInProgress.tmp file to find out if the last operation was finished correctly
        this file is created before an operation starts and deleted afterwards
        """
        if self.__file_system.path_exists(self.__temp_progress_path + "/" + "opInProgress.tmp"):
            self.rebuild()
            
    def __create_inprogress_file(self):
        """
        creates a temporary file during an operation in progress to handle operation interruption
        """
        self.__file_system.create_file(self.__temp_progress_path + "/" + "opInProgress.tmp")

    def __remove_inprogress_file(self):
        """
        removes the temporary file after the operation succeeded
        """
        self.__file_system.remove_file(self.__temp_progress_path + "/" + "opInProgress.tmp")
        
    def __handle_file_expiry(self):
        """
        looks for expired items and moves & renames them to filename including tags in the expiry_directory
        """
        expiry_date_files = self.__tag_wrapper.get_files_with_expiry_tags(self.__expiry_prefix)
        now = datetime.datetime.now()
        for file in expiry_date_files:
            file_extension = "." + file["filename"].split(".")[-1]
            file_name = file["filename"]
            file_name = file_name[:len(file_name)-len(file_extension)]
            if int(file["exp_year"]) < now.year or (int(file["exp_year"]) == now.year and int(file["exp_month"]) < now.month):
                new_filename = file_name + " - " + "; ".join(file["category"]) + " - " + "; ".join(file["tags"]) + file_extension
                self.__file_system.rename_file(self.__watcher_path + "/" + file["filename"], self.__path + "/" + self.__expiry_dir_name + "/" + new_filename)
    
    def __get_lockfile_path(self):
        return self.__path + "/" + self.__storage_dir_name + "/" + TsConstants.DEFAULT_SYNCHRONIZATION_LOCKFILE_NAME
    
                    
    def __is_sync_active(self):
        """
        returns true when the sync is active
        """
  
        # get lock path
        path = self.__get_lockfile_path();
  
        # check if path exists        
        result = self.__file_system.path_exists(path)
        if result == False:
            return result
        
        
        # read pid from file
        old_pid = None 
        pid_file = open(path, "r")
        
        for line in pid_file.readlines():
            old_pid = line

        # close pid file
        pid_file.close()
        
        if old_pid is None or old_pid == "":
            # empty file remove
            self.__file_system.remove_file(path)
            return False
        
        # check if the pid still exists
        return PidHelper.pid_exists(old_pid)

    def __handle_file_changes(self, path):
        """
        handles the stores file and dir changes to find out if a file/directory was added, renamed, removed
        """
        if(path == self.__get_lockfile_path()):
            return
        
        ## if there is a synchronize procedure running - just do nothing
        #if(self.__is_synchronize_in_progress()):
        #    print "recognized a new file - but this must be from the sync-process"
        #    return
        
        if self.__is_sync_active():
            self.__log.info("__handle_file_changes: sync is active")
            return

        if self.__is_android_store():
            # no notifications on android stores
            self.__log.info("__handle_file_changes: no notifications on android stores")
            return
        
        # sync any changes if the´sync was active
        self.__tag_wrapper.sync_settings()
        
        ## this method does not handle the renaming or deletion of the store directory itself (only childs)
        existing_files = set(self.__file_system.get_files(path))
        existing_dirs = set(self.__file_system.get_directories(path))
        config_files = set(self.__tag_wrapper.get_files())
        captured_added_files = set(self.__pending_changes.get_added_names())
        captured_removed_files = set(self.__pending_changes.get_removed_names())

        data_files = (config_files | captured_added_files) - captured_removed_files 
        added = list((existing_files | existing_dirs) - data_files)
        removed = list(data_files - (existing_files | existing_dirs))
    
        #names = self.__pending_changes.get_added_names()
        #for name in names:
        #    self.__log.info(name)
    
        if len(added) == 1 and len(removed) == 1:
            self.__pending_changes.register(removed[0], self.__get_type(removed[0]), EFileEvent.REMOVED_OR_RENAMED)
            self.__pending_changes.register(added[0], self.__get_type(added[0]), EFileEvent.ADDED_OR_RENAMED)
            self.emit(QtCore.SIGNAL("file_renamed(PyQt_PyObject, QString, QString)"), self, removed[0], added[0])
        else:
            if len(removed) > 0:
                if len(added) == 0:
                    for item in removed:
                        self.__pending_changes.register(item, self.__get_type(item), EFileEvent.REMOVED)
                        self.emit(QtCore.SIGNAL("file_removed(PyQt_PyObject, QString)"), self, item)
                else:
                    for item in removed:
                        self.__pending_changes.register(item, self.__get_type(item), EFileEvent.REMOVED_OR_RENAMED)
                        self.emit(QtCore.SIGNAL("pending_operations_changed(PyQt_PyObject)"), self)
            if len(added) > 0:
                if len(removed) == 0:
                    for item in added:
                        self.__pending_changes.register(item, self.__get_type(item), EFileEvent.ADDED)
                        self.emit(QtCore.SIGNAL("pending_operations_changed(PyQt_PyObject)"), self)
                else:
                    for item in added:
                        self.__pending_changes.register(item, self.__get_type(item), EFileEvent.ADDED_OR_RENAMED)
                        self.emit(QtCore.SIGNAL("pending_operations_changed(PyQt_PyObject)"), self)                
        
    def __get_type(self, item):
        """
        returns the items type to be stored in pending_changes
        """
        if self.__file_system.is_directory(self.__watcher_path + "/" + unicode(item)):
            return EFileType.DIRECTORY
        return EFileType.FILE
    
    def get_name(self):
        """
        returns the stores name
        """
        return self.__name
    
    def get_id(self):
        """
        returns the stores id
        """
        return unicode(self.__id)
    def get_max_tags(self):
        return self.__store_config_wrapper.get_max_tags()
    def get_tag_separator(self):
        return self.__store_config_wrapper.get_tag_seperator()

    def get_store_path(self):
        """
        returns the root of the tagstore
        """
        return self.__path
    
    def get_pending_changes(self):
        """
        returns the stores unhandled changes 
        """
        return self.__pending_changes
    
    def move(self, new_path):
        """
        moves the whole path to the specified place 
        """
        ## first of all move the physical data to the new location 
        self.__file_system.move(self.__path, new_path)
        ## re-set the path variable
        self.__path = new_path
        ## initialize to update all necessary membervariables
        self.init()
        ## rebuild the whole store structure to make sure all links are updated
        self.rebuild()

    def remove(self):
        """
        removes all directories and links in the stores describing_nav path
        """
        for path in self.__paths_to_maintain:
            self.__file_system.delete_dir_content(path)
        
        self.emit(QtCore.SIGNAL("store_delete_end"), self.__id)
    
    def rebuild(self):
        """
        removes and rebuilds all links in the describing_nav path
        """
        self.__create_inprogress_file()
        self.__log.info("START rebuild progress")
        self.remove()
        for file in self.__tag_wrapper.get_files():
            describing_tag_list = self.__tag_wrapper.get_file_tags(file)
            categorising_tag_list = self.__tag_wrapper.get_file_categories(file)
            self.add_item_with_tags(file, describing_tag_list, categorising_tag_list)
        self.__remove_inprogress_file()
        self.emit(QtCore.SIGNAL("store_rebuild_end"), self.__name)
        self.__log.info("rebuild progress END")
    
    def rename_file(self, old_file_name, new_file_name):
        """
        renames an existing file: links and config settings 
        """
        self.__log.info("renaming: %s to %s" % (old_file_name, new_file_name))
        self.__create_inprogress_file()
        if self.__tag_wrapper.file_exists(old_file_name):
            describing_tag_list = self.__tag_wrapper.get_file_tags(old_file_name)
            categorising_tag_list = self.__tag_wrapper.get_file_categories(old_file_name)
            self.remove_file(old_file_name)
            self.add_item_with_tags(new_file_name, describing_tag_list, categorising_tag_list)

            self.__pending_changes.remove(old_file_name)
            self.__pending_changes.remove(new_file_name)
        else:
            self.__pending_changes.edit(old_file_name, new_file_name)
            self.emit(QtCore.SIGNAL("pending_operations_changed(PyQt_PyObject)"), self)
        self.__remove_inprogress_file()
        
    def remove_file(self, file_name):
        """
        removes a file: links and config settings 
        """
        self.__log.info("remove file: %s" % file_name)
        self.__create_inprogress_file()
        self.__pending_changes.remove(file_name)
        if self.__tag_wrapper.file_exists(file_name):
            describing_tag_list = self.__tag_wrapper.get_file_tags(file_name)
            categorising_tag_list = self.__tag_wrapper.get_file_categories(file_name)
            for path in self.__paths_to_maintain:
                if path == self.__describing_nav_path:
                    self.__delete_links(file_name, describing_tag_list, self.__describing_nav_path)
                if path == self.__categorising_nav_path:
                    self.__delete_links(file_name, categorising_tag_list, self.__categorising_nav_path)
                if path == self.__navigation_path:
                    #changed fron cat -> do desc tags because there are just descrbing tags when
                    #there is just the "navigation" folder
                    #self.__delete_links(file_name, categorising_tag_list, self.__navigation_path)
                    self.__delete_links(file_name, describing_tag_list, self.__navigation_path)
            self.__tag_wrapper.remove_file(file_name)
        else:
            self.emit(QtCore.SIGNAL("pending_operations_changed(PyQt_PyObject)"), self)
        self.__remove_inprogress_file()
        
    def __delete_links(self, file_name, tag_list, current_path):
        """
        deletes all links to the given file
        """
        self.__create_inprogress_file()
        for tag in tag_list:
            recursive_list = [] + tag_list
            recursive_list.remove(tag)
            self.__delete_links(file_name, recursive_list, current_path + "/" + tag)
            self.__file_system.remove_link(current_path + "/" + tag + "/" + file_name)
        self.__remove_inprogress_file()
    
    def change_expiry_prefix(self, new_prefix):
        """
        changes the expiry prefix and all existing expiry tags
        """
        ## handle changes only
        if self.__expiry_prefix == new_prefix:  
            return

        self.__log.info("changing the expiry prefix from %s to %s" % (self.__expiry_prefix, new_prefix))
        
        self.__create_inprogress_file()
        expiry_date_files = self.__tag_wrapper.get_files_with_expiry_tags(self.__expiry_prefix)
        
        for file in expiry_date_files:
            for tag in file["tags"]:
                match = re.match("^(" + self.__expiry_prefix + ")([0-9]{4})(-)([0-9]{2})", tag)
                if match:
                    self.rename_tag(tag, tag.replace(self.__expiry_prefix, new_prefix))
            
            for tag in file["category"]:
                match = re.match("^(" + self.__expiry_prefix + ")([0-9]{4})(-)([0-9]{2})", tag)
                if match:
                    self.rename_tag(tag, tag.replace(self.__expiry_prefix, new_prefix))

        ## set new prefix
        self.__expiry_prefix = new_prefix
        self.__remove_inprogress_file()
    
    def get_items(self):
        """
        returns a list of all item names in the store 
        """
        return self.__tag_wrapper.get_files()
    
    def get_tags(self):
        """
        returns a list of all describing  tags
        """
        return self.__tag_wrapper.get_all_tags()

    def get_categorizing_tags(self):
        """
        returns a list of categorizing all tags
        """
        return self.__tag_wrapper.get_all_categorizing_tags()

    def get_recent_tags(self, number):
        """
        returns a given number of recently entered tags
        """
        return self.__tag_wrapper.get_recent_tags(number)
    
    def get_popular_tags(self, number):
        """
        returns a given number of the most popular tags
        """
        return self.__tag_wrapper.get_popular_tags(number)

    def get_popular_categories(self, number):
        """
        returns a given number of the most popular tags
        """
        return self.__tag_wrapper.get_popular_categories(number)

    def get_recent_categories(self, number):
        """
        returns a given number of recently entered tags
        """
        return self.__tag_wrapper.get_recent_categories(number)

    def get_describing_tags_for_item(self, item_name):
        """
        returns all describing tags associated with the given item
        """
        return self.__tag_wrapper.get_file_tags(item_name)
        
    def get_categorizing_tags_for_item(self, item_name):
        """
        returns all categorizing tags associated with the given item
        """
        return self.__tag_wrapper.get_file_categories(item_name)
        

    def get_show_category_line(self):
        return self.__store_config_wrapper.get_show_category_line()

    def is_controlled_vocabulary(self):
        """
        return True if there is just controlled vocabulary allowed in the second tagline 
        """
        setting = self.__store_config_wrapper.get_show_category_line()
        if setting == ECategorySetting.ENABLED_ONLY_PERSONAL or setting == ECategorySetting.ENABLED_SINGLE_CONTROLLED_TAGLINE:
            return True
        return False
    
    def get_datestamp_format(self):
        return self.__store_config_wrapper.get_datestamp_format()

    def get_datestamp_hidden(self):
        return self.__store_config_wrapper.get_datestamp_hidden()
    
    def get_category_mandatory(self):
        return self.__store_config_wrapper.get_category_mandatory()

    def __name_in_conflict(self, file_name, describing_tag_list, categorising_tag_list):
        """
        checks for conflicts and returns the result as boolean
        """
        
        ## both lists could be none if there is just one tagline
        if(categorising_tag_list is None):
            categorising_tag_list = []
        if(describing_tag_list is None):
            describing_tag_list = []
        
        #TODO: extend functionality: have a look at #18 (Wiki)
        existing_files = self.__tag_wrapper.get_files()
        existing_tags = self.__tag_wrapper.get_all_tags()
        tag_list = list(set(describing_tag_list) | set(categorising_tag_list))

        file_name = unicode(file_name)        

        if file_name in existing_tags:
            return [file_name, EConflictType.FILE]
        for tag in tag_list:
            if tag in existing_files:
                return [tag, EConflictType.TAG]
        return ["", None]
    
    def add_item_list_with_tags(self, file_name_list, describing_tag_list, categorising_tag_list=None, silent=False):
        for item in file_name_list:
            self.add_item_with_tags(item, describing_tag_list, categorising_tag_list, silent)
        
    def add_item_with_tags(self, file_name, describing_tag_list, categorising_tag_list=None, silent=False):
        """
        adds tags to the given file, resets existing tags
        """
        #TODO: if file_name already in config, delete missing tags and recreate whole link structure
        #existing tags will not be recreated in windows-> linux, osx???
         
        #self.__log.info("add item with tags to navigation: itemname: %s" % file_name)
        #self.__log.info("describing tags: %s" % describing_tag_list)
        #self.__log.info("categorizing tags: %s" % categorising_tag_list)
        ## throw error if inodes run short
        if self.__file_system.inode_shortage(self.__config_path):
            self.__log.error("inode threshold has exceeded")
            raise InodeShortageException(TsRestrictions.INODE_THRESHOLD)
        ## throw error if item-names and tag-names (new and existing) are in conflict
        conflict = self.__name_in_conflict(file_name, describing_tag_list, categorising_tag_list)
        if conflict[0] != "":
            self.__log.error("name_in_conflict_error: %s, %s" % (conflict[0], conflict[1]))
            raise NameInConflictException(conflict[0], conflict[1])
        ## ignore multiple tags
        describing_tags = list(set(describing_tag_list))
        categorising_tags = []
        if categorising_tag_list is not None:
            categorising_tags = list(set(categorising_tag_list))


        #try:
        self.__create_inprogress_file()
        
        #print "-----"
        #print self.__describing_nav_path
        #print self.__categorising_nav_path
        #print self.__navigation_path
        # is it not an android store

        start = time()#time.clock() ## performance measure
        self.__log.info("starting to create TagTrees for item: %s" % file_name) ## FIXXME: remove this line if time measurement works

        if not self.__is_android_store():
            for path in self.__paths_to_maintain:
                if path == self.__describing_nav_path:
                    self.__build_store_navigation(file_name, describing_tags, self.__describing_nav_path)
                elif path == self.__categorising_nav_path:
                    self.__build_store_navigation(file_name, categorising_tags, self.__categorising_nav_path)
                elif path == self.__navigation_path:
                    self.__build_store_navigation(file_name, describing_tags, self.__navigation_path)
        #except:
        #    raise Exception, self.trUtf8("An error occurred during building the navigation path(s) and links!")
        #try:
        
        self.__tag_wrapper.set_file(file_name, describing_tags, categorising_tags)

        self.__pending_changes.remove(file_name)
        self.__remove_inprogress_file()
        #except:
        #    raise Exception, self.trUtf8("An error occurred during saving file and tags to configuration file!")
        ## scalability test
        ## print "number of tags: " + str(len(tags)) + ", time: " + str(time.clock()-start)
        self.__log.info("tagged item " + file_name + \
                            ", # descr tags: " + str(len(describing_tags)) + \
                            ", # categ tags: " + str(len(categorising_tags)) + \
                            "; %f" % (time()-start) )  ## performance measure
        ## CAUTION: time.clock() measures something weird, but not actual time
        
    def __build_store_navigation(self, link_name, tag_list, current_path):
        """
        builds the whole directory and link-structure (describing & categorising nav path) inside a stores filesystem
        """
        link_source = self.__watcher_path + "/" + link_name

        for tag in tag_list:
            self.__file_system.create_dir(current_path + "/" + tag)
            self.__file_system.create_link(link_source, current_path + "/" + tag + "/" + link_name)
            recursive_list = [] + tag_list
            recursive_list.remove(tag)
            self.__build_store_navigation(link_name, recursive_list, current_path + "/" + tag)
    
    def rename_tag(self, old_tag_name, new_tag_name):
        """
        renames a tag inside the store 
        """
        self.__create_inprogress_file()
        ##get all affected files per tag
        files = self.__tag_wrapper.get_files_with_tag(old_tag_name)
        self.delete_tags([old_tag_name])
        for file in files:
            if old_tag_name in file["tags"]:
                file["tags"].append(new_tag_name)
                file["tags"].remove(old_tag_name)
            if old_tag_name in file["category"]:
                file["category"].append(new_tag_name)
                file["category"].remove(old_tag_name)
            self.add_item_with_tags(file["filename"], file["tags"], file["category"]) 
        self.__remove_inprogress_file()
    
    def item_exists(self, item_name):
        """
        returns True or False if the item is entered in the store.tgs
        """
        return self.__tag_wrapper.file_exists(item_name)
    
    def delete_tags(self, tag_list):
        """
        delete tags inside the store
        """
        self.__create_inprogress_file()
        for tag_name in tag_list:
            ##get all affected files per tag
            files = self.__tag_wrapper.get_files_with_tag(tag_name)
            for file in files:
                for path in self.__paths_to_maintain:
                    if path == self.__describing_nav_path:
                        self.__delete_tag_folders(tag_name, file["tags"], self.__describing_nav_path)
                    if path == self.__categorising_nav_path:
                        self.__delete_tag_folders(tag_name, file["category"], self.__categorising_nav_path)
                    if path == self.__navigation_path:
                        self.__delete_tag_folders(tag_name, file["tags"], self.__navigation_path)
            ##remove tag from config file
            self.__tag_wrapper.remove_tag(tag_name)
        self.__remove_inprogress_file()
        
    def __delete_tag_folders(self, affected_tag, tag_list, current_path):
        """
        recursive function to delete the tag directories within the describing_nav structure
        """
        if affected_tag not in tag_list:
            return
        self.__file_system.delete_dir(current_path + "/" + affected_tag)
        diff_list = [] + tag_list
        diff_list.remove(affected_tag)
        for tag in diff_list:
            recursive_list = [] + tag_list
            recursive_list.remove(tag)
            self.__delete_tag_folders(affected_tag, recursive_list, current_path + "/" + tag)
        
    def get_controlled_vocabulary(self):
        """
        returns a predefined list of allowed strings (controlled vocabulary) to be used for categorizing
        """
        return self.__vocabulary_wrapper.get_vocabulary()
    
    def set_controlled_vocabulary(self, vocabulary_set):
        self.__vocabulary_wrapper.set_vocabulary(vocabulary_set)

    def get_storage_directory(self):
        """
        returns the path of the storage directory where the items are stored
        """        
        return self.__watcher_path
    
    def get_android_root_directory(self):
        """
        returns the root directory of the android removable storage drive
        """
        res = self.__path[:self.__path.find("/tagstore")] ###FIXME hardcoded constant
        return res
    
    def __is_android_store(self):
        """
        returns True if the store is an android store
        """
        
        # get 'android_store' store setting
        result = self.__store_config_wrapper.get_android_store()
        
        # is this setting active
        if result is None or result =="":
            return False
        
        if int(result) == 0:
            return False
        else:
            return True
        
    def is_android_store(self):
        return self.__is_android_store()
        
    def set_sync_tags(self, file_name, describing_tags, categorising_tags):
        """
        updates the sync tags
        """
        
        # is the and sync tag wrapper initialized
        if self.__sync_tag_wrapper != None:
            self.__sync_tag_wrapper.set_file(file_name, describing_tags, categorising_tags)

    
    def is_sync_active(self):
        """
        returns True when the sync is active
        """
        return self.__is_sync_active()
    
    def create_sync_lock_file(self):
        """
        creates the sync lock file
        """
        
        if self.__is_sync_active():
            # sync is already active
            return False
        
        # get sync lock file
        path = self.__get_lockfile_path()
        
        # get current pid
        pid = PidHelper.get_current_pid()
        
        # write new pid file
        pid_file = open(path, "w")
        pid_file.write(str(pid))
        pid_file.close()
        
        # done
        return True
    
    def remove_sync_lock_file(self):
        """
        removes the sync lock file
        """
        
        # sync lockfile
        path = self.__get_lockfile_path()
        
        # remove lock file
        self.__file_system.remove_file(path)

    def finish_sync(self):
        """
        writes all changes to the config file / sync file
        """
        
        if self.__tag_wrapper != None:
            self.__tag_wrapper.sync_settings()
        
        if self.__sync_tag_wrapper != None:
            self.__sync_tag_wrapper.sync_settings()


    def get_tag_recommendation(self, number, file_name):
        """
        Changes from Georg
        returns the recommendation
        """
        dictionary = self.__recommender.get_tag_recommendation(
                              self.__tag_wrapper,
                              file_name,
                              number,
                              self.__storage_dir_name)
        
        
        list_with_high_prio = []
        
        threshold = 0.9
        if len(dictionary) <= number:
            threshold = 0.5
        
        for tag_name, rating in dictionary.iteritems():
            if rating > threshold:
                list_with_high_prio.append(tag_name)
    
        
        
        list1 = sorted(dictionary.iteritems(), key=lambda (k,v): (v,k), reverse=True)
        return_list = []
        #for item in list[:number]:
        for item in list1[:15]:
            if item[0] in list_with_high_prio:
                return_list.append(item[0])  
        return return_list

        #list = sorted(dictionary.iteritems(), key=lambda (k,v): (v,k), reverse=True)
        #return_list = []
        #print list
        #for item in list[:number]:
        #for item in list:
        #    return_list.append(item[0])
        #print return_list
        #return return_list

        
    def get_cat_recommendation(self, number, file_name):
        """
        Changes from Georg
        returns the recommendation
        """
        allowed_dict = {}
        if self.is_controlled_vocabulary():
                allowed_dict = self.get_controlled_vocabulary()
        
        dictionary = self.__recommender.get_cat_recommendation(
                              self.__tag_wrapper,
                              file_name,
                              number,
                              self.__storage_dir_name,
                              allowed_dict)
        

        list_with_high_prio = []
        
        threshold = 0.9
        if len(dictionary) <= number:
            threshold = 0.5
        
        for tag_name, rating in dictionary.iteritems():
            if rating > threshold:
                list_with_high_prio.append(tag_name)
    
        
        
        list1 = sorted(dictionary.iteritems(), key=lambda (k,v): (v,k), reverse=True)
        return_list = []
        #for item in list[:number]:
        for item in list1[:15]:
            if item[0] in list_with_high_prio:
                return_list.append(item[0])  
        
        return return_list
        '''
        if len(dictionary) > number/2:
            for item in list:
                if dictionary[item] < 0.6:
                    return_list.append(item[0])
            print "if"
            print return_list
            
        else:
            for item in list:
                return_list.append(item[0])
            print return_list
        return return_list
        '''
        
        
    def get_tag_cloud(self, name):
        dict = self.__tag_wrapper.get_tag_dict(self.__tag_wrapper.KEY_TAGS)
        if len(dict) < 10:
            extension = self.__recommender.get_file_extension(name)
            self.__recommender.recommend_new_tags(dict, extension)
        return self.__tagcloud.create_tag_cloud(dict)
    
    def get_cat_cloud(self, name):
        tmp_dict = self.__tag_wrapper.get_tag_dict(self.__tag_wrapper.KEY_CATEGORIES)
        
        if len(tmp_dict) < 10:
            extension = self.__recommender.get_file_extension(name)
            self.__recommender.recommend_new_tags(tmp_dict, extension)
        
        
        dict = {}
        if self.is_controlled_vocabulary():
            allowed_list = self.get_controlled_vocabulary()
            for cat, size in tmp_dict.iteritems():
                if cat in allowed_list:
                    dict.setdefault(cat, size)
            for cat in allowed_list:
                if cat not in dict:
                    dict.setdefault(cat, 0)
            return self.__tagcloud.create_tag_cloud(dict)
        else:
            return self.__tagcloud.create_tag_cloud(tmp_dict)
    
    def get_tagline_config(self):
        return self.__tagline_config