Skip to content

Commit b454bb1

Browse files
cleaned up onboarding script
1 parent ffa6809 commit b454bb1

3 files changed

Lines changed: 496 additions & 655 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#!/usr/bin/env python3
2+
"""
3+
📝 EMPLOYEE POLICY ONBOARDING
4+
==============================
5+
6+
This script exemplifies a typical HR onboarding workflow for new employees.
7+
As an HR professional, it's necessary to ensure all new hires review and sign
8+
required company policies before their start date. Manual distribution and
9+
tracking of signatures is time-consuming and error-prone, especially when
10+
onboarding multiple employees simultaneously.
11+
12+
This workflow automates policy distribution and signature collection. The script
13+
processes each new employee individually - for every person in the CSV file, it
14+
creates a signature envelope containing all company policy documents, sends it
15+
via email with signature fields pre-configured, monitors the signing status, and
16+
automatically downloads the signed documents once completed. Each employee's
17+
signed policies are organized in their own folder, creating an audit-ready
18+
archive of onboarding documentation.
19+
20+
21+
NOTE: To run this script and see the complete workflow, you must provide a CSV file
22+
with valid employee names and email addresses. The script will send actual signature
23+
requests to these email addresses and wait for them to be signed.
24+
25+
EMPLOYEE CSV FORMAT:
26+
name,email
27+
John Doe,john.doe@company.com
28+
Jane Smith,jane.smith@company.com
29+
Bob Johnson,bob.johnson@company.com
30+
31+
USAGE:
32+
python employee_policy_onboarding.py <policies_folder> <employees_csv>
33+
34+
EXAMPLE:
35+
python employee_policy_onboarding.py ./policies ./new_hires_jan2025.csv
36+
37+
OUTPUT STRUCTURE:
38+
output/
39+
├── john-doe/
40+
│ ├── signed-documents/
41+
│ ├── code-of-conduct-signed.pdf
42+
│ ├── confidentiality-agreement-signed.pdf
43+
│ └── audit-trail.pdf
44+
├── jane-smith/
45+
├── signed-documents/
46+
"""
47+
48+
import sys
49+
import csv
50+
import time
51+
import json
52+
import base64
53+
from pathlib import Path
54+
from api.sign_api import SignAPIClient
55+
from helper_functions.sign_helpers import (
56+
load_employees_from_csv,
57+
load_policy_documents_from_folder,
58+
create_employee_folder_name,
59+
create_signature_envelope,
60+
add_signature_fields_to_documents,
61+
send_and_monitor_envelope,
62+
download_signed_document,
63+
log_step
64+
)
65+
66+
67+
68+
69+
70+
71+
72+
def main():
73+
# Check command-line arguments
74+
if len(sys.argv) != 3:
75+
print('Usage: python send_policies_to_employees.py <policies_folder> <employees_csv>')
76+
sys.exit(1)
77+
78+
# Parse arguments
79+
policies_folder = Path(sys.argv[1])
80+
employees_csv = Path(sys.argv[2])
81+
output_folder = Path('output')
82+
83+
# Validate inputs
84+
if not policies_folder.exists() or not policies_folder.is_dir():
85+
print(f'❌ Policies folder not found: {policies_folder}')
86+
sys.exit(1)
87+
88+
if not employees_csv.exists():
89+
print(f'❌ Employees CSV not found: {employees_csv}')
90+
sys.exit(1)
91+
92+
# Display header
93+
print('=' * 60)
94+
print('📝 SEND POLICIES TO EMPLOYEES')
95+
print('=' * 60)
96+
print(f'Policies: {policies_folder}')
97+
print(f'Employees: {employees_csv}')
98+
print(f'Output: {output_folder}')
99+
print('=' * 60)
100+
print()
101+
102+
try:
103+
# Load employees and documents
104+
employees = load_employees_from_csv(employees_csv)
105+
print(f'👥 Found {len(employees)} employee(s)\n')
106+
107+
documents = load_policy_documents_from_folder(policies_folder)
108+
109+
# Create output folder
110+
output_folder.mkdir(parents=True, exist_ok=True)
111+
112+
# Initialize Sign API client
113+
sign_client = SignAPIClient()
114+
115+
# Process each employee
116+
print('=' * 60)
117+
print(f'📤 PROCESSING {len(employees)} EMPLOYEE(S)')
118+
print('=' * 60)
119+
120+
success_count = 0
121+
failed_count = 0
122+
123+
for i, employee in enumerate(employees, 1):
124+
name = employee['name']
125+
email = employee['email']
126+
127+
print(f"\n[{i}/{len(employees)}] {name}")
128+
129+
try:
130+
# Create employee-specific output folder
131+
folder_name = create_employee_folder_name(name)
132+
employee_folder = output_folder / folder_name
133+
employee_folder.mkdir(parents=True, exist_ok=True)
134+
135+
# Create envelope and upload documents
136+
log_step('📝 Creating envelope...')
137+
envelope_id, document_ids = create_signature_envelope(sign_client, documents, name, email)
138+
139+
# Add participant (signer)
140+
log_step('👤 Adding signer...')
141+
participant_data = {'email': email, 'role': 'signer', 'name': name}
142+
participant = sign_client.create_participant(envelope_id, participant_data)
143+
participant_id = participant['ID']
144+
145+
# Add signature fields to all documents
146+
log_step('✍️ Adding fields...')
147+
add_signature_fields_to_documents(sign_client, envelope_id, document_ids, participant_id)
148+
149+
# Send and monitor envelope
150+
log_step('📤 Sending...')
151+
status = send_and_monitor_envelope(sign_client, envelope_id, email, timeout_minutes=60)
152+
153+
if status != 'sealed':
154+
raise Exception(f'Envelope not signed: {status}')
155+
156+
# Download signed documents
157+
log_step('📥 Downloading...')
158+
download_signed_document(sign_client, envelope_id, employee_folder, 'signed-policies.zip')
159+
160+
print(f" ✅ Completed\n")
161+
success_count += 1
162+
163+
except Exception as e:
164+
print(f" ❌ FAILED: {e}\n")
165+
failed_count += 1
166+
167+
# Display summary
168+
print('=' * 60)
169+
print(f"✅ {success_count} employee(s) completed")
170+
if failed_count > 0:
171+
print(f"❌ {failed_count} failed")
172+
print(f"📂 Output: {output_folder.absolute()}")
173+
print('=' * 60)
174+
175+
except KeyboardInterrupt:
176+
print('\n\n⚠️ Interrupted by user')
177+
sys.exit(1)
178+
except Exception as e:
179+
print(f'\n❌ Error: {e}')
180+
sys.exit(1)
181+
182+
183+
if __name__ == '__main__':
184+
main()

0 commit comments

Comments
 (0)