Skip to content

Instantly share code, notes, and snippets.

@konnov
Created October 12, 2010 15:26
Show Gist options
  • Save konnov/622362 to your computer and use it in GitHub Desktop.
Save konnov/622362 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
#
# Convenient script to store scientific papers in a local repository
# and set various links, i.e. tags, on them.
#
# Igor Konnov <konnov at cs.msu.su>, 2009-2010
#
# USAGE:
#
# For instance, if the repository is set as ~/sci and one wishes
# to add the paper 2020doe_ufo.pdf tagged by words 'ufo', 'astronomy'
# do just the following:
#
# ./sciit --add 2020doe_ufo.pdf ufo astronomy
#
# It results in moving 2020doe_ufo.pdf into ~/sci/.stor/ and then
# sets symbolic link ~/sci/ufo/2020doe_ufo.pdf -> ~/sci/.stor/2020doe_ufo.pdf
# as well as ~/sci/astronomy/2020doe_ufo.pdf -> ~/sci/.stor/2020doe_ufo.pdf
#
# CONFIGURATION
#
# Create a file ~/.sciitrc and write the following:
#
# [default]
# repo-path = <path-to-your-repository>
import ConfigParser
import exceptions
import getopt
import os
import re
import shutil
import sys
class ConfigReader:
def __init__(self):
self.config = ConfigParser.ConfigParser()
self.config.read(os.path.expanduser("~/.sciitrc"))
def repo_path(self):
return self.config.get("default", "repo-path")
class FileNotFoundException(exceptions.Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
class ConventionsViolatedException(exceptions.Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
class Command:
def run(self, **kwargs):
print "Nothing to do"
class AddCommand:
def __init__(self, cfg_reader):
self._cfg_reader = cfg_reader
def run(self, filename, tags):
if not os.path.exists(filename):
raise FileNotFoundException("File %s not found" % filename)
stor_path = os.path.expanduser(self._cfg_reader.repo_path())
if not os.path.exists(stor_path):
raise FileNotFoundException("Storage home %s not found" % stor_path)
basename = os.path.basename(filename)
self._check_conventions(basename)
target = os.path.join(stor_path, ".stor", basename)
print "%s -> %s" % (filename, target)
if not os.path.isfile(filename):
print "%s is not a file. Refusing to continue" % filename
try:
os.rename(filename, target)
except OSError, e:
# probably, filename and target reside on different volumes
shutil.copy2(filename, target)
os.remove(filename)
for tag in tags:
tag_dir = os.path.join(stor_path, tag)
if not os.path.exists(tag_dir):
os.makedirs(tag_dir)
tag_link = os.path.join(tag_dir, basename)
print "symlink %s to %s" % (tag_link, target)
os.symlink(target, tag_link)
def _check_conventions(self, basename):
m = re.match("^[0-9]{4,4}[a-z]+_[0-9a-z]+(\.[a-z]+)+$", basename)
if not m:
raise ConventionsViolatedException("Conventions violated for %s" \
% basename)
class TagCommand:
def __init__(self, cfg_reader):
self._cfg_reader = cfg_reader
def run(self, filename, tags):
stor_path = os.path.expanduser(self._cfg_reader.repo_path())
basename = os.path.basename(filename)
source = os.path.join(stor_path, ".stor", basename)
if not os.path.exists(source):
raise FileNotFoundException("File %s not found" % source)
for tag in tags:
tag_dir = os.path.join(stor_path, tag)
if not os.path.exists(tag_dir):
os.makedirs(tag_dir)
tag_link = os.path.join(tag_dir, basename)
print "symlink %s to %s" % (tag_link, source)
if not os.path.exists(tag_link):
os.symlink(source, tag_link)
class UntagCommand:
def __init__(self, cfg_reader):
self._cfg_reader = cfg_reader
def run(self, filename, tags):
stor_path = os.path.expanduser(self._cfg_reader.repo_path())
basename = os.path.basename(filename)
source = os.path.join(stor_path, ".stor", basename)
if not os.path.exists(source):
raise FileNotFoundException("File %s not found" % source)
for tag in tags:
tag_dir = os.path.join(stor_path, tag)
if not os.path.exists(tag_dir):
os.makedirs(tag_dir)
tag_link = os.path.join(tag_dir, basename)
print "unlink %s" % tag_link
if os.path.exists(tag_link):
os.unlink(tag_link)
def usage():
print "Use: sciit command file tag1 ... tagN"
print " where command is one of the following:"
print " --add add file to repository and set the tags given"
print " --tag add tags to the file in repository"
print " --untag remove tags of the file in repository"
if __name__ == "__main__":
try:
opts, args = getopt.getopt(sys.argv[1:], "h",
["help", "add=", "tag=", "untag="])
except getopt.GetoptError, err:
# print help information and exit:
print str(err) # will print something like "option -a not recognized"
usage()
sys.exit(2)
cfg_reader = ConfigReader()
cmd = None
cmd_args = []
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
elif o in ("--add"):
cmd = AddCommand(cfg_reader)
cmd_args = { "filename" : a, "tags" : args }
elif o in ("--tag"):
cmd = TagCommand(cfg_reader)
cmd_args = { "filename" : a, "tags" : args }
elif o in ("--untag"):
cmd = UntagCommand(cfg_reader)
cmd_args = { "filename" : a, "tags" : args }
else:
assert False, "unhandled option"
if cmd:
try:
cmd.run(**cmd_args)
except FileNotFoundException, e:
print str(e)
except ConventionsViolatedException, e:
print str(e)
else:
usage()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment