15

Running Ubuntu Gnome.

I have a lot of PDF and other documents and I will like to tag them. And search them later based on these tags. Even if I move the files to different folders (so, the tags stick to the files).

I searched but Files and Documents do not provide this options.

Am I doing something wrong? How can I tag files so I can search them based on tags later?

deshmukh
  • 4,121

4 Answers4

9

Contents:

  1. Introduction
  2. Installation
  3. Usage
  4. Source code

1. Introduction

This solution consists of two scripts - one for tagging, one for reading the list of files under specific tag. Both have to live in ~/.local/share/nautilus/scripts and activated via right-click in Nautilus file manager on any file, and navigating to Scripts submenu. The source code for each script is provided here as well as on GitHub

2. Installation

Both scripts have to be saved to ~/.local/share/nautilus/scripts , where ~ is user's home directory, and made executable with chmod +x filename. For easy installation, use the following bash script:

#!/bin/bash

N_SCRIPTS="$HOME/.local/share/nautilus/scripts"
cd /tmp
rm master.zip*
rm -rf nautilus_scripts-master
wget https://github.com/SergKolo/nautilus_scripts/archive/master.zip
unzip master.zip
install nautilus_scripts-master/tag_file.py "$N_SCRIPTS/tag_file.py"
install nautilus_scripts-master/read_tags.py "$N_SCRIPTS/read_tags.py"

3. Usage:

Tagging files:

Select files in Nautilus file manager, right click on them, and navigate to Scripts submenu. Select tag_file.py. Hit Enter enter image description here First time you run this script, there will be no configuration file, so you will see this:

enter image description here

Next time, when you already have some files tagged, you will see a popup that allows you to select a tag and/or add new one ( this way you can record files under multiple tags). Hit OK to add files to this tag. Note:Avoid having "|" symbol in the tag name.

enter image description here

