"""Logic for smart_downloader.""" import datetime import logging import subprocess import time from typing import Iterable from config import DEFAULTT_N_DOWNLOAD_THREADS logger = logging.getLogger(__name__) def parallel_download_curl_to_s3( tasks: Iterable[dict], n_threads=DEFAULTT_N_DOWNLOAD_THREADS) -> dict: """Download files from URLs to S3 in parallel.""" if not n_threads: n_threads = DEFAULTT_N_DOWNLOAD_THREADS bash_commands = generate_bash_commands( tasks=tasks, ) n_tasks = len(bash_commands) command = ['/usr/bin/xargs', '--delimiter', '\\n', f'--max-procs={n_threads}', '-i', 'bash', '-c', '{}', ';'] logger.info(f'Running {n_tasks} commands: {bash_commands}') logger.info(f'Wrapped by {command}') processing_started_at = datetime.datetime.utcnow() started = time.perf_counter() completed_process = subprocess.run( args=command, input=bytearray('\n'.join(bash_commands), 'utf-8') ) finished = time.perf_counter() duration = finished - started result = dict( return_code=completed_process.returncode, duration=f'{duration:.2f}', n_threads=n_threads, n_tasks=n_tasks, processing_started_at=processing_started_at.isoformat(), ) return result def generate_bash_commands(tasks: Iterable[dict]): """Generate bash commands for downloading files from URLs to S3.""" result = [] for task in tasks: destination_url = task['destination_url'] source_url = task['source_url'] assert destination_url.startswith('s3://'), \ f'Destination should be S3 url {destination_url}' assert source_url.startswith('https://'), \ f'Source should be HTTPS url {destination_url}' command = f'./curl-to-s3.sh "{source_url}" "{destination_url}"' result.append(command) return result