DataPuller/DataPuller.py

195 lines
6 KiB
Python

#! /usr/bin/python3
import os
import argparse
import shutil
import filecmp
# Argument Parsing
parser = argparse.ArgumentParser(
prog="DataPuller",
description="Check differences between folders and update files if needed",
)
parser.add_argument("-i", "--input")
parser.add_argument("-o", "--output")
parser.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("-y", "--confirm", action="store_true")
parser.add_argument("-d", "--delete", action="store_true")
parser.add_argument("--dryrun", action="store_true")
args = parser.parse_args()
# Paths
if not args.input:
inpath = input("Source Path: ")
else:
inpath = args.input
if not args.output:
outpath = input("Target Path: ")
else:
outpath = args.output
def get_files(path):
file_info = {}
if not os.path.exists(path):
return file_info
for folder, _, files in os.walk(path):
for file in files:
full_path = os.path.join(folder, file)
rel_path = os.path.relpath(full_path, path)
try:
stat = os.stat(full_path)
file_info[rel_path] = {
"size": stat.st_size,
"modified": stat.st_mtime,
}
except Exception:
pass
return file_info
def main():
startstring = " FOLDER-SYNC 1.0 "
if not args.quiet:
print(
len(startstring) * "=" + "\n" + startstring + "\n" + len(startstring) * "="
)
# Test Folder availability
if not os.path.exists((inpath)):
print(f"ERROR: The input folder ({inpath}) is unavailable or non-existant.")
if not args.quiet:
input("\nPress ENTER to exit...")
exit(1)
if not os.path.exists(outpath):
print(f"ERROR: The output folder ({outpath}] is unavailable or non-existant.")
if not args.quiet:
input("\nPress ENTER to exit...")
exit(1)
print("Analyzing files. Please wait...\n")
source = get_files(inpath)
target = get_files(outpath)
new = []
changed = []
deleted = []
# Check for file additions and changes
for path, info in source.items():
if path not in target:
new.append(path)
elif not filecmp.cmp(os.path.join(inpath, path), os.path.join(outpath, path)):
changed.append(path)
# Check for file deletions
for path in target:
if path not in source:
deleted.append(path)
if not new and not changed and not deleted:
if not args.quiet:
print("No changes found.")
input("\nPress ENTER to exit...")
exit(0)
# Print overview
if not args.quiet:
print("Found the following changes:\n")
if new:
print("Added:")
for i in new:
print(f" + {i}")
print()
if changed:
print("Changed:")
for i in changed:
print(f" ~ {i}")
if deleted:
print("Deleted:")
for i in deleted:
print(f" - {i}")
# Copy and update files
if not args.dryrun:
if new or changed:
if args.confirm:
copy_confirm = "y"
else:
copy_confirm = (
input("Write new and changed files? (y/N): ").strip().lower()
)
if copy_confirm in ["ja", "j", "yes", "y"]:
if not args.quiet:
print("\nCopying files...")
for path in new + changed:
source_path = os.path.join(inpath, path)
target_path = os.path.join(outpath, path)
# Create new folders if needed
os.makedirs(os.path.dirname(target_path), exist_ok=True)
# Copy file
try:
shutil.copy(source_path, target_path)
if not args.quiet:
print(f"Successfully copied {path}")
except Exception as error:
print(f"Failed to copy {path} with error {error}")
if not args.confirm:
if not input("Continue? (y/N): ") in [
"ja",
"j",
"yes",
"y",
]:
exit(1)
elif not args.quiet:
print("\nSkipping changes...")
# Delete files
if not args.dryrun:
if deleted:
alert = " ALERT: Some files only exist at the destination. "
if not args.quiet:
print(f"{len(alert) * '*'}\n{alert}\n{len(alert) * '*'}")
if args.delete:
delete_confirm = "y"
else:
delete_confirm = (
input("Delete files on the target? (y/N): ").strip().lower()
)
if delete_confirm in ["ja", "j", "yes", "y"]:
if not args.quiet:
print("\nDeleting files...")
for path in deleted:
target_path = os.path.join(outpath, path)
try:
os.remove(target_path)
if not args.quiet:
print(f"Successfully deleted {path}")
except Exception as error:
print(f"Failed to delete {path} with error {error}")
if not args.confirm:
if not input("Continue? (y/N): ") in [
"ja",
"j",
"yes",
"y",
]:
exit(1)
elif not args.quiet:
print("\nSkipping deletion...")
if not args.quiet:
print("\nFolder sync finished!")
input("\nPress ENTER to exit...")
if __name__ == "__main__":
main()