The script records everything in ~/.tagged_files. That file is essentially a json dictionary ( which is not something regular users should care about, but it's convenient for programmers :) ). The format of that file is as so:

{
    "Important Screenshots": [
        "/home/xieerqi/\u56fe\u7247/Screenshot from 2016-10-01 09-15-46.png",
        "/home/xieerqi/\u56fe\u7247/Screenshot from 2016-09-30 18-47-12.png",
        "/home/xieerqi/\u56fe\u7247/Screenshot from 2016-09-30 18-46-46.png",
        "/home/xieerqi/\u56fe\u7247/Screenshot from 2016-09-30 17-35-32.png"
    ],
    "Translation Docs": [
        "/home/xieerqi/Downloads/908173 - \u7ffb\u8bd1.doc",
        "/home/xieerqi/Downloads/911683\u7ffb\u8bd1.docx",
        "/home/xieerqi/Downloads/914549 -\u7ffb\u8bd1.txt"
    ]
}

If you ever want to "untag" some file, just delete an entry from that list. Mind the format and commas.

Searching by tag:

Now that you have a nice ~/.tagged_files database of files, you can either read that file, or use read_tags.py script.

Right click on any file in Nautilus ( really doesn't matter which ).Select read_tags.py. Hit Enter enter image description here

You will see a popup asking you what tag you want to search:

enter image description here

Select one, click OK. You will see a list dialog showing you want files are there for the tag you selected. You can select any single file and it will open with a default program assigned to that file type.

enter image description here

4. Source code:

tag_file.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Author: Serg Kolo  
# Date: Oct 1st, 2016
# Description: tag_file.py, script for
#    recording paths to files under 
#    specific , user-defined tag
#    in ~/.tagged_files
# Written for: http://askubuntu.com/q/827701/295286
# Tested on : Ubuntu ( Unity ) 16.04

from __future__ import print_function
import subprocess
import json
import os
import sys

def show_error(string):
    subprocess.call(['zenity','--error',
                     '--title',__file__,
                     '--text',string
    ])
    sys.exit(1)

def run_cmd(cmdlist):
    """ Reusable function for running external commands """
    new_env = dict(os.environ)
    new_env['LC_ALL'] = 'C'
    try:
        stdout = subprocess.check_output(cmdlist, env=new_env)
    except subprocess.CalledProcessError:
        pass
    else:
        if stdout:
            return stdout


def write_to_file(conf_file,tag,path_list):

    # if config file exists , read it
    data = {}
    if os.path.exists(conf_file):
        with open(conf_file) as f:
            data = json.load(f)

    if tag in data:
        for path in path_list:
            if path in data[tag]:
               continue
            data[tag].append(path)
    else:
        data[tag] = path_list

    with open(conf_file,'w') as f:
        json.dump(data,f,indent=4,sort_keys=True)

def get_tags(conf_file):

    if os.path.exists(conf_file):
       with open(conf_file) as f:
            data = json.load(f)
            return '|'.join(data.keys())

def main():

    user_home = os.path.expanduser('~')
    config = '.tagged_files'
    conf_path = os.path.join(user_home,config)
    file_paths = [ os.path.abspath(f) for f in sys.argv[1:] ]
    tags = None

    try:
        tags = get_tags(conf_path)
    except Exception as e:
        show_error(e)

    command = [ 'zenity','--forms','--title',
                'Tag the File' 
    ]

    if tags:
       combo = ['--add-combo','Existing Tags',
                '--combo-values',tags
       ]

       command = command + combo

    command = command + ['--add-entry','New Tag']

    result = run_cmd(command)
    if not result: sys.exit(1)
    result = result.decode().strip().split('|')
    for tag in result:
        if tag == '':
           continue
        write_to_file(conf_path,tag,file_paths)

if __name__ == '__main__':
     main()

read_tags.py:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Author: Serg Kolo  
# Date: Oct 1st, 2016
# Description: read_tags.py, script for
#    reading  paths to files under 
#    specific , user-defined tag
#    in ~/.tagged_files
# Written for: http://askubuntu.com/q/827701/295286
# Tested on : Ubuntu ( Unity ) 16.04

import subprocess
import json
import sys
import os


def run_cmd(cmdlist):
    """ Reusable function for running external commands """
    new_env = dict(os.environ)
    new_env['LC_ALL'] = 'C'
    try:
        stdout = subprocess.check_output(cmdlist, env=new_env)
    except subprocess.CalledProcessError as e:
        print(str(e))
    else:
        if stdout:
            return stdout

def show_error(string):
    subprocess.call(['zenity','--error',
                     '--title',__file__,
                     '--text',string
    ])
    sys.exit(1)

def read_tags_file(file,tag):

    if os.path.exists(file):
       with open(file) as f:
            data = json.load(f)
            if tag in data.keys():
                return data[tag]
            else:
                show_error('No such tag')
    else:
       show_error('Config file doesnt exist')

def get_tags(conf_file):
    """ read the tags file, return
        a string joined with | for
        further processing """    
    if os.path.exists(conf_file):
       with open(conf_file) as f:
            data = json.load(f)
            return '|'.join(data.keys())

def main():

    user_home = os.path.expanduser('~')
    config = '.tagged_files'
    conf_path = os.path.join(user_home,config)

    tags = get_tags(conf_path)
    command = ['zenity','--forms','--add-combo',
               'Which tag ?', '--combo-values',tags
    ]

    tag = run_cmd(command)

    if not tag:
       sys.exit(0)

    tag = tag.decode().strip()
    file_list = read_tags_file(conf_path,tag)
    command = ['zenity', '--list', 
               '--text','Select a file to open',
               '--column', 'File paths'
    ]
    selected = run_cmd(command + file_list)    
    if selected:
       selected = selected.decode().strip()
       run_cmd(['xdg-open',selected])

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        show_error(str(e))

1

I have found a way to do this.

Open a terminal (CTRL+ALT+T) and then run this command:

sudo add-apt-repository ppa:tracker-team/tracker

Enter your password, and when Prompted, press enter, then run

sudo apt-get update

then

sudo apt-get install tracker tracker-gui

Don't worry if it says it's already the latest version.

Now open Nautilus/Files and right click the document you want to add tags to. Select properties, then select the tab that says "Tags". Enter a tag into the text box and press enter or click the Add button to add it. You can also click a tag you have already added and select the Remove button to remove a tag. Please note that tags are case sensitive. The tags you create will be persistent throughout the system, so you can easily put a check next to a tag you already created to mark the file instead of manually typig it again.

After tagging the items wanted, you can now search for them, but not in Files. Go to activities, and search for the app Desktop Search. Launch it, and look at the options at the top. At the top left side of the Window, click the folder icon with the tool tip "Display results by files in a list". Now you have more options. Select the option just to the left of the search box with the tool tip "Find search criteria in file tags only". Now you can search tags!

To use this, enter the tags you want to search, seperated by commas and press enter. For example:

Important, September, Presentation

This will only show files which have all three tags: "Important", "September", and "Presentation".

By double clicking one, it will open the file in the default program, and by right clicking and selecting "Show Parent Directory", it will open it's location in Nautilus.

In Desktop Search, you can also click the second button from the right at the top of the window (usually a star or heart) to edit tags in the app itself!

There you have it! Hope this helps. If you have any more questions, let me know.

0

Short answer: This is not possible natively in Ubuntu. You need some extension or a document database, as suggested by the other answers.

Another option I can recommend is to use the app TagSpaces. This app may save tags in the filename, which makes tagging durable and portable. You may edit the filename in Nautilus or within your Cloud-Storage and the tag will appear in TagSpaces and vice versa.

screenshot

0

Apple implements this in its Journaling File System. For Linux, this is not the case, as of yet. Of course, there needs to be a DB to keep the associations.

So, 2 recommendations:

  1. If the file is a plain text file, use Evernote instead, or any another note taking app that supports tagging. Evernote is awesome, I've been using it for years. The searches are lightning fast and the tags are so easy to maintain (from the left sidebar).
  2. If you do not want to install some other half-baked app as suggested above (kudos for suggesting it, though), then just put the tags at the top or bottom of the file, on a single line, and comma-separate them. That way, you will be able to search for them in CSV mannner, as long as you remember the sequence, or just by including single tags followed or preceded by a comma, like so: "shoes," ",shirts". I know this second solution is a bit ghetto, but it would be workable on a small scale - limited number of tags applied to a limited number of files :)