Removed old DataPuller
This commit is contained in:
parent
12bbe7c9d8
commit
d313833642
2 changed files with 145 additions and 302 deletions
278
DataPuller.py
Executable file → Normal file
278
DataPuller.py
Executable file → Normal file
|
|
@ -1,156 +1,174 @@
|
||||||
import os
|
import os
|
||||||
|
import argparse
|
||||||
import shutil
|
import shutil
|
||||||
|
import filecmp
|
||||||
|
|
||||||
# --- Hier die Pfade definieren ---
|
# Argument Parsing
|
||||||
QUELL_ORDNER = input("Quellordner: ")
|
parser = argparse.ArgumentParser(
|
||||||
ZIEL_ORDNER = input("Zielordner: ")
|
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")
|
||||||
|
|
||||||
def hole_datei_infos(verzeichnis):
|
args = parser.parse_args()
|
||||||
"""Sammelt alle Dateien mit ihrer Größe und dem Änderungsdatum."""
|
|
||||||
datei_infos = {}
|
|
||||||
if not os.path.exists(verzeichnis):
|
|
||||||
return datei_infos
|
|
||||||
|
|
||||||
for ordner_pfad, _, dateien in os.walk(verzeichnis):
|
# Paths
|
||||||
for datei in dateien:
|
if not args.input:
|
||||||
voller_pfad = os.path.join(ordner_pfad, datei)
|
inpath = input("Source Path: ")
|
||||||
# Relativen Pfad ermitteln (z. B. "unterordner\dokument.docx")
|
else:
|
||||||
relativer_pfad = os.path.relpath(voller_pfad, verzeichnis)
|
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:
|
try:
|
||||||
stat = os.stat(voller_pfad)
|
stat = os.stat(full_path)
|
||||||
datei_infos[relativer_pfad] = {
|
file_info[rel_path] = {
|
||||||
"groesse": stat.st_size,
|
"size": stat.st_size,
|
||||||
"aenderungsdatum": stat.st_mtime,
|
"modified": stat.st_mtime,
|
||||||
}
|
}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
return datei_infos
|
return file_info
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("=" * 50)
|
startstring = " FOLDER-SYNC 1.0 "
|
||||||
print(" USB-STICK SYNCHRONISATION")
|
if not args.quiet:
|
||||||
print("=" * 50)
|
print(len(startstring)*"=" + "\n" + startstring + "\n" + len(startstring)*"=")
|
||||||
|
|
||||||
# Prüfen, ob Laufwerke da sind
|
# Test Folder availability
|
||||||
if not os.path.exists(QUELL_ORDNER):
|
if not os.path.exists((inpath)):
|
||||||
print(f"Fehler: Das Schul-Laufwerk ({QUELL_ORDNER}) ist nicht erreichbar!")
|
print(f"ERROR: The input folder ({inpath}) is unavailable or non-existant.")
|
||||||
input("\nDrücke Enter zum Beenden...")
|
if not args.quiet:
|
||||||
return
|
input("\nPress ENTER to exit...")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
ziel_laufwerk = os.path.splitdrive(ZIEL_ORDNER)[0] + "\\"
|
if not os.path.exists(outpath):
|
||||||
if not os.path.exists(ziel_laufwerk):
|
print(f"ERROR: The output folder ({outpath}] is unavailable or non-existant.")
|
||||||
print(
|
if not args.quiet:
|
||||||
f"Fehler: Laufwerk {ziel_laufwerk} nicht gefunden. Ist der USB-Stick eingesteckt?"
|
input("\nPress ENTER to exit...")
|
||||||
)
|
exit(1)
|
||||||
input("\nDrücke Enter zum Beenden...")
|
|
||||||
return
|
|
||||||
|
|
||||||
print("Analysiere Dateien. Bitte warten...\n")
|
print("Analyzing files. Please wait...\n")
|
||||||
|
|
||||||
quelle = hole_datei_infos(QUELL_ORDNER)
|
source = get_files(inpath)
|
||||||
ziel = hole_datei_infos(ZIEL_ORDNER)
|
target = get_files(outpath)
|
||||||
|
|
||||||
neu = []
|
new = []
|
||||||
geaendert = []
|
changed = []
|
||||||
geloescht = []
|
deleted = []
|
||||||
|
|
||||||
# Prüfen, was auf P: neu oder geändert wurde
|
# Check for file additions and changes
|
||||||
for pfad, info in quelle.items():
|
for path, info in source.items():
|
||||||
if pfad not in ziel:
|
if path not in target:
|
||||||
neu.append(pfad)
|
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:
|
else:
|
||||||
# Wir prüfen die Dateigröße und das Datum (2 Sekunden Toleranz für USB-Sticks)
|
copy_confirm = input("Write new and changed files? (y/N): ").strip().lower()
|
||||||
if (
|
if copy_confirm in ["ja", "j", "yes", "y"]:
|
||||||
info["groesse"] != ziel[pfad]["groesse"]
|
if not args.quiet:
|
||||||
or abs(info["aenderungsdatum"] - ziel[pfad]["aenderungsdatum"]) > 2
|
print("\nCopying files...")
|
||||||
):
|
for path in new + changed:
|
||||||
geaendert.append(pfad)
|
source_path = os.path.join(inpath, path)
|
||||||
|
target_path = os.path.join(outpath, path)
|
||||||
|
|
||||||
# Prüfen, was von P: gelöscht wurde und auf dem Stick auch weg kann
|
# Create new folders if needed
|
||||||
for pfad in ziel:
|
os.makedirs(os.path.dirname(target_path), exist_ok=True)
|
||||||
if pfad not in quelle:
|
|
||||||
geloescht.append(pfad)
|
|
||||||
|
|
||||||
# Wenn alles aktuell ist
|
# Copy file
|
||||||
if not neu and not geaendert and not geloescht:
|
|
||||||
print(
|
|
||||||
"Alles ist bereits auf dem neuesten Stand! Keine Änderungen erforderlich."
|
|
||||||
)
|
|
||||||
input("\nDrücke Enter zum Beenden...")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Übersicht ausgeben
|
|
||||||
print("Folgende Änderungen wurden gefunden:\n")
|
|
||||||
if neu:
|
|
||||||
print("NEUE DATEIEN (werden auf den Stick kopiert):")
|
|
||||||
for d in neu:
|
|
||||||
print(f" + {d}")
|
|
||||||
print()
|
|
||||||
if geaendert:
|
|
||||||
print("GEÄNDERTE DATEIEN (Inhalt hat sich geändert, werden aktualisiert):")
|
|
||||||
for d in geaendert:
|
|
||||||
print(f" ~ {d}")
|
|
||||||
print()
|
|
||||||
if geloescht:
|
|
||||||
print("GELÖSCHTE DATEIEN (sind nicht mehr auf dem Schul-Laufwerk):")
|
|
||||||
for d in geloescht:
|
|
||||||
print(f" - {d}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
# --- SCHRITT 1: KOPIEREN & AKTUALISIEREN ---
|
|
||||||
if neu or geaendert:
|
|
||||||
antwort_kopieren = (
|
|
||||||
input("Sollen die NEUEN und GEÄNDERTEN Dateien kopiert werden? (ja/nein): ")
|
|
||||||
.strip()
|
|
||||||
.lower()
|
|
||||||
)
|
|
||||||
if antwort_kopieren in ["ja", "j", "yes", "y"]:
|
|
||||||
print("\nKopiere Dateien...")
|
|
||||||
for pfad in neu + geaendert:
|
|
||||||
quell_pfad = os.path.join(QUELL_ORDNER, pfad)
|
|
||||||
ziel_pfad = os.path.join(ZIEL_ORDNER, pfad)
|
|
||||||
|
|
||||||
# Ordnerstruktur auf dem Stick erstellen, falls ein neuer Unterordner da ist
|
|
||||||
os.makedirs(os.path.dirname(ziel_pfad), exist_ok=True)
|
|
||||||
|
|
||||||
# Datei kopieren
|
|
||||||
shutil.copy2(quell_pfad, ziel_pfad)
|
|
||||||
print(f"Erfolgreich kopiert: {pfad}")
|
|
||||||
else:
|
|
||||||
print("\nKopieren übersprungen.")
|
|
||||||
|
|
||||||
# --- SCHRITT 2: LÖSCHEN ---
|
|
||||||
if geloescht:
|
|
||||||
print("\n" + "!" * 55)
|
|
||||||
print(" ACHTUNG: Einige Dateien existieren nur auf dem Stick.")
|
|
||||||
print("!" * 55)
|
|
||||||
antwort_loeschen = (
|
|
||||||
input(
|
|
||||||
"Sollen die GELÖSCHTEN Dateien nun auch vom Stick ENTFERNT werden? (ja/nein): "
|
|
||||||
)
|
|
||||||
.strip()
|
|
||||||
.lower()
|
|
||||||
)
|
|
||||||
|
|
||||||
if antwort_loeschen in ["ja", "j", "yes", "y"]:
|
|
||||||
print("\nLösche Dateien...")
|
|
||||||
for pfad in geloescht:
|
|
||||||
ziel_pfad = os.path.join(ZIEL_ORDNER, pfad)
|
|
||||||
try:
|
try:
|
||||||
os.remove(ziel_pfad)
|
shutil.copy(source_path, target_path)
|
||||||
print(f"Erfolgreich gelöscht: {pfad}")
|
if not args.quiet:
|
||||||
except Exception as e:
|
print(f"Successfully copied {path}")
|
||||||
print(f"Fehler beim Löschen von {pfad}: {e}")
|
except Exception as error:
|
||||||
else:
|
print(f"Failed to copy {path} with error {error}")
|
||||||
print(
|
if not args.confirm:
|
||||||
"\nLöschen übersprungen. Die Dateien bleiben als Backup auf dem Stick erhalten."
|
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...")
|
||||||
|
|
||||||
print("\nSynchronisation abgeschlossen!")
|
|
||||||
input("\nDrücke Enter zum Beenden...")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
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()
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue