174 lines
6.1 KiB
Python
174 lines
6.1 KiB
Python
"""
|
||
create_adobe_template.py
|
||
------------------------
|
||
Creates "Paul Adobe Template" in Adobe Sign by:
|
||
1. Using the exact field positions from the downloaded David Tag Demo Form
|
||
2. Adding 4 extra fields (Number, Email, Company, Title) in available gaps
|
||
|
||
Usage:
|
||
python3 src/create_adobe_template.py
|
||
"""
|
||
|
||
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_post_multipart, adobe_api_post_json, adobe_api_put_json
|
||
|
||
PDF_PATH = os.path.join(
|
||
os.path.dirname(__file__), "..",
|
||
"downloads", "David Tag Demo Form__CBJCHBCA", "Tag Demo Form_docx_pdf"
|
||
)
|
||
|
||
FIELDS_JSON_PATH = os.path.join(
|
||
os.path.dirname(__file__), "..",
|
||
"downloads", "David Tag Demo Form__CBJCHBCA", "form_fields.json"
|
||
)
|
||
|
||
TEMPLATE_NAME = "Paul Adobe Template"
|
||
|
||
# Keys the Adobe Sign API accepts when writing form fields.
|
||
# We strip server-generated metadata (origin, signerIndex, font/border styling).
|
||
ALLOWED_KEYS = {
|
||
"name", "inputType", "contentType", "validation", "validationData",
|
||
"required", "readOnly", "locations", "assignee",
|
||
"hiddenOptions", "visibleOptions", "defaultValue",
|
||
"masked", "maskingText", "calculated", "urlOverridable",
|
||
"minLength", "maxLength", "minValue", "maxValue",
|
||
"validationErrMsg", "currency", "conditionalAction",
|
||
"radioCheckType",
|
||
}
|
||
|
||
# Extra fields that cover types not present in the original David Tag form:
|
||
# - NUMBER validation (TEXT_FIELD / DATA / NUMBER)
|
||
# - SIGNER_EMAIL auto-fill
|
||
# - COMPANY auto-fill
|
||
# - TITLE auto-fill
|
||
#
|
||
# Placed in the two clear vertical gaps in the original layout:
|
||
# Gap A: y=375–432 (between checkboxes and Initials 1), right side (left=350)
|
||
# Gap B: y=513–582 (between Date of Signing 1 and Signature block), left side (left=106)
|
||
EXTRA_FIELDS = [
|
||
{
|
||
"name": "Company",
|
||
"inputType": "TEXT_FIELD", "contentType": "COMPANY", "validation": "NONE",
|
||
"required": False, "readOnly": False,
|
||
"locations": [{"pageNumber": 1, "top": 378, "left": 350, "width": 150, "height": 24}],
|
||
"assignee": "recipient0",
|
||
},
|
||
{
|
||
"name": "Title",
|
||
"inputType": "TEXT_FIELD", "contentType": "TITLE", "validation": "NONE",
|
||
"required": False, "readOnly": False,
|
||
"locations": [{"pageNumber": 1, "top": 410, "left": 350, "width": 150, "height": 24}],
|
||
"assignee": "recipient0",
|
||
},
|
||
{
|
||
"name": "Number Field",
|
||
"inputType": "TEXT_FIELD", "contentType": "DATA", "validation": "NUMBER",
|
||
"required": False, "readOnly": False,
|
||
"locations": [{"pageNumber": 1, "top": 516, "left": 106, "width": 150, "height": 24}],
|
||
"assignee": "recipient0",
|
||
},
|
||
{
|
||
"name": "Recipient Email",
|
||
"inputType": "TEXT_FIELD", "contentType": "SIGNER_EMAIL", "validation": "NONE",
|
||
"required": False, "readOnly": True,
|
||
"locations": [{"pageNumber": 1, "top": 548, "left": 106, "width": 175, "height": 24}],
|
||
"assignee": "recipient0",
|
||
},
|
||
]
|
||
|
||
|
||
def clean_field(f):
|
||
"""Strip server-only keys, keep only what the write API accepts."""
|
||
out = {k: v for k, v in f.items() if k in ALLOWED_KEYS}
|
||
out.setdefault("validation", "NONE")
|
||
out.setdefault("required", False)
|
||
out.setdefault("readOnly", False)
|
||
out.setdefault("masked", False)
|
||
out.setdefault("maskingText", "*")
|
||
out.setdefault("calculated", False)
|
||
out.setdefault("urlOverridable", False)
|
||
out.setdefault("minLength", -1)
|
||
out.setdefault("maxLength", -1)
|
||
out.setdefault("minValue", -1.0)
|
||
out.setdefault("maxValue", -1.0)
|
||
out.setdefault("validationErrMsg", "")
|
||
out.setdefault("conditionalAction", {"anyOrAll": "ANY", "action": "SHOW"})
|
||
return out
|
||
|
||
|
||
def load_source_fields():
|
||
with open(FIELDS_JSON_PATH) as f:
|
||
data = json.load(f)
|
||
fields = [clean_field(field) for field in data["fields"]]
|
||
groups = data.get("formFieldGroups", [])
|
||
print(f" Loaded {len(fields)} fields from David Tag Demo Form download")
|
||
return fields, groups
|
||
|
||
|
||
def upload_transient_doc():
|
||
print(f"Uploading PDF: {PDF_PATH}")
|
||
with open(PDF_PATH, "rb") as f:
|
||
files = {"File": ("Tag Demo Form.pdf", f, "application/pdf")}
|
||
result = adobe_api_post_multipart("transientDocuments", files=files)
|
||
doc_id = result["transientDocumentId"]
|
||
print(f" Transient document ID: {doc_id}")
|
||
return doc_id
|
||
|
||
|
||
def create_library_doc(transient_id):
|
||
print(f"Creating library document '{TEMPLATE_NAME}'...")
|
||
body = {
|
||
"fileInfos": [{"transientDocumentId": transient_id}],
|
||
"name": TEMPLATE_NAME,
|
||
"templateTypes": ["DOCUMENT", "FORM_FIELD_LAYER"],
|
||
"sharingMode": "USER",
|
||
"state": "ACTIVE",
|
||
}
|
||
result = adobe_api_post_json("libraryDocuments", body)
|
||
lib_id = result["id"]
|
||
print(f" Library document ID: {lib_id}")
|
||
return lib_id
|
||
|
||
|
||
def put_form_fields(lib_id, source_fields, groups):
|
||
all_fields = source_fields + [clean_field(f) for f in EXTRA_FIELDS]
|
||
print(f"Writing {len(all_fields)} fields ({len(source_fields)} original + {len(EXTRA_FIELDS)} extra)...")
|
||
|
||
body = {"fields": all_fields}
|
||
if groups:
|
||
body["formFieldGroups"] = groups
|
||
|
||
result = adobe_api_put_json(f"libraryDocuments/{lib_id}/formFields", body)
|
||
saved = len(result.get("fields", []))
|
||
print(f" {saved} fields saved.")
|
||
for field in result.get("fields", []):
|
||
print(f" {field['inputType']:15} {field.get('contentType',''):20} '{field['name']}'")
|
||
return result
|
||
|
||
|
||
def main():
|
||
if not os.path.exists(PDF_PATH):
|
||
print(f"ERROR: PDF not found at {PDF_PATH}")
|
||
sys.exit(1)
|
||
if not os.path.exists(FIELDS_JSON_PATH):
|
||
print(f"ERROR: form_fields.json not found at {FIELDS_JSON_PATH}")
|
||
sys.exit(1)
|
||
|
||
source_fields, groups = load_source_fields()
|
||
transient_id = upload_transient_doc()
|
||
lib_id = create_library_doc(transient_id)
|
||
put_form_fields(lib_id, source_fields, groups)
|
||
print(f"\nDone. Template '{TEMPLATE_NAME}' created (ID: {lib_id})")
|
||
print("Note: If Company/Title fields need their contentType set, do so in Adobe Sign UI.")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|