"""Handler for downloading and managing Jira attachments."""importloggingfrompathlibimportPathfrom.exceptionsimportAttachmentDownloadError
[docs]classAttachmentHandler:"""Manages downloading and saving of issue attachments."""
[docs]def__init__(self,api_client):"""Initialize the attachment handler. Args: api_client: JiraApiClient instance for API communication """self.api_client=api_clientself.logger=logging.getLogger(__name__)
[docs]defdownload_attachment(self,attachment,output_dir):"""Download a single attachment. Args: attachment: Attachment metadata from Jira API output_dir: Directory to save the attachment to Returns: dict: Information about the downloaded attachment including filename, original_filename, mime_type, and path Raises: AttachmentDownloadError: If download fails """filename=attachment["filename"]content_url=attachment["content"]mime_type=attachment.get("mimeType","")size=attachment.get("size",0)file_path=output_dir/filename# Handle filename conflictscounter=1original_path=file_pathwhilefile_path.exists():stem=original_path.stemsuffix=original_path.suffixfile_path=original_path.parent/f"{stem}_{counter}{suffix}"counter+=1self.logger.info(f" Downloading {filename} ({self._format_size(size)})...")try:response=self.api_client.download_attachment_stream(content_url)withopen(file_path,"wb")asf:forchunkinresponse.iter_content(chunk_size=8192):ifchunk:f.write(chunk)return{"filename":file_path.name,"original_filename":filename,"mime_type":mime_type,"path":file_path,}exceptExceptionase:raiseAttachmentDownloadError(f"Error downloading {filename}: {e}",filename=filename)
[docs]defdownload_all_attachments(self,attachments,output_dir):"""Download all attachments for an issue. Args: attachments: List of attachment metadata from Jira API output_dir: Directory to save attachments to Returns: list: Information about all successfully downloaded attachments """ifnotattachments:return[]output_dir=Path(output_dir)output_dir.mkdir(parents=True,exist_ok=True)self.logger.info(f"Downloading {len(attachments)} attachment(s)...")downloaded=[]forattachmentinattachments:try:result=self.download_attachment(attachment,output_dir)ifresult:downloaded.append(result)exceptAttachmentDownloadErrorase:self.logger.error(str(e))# Continue downloading other attachmentsreturndownloaded
def_format_size(self,size):"""Format file size in human-readable format. Args: size: Size in bytes Returns: str: Formatted size string """forunitin["B","KB","MB","GB"]:ifsize<1024.0:returnf"{size:.1f}{unit}"size/=1024.0returnf"{size:.1f} TB"