From 31044e073bb7b979de844e71859702ae5386abc2 Mon Sep 17 00:00:00 2001 From: Karl Voit Date: Tue, 14 May 2013 22:49:44 +0200 Subject: [PATCH] first working version with tests --- filetag.py | 110 +++++++++++++++++++++++++++++++------------- tests/unit_tests.py | 50 ++++++++++++++------ 2 files changed, 112 insertions(+), 48 deletions(-) diff --git a/filetag.py b/filetag.py index 0e1a652..7314d4f 100755 --- a/filetag.py +++ b/filetag.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Time-stamp: <2013-05-14 18:20:58 vk> +# Time-stamp: <2013-05-14 22:41:20 vk> ## TODO: ## * fix parts marked with «FIXXME» @@ -31,7 +31,7 @@ FILENAME_TAG_SEPARATOR = u' -- ' BETWEEN_TAG_SEPARATOR = u' ' USAGE = u"\n\ - " + sys.argv[0] + u"\n\ + " + sys.argv[0] + u" [] \n\ \n\ FIXXME\n\ https://github.com/novoid/FIXXME\n\ @@ -64,6 +64,9 @@ parser.add_option("-r", "--remove", "-d", "--delete", action="store_true", parser.add_option("-i", "--interactive", action="store_true", help="interactive mode: ask for (a)dding or (r)emoving and name of tag(s)") +parser.add_option("-s", "--dryrun", dest="dryrun", action="store_true", + help="enable dryrun mode: just simulate what would happen, do not modify files") + parser.add_option("-v", "--verbose", dest="verbose", action="store_true", help="enable verbose mode") @@ -199,29 +202,52 @@ def removing_tag_from_filename(filename, tagname): u'.' + extension -def query_folder(folder, list_of_files_found): - """Walk the folder and its sub-folders and collect files matching - INCLUDE_FILES_REGEX whose folder do not match - EXCLUDE_FOLDERS_REGEX.""" +def extract_tags_from_argument(argument): + """ + @param argument: string containing one or more tags + @param return: a list of unicode tags + """ - ## http://stackoverflow.com/questions/5141437/filtering-os-walk-dirs-and-files - - for root, dirs, files in os.walk(folder): - - # exclude dirs - dirs[:] = [os.path.join(root, d) for d in dirs] - dirs[:] = [d for d in dirs if not re.match(EXCLUDE_FOLDERS_REGEX, d)] - - # exclude/include files - files = [f for f in files if re.match(INCLUDE_FILES_REGEX, f)] - files = [os.path.join(root, f) for f in files] - - for fname in files: - list_of_files_found.append(fname) - - return list_of_files_found + return argument.split(unicode(BETWEEN_TAG_SEPARATOR)) +def extract_filenames_from_argument(argument): + """ + @param argument: string containing one or more file names + @param return: a list of unicode file names + """ + + return argument + + +def handle_file(filename, tags, do_remove, dryrun): + """ + @param filename: list containing one or more file names + @param tags: list containing one or more tags + @param do_remove: boolean which defines if tags should be added (False) or removed (True) + @param dryrun: boolean which defines if files should be changed (False) or not (True) + @param return: error value + """ + + if os.path.isdir(filename): + logging.warning("Skipping directory \"%s\" because this tool only renames file names." % filename) + return + elif not os.path.isfile(filename): + logging.error("Skipping \"%s\" because this tool only renames existing file names." % filename) + return + + new_filename = filename + for tagname in tags: + if do_remove: + new_filename = removing_tag_from_filename(new_filename, tagname) + else: + new_filename = adding_tag_to_filename(new_filename, tagname) + + if dryrun: + logging.info("renaming: \"%s\" > \"%s\"" % (filename, new_filename)) + else: + logging.debug("renaming \"%s\" > \"%s\" ..." % (filename, new_filename)) + os.rename(filename, new_filename) def main(): @@ -248,23 +274,41 @@ def main(): error_exit(3, "I found option \"--tag\" and option \"--interactive\". \n" + "Please choose either tag option OR interactive mode.") - - ## interactive mode and remove flag is given (FIXXME: make it possible in future versions) - if options.interactive and options.remove: - error_exit(4, "I found option \"--interactive\" and option \"--remove\". \n" + - "Please choose either interactive mode OR specify tag(s) to remove together with the \"--tag\" option.") + tags = [] + if options.interactive: - ## extract list of tags - ## FIXXME + if options.remove: + logging.info("Interactive mode: tags get REMOVED from file names ...") + else: + logging.info("Interactive mode: tags get ADDED to file names ...") - ## extract list of files - ## FIXXME + ## interactive: ask for list of tags + logging.debug("interactive mode: asking for tags ...") - ## iterate over files - ## FIXXME + print "Please enter one or more tags (separated by \"" + BETWEEN_TAG_SEPARATOR + "\"): (abort with Ctrl-C)" + entered_tags = sys.stdin.readline().strip() + tags = extract_tags_from_argument(entered_tags) + else: + ## non-interactive: extract list of tags + logging.debug("non-interactive mode: extracting tags from argument ...") + + tags = extract_tags_from_argument(options.tags) + + logging.debug("tags found: [%s]" % '], ['.join(tags)) + + logging.debug("extracting list of files ...") + logging.debug("len(args) [%s]" % str(len(args))) + if len(args)<1: + error_exit(5, "Please add at least one file name as argument") + files = extract_filenames_from_argument(args) + logging.debug("filenames found: [%s]" % '], ['.join(files)) + + logging.debug("iterate over files ...") + for filename in files: + handle_file(filename, tags, options.remove, options.dryrun) logging.info("successfully finished.") diff --git a/tests/unit_tests.py b/tests/unit_tests.py index f6a67f6..d674a53 100755 --- a/tests/unit_tests.py +++ b/tests/unit_tests.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Time-stamp: <2013-05-14 18:21:24 vk> +# Time-stamp: <2013-05-14 22:49:16 vk> ## invoke tests using following command line: ## ~/src/vktag % PYTHONPATH="~/src/vktag:" tests/unit_tests.py --verbose @@ -57,13 +57,10 @@ class TestMethods(unittest.TestCase): pass -class NO_TestFiles(unittest.TestCase): +class TestFileWithoutTags(unittest.TestCase): tempdir = None - testfile_without_tags = 'file_without_tags.txt' - testfile_with_multiple_dots_and_no_tags = 'file.without.tags.txt' - testfile_with_tag_foo = 'filename -- foo.txt' - testfile_with_tag_bar = 'filename -- bar.txt' + testfilename = 'a test file . for you.txt' def setUp(self): @@ -73,13 +70,10 @@ class NO_TestFiles(unittest.TestCase): print "\ntemporary directory: " + self.tempdir ## create set of test files: - self.create_tmp_file(self.testfile_without_tags) - self.create_tmp_file(self.testfile_with_multiple_dots_and_no_tags) - self.create_tmp_file(self.testfile_with_tag_foo) - self.create_tmp_file(self.testfile_with_tag_bar) + self.create_tmp_file(self.testfilename) ## double-check set-up: - self.assertTrue(self.file_exists(self.testfile_without_tags)) + self.assertTrue(self.file_exists(self.testfilename)) def create_tmp_file(self, name): @@ -92,14 +86,40 @@ class NO_TestFiles(unittest.TestCase): return os.path.isfile(os.path.join(self.tempdir, name)) - def test_all(self): + def test_add_and_remove_tags(self): - print "testing: foo bar\n" - self.assertEqual(3 * 4, 12) + ## adding a tag to a file without any tags: + filetag.handle_file(os.path.join(self.tempdir, self.testfilename), [u'bar'], False, False) + self.assertEqual(self.file_exists(u'a test file . for you -- bar.txt'), True) + + ## adding a second tag: + filetag.handle_file(os.path.join(self.tempdir, u'a test file . for you -- bar.txt'), + [u'foo'], do_remove=False, dryrun=False) + self.assertEqual(self.file_exists(u'a test file . for you -- bar foo.txt'), True) + + ## adding two tags: + filetag.handle_file(os.path.join(self.tempdir, u'a test file . for you -- bar foo.txt'), + [u'one', u'two'], do_remove=False, dryrun=False) + self.assertEqual(self.file_exists(u'a test file . for you -- bar foo one two.txt'), True) + + ## simulating another tag: + filetag.handle_file(os.path.join(self.tempdir, u'a test file . for you -- bar foo one two.txt'), + [u'one', u'two'], do_remove=False, dryrun=True) + self.assertEqual(self.file_exists(u'a test file . for you -- bar foo one two.txt'), True) + + ## removing three tag: + filetag.handle_file(os.path.join(self.tempdir, u'a test file . for you -- bar foo one two.txt'), + [u'bar', u'one', u'foo'], do_remove=True, dryrun=False) + self.assertEqual(self.file_exists(u'a test file . for you -- two.txt'), True) + + ## removing last tag: + filetag.handle_file(os.path.join(self.tempdir, u'a test file . for you -- two.txt'), + [u'two'], do_remove=True, dryrun=False) + self.assertEqual(self.file_exists(u'a test file . for you.txt'), True) def tearDown(self): - + rmtree(self.tempdir)