def retry_failed_payments(self, retry_injected=False):
        logger.debug("retry_failed_payments started")

        # 1 - list csv files under payments/failed directory
        # absolute path of csv files found under payments_root/failed directory
        failed_payments_dir = get_failed_payments_dir(self.payments_root)
        payment_reports_failed = [
            os.path.join(failed_payments_dir, x)
            for x in os.listdir(failed_payments_dir) if x.endswith('.csv')
        ]

        if payment_reports_failed:
            payment_reports_failed = sorted(
                payment_reports_failed,
                key=lambda x: int(os.path.splitext(os.path.basename(x))[0]))
            logger.debug("Failed payment files found are: '{}'".format(
                ",".join(payment_reports_failed)))
        else:
            logger.debug(
                "No failed payment files found under directory '{}'".format(
                    failed_payments_dir))

        # 2- for each csv file with name csv_report.csv
        for payment_failed_report_file in payment_reports_failed:
            logger.info("Working on failed payment file {}".format(
                payment_failed_report_file))

            # 2.1 - if there is a file csv_report.csv under payments/done, it means payment is already done
            if os.path.isfile(
                    payment_failed_report_file.replace(PAYMENT_FAILED_DIR,
                                                       PAYMENT_DONE_DIR)):
                # remove payments/failed/csv_report.csv
                os.remove(payment_failed_report_file)
                logger.info(
                    "Payment for failed payment {} is already done. Removing.".
                    format(payment_failed_report_file))

                # remove payments/failed/csv_report.csv.BUSY
                # if there is a busy failed payment report file, remove it.
                remove_busy_file(payment_failed_report_file)

                # do not double pay
                continue

            # 2.2 - if queue is full, wait for sometime
            # make sure the queue is not full
            while self.payments_queue.full():
                logger.debug("Payments queue is full. Wait a few minutes.")
                time.sleep(60 * 3)

            cycle = int(
                os.path.splitext(
                    os.path.basename(payment_failed_report_file))[0])

            # 2.3 read payments/failed/csv_report.csv file into a list of dictionaries
            batch = CsvPaymentFileParser().parse(payment_failed_report_file,
                                                 cycle)

            nb_paid = len(
                list(filter(lambda f: f.paid == PaymentStatus.PAID, batch)))
            nb_done = len(
                list(filter(lambda f: f.paid == PaymentStatus.DONE, batch)))
            nb_injected = len(
                list(filter(lambda f: f.paid == PaymentStatus.INJECTED,
                            batch)))
            nb_failed = len(
                list(filter(lambda f: f.paid == PaymentStatus.FAIL, batch)))

            logger.info(
                "Summary {} paid, {} done, {} injected, {} fail".format(
                    nb_paid, nb_done, nb_injected, nb_failed))

            if retry_injected:
                nb_converted = 0
                for pl in batch:
                    if pl.paid == PaymentStatus.INJECTED:
                        pl.paid = PaymentStatus.FAIL
                        nb_converted += 1
                        logger.debug(
                            "Reward converted from %s to fail for cycle %s, address %s, amount %f, tz type %s",
                            pl.paid, pl.cycle, pl.address, pl.amount, pl.type)

                if nb_converted:
                    logger.info(
                        "{} rewards converted from injected to fail.".format(
                            nb_converted))

            # 2.4 - Filter batch to only include those which failed. No need to mess with PAID/DONE
            batch = list(filter(lambda f: f.paid == PaymentStatus.FAIL, batch))

            # 2.5 - Need to fetch current balance for addresses of any failed payments
            self.reward_api.update_current_balances(batch)

            # 2.6 - put records into payment_queue. payment_consumer will make payments
            self.payments_queue.put(PaymentBatch(self, cycle, batch))

            # 2.7 - rename payments/failed/csv_report.csv to payments/failed/csv_report.csv.BUSY
            # mark the files as in use. we do not want it to be read again
            # BUSY file will be removed, if successful payment is done
            os.rename(payment_failed_report_file,
                      payment_failed_report_file + BUSY_FILE)
    def retry_failed_payments(self):
        logger.info("retry_failed_payments started")

        # 1 - list csv files under payments/failed directory
        # absolute path of csv files found under payments_root/failed directory
        failed_payments_dir = get_failed_payments_dir(self.payments_root)
        payment_reports_failed = [
            os.path.join(failed_payments_dir, x)
            for x in os.listdir(failed_payments_dir) if x.endswith('.csv')
        ]

        logger.debug("Trying failed payments : '{}'".format(
            ",".join(payment_reports_failed)))

        # 2- for each csv file with name csv_report.csv
        for payment_failed_report_file in payment_reports_failed:
            logger.debug("Working on failed payment file {}".format(
                payment_failed_report_file))

            # 2.1 - if there is a file csv_report.csv under payments/done, it means payment is already done
            if os.path.isfile(
                    payment_failed_report_file.replace(PAYMENT_FAILED_DIR,
                                                       PAYMENT_DONE_DIR)):
                # remove payments/failed/csv_report.csv
                os.remove(payment_failed_report_file)
                logger.debug(
                    "Payment for failed payment {} is already done. Removing.".
                    format(payment_failed_report_file))

                # remove payments/failed/csv_report.csv.BUSY
                # if there is a busy failed payment report file, remove it.
                remove_busy_file(payment_failed_report_file)

                # do not double pay
                continue

            # 2.2 - if queue is full, wait for sometime
            # make sure the queue is not full
            while self.payments_queue.full():
                logger.info("Payments queue is full. Wait for 30 seconds.")
                time.sleep(30)

            cycle = int(
                os.path.splitext(
                    os.path.basename(payment_failed_report_file))[0])

            # 2.3 read payments/failed/csv_report.csv file into a list of dictionaries
            with open(payment_failed_report_file) as f:
                # read csv into list of dictionaries
                dict_rows = [{key: value
                              for key, value in row.items()}
                             for row in csv.DictReader(
                                 f, delimiter='\t', skipinitialspace=True)]

                batch = PaymentRecord.FromPaymentCSVDictRows(dict_rows, cycle)

                # 2.4 put records into payment_queue. payment_consumer will make payments
                if batch:
                    self.payments_queue.put(batch)
                else:
                    logger.info("Nothing to pay.")

            # 2.5 rename payments/failed/csv_report.csv to payments/failed/csv_report.csv.BUSY
            # mark the files as in use. we do not want it to be read again
            # BUSY file will be removed, if successful payment is done
            os.rename(payment_failed_report_file,
                      get_busy_file(payment_failed_report_file))