Coverage for src/usaspending/cli/download_award.py: 0%
48 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-03 17:15 -0700
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-03 17:15 -0700
1# src/usaspending/cli/download_award.py
3import argparse
4import sys
5import os
6from typing import List
8# Helper to ensure the package root is in sys.path if running the script directly during development
9if __name__ == "__main__":
10 # Adjust path relative to the script location (src/usaspending/cli)
11 project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
12 if project_root not in sys.path:
13 sys.path.insert(0, project_root)
15# Import necessary components from the library
16from usaspending.client import USASpending
17from usaspending.exceptions import DownloadError
18from usaspending.logging_config import USASpendingLogger
20def main():
21 parser = argparse.ArgumentParser(
22 description="Download detailed award data from USASpending.gov.",
23 epilog="This CLI tool queues a download job, waits for completion, downloads the zip file, and extracts the contents."
24 )
26 parser.add_argument("award_id", help="The unique award identifier (e.g., PIIN/FAIN/etc.)")
27 parser.add_argument("-o", "--output-dir", help="The directory to save and extract the files (defaults to current directory).")
28 parser.add_argument("-f", "--format", choices=["csv", "tsv", "pstxt"], default="csv", help="The format of the files (default: csv).")
29 parser.add_argument("--timeout", type=int, default=1800, help="Maximum time in seconds to wait (default: 1800s/30min).")
30 parser.add_argument("--poll-interval", type=int, default=30, help="Interval in seconds between status checks (default: 30s).")
31 parser.add_argument("--no-cleanup", action="store_true", help="Keep the zip file after extraction.")
32 parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose debug logging.")
34 args = parser.parse_args()
36 # Configure logging
37 log_level = "DEBUG" if args.verbose else "INFO"
38 USASpendingLogger.configure(level=log_level, debug_mode=args.verbose)
39 logger = USASpendingLogger.get_logger("usaspending.cli.download")
41 try:
42 # Initialize client (assuming default configuration handling)
43 client = USASpending()
45 # Get award data
46 award = client.awards.find_by_award_id(args.award_id)
48 logger.info("--- Starting USASpending Award Download CLI ---")
49 logger.info(f"Parameters: Award ID={args.award_id}, Type={award.category}, Format={args.format}")
51 job = award.download(file_format=args.format, destination_dir=args.output_dir)
53 logger.info(f"Job successfully queued. Tracking File: {job.file_name}")
55 # 2. Wait for completion (blocking operation)
56 extracted_files: List[str] = job.wait_for_completion(
57 timeout=args.timeout,
58 poll_interval=args.poll_interval,
59 cleanup_zip=not args.no_cleanup
60 )
62 logger.info("--- Download and Extraction Complete ---")
63 # Determine the final extraction path
64 extract_subdir_name = os.path.splitext(job.file_name)[0]
65 final_path = os.path.join(job.destination_dir, extract_subdir_name)
66 logger.info(f"Data extracted to: {os.path.abspath(final_path)}")
67 logger.info(f"Total files extracted: {len(extracted_files)}")
69 except DownloadError as e:
70 logger.error(f"Download failed. Status: {e.status}. Message: {e}")
71 sys.exit(1)
72 except KeyboardInterrupt:
73 logger.warning("Download interrupted by user.")
74 sys.exit(1)
75 except Exception as e:
76 logger.exception(f"An unexpected error occurred: {e}")
77 sys.exit(1)
79if __name__ == "__main__":
80 main()