feat: add rclone support to import command and implement duplicate document skipping during sync
This commit is contained in:
parent
dda68cabec
commit
0ab5fee09b
1 changed files with 43 additions and 9 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
|
@ -15,15 +16,20 @@ User = get_user_model()
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = (
|
help = (
|
||||||
"Imports As-Built documents from a local directory into the "
|
"Imports As-Built documents from a local directory or via rclone from OneDrive "
|
||||||
"'Electromechanics / As-Built' subfolder of specified structures."
|
"into the 'Electromechanics / As-Built' subfolder of specified structures."
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--source-dir",
|
"--source-dir",
|
||||||
required=True,
|
default="/tmp/as_built_sync",
|
||||||
help="Path to the directory containing files/folders to import.",
|
help="Path to local directory containing files/folders to import (default: /tmp/as_built_sync).",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--rclone-remote",
|
||||||
|
default=None,
|
||||||
|
help="Optional rclone remote path (e.g. 'onedrive:DPO_TUNNEL_EM_AS-BUILT') to sync before import.",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--structures",
|
"--structures",
|
||||||
|
|
@ -49,17 +55,32 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
source_dir = options["source_dir"]
|
source_dir = options["source_dir"]
|
||||||
|
rclone_remote = options["rclone_remote"]
|
||||||
target_codes = options["structures"]
|
target_codes = options["structures"]
|
||||||
dry_run = options["dry_run"]
|
dry_run = options["dry_run"]
|
||||||
username = options["user"]
|
username = options["user"]
|
||||||
tag_name = options["tag"]
|
tag_name = options["tag"]
|
||||||
|
|
||||||
if not os.path.isdir(source_dir):
|
|
||||||
raise CommandError(f"Source directory '{source_dir}' does not exist or is not a directory.")
|
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
self.stdout.write(self.style.WARNING("DRY RUN — no changes will be committed."))
|
self.stdout.write(self.style.WARNING("DRY RUN — no changes will be committed."))
|
||||||
|
|
||||||
|
# Optionnel: Synchronisation via rclone si un remote est spécifié
|
||||||
|
if rclone_remote:
|
||||||
|
self.stdout.write(self.style.NOTICE(f"Syncing from rclone remote '{rclone_remote}' to '{source_dir}'..."))
|
||||||
|
os.makedirs(source_dir, exist_ok=True)
|
||||||
|
rclone_cmd = ["rclone", "copy", rclone_remote, source_dir]
|
||||||
|
rclone_conf = os.path.join(os.getcwd(), "rclone.conf")
|
||||||
|
if os.path.exists(rclone_conf):
|
||||||
|
rclone_cmd.extend(["--config", rclone_conf])
|
||||||
|
|
||||||
|
res = subprocess.run(rclone_cmd, capture_output=True, text=True)
|
||||||
|
if res.returncode != 0:
|
||||||
|
raise CommandError(f"rclone sync failed:\n{res.stderr}")
|
||||||
|
self.stdout.write(self.style.SUCCESS("rclone sync completed successfully."))
|
||||||
|
|
||||||
|
if not os.path.isdir(source_dir):
|
||||||
|
raise CommandError(f"Source directory '{source_dir}' does not exist or is not a directory.")
|
||||||
|
|
||||||
# Creator user lookup
|
# Creator user lookup
|
||||||
creator_user = None
|
creator_user = None
|
||||||
if username:
|
if username:
|
||||||
|
|
@ -134,6 +155,7 @@ class Command(BaseCommand):
|
||||||
structures = Structure.objects.all()
|
structures = Structure.objects.all()
|
||||||
|
|
||||||
total_imported = 0
|
total_imported = 0
|
||||||
|
total_skipped = 0
|
||||||
|
|
||||||
for structure in structures:
|
for structure in structures:
|
||||||
# Check if there is a dedicated subfolder for this structure code or name in source_dir
|
# Check if there is a dedicated subfolder for this structure code or name in source_dir
|
||||||
|
|
@ -173,12 +195,24 @@ class Command(BaseCommand):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.stdout.write(f" Target subfolder: Electromechanics / As-Built (ID: {as_built_folder.pk or 'dry-run'})")
|
self.stdout.write(f" Target subfolder: Electromechanics / As-Built (ID: {as_built_folder.pk or 'dry-run'})")
|
||||||
self.stdout.write(f" Found {len(files_to_import)} file(s) to import.")
|
self.stdout.write(f" Found {len(files_to_import)} file(s) to process.")
|
||||||
|
|
||||||
for file_path in files_to_import:
|
for file_path in files_to_import:
|
||||||
rel_name = os.path.basename(file_path)
|
rel_name = os.path.basename(file_path)
|
||||||
doc_title = os.path.splitext(rel_name)[0]
|
doc_title = os.path.splitext(rel_name)[0]
|
||||||
|
|
||||||
|
# Deduplication check
|
||||||
|
if as_built_folder.pk:
|
||||||
|
existing_doc = ManagedDocument.objects.filter(
|
||||||
|
folders=as_built_folder,
|
||||||
|
title=doc_title,
|
||||||
|
is_archived=False
|
||||||
|
).first()
|
||||||
|
if existing_doc:
|
||||||
|
self.stdout.write(f" Document '{rel_name}' already exists in As-Built folder (Doc ID: {existing_doc.pk}). Skipped.")
|
||||||
|
total_skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
self.stdout.write(f" [Dry-run] Would import '{rel_name}' into structure {structure.code}")
|
self.stdout.write(f" [Dry-run] Would import '{rel_name}' into structure {structure.code}")
|
||||||
total_imported += 1
|
total_imported += 1
|
||||||
|
|
@ -216,4 +250,4 @@ class Command(BaseCommand):
|
||||||
self.stdout.write(self.style.SUCCESS(f" Imported '{rel_name}' (Doc ID: {doc.pk})"))
|
self.stdout.write(self.style.SUCCESS(f" Imported '{rel_name}' (Doc ID: {doc.pk})"))
|
||||||
total_imported += 1
|
total_imported += 1
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS(f"\nDone! Total documents imported: {total_imported}"))
|
self.stdout.write(self.style.SUCCESS(f"\nDone! Imported: {total_imported}, Skipped (already imported): {total_skipped}"))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue