Coverage for scripts / get_approvals.py: 17%
77 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-25 18:08 -0500
1#!/usr/bin/env python3
2"""
3Get approvals for a JSM request.
5This script retrieves approval requests for JSM requests, showing approval status,
6approvers, and action hints for pending approvals.
7"""
9import sys
10import argparse
11import json
12from pathlib import Path
13from datetime import datetime
15# Add shared lib to path
16sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / 'shared' / 'scripts' / 'lib'))
18from config_manager import get_jira_client
19from error_handler import handle_errors
22def get_jira_client(profile=None):
23 """Get JIRA client (overridable for testing)."""
24 from config_manager import get_jira_client as _get_client
25 return _get_client(profile)
28def format_approvals_table(approvals: list, issue_key: str) -> None:
29 """Format approvals as table."""
30 if not approvals:
31 print("No approvals found.")
32 return
34 # Count by status
35 pending_count = sum(1 for a in approvals if a.get('finalDecision') == 'pending')
36 approved_count = sum(1 for a in approvals if a.get('finalDecision') == 'approve')
37 declined_count = sum(1 for a in approvals if a.get('finalDecision') == 'decline')
39 print(f"\nApprovals for {issue_key} ({pending_count} pending, {approved_count} approved, {declined_count} declined):\n")
41 print(f"{'ID':<10} {'Name':<25} {'Status':<12} {'Approvers':<25} {'Created':<20} {'Completed'}")
42 print("─" * 120)
44 for approval in approvals:
45 approval_id = approval.get('id', 'unknown')
46 name = approval.get('name', 'Unknown')[:24]
47 decision = approval.get('finalDecision', 'unknown').upper()
49 # Format approvers
50 approvers = approval.get('approvers', [])
51 if len(approvers) == 1:
52 approvers_str = approvers[0].get('displayName', 'Unknown')[:24]
53 elif len(approvers) > 1:
54 approvers_str = f"{approvers[0].get('displayName', 'Unknown')[:15]} (+{len(approvers)-1})"
55 else:
56 approvers_str = "None"
58 # Format dates
59 created = approval.get('createdDate', '')
60 completed = approval.get('completedDate') or '-'
62 try:
63 created_dt = datetime.fromisoformat(created.replace('Z', '+00:00'))
64 created_str = created_dt.strftime('%Y-%m-%d %H:%M')
65 except:
66 created_str = created[:16] if created else 'Unknown'
68 if completed != '-':
69 try:
70 completed_dt = datetime.fromisoformat(completed.replace('Z', '+00:00'))
71 completed_str = completed_dt.strftime('%Y-%m-%d %H:%M')
72 except:
73 completed_str = completed[:16]
74 else:
75 completed_str = '-'
77 print(f"{approval_id:<10} {name:<25} {decision:<12} {approvers_str:<25} {created_str:<20} {completed_str}")
79 # Show action hints for pending approvals
80 if pending_count > 0:
81 print("\nTo approve/decline:")
82 print(f" python approve_request.py {issue_key} --approval-id <APPROVAL-ID>")
83 print(f" python decline_request.py {issue_key} --approval-id <APPROVAL-ID>")
86@handle_errors
87def main(args=None):
88 """Main function."""
89 parser = argparse.ArgumentParser(
90 description='Get approvals for JSM request',
91 formatter_class=argparse.RawDescriptionHelpFormatter,
92 epilog="""
93Examples:
94 # Pending approvals (default)
95 %(prog)s REQ-123
97 # All approvals (including completed)
98 %(prog)s REQ-123 --all
100 # Specific approval
101 %(prog)s REQ-123 --id 10050
103 # JSON output
104 %(prog)s REQ-123 --output json
105 """
106 )
108 parser.add_argument('issue_key',
109 help='Request key (e.g., REQ-123)')
110 parser.add_argument('--id',
111 help='Get specific approval by ID')
112 parser.add_argument('--pending', action='store_true',
113 help='Show only pending approvals (default)')
114 parser.add_argument('--all', action='store_true', dest='show_all',
115 help='Show all approvals (pending, approved, declined)')
116 parser.add_argument('--output', choices=['text', 'json'], default='text',
117 help='Output format (default: text)')
118 parser.add_argument('--profile',
119 help='JIRA profile to use')
121 parsed_args = parser.parse_args(args)
123 # Get JIRA client
124 jira = get_jira_client(parsed_args.profile)
126 # Get specific approval by ID
127 if parsed_args.id:
128 approval = jira.get_request_approval(parsed_args.issue_key, parsed_args.id)
129 if parsed_args.output == 'json':
130 print(json.dumps(approval, indent=2))
131 else:
132 format_approvals_table([approval], parsed_args.issue_key)
133 return 0
135 # Get all approvals
136 response = jira.get_request_approvals(parsed_args.issue_key, start=0, limit=100)
137 approvals = response.get('values', [])
139 # Filter to pending only if not --all
140 if not parsed_args.show_all:
141 approvals = [a for a in approvals if a.get('finalDecision') == 'pending']
143 # Output
144 if parsed_args.output == 'json':
145 print(json.dumps(approvals, indent=2))
146 else:
147 format_approvals_table(approvals, parsed_args.issue_key)
149 return 0
152if __name__ == '__main__':
153 sys.exit(main())