aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElisa Sohier <elisa.sohier@art-software.fr>2019-08-01 09:48:41 +0200
committerElisa Sohier <elisa.sohier@art-software.fr>2019-08-01 09:48:41 +0200
commit3b47005fa0e0267c061b3d5d244810b8b45fd639 (patch)
treebb696c8bf46316d7c53395ae8ad183acf96422f4
init
-rw-r--r--README.mkd1
-rw-r--r--autosync/__init__.py112
-rw-r--r--setup.py48
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"
+ ]
+ }
+)