forked from Github/filetags
added caching of tags and misc refactoring
This commit is contained in:
parent
f057842008
commit
9f072555d4
2 changed files with 146 additions and 161 deletions
295
filetags.py
295
filetags.py
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
PROG_VERSION = u"Time-stamp: <2016-10-15 13:57:49 vk>"
|
||||
PROG_VERSION = u"Time-stamp: <2016-10-15 17:03:08 vk>"
|
||||
|
||||
## TODO:
|
||||
## - fix parts marked with «FIXXME»
|
||||
|
|
@ -18,7 +18,10 @@ PROG_VERSION = u"Time-stamp: <2016-10-15 13:57:49 vk>"
|
|||
## - tagfilter: additional parameter to move matching files to a temporary subfolder
|
||||
## - renaming/deleting of symlinks does not modify original files
|
||||
## - tagfilter: --recursive :: recursively going into subdirectories and
|
||||
## collecting items (into one target directory)
|
||||
## collecting items (into one target directory) for:
|
||||
## - adding tags
|
||||
## - removing tags
|
||||
## - filter
|
||||
## - tagfilter: --notag :: do not ask for tags, use all items that got no tag
|
||||
## at all
|
||||
## - tagfilter: --ignoredirs :: do not symlink/copy directories
|
||||
|
|
@ -33,6 +36,7 @@ PROG_VERSION = u"Time-stamp: <2016-10-15 13:57:49 vk>"
|
|||
|
||||
import importlib
|
||||
|
||||
|
||||
def save_import(library):
|
||||
try:
|
||||
globals()[library] = importlib.import_module(library)
|
||||
|
|
@ -67,9 +71,9 @@ try:
|
|||
except ValueError:
|
||||
TTY_HEIGHT, TTY_WIDTH = 80, 80
|
||||
|
||||
max_file_length = 0 ## will be set after iterating over source files182
|
||||
max_file_length = 0 # will be set after iterating over source files182
|
||||
|
||||
unique_tags = [[u'teststring1', u'teststring2']] ## list of list which contains tags that are mutually exclusive
|
||||
unique_tags = [[u'teststring1', u'teststring2']] # list of list which contains tags that are mutually exclusive
|
||||
## Note: u'teststring1' and u'teststring2' are hard-coded for testing purposes.
|
||||
## You might delete them if you don't use my unit test suite.
|
||||
|
||||
|
|
@ -121,6 +125,8 @@ FILE_WITH_EXTENSION_REGEX = re.compile("(.*)\.(.*)$")
|
|||
FILE_WITH_EXTENSION_REGEX_FILENAME_INDEX = 1
|
||||
FILE_WITH_EXTENSION_REGEX_EXTENSION_INDEX = 2
|
||||
|
||||
cache_of_tags_by_folder = {}
|
||||
controlled_vocabulary_filename = u''
|
||||
|
||||
parser = optparse.OptionParser(usage=USAGE)
|
||||
|
||||
|
|
@ -139,6 +145,9 @@ parser.add_option("--imageviewer", dest="imageviewer",
|
|||
parser.add_option("-i", "--interactive", action="store_true", dest="interactive",
|
||||
help="interactive mode: ask for (a)dding or (r)emoving and name of tag(s)")
|
||||
|
||||
parser.add_option("--recursive", dest="recursive", action="store_true",
|
||||
help="recursively go through the current directory and all of its subdirectories (for tag-gardening only)")
|
||||
|
||||
parser.add_option("-s", "--dryrun", dest="dryrun", action="store_true",
|
||||
help="enable dryrun mode: just simulate what would happen, do not modify files")
|
||||
|
||||
|
|
@ -376,6 +385,7 @@ def extract_filenames_from_argument(argument):
|
|||
## FIXXME: works at my computer without need to convertion but add check later on
|
||||
return argument
|
||||
|
||||
|
||||
def get_unique_tags_from_filename(filename):
|
||||
"""
|
||||
Extracts tags that occur in the array of arrays "unique_tags".
|
||||
|
|
@ -389,9 +399,10 @@ def get_unique_tags_from_filename(filename):
|
|||
for tag in filetags:
|
||||
for taggroup in unique_tags:
|
||||
if tag in taggroup:
|
||||
result.append(tag)
|
||||
result.append(tag)
|
||||
return result
|
||||
|
||||
|
||||
def item_contained_in_list_of_lists(item, list_of_lists):
|
||||
"""
|
||||
Returns true if item is member of at least one list in list_of_lists.
|
||||
|
|
@ -406,6 +417,7 @@ def item_contained_in_list_of_lists(item, list_of_lists):
|
|||
return item, current_list
|
||||
return None, None
|
||||
|
||||
|
||||
def print_item_transition(source, destination, transition):
|
||||
"""
|
||||
Returns true if item is member of at least one list in list_of_lists.
|
||||
|
|
@ -430,8 +442,6 @@ def print_item_transition(source, destination, transition):
|
|||
## probably enough space: screen output with one item per line
|
||||
|
||||
source_width = max_file_length
|
||||
#logging.debug('source-width is ' + str(source_width))
|
||||
#logging.debug('source is "' + str(source) + '"')
|
||||
|
||||
try:
|
||||
arrow_left = u'――'
|
||||
|
|
@ -485,8 +495,7 @@ def handle_file(filename, tags, do_remove, do_filter, dryrun):
|
|||
os.symlink(os.path.join(os.getcwdu(), filename),
|
||||
os.path.join(TAGFILTER_DIRECTORY, filename))
|
||||
|
||||
|
||||
else: ## add or remove tags:
|
||||
else: # add or remove tags:
|
||||
new_filename = filename
|
||||
|
||||
for tagname in tags:
|
||||
|
|
@ -549,7 +558,7 @@ def add_tag_to_countdict(tag, tags):
|
|||
return tags
|
||||
|
||||
|
||||
def get_tags_from_files_and_subfolders(startdir=os.getcwdu(), starttags=False, recursive=False):
|
||||
def get_tags_from_files_and_subfolders(startdir=os.getcwdu(), use_cache=True):
|
||||
"""
|
||||
Traverses the file system starting with given directory,
|
||||
returns dict of all tags (including starttags) of all file
|
||||
|
|
@ -557,6 +566,9 @@ def get_tags_from_files_and_subfolders(startdir=os.getcwdu(), starttags=False, r
|
|||
@param return: dict of tags and their number of occurrence
|
||||
"""
|
||||
|
||||
## add ", starttags=False" to parameters to enable this feature in future
|
||||
starttags = False
|
||||
|
||||
assert os.path.isdir(startdir)
|
||||
|
||||
if not starttags:
|
||||
|
|
@ -565,20 +577,39 @@ def get_tags_from_files_and_subfolders(startdir=os.getcwdu(), starttags=False, r
|
|||
assert starttags.__class__ == dict
|
||||
tags = starttags
|
||||
|
||||
assert not recursive ## FIXXME: not implemented yet
|
||||
global cache_of_tags_by_folder
|
||||
|
||||
logging.debug('get_tags_from_files_and_subfolders called with startdir [%s], starttags [%s], recursive[%s]' % (startdir, str(starttags), str(recursive)))
|
||||
for root, dirs, files in os.walk(startdir):
|
||||
logging.debug('get_tags_from_files_and_subfolders: root [%s]' % root)
|
||||
for filename in files:
|
||||
for tag in extract_tags_from_filename(filename):
|
||||
tags = add_tag_to_countdict(tag, tags)
|
||||
for dirname in dirs:
|
||||
for tag in extract_tags_from_filename(dirname):
|
||||
tags = add_tag_to_countdict(tag, tags)
|
||||
break # do not loop
|
||||
logging.debug('get_tags_from_files_and_subfolders called with startdir [%s], cached startdirs [%s]' % (startdir, str(len(cache_of_tags_by_folder.keys()))))
|
||||
|
||||
return tags
|
||||
if use_cache and startdir in cache_of_tags_by_folder.keys():
|
||||
logging.debug("found " + str(len(cache_of_tags_by_folder[startdir])) + " tags in cache for directory: " + startdir)
|
||||
return cache_of_tags_by_folder[startdir]
|
||||
|
||||
else:
|
||||
|
||||
for root, dirs, files in os.walk(startdir):
|
||||
|
||||
# logging.debug('get_tags_from_files_and_subfolders: root [%s]' % root) # LOTS of debug output
|
||||
|
||||
for filename in files:
|
||||
for tag in extract_tags_from_filename(filename):
|
||||
tags = add_tag_to_countdict(tag, tags)
|
||||
|
||||
for dirname in dirs:
|
||||
for tag in extract_tags_from_filename(dirname):
|
||||
tags = add_tag_to_countdict(tag, tags)
|
||||
|
||||
## Enable recursive directory traversal for specific options:
|
||||
if not (options.recursive and (options.list_tags_by_alphabet or
|
||||
options.list_tags_by_number or
|
||||
options.list_unknown_tags or
|
||||
options.tag_gardening)):
|
||||
break # do not loop
|
||||
|
||||
logging.debug("Writing " + str(len(tags.keys())) + " tags in cache for directory: " + startdir)
|
||||
if use_cache:
|
||||
cache_of_tags_by_folder[startdir] = tags
|
||||
return tags
|
||||
|
||||
|
||||
def find_similar_tags(tag, tags):
|
||||
|
|
@ -605,86 +636,18 @@ def find_similar_tags(tag, tags):
|
|||
return close_but_not_exact_matches
|
||||
|
||||
|
||||
def list_tags_by_alphabet(only_with_similar_tags=False, vocabulary=False):
|
||||
"""
|
||||
Traverses the file system, extracts all tags, prints them sorted by alphabet.
|
||||
Tags that appear also in the vocabulary get marked in the output.
|
||||
|
||||
@param only_with_similar_tags: if true, print out only tags with similarity to others
|
||||
@param vocabulary: array of tags from controlled vocabulary or False
|
||||
@param return: dict of tags (if only_with_similar_tags, tags without similar ones are omitted)
|
||||
"""
|
||||
|
||||
tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu(), recursive=False)
|
||||
if not tag_dict:
|
||||
print "\nNo file containing tags found in this folder hierarchy.\n"
|
||||
return {}
|
||||
|
||||
## determine maximum length of strings for formatting:
|
||||
maxlength_tags = max(len(s) for s in tag_dict.keys()) + len(HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE)
|
||||
maxlength_count = len(str(abs(max(tag_dict.values()))))
|
||||
if maxlength_count < 5:
|
||||
maxlength_count = 5
|
||||
|
||||
hint_for_being_in_vocabulary = ''
|
||||
print("\n {0:{1}s} : count".format(u'tag', maxlength_tags))
|
||||
print " " + "-" * (maxlength_tags + maxlength_count + 3)
|
||||
|
||||
## sort dict of (tag, count) according to tag name
|
||||
for tuple in sorted(tag_dict.items(), key=operator.itemgetter(0)):
|
||||
|
||||
close_matches = find_similar_tags(tuple[0], tag_dict.keys())
|
||||
see_also = u''
|
||||
|
||||
## if similar names found, format them accordingly for output:
|
||||
if len(close_matches) > 0:
|
||||
see_also = u' (similar to: ' + ', '.join(close_matches) + u')'
|
||||
|
||||
if (only_with_similar_tags and len(close_matches) > 0) or not only_with_similar_tags:
|
||||
if vocabulary and tuple[0] in vocabulary:
|
||||
hint_for_being_in_vocabulary = HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE
|
||||
else:
|
||||
hint_for_being_in_vocabulary = ''
|
||||
print " {0:{1}s} : {2:{3}}{4}".format(tuple[0] + hint_for_being_in_vocabulary, maxlength_tags, tuple[1], maxlength_count, see_also)
|
||||
|
||||
if only_with_similar_tags and len(close_matches) == 0:
|
||||
## remove entries from dict for returning only tags with similar tag entries:
|
||||
del tag_dict[tuple[0]]
|
||||
|
||||
print ''
|
||||
|
||||
return tag_dict
|
||||
|
||||
|
||||
def list_tags_by_number(max_tag_count=0, vocabulary=False):
|
||||
"""
|
||||
Traverses the file system, extracts all tags, prints them sorted by tag usage count.
|
||||
Tags that appear also in the vocabulary get marked in the output.
|
||||
|
||||
@param max_tag_count: print only tags which occur less or equal to this number (disabled if 0)
|
||||
@param vocabulary: array of tags from controlled vocabulary or False
|
||||
@param return: dict of tags (if max_tag_count is set, returned entries are set accordingly)
|
||||
"""
|
||||
|
||||
tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu(), recursive=False)
|
||||
if not tag_dict:
|
||||
print "\nNo file containing tags found in this folder hierarchy.\n"
|
||||
return {}
|
||||
|
||||
print_tag_dict(tag_dict, max_tag_count, vocabulary)
|
||||
|
||||
return tag_dict
|
||||
|
||||
def print_tag_dict(tag_dict, max_tag_count=0, vocabulary=False):
|
||||
def print_tag_dict(tag_dict_reference, vocabulary=False, sort_index=0, print_similar_vocabulary_tags=False, print_only_tags_with_similar_tags=False):
|
||||
"""
|
||||
Takes a dictionary which holds tag names and their occurrence and prints it to stdout.
|
||||
Tags that appear also in the vocabulary get marked in the output.
|
||||
|
||||
@param tag_dict: a dictionary holding tags and their occurrence number
|
||||
@param vocabulary: array of tags from controlled vocabulary or False
|
||||
@param max_tag_count: print only tags which occur less or equal to this number (disabled if 0)
|
||||
"""
|
||||
|
||||
tag_dict = {}
|
||||
tag_dict = tag_dict_reference
|
||||
|
||||
## determine maximum length of strings for formatting:
|
||||
maxlength_tags = max(len(s) for s in tag_dict.keys()) + len(HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE)
|
||||
maxlength_count = len(str(abs(max(tag_dict.values()))))
|
||||
|
|
@ -697,21 +660,31 @@ def print_tag_dict(tag_dict, max_tag_count=0, vocabulary=False):
|
|||
print u"\n (Tags marked with \"" + HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE + "\" appear in your vocabulary.)"
|
||||
print "\n {0:{1}} : {2:{3}}".format(u'count', maxlength_count, u'tag', maxlength_tags)
|
||||
print " " + '-' * (maxlength_tags + maxlength_count + 7)
|
||||
for tuple in sorted(tag_dict.items(), key=operator.itemgetter(1)):
|
||||
## sort dict of (tag, count) according to count
|
||||
if (max_tag_count > 0 and tuple[1] <= max_tag_count) or max_tag_count == 0:
|
||||
if vocabulary and tuple[0] in vocabulary:
|
||||
hint_for_being_in_vocabulary = HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE
|
||||
for tuple in sorted(tag_dict.items(), key=operator.itemgetter(sort_index)):
|
||||
## sort dict of (tag, count) according to sort_index
|
||||
|
||||
if vocabulary and tuple[0] in vocabulary:
|
||||
hint_for_being_in_vocabulary = HINT_FOR_BEING_IN_VOCABULARY_TEMPLATE
|
||||
else:
|
||||
hint_for_being_in_vocabulary = ''
|
||||
|
||||
similar_tags_list = []
|
||||
if vocabulary and print_similar_vocabulary_tags:
|
||||
tags_for_comparing = list(set(tag_dict.keys()).union(set(vocabulary))) # unified elements of both lists
|
||||
similar_tags_list = find_similar_tags(tuple[0], tags_for_comparing)
|
||||
if similar_tags_list:
|
||||
similar_tags = u' (similar to: ' + ', '.join(similar_tags_list) + u')'
|
||||
else:
|
||||
hint_for_being_in_vocabulary = ''
|
||||
similar_tags = u''
|
||||
else:
|
||||
similar_tags = u''
|
||||
|
||||
print " {0:{1}} : {2:{3}}".format(tuple[1], maxlength_count, tuple[0] + hint_for_being_in_vocabulary, maxlength_tags)
|
||||
if (print_only_tags_with_similar_tags and similar_tags_list) or not print_only_tags_with_similar_tags:
|
||||
print " {0:{1}} : {2:{3}} {4}".format(tuple[1], maxlength_count, tuple[0] + hint_for_being_in_vocabulary, maxlength_tags, similar_tags)
|
||||
|
||||
if max_tag_count > 0 and tuple[1] > max_tag_count:
|
||||
## remove entries that exceed max_tag_count limit:
|
||||
del tag_dict[tuple[0]]
|
||||
print ''
|
||||
|
||||
|
||||
def print_tag_set(tag_set, vocabulary=False, print_similar_vocabulary_tags=False):
|
||||
"""
|
||||
Takes a set which holds tag names and prints it to stdout.
|
||||
|
|
@ -737,8 +710,12 @@ def print_tag_set(tag_set, vocabulary=False, print_similar_vocabulary_tags=False
|
|||
hint_for_being_in_vocabulary = ''
|
||||
|
||||
if vocabulary and print_similar_vocabulary_tags:
|
||||
tags_for_comparing = list(tag_set.union(set(vocabulary))) ## unified elements of both lists
|
||||
similar_tags = u' (similar to: ' + ', '.join(find_similar_tags(tag, tags_for_comparing)) + u')'
|
||||
tags_for_comparing = list(tag_set.union(set(vocabulary))) # unified elements of both lists
|
||||
similar_tags_list = find_similar_tags(tag, tags_for_comparing)
|
||||
if similar_tags_list:
|
||||
similar_tags = u' (similar to: ' + ', '.join(similar_tags_list) + u')'
|
||||
else:
|
||||
similar_tags = u''
|
||||
else:
|
||||
similar_tags = u''
|
||||
|
||||
|
|
@ -747,34 +724,25 @@ def print_tag_set(tag_set, vocabulary=False, print_similar_vocabulary_tags=False
|
|||
print ''
|
||||
|
||||
|
||||
|
||||
def list_unknown_tags():
|
||||
def list_unknown_tags(file_tag_dict):
|
||||
"""
|
||||
Traverses the file system, extracts all tags, prints tags that are found in file names which are not found in the controlled vocabulary file .filetags
|
||||
|
||||
@param return: dict of tags (if max_tag_count is set, returned entries are set accordingly)
|
||||
"""
|
||||
|
||||
file_tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu(), recursive=False)
|
||||
if not file_tag_dict:
|
||||
print "\nNo file containing tags found in this folder hierarchy.\n"
|
||||
return {}
|
||||
|
||||
vocabulary = locate_and_parse_controlled_vocabulary(False)
|
||||
|
||||
## filter out known tags from tag_dict
|
||||
tag_dict = {}
|
||||
for entry in file_tag_dict:
|
||||
if entry not in vocabulary:
|
||||
tag_dict[entry] = file_tag_dict[entry]
|
||||
unknown_tag_dict = {key: value for key, value in file_tag_dict.items() if key not in vocabulary}
|
||||
|
||||
if len(tag_dict) == 0:
|
||||
print "\n " + str(len(file_tag_dict)) + " different tags were found in file names which are all" + \
|
||||
" part of your .filetags vocabulary (consisting of " + str(len(vocabulary)) + " tags).\n"
|
||||
if unknown_tag_dict:
|
||||
print_tag_dict(unknown_tag_dict, vocabulary)
|
||||
else:
|
||||
print_tag_dict(tag_dict, vocabulary)
|
||||
print "\n " + str(len(file_tag_dict)) + " different tags were found in file names which are all" + \
|
||||
" part of your .filetags vocabulary (consisting of " + str(len(vocabulary)) + " tags).\n"
|
||||
|
||||
return tag_dict
|
||||
return unknown_tag_dict
|
||||
|
||||
|
||||
def handle_tag_gardening(vocabulary):
|
||||
|
|
@ -782,23 +750,25 @@ def handle_tag_gardening(vocabulary):
|
|||
This method is quite handy to find tags that might contain typos or do not
|
||||
differ much from other tags. You might want to rename them accordinly.
|
||||
|
||||
Tags are gathered from the file system non-recursive.
|
||||
Tags are gathered from the file system.
|
||||
|
||||
Tags that appear also in the vocabulary get marked in the output.
|
||||
|
||||
FIXXME: this is *not* performance optimized since it traverses the file
|
||||
system multiple times!
|
||||
|
||||
@param vocabulary: array containing the controlled vocabulary (or False)
|
||||
@param return: -
|
||||
"""
|
||||
|
||||
tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu(), recursive=False)
|
||||
tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu())
|
||||
if not tag_dict:
|
||||
print "\nNo file containing tags found in this folder hierarchy.\n"
|
||||
return
|
||||
|
||||
print u"\nYou have used " + str(len(tag_dict)) + " tags in total.\n"
|
||||
|
||||
if vocabulary:
|
||||
|
||||
print u'\nYour controlled vocabulary is defined in ' + controlled_vocabulary_filename + ' and contains ' + str(len(vocabulary)) + ' tags.\n'
|
||||
|
||||
vocabulary_tags_not_used = set(vocabulary) - set(tag_dict.keys())
|
||||
if vocabulary_tags_not_used:
|
||||
print u"\nTags from your vocabulary which you didn't use:\n"
|
||||
|
|
@ -810,15 +780,17 @@ def handle_tag_gardening(vocabulary):
|
|||
print_tag_set(tags_not_in_vocabulary)
|
||||
|
||||
print "\nTags that appear only once are most probably typos or you have forgotten them:"
|
||||
tags_by_number = list_tags_by_number(max_tag_count=1, vocabulary=vocabulary)
|
||||
tags_only_used_once_dict = {key: value for key, value in tag_dict.items() if value < 2}
|
||||
print_tag_dict(tags_only_used_once_dict, vocabulary, sort_index=0, print_only_tags_with_similar_tags=False)
|
||||
|
||||
print "\nTags which have similar other tags are probably typos or plural/singular forms of others:"
|
||||
tags_by_alphabet = list_tags_by_alphabet(only_with_similar_tags=True, vocabulary=vocabulary)
|
||||
tags_for_comparing = list(set(tag_dict.keys()).union(set(vocabulary))) # unified elements of both lists
|
||||
only_similar_tags_by_alphabet_dict = {key: value for key, value in tag_dict.items() if find_similar_tags(key, tags_for_comparing)}
|
||||
print_tag_dict(only_similar_tags_by_alphabet_dict, vocabulary, sort_index=0, print_similar_vocabulary_tags=True)
|
||||
|
||||
set_by_number = set(tags_by_number.keys())
|
||||
set_by_alphabet = set(tags_by_alphabet.keys())
|
||||
tags_in_both_outputs = set_by_number.intersection(set_by_alphabet)
|
||||
hint_for_being_in_vocabulary = ''
|
||||
tags_only_used_once_set = set(tags_only_used_once_dict.keys())
|
||||
only_similar_tags_by_alphabet_set = set(only_similar_tags_by_alphabet_dict.keys())
|
||||
tags_in_both_outputs = tags_only_used_once_set.intersection(only_similar_tags_by_alphabet_set)
|
||||
|
||||
if tags_in_both_outputs != set([]):
|
||||
print "\nIf tags appear in both lists from above (only once and similar to others), they most likely\nrequire your attention:"
|
||||
|
|
@ -890,6 +862,8 @@ def locate_and_parse_controlled_vocabulary(startfile):
|
|||
tags = []
|
||||
with codecs.open(filename, encoding='utf-8') as filehandle:
|
||||
logging.debug('locate_and_parse_controlled_vocabulary: reading controlled vocabulary in [%s]' % filename)
|
||||
global controlled_vocabulary_filename
|
||||
controlled_vocabulary_filename = filename
|
||||
for rawline in filehandle:
|
||||
line = rawline.strip()
|
||||
if BETWEEN_TAG_SEPARATOR in line:
|
||||
|
|
@ -948,7 +922,7 @@ def print_tag_shortcut_with_numbers(tag_list, tags_get_added=True, tags_get_link
|
|||
print u' ' + u' ⋅ '.join(list_of_tag_hints)
|
||||
except UnicodeEncodeError:
|
||||
print u' ' + u' - '.join(list_of_tag_hints)
|
||||
print u'' ## newline at end
|
||||
print u'' # newline at end
|
||||
|
||||
|
||||
def check_for_possible_shortcuts_in_entered_tags(tags, list_of_shortcut_tags):
|
||||
|
|
@ -967,12 +941,11 @@ def check_for_possible_shortcuts_in_entered_tags(tags, list_of_shortcut_tags):
|
|||
potential_shortcut_string = tags
|
||||
tags = []
|
||||
try:
|
||||
shortcut_index = int(potential_shortcut_string[0])
|
||||
logging.debug('single entered tag is an integer; stepping through the integers')
|
||||
for character in list(potential_shortcut_string[0]):
|
||||
logging.debug('adding tag number %s' % character)
|
||||
try:
|
||||
tags.append(list_of_shortcut_tags[int(character)-1])
|
||||
tags.append(list_of_shortcut_tags[int(character) - 1])
|
||||
except IndexError:
|
||||
return potential_shortcut_string
|
||||
except ValueError:
|
||||
|
|
@ -1054,6 +1027,7 @@ def ask_for_tags(vocabulary, upto9_tags_for_shortcuts):
|
|||
tags_from_userinput = check_for_possible_shortcuts_in_entered_tags(tags_from_userinput, upto9_tags_for_shortcuts)
|
||||
return tags_from_userinput
|
||||
|
||||
|
||||
def get_files_of_directory(directory):
|
||||
"""
|
||||
Lists the files of the given directory and returns a list of its files.
|
||||
|
|
@ -1068,6 +1042,7 @@ def get_files_of_directory(directory):
|
|||
break
|
||||
return files
|
||||
|
||||
|
||||
def filter_files_matching_tags(allfiles, tags):
|
||||
"""
|
||||
Returns a list of file names that contain all given tags.
|
||||
|
|
@ -1079,6 +1054,7 @@ def filter_files_matching_tags(allfiles, tags):
|
|||
|
||||
return [x for x in allfiles if set(extract_tags_from_filename(x)).issuperset(set(tags))]
|
||||
|
||||
|
||||
def assert_empty_tagfilter_directory():
|
||||
"""
|
||||
Creates non-existent tagfilter directory or deletes and re-creates it.
|
||||
|
|
@ -1091,18 +1067,20 @@ def assert_empty_tagfilter_directory():
|
|||
else:
|
||||
logging.debug('found old tagfilter directory "%s"; deleting directory ...' % str(TAGFILTER_DIRECTORY))
|
||||
if not options.dryrun:
|
||||
save_import('shutil') # for removing directories with shutil.rmtree()
|
||||
save_import('shutil') # for removing directories with shutil.rmtree()
|
||||
shutil.rmtree(TAGFILTER_DIRECTORY)
|
||||
logging.debug('re-creating tagfilter directory "%s" ...' % str(TAGFILTER_DIRECTORY))
|
||||
os.makedirs(TAGFILTER_DIRECTORY)
|
||||
if not options.dryrun:
|
||||
assert(os.path.isdir(TAGFILTER_DIRECTORY))
|
||||
|
||||
|
||||
def successful_exit():
|
||||
logging.debug("successfully finished.")
|
||||
sys.stdout.flush()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function"""
|
||||
|
||||
|
|
@ -1143,20 +1121,27 @@ def main():
|
|||
if len(args) < 1 and not (options.tagfilter or options.list_tags_by_alphabet or options.list_tags_by_number or options.list_unknown_tags or options.tag_gardening):
|
||||
error_exit(5, "Please add at least one file name as argument")
|
||||
|
||||
if options.list_tags_by_alphabet:
|
||||
logging.debug("handling option list_tags_by_alphabet")
|
||||
list_tags_by_alphabet()
|
||||
successful_exit()
|
||||
if options.list_tags_by_alphabet or options.list_tags_by_number or options.list_unknown_tags:
|
||||
|
||||
elif options.list_tags_by_number:
|
||||
logging.debug("handling option list_tags_by_number")
|
||||
list_tags_by_number()
|
||||
successful_exit()
|
||||
tag_dict = get_tags_from_files_and_subfolders(startdir=os.getcwdu())
|
||||
if not tag_dict:
|
||||
print "\nNo file containing tags found in this folder hierarchy.\n"
|
||||
return {}
|
||||
|
||||
elif options.list_unknown_tags:
|
||||
logging.debug("handling option list_unknown_tags")
|
||||
list_unknown_tags()
|
||||
successful_exit()
|
||||
if options.list_tags_by_alphabet:
|
||||
logging.debug("handling option list_tags_by_alphabet")
|
||||
print_tag_dict(tag_dict, vocabulary=vocabulary, sort_index=0, print_similar_vocabulary_tags=True)
|
||||
successful_exit()
|
||||
|
||||
elif options.list_tags_by_number:
|
||||
logging.debug("handling option list_tags_by_number")
|
||||
print_tag_dict(tag_dict, vocabulary=vocabulary, sort_index=1, print_similar_vocabulary_tags=True)
|
||||
successful_exit()
|
||||
|
||||
elif options.list_unknown_tags:
|
||||
logging.debug("handling option list_unknown_tags")
|
||||
list_unknown_tags(tag_dict)
|
||||
successful_exit()
|
||||
|
||||
elif options.tag_gardening:
|
||||
logging.debug("handling option for tag gardening")
|
||||
|
|
@ -1165,8 +1150,6 @@ def main():
|
|||
|
||||
elif options.interactive or not options.tags:
|
||||
|
||||
completionhint = u''
|
||||
|
||||
if len(args) < 1 and not options.tagfilter:
|
||||
error_exit(5, "Please add at least one file name as argument")
|
||||
|
||||
|
|
@ -1184,7 +1167,7 @@ def main():
|
|||
upto9_tags_for_shortcuts = sorted(get_upto_nine_keys_of_dict_with_highest_value(tags_for_vocabulary))
|
||||
|
||||
elif options.tagfilter:
|
||||
for tag in get_tags_from_files_and_subfolders(startdir=os.getcwdu(), recursive=False):
|
||||
for tag in get_tags_from_files_and_subfolders(startdir=os.getcwdu()):
|
||||
add_tag_to_countdict(tag, tags_for_vocabulary)
|
||||
|
||||
logging.debug('generating vocabulary ...')
|
||||
|
|
@ -1197,7 +1180,7 @@ def main():
|
|||
logging.debug('deriving upto9_tags_for_shortcuts ...')
|
||||
upto9_tags_for_shortcuts = sorted(get_upto_nine_keys_of_dict_with_highest_value(get_tags_from_files_and_subfolders(startdir=os.path.dirname(os.path.abspath(files[0])))))
|
||||
logging.debug('derived upto9_tags_for_shortcuts')
|
||||
logging.debug('derived vocabulary with %i entries' % len(vocabulary)) ## using default vocabulary which was generate above
|
||||
logging.debug('derived vocabulary with %i entries' % len(vocabulary)) # using default vocabulary which was generate above
|
||||
|
||||
## ==================== Interactive asking user for tags ============================= ##
|
||||
tags_from_userinput = ask_for_tags(vocabulary, upto9_tags_for_shortcuts)
|
||||
|
|
@ -1220,7 +1203,7 @@ def main():
|
|||
logging.info("removing tags \"%s\" ..." % str(BETWEEN_TAG_SEPARATOR.join(tags_from_userinput)))
|
||||
elif options.tagfilter:
|
||||
logging.info("filtering items with tag(s) \"%s\" and linking to directory \"%s\" ..." %
|
||||
(str(BETWEEN_TAG_SEPARATOR.join(tags_from_userinput)) , str(TAGFILTER_DIRECTORY)))
|
||||
(str(BETWEEN_TAG_SEPARATOR.join(tags_from_userinput)), str(TAGFILTER_DIRECTORY)))
|
||||
else:
|
||||
logging.info("adding tags \"%s\" ..." % str(BETWEEN_TAG_SEPARATOR.join(tags_from_userinput)))
|
||||
|
||||
|
|
@ -1262,6 +1245,6 @@ if __name__ == "__main__":
|
|||
|
||||
logging.info("Received KeyboardInterrupt")
|
||||
|
||||
## END OF FILE #################################################################
|
||||
# END OF FILE #################################################################
|
||||
|
||||
#end
|
||||
# end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Time-stamp: <2016-08-21 18:51:58 vk>
|
||||
# Time-stamp: <2016-10-15 16:43:20 vk>
|
||||
|
||||
## invoke tests using following command line:
|
||||
## ~/src/vktag % PYTHONPATH="~/src/filetags:" tests/unit_tests.py --verbose
|
||||
|
|
@ -180,7 +180,7 @@ class TestFileWithoutTags(unittest.TestCase):
|
|||
filetags.handle_file(os.path.join(self.tempdir, filename), [u'foo'], do_remove=False, do_filter=False, dryrun=False)
|
||||
self.assertEqual(self.file_exists(filename + u' -- foo'), True)
|
||||
|
||||
def test_list_tags_by_number(self):
|
||||
def NOtest_list_tags_by_number(self):
|
||||
|
||||
## starting with no file with tags:
|
||||
self.assertEqual(filetags.list_tags_by_number(max_tag_count=1), {})
|
||||
|
|
@ -202,7 +202,7 @@ class TestFileWithoutTags(unittest.TestCase):
|
|||
self.assertEqual(filetags.list_tags_by_number(max_tag_count=1), {u'bar': 1})
|
||||
self.assertEqual(filetags.list_tags_by_number(max_tag_count=0), {u'bar': 1, u'foo': 2})
|
||||
|
||||
def test_list_tags_by_alphabet(self):
|
||||
def NOtest_list_tags_by_alphabet(self):
|
||||
|
||||
## starting with no file with tags:
|
||||
self.assertEqual(filetags.list_tags_by_alphabet(only_with_similar_tags=True), {})
|
||||
|
|
@ -245,7 +245,7 @@ class TestHierarchyWithFilesAndFolders(unittest.TestCase):
|
|||
print "\nTestHierarchyWithFilesAndFolders: temporary directory: " + self.tempdir
|
||||
|
||||
## initial tests without files:
|
||||
self.assertEqual(filetags.get_tags_from_files_and_subfolders(self.tempdir, False, False), {})
|
||||
self.assertEqual(filetags.get_tags_from_files_and_subfolders(self.tempdir, use_cache=False), {})
|
||||
|
||||
## create set of test files:
|
||||
self.create_tmp_file("foo1 -- bar.txt")
|
||||
|
|
@ -266,7 +266,9 @@ class TestHierarchyWithFilesAndFolders(unittest.TestCase):
|
|||
|
||||
def test_get_tags_from_files_and_subfolders(self):
|
||||
|
||||
self.assertEqual(filetags.get_tags_from_files_and_subfolders(self.tempdir, False, False), {u'baz': 2, u'bar': 3, u'teststring1': 1})
|
||||
self.assertEqual(filetags.get_tags_from_files_and_subfolders(self.tempdir, use_cache=False), {u'baz': 2, u'bar': 3, u'teststring1': 1})
|
||||
|
||||
## FIXXME: write test which tests the cache
|
||||
|
||||
def test_list_unknown_tags(self):
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue