{"id":13595650,"url":"https://github.com/novoid/filetags","last_synced_at":"2025-04-04T07:09:52.104Z","repository":{"id":8387251,"uuid":"9962415","full_name":"novoid/filetags","owner":"novoid","description":"Management of simple tags within file names","archived":false,"fork":false,"pushed_at":"2024-08-26T18:09:21.000Z","size":892,"stargazers_count":280,"open_issues_count":17,"forks_count":39,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-28T06:11:13.561Z","etag":null,"topics":["file","files","pim","shell","tagging","tags"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/novoid.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-05-09T15:43:14.000Z","updated_at":"2025-03-23T20:03:22.000Z","dependencies_parsed_at":"2023-01-13T14:46:40.462Z","dependency_job_id":"cbbcbfa2-45b3-4629-a16e-252de2b724f3","html_url":"https://github.com/novoid/filetags","commit_stats":{"total_commits":194,"total_committers":8,"mean_commits":24.25,"dds":0.09278350515463918,"last_synced_commit":"e5da313c41603a63fe83796040b17113e2619a5e"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novoid%2Ffiletags","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novoid%2Ffiletags/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novoid%2Ffiletags/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/novoid%2Ffiletags/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/novoid","download_url":"https://codeload.github.com/novoid/filetags/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247135147,"owners_count":20889421,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["file","files","pim","shell","tagging","tags"],"created_at":"2024-08-01T16:01:54.552Z","updated_at":"2025-04-04T07:09:52.081Z","avatar_url":"https://github.com/novoid.png","language":"Python","funding_links":[],"categories":["Python","shell"],"sub_categories":[],"readme":"* filetags\n\n#+BEGIN_HTML\n\u003ca href=\"https://karl-voit.at/demo-filetags-tagging\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/novoid/screencasts/master/file_management/filetags_tagging.gif\" /\u003e\n\u003c/a\u003e\n#+END_HTML\n\n[[file:bin/screencast.gif]]\n\n... filetags example demonstrating: controlled vocabulary file\n=~/.filetags=, tagging multiple files at once, removing tags by\nprepending a minus character, tagging using the proposed number\nshortcuts, tab completion of tags via =Tab=, and mutually exclusive\ntags (switching from =draft= to =final= without removing =draft=).\n\nThis Python script adds or removes tags to file names in the following\nform:\n\n- \"file without time stamp in name -​- *tag2*.txt\"\n- \"file name with several tags -​- *tag1 tag2*.jpeg\"\n- \"another example file name with multiple example tags -​- *fun* *videos* *kids*.mpeg\"\n- \"2013-05-09 a file name with ISO date stamp in name -​- *tag1*.jpg\"\n- \"2013-05-09T16.17 file name with time stamp -​- *tag3*.csv\"\n\nThe script accepts an arbitrary number of files (see your shell for\npossible length limitations).\n\n- *Target group*: users who are able to use command line tools and who\n  are using tags in file names.\n- Hosted on github: https://github.com/novoid/filetags\n- Note that [[https://en.wikipedia.org/wiki/Folder_(computing)#Folder_metaphor][\"directories\" are to \"folders\"]] what \"files\" are to\n  \"documents\".\n\n[[https://imgs.xkcd.com/comics/trained_a_neural_net.png]] (Source: [[https://xkcd.com/2173/][xkcd]])\n\n** Why\n\nBesides the fact that I am using [[https://en.wikipedia.org/wiki/Iso_date][ISO dates and times]] in file names\n(as shown in examples above), I am using tags with file names. To\nseparate tags from the file name, I am using the separator\n\"space dash dash space\".\n\nFor people familiar with [[https://en.wikipedia.org/wiki/Regex][Regular Expressions]]:\n\n: (\u003cISO date/time stamp\u003e)?(?\u003cdescriptive file name\u003e)?( -- \u003clist of tags separated by spaces\u003e)?.\u003cfile extension\u003e\n\nTagging files this way requires a file renaming process. Adding (or\nremoving) tag(s) to a set of file results in multiple renaming\nprocesses. Despite advanced renaming tools like vidir (from\n[[http://joeyh.name/code/moreutils/][moreutils]]) it's handy to have a tool that makes adding and removing\ntags as simple as possible.\n\nYou may like to add this tool to your image or file manager of\nchoice. I added mine to [[http://geeqie.sourceforge.net/][geeqie]] which is my favorite image viewer on\nGNU/Linux.\n\nHere is [[https://glt18-programm.linuxtage.at/events/321.html][a 45 minute talk I gave]] at [[https://glt18.linuxtage.at/][Linuxtage Graz 2018]] presenting the\nidea of and workflows related to filetags and other handy tools for\nfile management:\n\n[[https://media.ccc.de/v/GLT18_-_321_-_en_-_g_ap147_004_-_201804281550_-_the_advantages_of_file_name_conventions_and_tagging_-_karl_voit/][bin/2018-05-06 filetags demo slide for video preview with video button -- screenshots.png]]\n\n** Installation\n\nThis tool needs [[http://www.python.org/downloads/][Python 3 to be installed]].\n\nYou can install filetags either via [[https://packaging.python.org/tutorials/installing-packages/][pip]] which is the recommended way.\nOr you can install filetags using the source code, e.g., by cloning\nthe [[https://github.com/novoid/filetags/][GitHub repository of filetags]].\n\n*** Installation Via Pip\n\nIf you have installed Python 2 and Python 3 in parallel, make sure to\nuse the correct pip version. You might need to use =pip3= instead of\n=pip=. If you only have Python 3 installed, you don't have to care ;-)\n\nOn Microsoft Windows (only), you are going to need ~pip install\npypiwin32~ as prerequisite. For easy Windows File Explorer\nintegration, take a look at [[https://github.com/novoid/integratethis][integratethis]].\n\nNow install filetags via [[https://pip.pypa.io/en/stable/][pip]]: ~pip install filetags~\n\nYou get updates by executing the very same pip command again.\n\n*** Installation Via Source Code\n\nIf you use the GitHub sources (and not pip):\n\n- You need to install depending packages via: ~pip install -r requirements.txt~\n- The executable is ~filetags/__init__.py~. You might want to create a\n  symbolic link named \"filetags\" to that file.\n\n** Usage\n\n # #+BEGIN_SRC sh :results output :wrap src\n # ./filetags/__init__.py --help | sed 'sX/home/vkX\\$HOMEX'\n # #+END_SRC\n\n#+BEGIN_src\nusage: ./filetags/__init__.py [-h] [-t \"STRING WITH TAGS\"] [--remove] [-i]\n                              [-R] [-s] [--hardlinks] [-f]\n                              [--filebrowser PATH_TO_FILEBROWSER] [--tagtrees]\n                              [--tagtrees-handle-no-tag \"treeroot\" | \"ignore\" | \"FOLDERNAME\"]\n                              [--tagtrees-link-missing-mutual-tagged-items]\n                              [--tagtrees-dir \u003cexisting_directory\u003e]\n                              [--tagtrees-depth TAGTREES_DEPTH] [--ln] [--la]\n                              [--lu] [--tag-gardening] [-v] [-q] [--version]\n                              [FILE [FILE ...]]\n\nThis tool adds or removes simple tags to/from file names.\n\nTags within file names are placed between the actual file name and\nthe file extension, separated with \" -- \". Multiple tags are\nseparated with \" \":\n  Update for the Boss -- projectA presentation.pptx\n  2013-05-16T15.31.42 Error message -- screenshot projectB.png\n\nThis easy to use tag system has a drawback: for tagging a larger\nset of files with the same tag, you have to rename each file\nseparately. With this tool, this only requires one step.\n\nExample usages:\n  filetags --tags=\"presentation projectA\" *.pptx\n      … adds the tags \"presentation\" and \"projectA\" to all PPTX-files\n  filetags --tags=\"presentation -projectA\" *.pptx\n      … adds the tag \"presentation\" to and removes tag \"projectA\" from all PPTX-files\n  filetags -i *\n      … ask for tag(s) and add them to all files in current folder\n  filetags -r draft *report*\n      … removes the tag \"draft\" from all files containing the word \"report\"\n\nThis tools is looking for the optional first text file named \".filetags\" in\ncurrent and parent directories. Each of its lines is interpreted as a tag\nfor tag completion. Multiple tags per line are considered mutual exclusive.\n\nVerbose description: http://Karl-Voit.at/managing-digital-photographs/\n\npositional arguments:\n  FILE                  One or more files to tag\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -t \"STRING WITH TAGS\", --tags \"STRING WITH TAGS\"\n                        One or more tags (in quotes, separated by spaces) to\n                        add/remove\n  --remove              Remove tags from (instead of adding to) file name(s)\n  -i, --interactive     Interactive mode: ask for (a)dding or (r)emoving and\n                        name of tag(s)\n  -R, --recursive       Recursively go through the current directory and all\n                        of its subdirectories. Implemented for --tag-gardening\n                        and --tagtrees\n  -s, --dryrun          Enable dryrun mode: just simulate what would happen,\n                        do not modify files\n  --hardlinks           Use hard links instead of symbolic links. This is\n                        ignored on Windows systems. Note that renaming link\n                        originals when tagging does not work with hardlinks.\n  -f, --filter          Ask for list of tags and generate links in\n                        \"$HOME/.filetags_tagfilter\" containing links to all\n                        files with matching tags and start the filebrowser.\n                        Target directory can be overridden by --tagtrees-dir.\n  --filebrowser PATH_TO_FILEBROWSER\n                        Use this option to override the tool to view/manage\n                        files (for --filter; default: geeqie). Use \"none\" to\n                        omit the default one.\n  --tagtrees            This generates nested directories in\n                        \"$HOME/.filetags_tagfilter\" for each combination of\n                        tags up to a limit of 2. Target directory can be\n                        overridden by --tagtrees-dir. Please note that this\n                        may take long since it relates exponentially to the\n                        number of tags involved. Can be combined with\n                        --filter. See also http://Karl-Voit.at/tagstore/ and\n                        http://Karl-Voit.at/tagstore/downloads/Voit2012b.pdf\n  --tagtrees-handle-no-tag \"treeroot\" | \"ignore\" | \"FOLDERNAME\"\n                        When tagtrees are created, this parameter defines how\n                        to handle items that got no tag at all. The value\n                        \"treeroot\" is the default behavior: items without a\n                        tag are linked to the tagtrees root. The value\n                        \"ignore\" will not link any non-tagged items at all.\n                        Any other value is interpreted as a folder name within\n                        the tagreees which is used to link all non-tagged\n                        items to.\n  --tagtrees-link-missing-mutual-tagged-items\n                        When the controlled vocabulary holds mutual exclusive\n                        tags (multiple tags in one line) this option generates\n                        directories in the tagtrees root that hold links to\n                        items that have no single tag from those mutual\n                        exclusive sets. For example, when \"draft final\" is\n                        defined in the vocabulary, all items without \"draft\"\n                        and \"final\" are linked to the \"no-draft-final\"\n                        directory.\n  --tagtrees-dir \u003cexisting_directory\u003e\n                        When tagtrees are created, this parameter overrides\n                        the default target directory\n                        \"$HOME/.filetags_tagfilter\" with a user-defined\n                        one. It has to be an empty directory or a non-existing\n                        directory which will be created. This also overrides\n                        the default directory for --filter.\n  --tagtrees-depth TAGTREES_DEPTH\n                        When tagtrees are created, this parameter defines the\n                        level of depth of the tagtree hierarchy. The default\n                        value is 2. Please note that increasing the depth\n                        increases the number of links exponentially.\n                        Especially when running Windows (using lnk-files\n                        instead of symbolic links) the performance is really\n                        slow. Choose wisely.\n  --ln, --list-tags-by-number\n                        List all file-tags sorted by their number of use\n  --la, --list-tags-by-alphabet\n                        List all file-tags sorted by their name\n  --lu, --list-tags-unknown-to-vocabulary\n                        List all file-tags which are found in file names but\n                        are not part of .filetags\n  --tag-gardening       This is for getting an overview on tags that might\n                        require to be renamed (typos, singular/plural, ...).\n                        See also http://www.webology.org/2008/v5n3/a58.html\n  -v, --verbose         Enable verbose mode\n  -q, --quiet           Enable quiet mode\n  --version             Display version and exit\n\n:copyright: (c) by Karl Voit \u003ctools@Karl-Voit.at\u003e\n:license: GPL v3 or any later version\n:URL: https://github.com/novoid/filetags\n:bugreports: via github or \u003ctools@Karl-Voit.at\u003e\n:version: 2018-08-02\n·\n#+END_src\n\n*** Examples:\n\n: filetags --tags foo a_file_name.txt\n... adds tag \"foo\" such that it results in ~a_file_name -- foo.txt~\n\n: filetags -i *.jpeg\n... interactive mode: asking for list of tags (for the JPEG files) from the user\n\n: filetags --tags \"foo bar\" \"file name 1.jpg\" \"file name 2 -- foo.txt\" \"file name 3 -- bar.csv\"\n... adds tag \"foo\" such that it results in ...\n: \"file name 1 -- foo bar.jpg\"\n: \"file name 2 -- foo bar.txt\"\n: \"file name 3 -- bar foo.csv\"\n\n: filetags --remove --tags foo \"foo a_file_name -- foo.txt\"\n... removes tag \"foo\" such that it results in ~foo a_file_name.txt~\n\n: filetags --tag-gardening\n... prints out a summary of tags in current and sub-folders used and\ntags that are most likely typos or abandoned\n\nFor =--filter= and =--tagtrees= examples see sections below.\n\nIndependent to tags you might define on the fly, the optional file\n.filetags stores a controlled vocabulary of recurrent tags; adjust\nthis content to your needs.  In an interactive session, this set is\navailable to tag any file in the folder .filetags resides (click tab\nkey) and propagates into folders of lower hierachy.\n\n** Changelog\n\n- [[https://twitter.com/n0v0id/status/335043859404951554][2013-05-16]]: first version on GitHub\n- [[https://twitter.com/n0v0id/status/546449664179195904][2014-12-21]]: ~--list-tags-by-number~, ~--list-tags-by-alphabet~, and ~--tag-gardening~\n- [[https://twitter.com/n0v0id/status/551050830678605824][2015-01-02]]: tab completion for interactive tag input\n  - Example: entering =myt= + pressing =TAB= completes the entered\n    string to =mytag= if =mytag= is found in the vocabulary or\n    existing file tags\n- [[https://twitter.com/n0v0id/status/675388298735575041][2015-12-11]]: shortcut numbers for removing tags\n- [[https://twitter.com/n0v0id/status/685507528856367104][2016-01-08]]: shortcut numbers for top nine tags for adding tags\n  - Example: when filetags shows you =Top nine previously used tags in\n    this directory:= with =mytag(1) anothertag(2) oncemore(3)=, you\n    don't have to type in the tag names but use the numbers instead.\n    Combinations of numbers are fine as well.\n- [[https://twitter.com/n0v0id/status/767343476665159680][2016-08-21]]: mutually exclusive tags: see chapter below\n- [[https://twitter.com/n0v0id/status/768167397895180289][2016-08-23]]: installable via ~pip install filetags~\n- 2016-08-26: =--filter= option requires /all/ tags to be matching\n- 2016-10-15: added tag gardening: vocabulary tags not used + tags not\n  in vocabulary\n- 2016-10-16: interactively adding tags: omit already assigned tags in\n  shortcuts and vocabulary\n- 2016-11-27: added existing shared tags to visual tags\n- 2017-02-06: better help text for =--filter= option\n- 2017-02-25: shortcut tags can be mixed with non-shortcut tags\n  - Example: =mytag 49 anothertag= does add tags =mytag= and\n    =anothertag= and the shortcut tags =4= and =9=\n- 2017-04-09:\n  - interactively removing tags via =-tagname=:\n    - Example: the tag input =tagname -removeme= adds the tag\n      =tagname= and removes the tag =removeme= from the filename(s)\n  - try to find alternative filename if file not found\n    - Example: if you try to tag file =My file name.pdf= which is not\n      found, filetags tries to look for a different (unique and\n      existing) filename that shares the same start of the file name\n      such as =My file name -- mytag.pdf=. Very handy!\n    - This happens a lof when you are interactively adding multiple\n      tags one by one by simply re-executing the previous command\n      line: the file name changes in between because of the previous\n      tag(s) being added.\n- 2017-08-27: when tagging symbolic links whose source file has a\n  matching file name, the source file gets the same tags as the\n  symbolic link of it\n  - This is especially useful when using the =--filter= option\n- 2017-08-28:\n  - moved from optparse to [[https://docs.python.org/3/library/argparse.html][argparse]]\n  - removed option =--tag= (in favor to =--tags=)\n  - added option shortcut for recursive: =-R=\n  - renamed option =--imageviewer= to =--filebrowser= and enabled its functionality\n  - added new feature =--tagtrees=\n- 2017-08-31:\n  - improved screen output when renaming files\n- 2017-09-03:\n  - =--recursive= option now works for linking files to tagtrees as well\n  - corresponding =.filetags= files get linked to the output of tagtrees as well\n- 2017-11-11:\n  - removed command line options =-r=, =-d=, and =--delete=\n    - keeping =--remove= as the only option for removing tags\n    - removing tags was overrepresented in the command line options, blocking them to be used for other useful commands\n  - added =--tagtrees-handle-no-tag \"treeroot\" | \"ignore\" | \"FOLDERNAME\"=\n  - added =--tagtrees-link-missing-mutual-tagged-items=\n- 2017-12-30:\n  - added =--tagtrees-dir \u003cexisting_directory\u003e=\n    - overriding the default target directory for the tagtrees result\n  - added =--tagtrees-depth TAGTREES_DEPTH=\n    - allowing to override the default depth of tagtrees\n    - use with care: especially on Windows a larger depth than 2 takes very long\n  - tagtrees now work with Windows using =lnk= files\n    - in contrast to symbolic links, that have rather poor performance\n      though: generation of tagtrees take way longer than on Linux or\n      macOS\n- 2018-01-30:\n  - fixed the pip3 package\n- 2018-03-18:\n  - added more detailed statistics on usage of tag groups when doing tag gardening\n  - added internal data structure =cache_of_files_with_metadata=\n- 2018-04-05:\n  - =--tagtrees-dir= can now be used for =--filter=\n  - much deeper support for Windows =.lnk= files:\n    - tagging lnk files within tagtrees also tag their original files\n    - .filetags files can now be .lnk files as well\n    - the unit tests now work on Windows and test some Windows specialities\n- 2018-04-18:\n  - comments in =.filetags= files that contain the controlled vocabulary\n- 2018-04-25:\n  - added hints to [[https://github.com/novoid/integratethis][=integratethis=]] to ease the Windows Explorer\n    integration\n- 2018-07-23: =--tagtrees== can now be filtered with =--filter=\n- 2018-08-02: added option =--hardlinks= as an alternative for non-Windows systems\n- 2019-12-22: added manual file globbing for Windows because of [[https://github.com/novoid/filetags/issues/25][#25]]\n- 2021-04-03: added support for =#donotsuggest= lines within =.filetags= files to omit tags from being proposed\n\n** Get the most out of filetags: controlled vocabulary ~.filetags~\n:PROPERTIES:\n:ID: 2018-07-08-cv\n:CREATED:  [2015-01-02 Fri 17:12]\n:END:\n\nThis awesome tool is providing support for [[https://en.wikipedia.org/wiki/Controlled_vocabulary][controlled vocabularies]].\nWhen invoked for interactive tagging, it is looking for files named\n~.filetags~ in the current working directory and its parent\ndirectories as well. The first file of this name found is read in.\nEach line represents one tag. Those tags are used for *tag\ncompletion*.\n\nThis is purely great: with tags within ~.filetags~ you don't have to\nenter the tags entrirely: just type the first characters and press =TAB=\n(twice to show you all possibilities). You will be amazed how\nefficiently you are going to tag things! :-)\n\nOf course, you can remove existing tags by prepending a =-= character\nto the tag: =-tagname=. This also works interactively using the tab\ncompletion feature.\n\nYou can use comments in =.filetags= files: everything after a =#=\ncharacter is considered a comment. You can even add a comment after a\ntag like \"=mytag   # this is a test tag=\".\n\nIf you do use tags you do not want to get proposed for tagging, you\ncan write them in lines like the following ones to omit their proposal\n(case insensitive):\n\n: #donotsuggest omit-this-tag dontshow\n: #donotsuggest wontpropose\n\n** Mutually exclusive tags\n:PROPERTIES:\n:ID:       2018-07-08-mutually-exclusive-tags\n:END:\n\nIf you enter multiple tags in the same line in ~.filetags~, they are\ninterpreted as *mutually exclusive tags*. For example, if your\n~.filetags~ contains the line ~winter spring summer autumn~, filetags\nreplaces any season-tag with the new one. So if you tag the file …\n\n: example file -- summer anothertag.txt\n\n… with the tag ~winter~, it gets renamed to …\n\n: example file -- winter anothertag.txt\n\n… without having to manually remove the tag ~summer~.\n\nCommon mutually exclusive tags are =draft final= or =confidential\ninternal public=.\n\n** Filter\n:PROPERTIES:\n:CREATED:  [2018-08-01 Wed 11:44]\n:END:\n\nConsider you have a directory that contains hundreds of files.\n\nIf you want to retrieve a file whose tags you know, you can skim\nthrough all the files. However, filetags offers you a more elegant\npossibility: you can filter the files according to one or more tags.\n\nFor example, we take a look at following situation:\n\n: $HOME/my party/\n: |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n: |_ 2018-07-31 Guest list -- correspondence.txt\n: |_ 2018-08-01T11.51.44 Uncle Bob arrives.jpg\n: |_ 2018-08-01T12.31.42 Sheila with her new boyfriend -- friends.jpg\n: |_ 2018-08-01T14.12.23 Start of BBQ with the big steak.jpg\n: |_ ...\n: |_ 2018-08-01T23.53.19 Even uncle Bob desides to go home -- fun.jpg\n: |_ 2018-08-05 Lessons learned for planning a party -- scan.pdf\n: |_ 2018-08-06 Thank-you letter Bob -- scan.pdf\n: |_ Bills/\n:   |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:   |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n\nFollowing command and interaction would generate following temporal\nlink structure:\n\n: filetags --filter\n\nUser gets asked to enter one or more tags and she enters \"scan\". What\nnow happens is that filetags creates a directory whose content\nconsists of links to all matching files from your query. By default,\nthe resulting directory is =.filetags_tagfilter= in your home\ndirectory. After invoking for our example, the content of this\nretrieval directory looks like that:\n\n: $HOME/.filetags_tagfilter/\n: |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n: |_ 2018-08-05 Lessons learned for planning a party -- scan.pdf\n: |_ 2018-08-06 Thank-you letter Bob -- scan.pdf\n\nThis way, our user is quickly able to skim through all scanned\ndocuments to locate the one desired to retrieve.\n\nTo locate all matching files in all sub-directories as well, the user\nis able to add the parameter =--recursive= ...\n\n: filetags --filter --recursive\n\n... and chooses to enter the tag \"scan\" which would generate following\ntemporal link structure:\n\n: $HOME/.filetags_tagfilter/\n: |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n: |_ 2018-08-05 Lessons learned for planning a party -- scan.pdf\n: |_ 2018-08-06 Thank-you letter Bob -- scan.pdf\n: |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n: |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n\n** TagTrees\n:PROPERTIES:\n:ID:       2018-07-08-tagtrees\n:END:\n\nThis functions is somewhat sophisticated as it is not a very\nwell-known thing to have. If you're really interested in the whole\nstory behind the visualization/navigation of tags using TagTrees, feel\nfree to read [[http://Karl-Voit.at/tagstore/downloads/Voit2012b.pdf][my PhD thesis]] about it on [[http://Karl-Voit.at/tagstore/][the tagstore webpage]]. It is\nsurely a piece of work I am proud of and the general chapters of it\nare written so that the average person is perfectly well able to\nfollow.\n\nIn short: this function takes the files of the current directory and\ngenerates hierarchies up to level of =$maxdepth= (by default 2, can be\noverridden via =--tagtrees-depth=) of all combinations of tags,\n[[https://en.wikipedia.org/wiki/Symbolic_link][linking]] all files according to their tags.\n\nToo complicated? Then let's explain it with some examples.\n\nConsider having a file like:\n\n: My new car -- car hardware expensive.jpg\n\nNow you generate the TagTrees, you'll find [[https://en.wikipedia.org/wiki/Symbolic_link][links]] to this file within\nsub-directories of =~/.filetags=, the default target directory: =car/=\nand =hardware/= and =expensive/= and =car/hardware/= and\n=car/expensive/= and =hardware/car/= and so on. You get the idea.\n\nThe default target directory can be overridden via =--tagtrees-dir=.\n\nTherefore, within the folder =new/expensive/= you will find all files\nthat have at least the tags \"new\" and \"expensive\" in any order. This\nis /really/ cool to have.\n\nFiles of the current directory that don't have any tag at all, are\nlinked directly to =~/.filetags= so that you can find and tag them\neasily.\n\nI personally, do use this feature within my image viewer of choice\n([[http://geeqie.sourceforge.net/][geeqie]]). I mapped it to =Alt-T= because =Alt-t= is occupied by\n=filetags= for tagging of course. So when I am within my image viewer\nand I press =Alt-T=, TagTrees of the currently shown images are\ncreated. Then an additional image viewer window opens up for me,\nshowing the resulting TagTrees. This way, I can quickly navigate\nthrough the tag combinations to easily interactively filter according\nto tags.\n\nPlease note: when you are tagging linked files within the TagTrees\nwith filetags, only the current link gets updated with the new name.\nAll other links to this modified filename within the other directories\nof the TagTrees gets broken. You have to re-create the TagTrees to\nupdate all the links after tagging files.\n\nThe option =--tagtrees-handle-no-tag= controls how files with no tags\nshould be handled. When set to =treeroot=, untagged files are linked\nin the TagTrees target directory directly. The option =ignore= does\nnot link them at all. The option =FOLDERNAME= links them to a\ndirectory named accordingly to the value which is a sub-directory of\nthe TagTrees target directory.\n\nWith the option =--tagtrees-link-missing-mutual-tagged-items= you can\ncontrol, whether or not there will be an additional TagTrees folder\nthat contains all files which lack one of the mutually exclusive tags.\nUsing the example ~winter spring summer autumn~ from above, all files\nthat got none of those four tags get linked to a TagTrees directory\nnamed \"no_winter_spring_summer_autumn\". This way, you can easily find\nand tag files that don't participate in this set of mutually exclusive\ntags.\n\nUsing the example files from above:\n\n: $HOME/my party/\n: |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n: |_ 2018-07-31 Guest list -- correspondence.txt\n: |_ 2018-08-01T11.51.44 Uncle Bob arrives.jpg\n: |_ 2018-08-01T12.31.42 Sheila with her new boyfriend -- friends.jpg\n: |_ 2018-08-01T14.12.23 Start of BBQ with the big steak.jpg\n: |_ ...\n: |_ 2018-08-01T23.53.19 Even uncle Bob desides to go home -- fun.jpg\n: |_ 2018-08-05 Lessons learned for planning a party -- scan.pdf\n: |_ 2018-08-06 Thank-you letter Bob -- scan.pdf\n: |_ Bills/\n:   |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:   |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n\n... and the command line ...\n\n: filetags --tagtrees --tagtrees-handle-no-tag \"has_no_tag\" --tagtrees-depth 2 --recursive\n\n... filetags generates the temporal link structure:\n\n: $HOME/.filetags_tagfilter/\n: |_ scan/\n:   |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n:   |_ 2018-08-05 Lessons learned for planning a party -- scan.pdf\n:   |_ 2018-08-06 Thank-you letter Bob -- scan.pdf\n:   |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:   |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n:   |_ correspondence/\n:     |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n:   |_ taxes/\n:     |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:     |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n: |_ correspondence/\n:   |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n:   |_ 2018-07-31 Guest list -- correspondence.txt\n:   |_ scan/\n:     |_ 2018-06-25 Party invitation -- scan correspondence.pdf\n: |_ friends/\n:   |_ 2018-08-01T12.31.42 Sheila with her new boyfriend -- friends.jpg\n: |_ fun/\n:   |_ 2018-08-01T23.53.19 Even uncle Bob desides to go home -- fun.jpg\n: |_ taxes/\n:   |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:   |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n:   |_ scan/\n:     |_ 2018-07-30 Beverages by FreshYouUp -- scan taxes.pdf\n:     |_ 2018-08-03 Bill of the butcher -- scan taxes.pdf\n: |_ has_no_tag/\n:   |_ 2018-08-01T11.51.44 Uncle Bob arrives.jpg\n:   |_ 2018-08-01T14.12.23 Start of BBQ with the big steak.jpg\n:   |_ ...\n\nThis looks complicated because there are many links generated the user\ndoes not really need. The beauty of this solution is that the user is\nable to navigate to a file using a wide set of different paths (the\nTagTrees) and she is able to choose the one path that suits the\ncurrent cognitive model.\n\nFor example, she might want to retrieve \"the one document from the\nlast party which she remembers of having scanned and which she used\nfor the invitation correspondence\". With this mind-set, she most\nlikely retrieves the document via\n=$HOME/.filetags_tagfilter/scan/correspondence/= or\n=$HOME/.filetags_tagfilter/correspondence/scan/= (does not matter\nwhich).\n\nThe large number of other TagTrees can be ignored for this retrieval\ntask.\n\nAnother retrieval task example would be \"all photos that do have no\ntag in order to continue tagging the photos\". In this example, the\nuser visits =$HOME/.filetags_tagfilter/has_no_tag/=, fires her image\nviewer (which has filetags integrated already - see below) and\ncontinues with the tagging activity. Since filetags synchronizes the\ntags within TagTrees linked files and the original files, the original\nfiles get renamed accordingly.\n\n** Bonus: Using tags to specify a sub-set of photographs\n:PROPERTIES:\n:ID:       2018-07-08-sel-photos\n:END:\n\nYou know the problem: got back from Paris and you can not show 937\nimage files to your friends. It's just too much.\n\nMy solution: I tag to define selections. For example, I am using ~sel~\n(\"selection\") for the ultimate cool photographs using ~filetags~, of\ncourse.\n\nWithin geeqie, which is my preferred image viewer, I redefined ~F~ to\ncall filetags with its =--filter= parameter. Now I get asked to enter\none or more tags to filter the current folder. For presenting only the\nfiles that were tagged with ~sel~, I enter ~sel~ and confirm with\n~Enter~.\n\nThis creates a temporary folder with symbolic links to all photographs\nof the current folder that contain the tag ~sel~ and it starts a new\n(additional) instance of geeqie.\n\nIn short: after returning from a trip, I mark all \"cool\" photographs\nwithin geeqie, choose ~t~ and tag them with ~sel~ (described in\nprevious section). For showing only ~sel~ images, I just press ~F~,\nenter ~sel~ and instead of 937 photographs, my friends just have to\nwatch the best 50 or so. :-)\n\nWatch [[https://media.ccc.de/v/GLT18_-_321_-_en_-_g_ap147_004_-_201804281550_-_the_advantages_of_file_name_conventions_and_tagging_-_karl_voit][this 45 minute talk]] on how I am using this (and other) features.\n\n* Integration Into Common Tools\n\nIf your system has Python 3 installed, you can start using filetags\nright away in any command line environment.\n\nHowever, users do want to integrate tools like filetags also in\nvarious GUI tools.\n\nThe [[file:Integration.org][Integration.org file]] explains integration in some tools that allow\nexternal commands being added:\n\n- [[http://geeqie.sourceforge.net/][geeqie]], a GNU/Linux image viewer I am using\n- [[https://en.wikipedia.org/wiki/Thunar][Thunar]] is a popular GNU/Linux file browser for the xfce environment\n- [[https://gitlab.gnome.org/GNOME/nautilus][GNOME Nautilus]] file manager\n- Windows Explorer\n- [[http://freecommander.com/en/summary/][FreeCommander]], my recommendated alternative to Windows explorer\n- [[https://en.wikipedia.org/wiki/Dired][Dired]], the GNU/Emacs file manager\n\nIf you have integrated filetags in additional commonly used tools,\nplease send me a short how-to so that others are able to get the most\nout of filetags as well.\n\n* Related Tools and Workflows\n\nThis tool is part of a tool-set which I use to manage my digital files\nsuch as photographs. My work-flows are described in [[http://karl-voit.at/managing-digital-photographs/][this blog posting]]\nyou might like to read and in the video which is linked above.\n\nIn short:\n\nFor *tagging*, please refer to [[https://github.com/novoid/filetags][filetags]] and its documentation.\n\nSee [[https://github.com/novoid/date2name][date2name]] for easily adding ISO *time-stamps or date-stamps* to\nfiles.\n\nFor *easily naming and tagging* files within file browsers that allow\nintegration of external tools, see [[https://github.com/novoid/appendfilename][appendfilename]] (once more) and\n[[https://github.com/novoid/filetags][filetags]].\n\nMoving to the archive folders is done using [[https://github.com/novoid/move2archive][move2archive]].\n\nHaving tagged photographs gives you many advantages. For example, I\nautomatically [[https://github.com/novoid/set_desktop_background_according_to_season][choose my *desktop background image* according to the\ncurrent season]].\n\nFiles containing an ISO time/date-stamp gets indexed by the\nfilename-module of [[https://github.com/novoid/Memacs][Memacs]].\n\n-----------\n\n- Alternative implementations of the =filetags= concept:\n  - [[https://github.com/beutelma/filetags.el][GitHub - DerBeutlin/filetags.el: Emacs package to manage filetags in the filename]]\n  - With [[https://github.com/protesilaos/denote][denote]], Protesilaos\n    Stavrou implemented a conceptually related approach to manage notes\n    within an Emacs buffer.  With ~dired~, this method equally may be\n    applied on files, too.\n\n- A research platform for testing file-tagging on all platforms: [[https://karl-voit.at/tagstore/][tagstore]]\n  - This happens to be an important part of [[https://karl-voit.at/tagstore/downloads/Voit2012b.pdf][my PhD thesis]] in PIM.\n\n- Good resources for tagging software in general\n  - [[https://turbofuture.com/computers/Whats-the-Best-Software-for-Tagging-Files-A-Review][What's the Best Software for Tagging Files? | TurboFuture]]\n  - \"Marktübersicht von Tagging-Werkzeugen und Vergleich mit tagstore\" (German, 2013): linked on [[https://karl-voit.at/tagstore/en/papers.shtml][this page]] of the [[https://karl-voit.at/tagstore/][tagstore project]]\n\n- If you do like filetags but you prefer the syntax of [[https://www.tagspaces.org/][TagSpaces]] for\n  adding tags to file names, you should check out [[https://github.com/jgru/filetags][this filetags fork]].\n  Maintenance is limited though. Please notice that my other tools\n  working with tags do not support TagSpaces-style either.\n\n- https://forge.chapril.org/tykayn/rangement.git\n  - An NPM implementation of a subset of GuessFileName (using image exif header), append2name, move2archive\n  - You probably need to read a bit of French\n\n* How to Thank Me\n\nI'm glad you like my tools. If you want to support me:\n\n- Send old-fashioned *postcard* per snailmail - I love personal feedback!\n  - see [[http://tinyurl.com/j6w8hyo][my address]]\n- Send feature wishes or improvements as an issue on GitHub\n- Create issues on GitHub for bugs\n- Contribute merge requests for bug fixes\n- Check out my other cool [[https://github.com/novoid][projects on GitHub]]\n\n* Exhaustive List of All Features\n:PROPERTIES:\n:CREATED:  [2018-07-08 Sun 13:09]\n:END:\n\nThis section is an exhaustive list of features of =filetags=. You\nmight skip this when you're a first-time user in order *not* to get\nirritated for simple use-cases only.\n\nThis section is particularily helpful for re-implementing =filetags=\nfunctionality and for power-users which are interested in the advanced\nfunctions provided by this tool.\n\n** General\n\n| *Before*                         | *When*             | *After*                          | *Note*                                     |\n|----------------------------------+--------------------+----------------------------------+--------------------------------------------|\n| =Some file name.jpeg=            | tagging with =foo= | =Some file name -- foo.jpeg=     | Tag separator is added automatically       |\n| =Some file name=                 | tagging with =foo= | =Some file name -- foo=          | There is no need for a file extension      |\n| =Some file name -- foo.jpeg=     | tagging with =bar= | =Some file name -- foo bar.jpeg= | =bar= becomes last tag                     |\n| =Some file name.jpeg.lnk=        | tagging with =bar= | =Some file name -- bar.jpeg.lnk= | The =.lnk= extension is taken into account |\n| =Some file name -- bar.jpeg=     | untagging =bar=    | =Some file name.jpeg=            | Tag separator is removed                   |\n| =Some file name -- foo bar.jpeg= | untagging =foo=    | =Some file name -- bar.jpeg=     | Tag order stays same when removing         |\n\n- =filetags= may be used\n  1. interactively (via =--interactive= or missing \"action\" command\n     line parameters) from command line or\n  2. in a script using command line parameters.\n- =filetags= offers a =--dryrun= option which does not modify any file\n  or directory.\n- Added tag(s) get appended as last tag(s).\n- When removing tags, their relative order is preserved.\n- When modifying any file that is a symbolic link or a Windows =.LNK=\n  file to a file that has the same basename (file name without path),\n  the linked/original file gets modified as well.\n  - This comes very handy when working within TagTrees (see below).\n  - However, when modifying links which do not share the same\n    base-name with its link source, the link might become a broken one\n    (depending on the link technology used).\n- When un-tagging tags from files that do not have those tags, it is silently ignored.\n- [ ] FIXXME: describe =find_unique_alternative_to_file(filename)= and implications\n\n- FUTURE: [[https://github.com/novoid/filetags/issues/13][support for tagging folders/directories · Issue #13 · novoid/filetags · GitHub]]\n- FUTURE: [[https://github.com/novoid/filetags/issues/14][Files within tagged directories do inherit the tags for all relevant features · Issue #14 · novoid/f…]]\n  - Inheritance applies to many features such as \"don't tag a file\n    with a tag from any parent directory\" and so forth.\n  - Not that simple to decide each use-case. This is a hard nut to\n    crack with many complex things to take care of.\n- FUTURE: [[https://github.com/novoid/filetags/issues/18][CV: add CLI option that prevents users from using tags that are not part of the used .filetags file …]]\n  - Enforcing CVs is a good practice IMHO.\n\n** Interactive Mode\n\n- Print used tags of selected file(s).\n  - For multiple files, show only the tags that are used within all\n    selected files.\n- =filetags= dialog shows up to nine topmost used tags (sorted by\n  number of usage) used for files within the current directory.\n  - E.g., =draft(1) projectX(2) customer(3) bill(4)=\n  - You can use =0-9= as shortcuts to select those tags.\n    - You can concatenate shortcut numbers without spaces in-between:\n      =143 foo= tags with the shortcuts number 1, 3 and 4 and adds new\n      tag =foo=.\n      - With the example above, it is equivalent to tagging with:\n        =draft bill customer foo= or =draft 4 3 foo=.\n- You can un-tag tags that appear in file name using the minus prefix.\n  - E.g., =-foo= un-tags the tag =foo=.\n  - Auto-completion is provided to un-tag existing tags.\n- Tags from the CV (within =.filetags= files) and from tags used in\n  the current directory can be auto-completed via =TAB=.\n  - Already used tags are not available for completion.\n- Multiple Files\n  - You can tag/un-tag multiple selected files at once.\n    - Selected files containing the tag(s) to tag are not modified and no tags get duplicated.\n    - Selected files not containing the tag(s) to un-tag are not modified.\n  - Tag suggestions for un-tagging contain the common tags of selected files.\n- Tagging dialog can be aborted any time via =Ctrl-c=.\n\n** Controlled Vocabulary (CV)\n\nPlease read [[id:2018-07-08-cv][this]] first in order to understand CVs.\n\n- CV is read from =.filetags= files.\n  - One tag per line: simple tag\n  - Multiple tags per line, separated via spaces: a group of [[id:2018-07-08-mutually-exclusive-tags][mutually exclusive tags]]\n    - E.g., =draft final approved=\n      - When tagging =My report -- draft.txt= with =final=, =draft=\n        gets replaced by =final= without the user un-tagging it\n        before.\n      - =filetags= does not prevent user from manually tagging files\n        with two or more mutually exclusive tags.\n  - The order of priority to locate \"matching\" =.filetags= files is:\n    1. Current directory of the first file to tag/un-tag.\n    2. Any higher-level directory from the current directory of the first file to tag/un-tag.\n    3. =.filetags= file from the HOME directory.\n       - FUTURE: may be changed to: [[https://github.com/novoid/filetags/issues/16][Use \"$HOME/.config/filetags\" for overriding default options · Issue #16 · novoid/filetags · GitHub]]\n  - =.filetags= files may be links (hardlinks, symbolic links or even Windows =.LNK= files)\n- Comments within =.filetags= files begin with one or more =#= characters that may be prepended by one or more spaces.\n- You can omit (case insensitive) tags from being proposed (selectable\n  via shortcuts =0-9=) by adding special comment lines like:\n  : #donotsuggest omit-this-tag dontshow\n  : #donotsuggest wontpropose\n- PLANNED: =.filetags= files may include other =.filetags= files via =#include \u003crelative or absolute path to another file\u003e=\n  - [[https://github.com/novoid/filetags/issues/7][.filetags CV-file: include other files · Issue #7 · novoid/filetags · GitHub]]\n- FUTURE: [[https://github.com/novoid/filetags/issues/17][CV: .filetags may contain mandatory options · Issue #17 · novoid/filetags · GitHub]]\n  - Probably a nice to have for different default-behavior in different sub-hierarchies of the file system.\n\n** Filter\n\nThis function is very handy for filtering groups of photographs within\na large set of photographs as described [[id:2018-07-08-sel-photos][here]].\n\n- The user defines one or more tags whose files are linked to a target\n  directory.\n  - When more than one tag is given, only files that got tagged by all\n    given tags are linked.\n  - FUTURE: [[https://github.com/novoid/filetags/issues/10][CLI parameter to switch between: use symlink, hardlink, or copy · Issue #10 · novoid/filetags · GitH…]]\n    - This would allow for copying files instead of linking them.\n- Any \"matching\" =.filetags= file is linked to the target directory.\n- A populated target directory is never overwritten.\n- The default target directory is =.filetags_tagfilter= and might be\n  changed by =--tagtrees-dir=.\n- When started interactively, a file browser is opened showing the\n  target directory.\n  - The file browser tool might be overwritten with =--filebrowser=.\n- The =--recursive= option is taken into account accordingly.\n\n** Features Related to TagTrees\n\n[[id:2018-07-08-tagtrees][The TagTrees concept]] was developed by me during my PhD thesis ([[http://Karl-Voit.at/tagstore/downloads/Voit2012b.pdf][PDF]])\nwhen developing with the [[http://Karl-Voit.at/tagstore/][tagstore research platform]].\n\nPlease note that in future, all functions related to TagTrees will be\n*moved into a separate tool* named =tagtrees=.\n\n- TagTrees are generated according to the tags found in tagged files.\n- The =--recursive= option is taken into account accordingly.\n- FUTURE: [[https://github.com/novoid/filetags/issues/21][Generate something like TagTrees but for ctime/mtime · Issue #21 · novoid/filetags · GitHub]]\n- FUTURE: [[https://github.com/novoid/filetags/issues/9][--filter options also works when generating tagtrees · Issue #9 · novoid/filetags · GitHub]]\n\n** Tag Gardening\n\nJust invoke =filetags --tag-gardening= or =filetags --recursive\n--tag-gardening= and read its output to learn about helpful analysis\nresults to curate your tags. My personal favorites are:\n\n- I am able to find typos in tags (tag count is low and similar tags are found).\n- I can determine tags I seldom use and therefore might be removed from CVs.\n- Statistics on tag usage like, e.g.:\n  - Distribution of mutually exclusive tag options.\n  - Fraction of files that are not tagged.\n- Tags I have used which are not in my CVs.\n- Unused tags.\n\nThis feature is really powerful when it comes to maintenance of your\nfile tags or get some insight related to your tagging patterns.\n\n* Local Variables                                                  :noexport:\n# Local Variables:\n# mode: auto-fill\n# mode: flyspell\n# eval: (ispell-change-dictionary \"en_US\")\n# End:\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnovoid%2Ffiletags","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnovoid%2Ffiletags","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnovoid%2Ffiletags/lists"}