feat: Adobe Sign template download pipeline
download_templates.py — subcommand CLI for listing and downloading library
templates from Adobe Sign.
list — print all templates with name, modified date, ID
download — download all templates (default)
download --all — explicit download all
download "Name" — download a single named template; picks the most
recently modified if duplicates exist
Each template is saved to downloads/<name>__<id8>/ containing metadata.json,
form_fields.json, documents.json, and the source PDF.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
343955241d
commit
e655d8b4f5
|
|
@ -0,0 +1,162 @@
|
||||||
|
"""
|
||||||
|
Download Adobe Sign library templates to the local downloads/ folder.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 src/download_templates.py list
|
||||||
|
List all templates available in the account (no download).
|
||||||
|
|
||||||
|
python3 src/download_templates.py download
|
||||||
|
python3 src/download_templates.py download --all
|
||||||
|
Download all templates.
|
||||||
|
|
||||||
|
python3 src/download_templates.py download "Template Name"
|
||||||
|
Download the named template. If multiple templates share the same name,
|
||||||
|
the most recently modified one is used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
|
|
||||||
|
from adobe_api import adobe_api_get, adobe_api_get_bytes
|
||||||
|
|
||||||
|
DOWNLOADS_DIR = os.path.join(os.path.dirname(__file__), "..", "downloads")
|
||||||
|
|
||||||
|
|
||||||
|
def safe_dirname(name):
|
||||||
|
return "".join(c if c.isalnum() or c in " -_" else "_" for c in name).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def save_json(path, data):
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_template_list():
|
||||||
|
result = adobe_api_get("libraryDocuments")
|
||||||
|
return result.get("libraryDocumentList", [])
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_list(_args):
|
||||||
|
print("Fetching template list...")
|
||||||
|
templates = fetch_template_list()
|
||||||
|
if not templates:
|
||||||
|
print("No templates found.")
|
||||||
|
return
|
||||||
|
print(f"{'Name':<45} {'Modified':<25} {'ID'}")
|
||||||
|
print("-" * 100)
|
||||||
|
for t in sorted(templates, key=lambda x: x.get("modifiedDate", ""), reverse=True):
|
||||||
|
print(f"{t['name']:<45} {t.get('modifiedDate', 'n/a'):<25} {t['id']}")
|
||||||
|
|
||||||
|
|
||||||
|
def download_one(template):
|
||||||
|
template_id = template["id"]
|
||||||
|
template_name = template["name"]
|
||||||
|
dir_name = f"{safe_dirname(template_name)}__{template_id[:8]}"
|
||||||
|
out_dir = os.path.join(DOWNLOADS_DIR, dir_name)
|
||||||
|
os.makedirs(out_dir, exist_ok=True)
|
||||||
|
|
||||||
|
print(f"\n--- {template_name} ---")
|
||||||
|
|
||||||
|
metadata = adobe_api_get(f"libraryDocuments/{template_id}")
|
||||||
|
save_json(os.path.join(out_dir, "metadata.json"), metadata)
|
||||||
|
|
||||||
|
try:
|
||||||
|
form_fields = adobe_api_get(f"libraryDocuments/{template_id}/formFields")
|
||||||
|
save_json(os.path.join(out_dir, "form_fields.json"), form_fields)
|
||||||
|
print(f" {len(form_fields.get('fields', []))} form fields saved.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" WARNING: Could not fetch form fields: {e}")
|
||||||
|
save_json(os.path.join(out_dir, "form_fields.json"), {"error": str(e)})
|
||||||
|
|
||||||
|
docs = adobe_api_get(f"libraryDocuments/{template_id}/documents")
|
||||||
|
save_json(os.path.join(out_dir, "documents.json"), docs)
|
||||||
|
|
||||||
|
for doc in docs.get("documents", []):
|
||||||
|
doc_id = doc["id"]
|
||||||
|
doc_name = doc.get("name", doc_id)
|
||||||
|
if not doc_name.lower().endswith(".pdf"):
|
||||||
|
doc_name += ".pdf"
|
||||||
|
safe_name = safe_dirname(doc_name)
|
||||||
|
pdf_path = os.path.join(out_dir, safe_name)
|
||||||
|
try:
|
||||||
|
pdf_bytes = adobe_api_get_bytes(
|
||||||
|
f"libraryDocuments/{template_id}/documents/{doc_id}"
|
||||||
|
)
|
||||||
|
with open(pdf_path, "wb") as f:
|
||||||
|
f.write(pdf_bytes)
|
||||||
|
print(f" PDF saved ({len(pdf_bytes) // 1024}KB) → {safe_name}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" WARNING: Could not download PDF: {e}")
|
||||||
|
|
||||||
|
print(f" Done → downloads/{dir_name}/")
|
||||||
|
return out_dir
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_download(args):
|
||||||
|
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
|
||||||
|
templates = fetch_template_list()
|
||||||
|
|
||||||
|
if not templates:
|
||||||
|
print("No templates found in the account.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Specific template name requested
|
||||||
|
if args.template_name:
|
||||||
|
matches = [t for t in templates if t["name"] == args.template_name]
|
||||||
|
if not matches:
|
||||||
|
print(f"ERROR: No template named '{args.template_name}' found.")
|
||||||
|
print("Run 'list' to see available templates.")
|
||||||
|
sys.exit(1)
|
||||||
|
if len(matches) > 1:
|
||||||
|
print(f" {len(matches)} templates named '{args.template_name}' — using most recently modified.")
|
||||||
|
target = max(matches, key=lambda t: t.get("modifiedDate", ""))
|
||||||
|
download_one(target)
|
||||||
|
return
|
||||||
|
|
||||||
|
# --all or default (no name given)
|
||||||
|
print(f"Downloading all {len(templates)} template(s)...")
|
||||||
|
for t in templates:
|
||||||
|
download_one(t)
|
||||||
|
print(f"\nAll templates downloaded to: {os.path.abspath(DOWNLOADS_DIR)}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Download Adobe Sign library templates",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog=__doc__,
|
||||||
|
)
|
||||||
|
subparsers = parser.add_subparsers(dest="command")
|
||||||
|
|
||||||
|
subparsers.add_parser("list", help="List all templates in the account")
|
||||||
|
|
||||||
|
dl_parser = subparsers.add_parser("download", help="Download templates")
|
||||||
|
dl_parser.add_argument(
|
||||||
|
"template_name", nargs="?", default=None,
|
||||||
|
metavar="TEMPLATE_NAME",
|
||||||
|
help="Name of template to download. Omit (or use --all) to download all.",
|
||||||
|
)
|
||||||
|
dl_parser.add_argument(
|
||||||
|
"--all", dest="download_all", action="store_true",
|
||||||
|
help="Download all templates (default when no name is given)",
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.command == "list":
|
||||||
|
cmd_list(args)
|
||||||
|
elif args.command == "download":
|
||||||
|
cmd_download(args)
|
||||||
|
else:
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue