def _get_afi_reports(
     self,
     yeswehack_client: YesWeHackApiClient,
     program: Program,
 ) -> List[Report]:
     program_slug = cast(str, program.slug)
     filters = {
         'filter[trackingStatus][0]': 'AFI',
     }
     synchronize_options = cast(SynchronizeOptions,
                                program.synchronize_options)
     feedback_options = cast(FeedbackOptions, program.feedback_options)
     include_tracked = any((
         synchronize_options.upload_private_comments,
         synchronize_options.upload_public_comments,
         synchronize_options.upload_details_updates,
         synchronize_options.upload_rewards,
         synchronize_options.upload_status_updates,
         feedback_options.download_tracker_comments,
         feedback_options.issue_closed_to_report_afv,
     ), )
     if include_tracked:
         filters['filter[trackingStatus][1]'] = 'T'
     try:
         return yeswehack_client.get_program_reports(
             slug=program_slug,
             filters=filters,
         )
     except YesWeHackApiClientError as e:
         raise SynchronizerError(
             f'Unable to get AFI/T reports for program {program_slug}',
         ) from e
 def _create_tracker_issue(self, ) -> TrackerIssue:
     try:
         tracker_issue = self._tracker_client.send_report(
             report=self._report, )
     except TrackerClientError as send_error:
         raise SynchronizerError(
             f'Unable to send report #{self._report.report_id} to {self._tracker_name}',
         ) from send_error
     return tracker_issue
 def _download_comment(
     self,
     tracker_issue: TrackerIssue,
     tracker_comment: TrackerIssueComment,
 ) -> None:
     attachments = {}
     for attachment_key, attachment in tracker_comment.attachments.items():
         try:
             uploaded_attachment = self._yeswehack_client.post_report_attachment(
                 report=self._report,
                 filename=attachment.filename,
                 file_content=attachment.content,
                 file_type=attachment.mime_type,
             )
         except YesWeHackApiClientError as upload_attachment_error:
             raise SynchronizerError(
                 f'Unable to upload attachment {attachment.filename} for report #{self._report.report_id}',
             ) from upload_attachment_error
         attachments[attachment_key] = uploaded_attachment
     try:
         self._yeswehack_client.post_report_tracker_message(
             report=self._report,
             tracker_name=self._tracker_name,
             issue_id=tracker_issue.issue_id,
             issue_url=tracker_issue.issue_url,
             comment=self._message_formatter.format_download_comment(
                 comment=tracker_comment,
                 attachments=attachments,
             ),
             attachments=[
                 uploaded_attachment.name for attachment_key,
                 uploaded_attachment in attachments.items()
             ],
         )
     except YesWeHackApiClientError as tracker_message_error:
         raise SynchronizerError(
             f'Unable to download comment {tracker_comment.comment_id} for report #{self._report.report_id}',
         ) from tracker_message_error
 def _get_tracker_issue_comments(
     self,
     tracker_issue: TrackerIssue,
     exclude_comments: Optional[List[str]] = None,
 ) -> TrackerIssueComments:
     try:
         return self._tracker_client.get_tracker_issue_comments(
             issue_id=tracker_issue.issue_id,
             exclude_comments=exclude_comments,
         )
     except TrackerClientError as issue_comments_error:
         raise SynchronizerError(
             f'Unable to get issue comments for #{tracker_issue.issue_id}',
         ) from issue_comments_error
 def _send_logs(
     self,
     tracker_issue: TrackerIssue,
     logs: List[Log],
 ) -> SendLogsResult:
     try:
         return self._tracker_client.send_logs(
             tracker_issue=tracker_issue,
             logs=logs,
         )
     except TrackerClientError as send_error:
         raise SynchronizerError(
             f'Unable to send logs for #{self._report.report_id} to {self._tracker_name}',
         ) from send_error
 def _update_tracking_status(
     self,
     tracker_issue: TrackerIssue,
 ) -> None:
     message = self._message_formatter.format_tracking_status_update_message(
         tracker_type=self._tracker_client.tracker_type,
         tracker_issue=tracker_issue,
     )
     try:
         self._yeswehack_client.put_report_tracking_status(
             report=self._report,
             status='T',
             tracker_name=self._tracker_name,
             issue_id=tracker_issue.issue_id,
             issue_url=tracker_issue.issue_url,
             comment=message,
         )
     except YesWeHackApiClientError as tracking_status_error:
         raise SynchronizerError(
             f'Unable to update tracking status for report #{self._report.report_id}',
         ) from tracking_status_error
 def _post_report_tracker_update(
     self,
     tracker_issue: TrackerIssue,
     tracker_issue_state: TrackerIssueState,
     send_logs_result: SendLogsResult,
     download_comments_result: DownloadCommentsResult,
     new_report_status: Optional[Tuple[str, str]],
     issue_status: str,
 ) -> None:
     post_update = any((
         send_logs_result.added_comments,
         download_comments_result.downloaded_comments,
         tracker_issue_state.changed,
     ), )
     if post_update:
         try:
             self._yeswehack_client.post_report_tracker_update(
                 report=self._report,
                 tracker_name=self._tracker_name,
                 issue_id=tracker_issue.issue_id,
                 issue_url=tracker_issue.issue_url,
                 token=StateEncryptor.encrypt(
                     key=self._report.report_id,
                     state=tracker_issue_state,
                 ),
                 comment=self._message_formatter.
                 format_synchronization_done_message(
                     tracker_type=self._tracker_client.tracker_type,
                     report=self._report,
                     send_logs_result=send_logs_result,
                     download_comments_result=download_comments_result,
                     new_report_status=new_report_status,
                     issue_status=issue_status,
                 ),
             )
         except YesWeHackApiClientError as tracker_update_error:
             raise SynchronizerError(
                 f'Unable to post tracker update for report #{self._report.report_id}',
             ) from tracker_update_error
    def synchronize_report(self, ) -> SynchronizeReportResult:
        """
        Synchronize a YesWeHack report and a tracker issue.

        Raises:
            SynchronizerError: if a synchronization error occur

        Returns:
            the result of the synchronization
        """
        tracker_issue = self._get_tracker_issue_from_logs()
        is_existing_issue = tracker_issue is not None
        if not is_existing_issue:
            tracker_issue = self._create_tracker_issue()
        if not isinstance(tracker_issue, TrackerIssue):
            raise SynchronizerError(
                f'Unable to create new or get existing issue for #{self._report.report_id} in {self._tracker_name}',
            )
        if self._report.tracking_status != 'T' or not is_existing_issue:
            self._update_tracking_status(tracker_issue=tracker_issue, )
        tracker_issue_state = TrackerIssueState(
            closed=False,
            bugtracker_name=self._tracker_name,
        )
        logs = self._report.logs
        if is_existing_issue:
            log_state = self._get_last_tracker_update_log()
            if log_state:
                last_tracker_update_log, tracker_issue_state = log_state
                if last_tracker_update_log:
                    logs = self._report.get_logs_after(
                        log=last_tracker_update_log, )
        send_logs_result = self._send_synchronizable_logs(
            tracker_issue=tracker_issue,
            logs=logs,
        )
        for added_comment in send_logs_result.added_comments:
            tracker_issue_state.add_downloaded_comment(
                added_comment.comment_id)
        download_comments_result = self._download_comments(
            tracker_issue=tracker_issue,
            exclude_comments=tracker_issue_state.downloaded_comments,
        )
        for downloaded_comment in download_comments_result.downloaded_comments:
            tracker_issue_state.add_downloaded_comment(downloaded_comment)
        new_report_status = self._update_report_status(
            tracker_issue=tracker_issue,
            tracker_issue_state=tracker_issue_state,
        )
        issue_status = self._get_issue_status_change(
            tracker_issue=tracker_issue,
            tracker_issue_state=tracker_issue_state,
        )
        tracker_issue_state.closed = tracker_issue.closed
        self._post_report_tracker_update(
            tracker_issue=tracker_issue,
            tracker_issue_state=tracker_issue_state,
            send_logs_result=send_logs_result,
            download_comments_result=download_comments_result,
            new_report_status=new_report_status,
            issue_status=issue_status,
        )
        return SynchronizeReportResult(
            is_existing_issue=is_existing_issue,
            new_report_status=new_report_status,
            send_logs_result=send_logs_result,
            download_comments_result=download_comments_result,
        )