106 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			106 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| import argparse
 | |
| import logging
 | |
| import pathlib
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| logger = logging.getLogger('rt-bulk-send')
 | |
| 
 | |
| def parse_arguments(arglist):
 | |
|     parser = argparse.ArgumentParser()
 | |
| 
 | |
|     action = parser.add_mutually_exclusive_group()
 | |
|     action.add_argument(
 | |
|         '--correspond',
 | |
|         dest='action', action='store_const', const='correspond',
 | |
|         default='correspond',
 | |
|         help="Send correspondence on found tickets (default)",
 | |
|     )
 | |
|     action.add_argument(
 | |
|         '--comment',
 | |
|         dest='action', action='store_const', const='comment',
 | |
|         help="Comment on found tickets",
 | |
|     )
 | |
| 
 | |
|     parser.add_argument(
 | |
|         '--loglevel',
 | |
|         choices=['debug', 'info', 'warning', 'error', 'critical'],
 | |
|         default='info',
 | |
|         help="Show log messages at this level (default %(default)s)",
 | |
|     )
 | |
| 
 | |
|     parser.add_argument(
 | |
|         'search',
 | |
|         help="TicketSQL search, like you would pass to `rt search`",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         'body_file',
 | |
|         type=pathlib.Path,
 | |
|         help="Path to file that has the content of your correspondence/comment",
 | |
|     )
 | |
|     parser.add_argument(
 | |
|         'rt_args', metavar='rt arguments',
 | |
|         nargs=argparse.REMAINDER,
 | |
|         help="Additional arguments to pass to `rt correspond/comment`",
 | |
|     )
 | |
|     return parser.parse_args(arglist)
 | |
| 
 | |
| def setup_logger(logger, loglevel, stream):
 | |
|     formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
 | |
|     handler = logging.StreamHandler(stream)
 | |
|     handler.setFormatter(formatter)
 | |
|     logger.addHandler(handler)
 | |
|     logger.setLevel(loglevel)
 | |
| 
 | |
| def act_on_ticket(ticket_id, args, body_file):
 | |
|     body_file.seek(0)
 | |
|     return subprocess.run(
 | |
|         ['rt', args.action, *args.rt_args, '-m', '-', ticket_id],
 | |
|         stdin=body_file,
 | |
|         check=True,
 | |
|     )
 | |
| 
 | |
| def main(arglist=None, stdout=sys.stdout, stderr=sys.stderr):
 | |
|     args = parse_arguments(arglist)
 | |
|     setup_logger(logger, getattr(logging, args.loglevel.upper()), stderr)
 | |
|     try:
 | |
|         body_file = args.body_file.open()
 | |
|     except OSError as error:
 | |
|         logger.critical("error opening {}: {}".format(args.body_file, error))
 | |
|         return 3
 | |
|     if not body_file.seekable():
 | |
|         logger.critical("file {} must be seekable".format(args.body_file))
 | |
|         with body_file:
 | |
|             return 3
 | |
| 
 | |
|     action_verb = args.action.title()
 | |
|     failures = 0
 | |
|     with body_file, subprocess.Popen(
 | |
|             ['rt', 'search', '-i', args.search],
 | |
|             stdout=subprocess.PIPE,
 | |
|             universal_newlines=True,
 | |
|     ) as search_pipe:
 | |
|         for line in search_pipe.stdout:
 | |
|             ticket_id = line.rstrip('\n')
 | |
|             if not ticket_id:
 | |
|                 continue
 | |
|             logger.info("%sing on %s", action_verb, ticket_id)
 | |
|             try:
 | |
|                 act_on_ticket(ticket_id, args, body_file)
 | |
|             except subprocess.CalledProcessError as error:
 | |
|                 logger.error("Failed to %s on %s: rt returned exit code %s",
 | |
|                              args.action, ticket_id, error.returncode)
 | |
|                 failures += 1
 | |
| 
 | |
|     if search_pipe.returncode != 0:
 | |
|         logger.critical("`rt search` returned exit code %s", search_pipe.returncode)
 | |
|         return 4
 | |
|     elif failures == 0:
 | |
|         return 0
 | |
|     else:
 | |
|         return min(10 + failures, 99)
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     exit(main())
 | 
