diff options
| author | Elisa Sohier <elisa.sohier@art-software.fr> | 2019-08-01 09:48:41 +0200 |
|---|---|---|
| committer | Elisa Sohier <elisa.sohier@art-software.fr> | 2019-08-01 09:48:41 +0200 |
| commit | 3b47005fa0e0267c061b3d5d244810b8b45fd639 (patch) | |
| tree | bb696c8bf46316d7c53395ae8ad183acf96422f4 | |
init
| -rw-r--r-- | README.mkd | 1 | ||||
| -rw-r--r-- | autosync/__init__.py | 112 | ||||
| -rw-r--r-- | setup.py | 48 |
3 files changed, 161 insertions, 0 deletions
diff --git a/README.mkd b/README.mkd new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/README.mkd @@ -0,0 +1 @@ + diff --git a/autosync/__init__.py b/autosync/__init__.py new file mode 100644 index 0000000..458a1f0 --- /dev/null +++ b/autosync/__init__.py @@ -0,0 +1,112 @@ +import glob +import os +import subprocess +import sys +import time + +from collections import deque + +server = False + +def msg(data): + print("\n\033[1;34m:: \033[1;37m{}\033[0m".format(data)) + +def sync(source, dest, ignoreFile=None, verbose=False): + """Synchronizes data between a source and a target.""" + args = ["rsync", "--delete", "-zHaAXS", source, dest] + if ignoreFile is not None: + args.append("--exclude-from="+ignoreFile) + if verbose: + args[2] += "v" + + return subprocess.call(args) + +def getTab(name="", elements=[]): + """Get entries""" + tabData = {} + if name == "": + tabFile = os.path.join(os.environ["HOME"], ".autoSync.tab") + else: + tabFile = os.path.join(os.environ["HOME"], ".autoSync.{}.tab".format(name)) + + with open(tabFile, "r") as f: + for line in f.readlines(): + name, src, dst = filter(lambda i: len(i) > 0, line.rstrip("\n").split("\t")) + tabData[name] = (os.path.expandvars(os.path.expanduser(src)), os.path.expandvars(os.path.expanduser(dst))) + + if len(elements) < 1: + elements = list(tabData.keys()) + + for itemName in elements: + if itemName in tabData.keys(): + yield (itemName, *tabData[itemName]) + +def getTimestamp(source, dest): + """Get source and destination timestamps""" + sourceTS = os.path.join(source, ".lastsync") + destTS = os.path.join(dest, ".lastsync") + + if sync(sourceTS, "/tmp/source.ts") != 0: + with open("/tmp/source.ts", "w") as f: + f.write("0") + if sync(destTS, "/tmp/dest.ts") != 0: + with open("/tmp/dest.ts", "w") as f: + f.write("0") + + with open("/tmp/source.ts", "r") as sourceF, open("/tmp/dest.ts", "r") as destF: + return (float(sourceF.read()), float(destF.read())) + +def run(): + global server + args = deque(sys.argv[1:]) + if len(args) < 1: + print("{0} @ – list available tab names\n{0} @@tabname – list available entries under tabname\n{0} @tabname entry1 entry2 – synchronizes entry1 and entry2 from tabname\n{0} @tabname – synchronizes every entry found in tabname\n{0} -s @[tabname][ entry1[ entry2[ …]]] – synchronizes in server mode (ie don't refresh timestamp)".format(os.path.basename(sys.argv[0]))) + return + + firstArg = args.popleft() + if firstArg == "-s": + server = True + firstArg = args.popleft() + + if firstArg.startswith("@"): + if firstArg.startswith("@@"): # list available entries for tabfile + elts = list(getTab(name=firstArg.lstrip("@"))) + + maxLenName = max(len(line[0]) for line in elts) + maxLenSrc = max(len(line[1]) for line in elts) + maxLenDst = max(len(line[1]) for line in elts) + + msg("Available entries for tabfile {}:".format(firstArg.lstrip("@"))) + print("\n\033[1;37m%-*s %-*s %-*s\033[0m" % (maxLenName, "Name", maxLenSrc, "Source", maxLenDst, "Target")) + for ename, esrc, edst in elts: + print("%-*s %-*s %-*s" % (maxLenName, ename, maxLenSrc, esrc, maxLenDst, edst)) + + elif firstArg == "@": # lists all available tabFiles + files = glob.glob(os.path.join(os.environ["HOME"], ".autoSync.*.tab")) + msg("Available tables:") + print("\n".join("– " + os.path.basename(fileName).split(".", 2)[2][:-4] for fileName in files)) + + else: + firstArg = firstArg.lstrip("@") + for name, src, dst in getTab(firstArg, args): + srcT, dstT = getTimestamp(src, dst) + ignoreFile = os.path.join(src, ".ignore.lst") + if not os.path.exists(ignoreFile): + ignoreFile = None + if srcT >= dstT: + msg("[{} @ {}] sending data to destination".format(name, firstArg)) + if not server: + with open(os.path.join(src, ".lastsync"), "w") as f: + f.write(str(time.time())) + sync(src, dst, ignoreFile, True) + else: + msg("[{} @ {}] gathering data from destination".format(name, firstArg)) + sync(dst, src, ignoreFile, True) + if not server: + with open(os.path.join(src, ".lastsync"), "w") as f: + f.write(str(time.time())) + + if os.path.exists("/tmp/source.ts"): + os.unlink("/tmp/source.ts") + if os.path.exists("/tmp/dest.ts"): + os.unlink("/tmp/dest.ts") diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..140a1a8 --- /dev/null +++ b/setup.py @@ -0,0 +1,48 @@ +from setuptools import setup, find_packages +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) +with open(path.join(here, 'README.mkd'), encoding='UTF-8') as f: + long_description = f.read() + +setup( + name='AutoSync', + version='0.0.0', + description='Automatic synchronization utility', + long_description=long_description, + url='http://git.art-software.fr/electron/autosync.git', + author='Elisa Sohier', + author_email='elisa.sohier@art-software.fr', + license='GPL-3', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + # Maturity ? + # 3 - Alpha + # 4 - Beta + # 5 - Prod / stable + 'Development Status :: 3 - Alpha', + + # Target ? + 'Intended Audience :: Developers', + # 'Topic :: Software Development :: Build Tools', + + # License + 'License :: OSI Approved :: GPLv3 License', + + # Python versions + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + ], + + keywords='python sync rsync utility', + packages=find_packages(exclude=['contrib', 'docs', 'tests']), + install_requires=[ + ], + entry_points={ + 'console_scripts': [ + "ascp = autosync:run" + ] + } +) |
