Ejemplo n.º 1
0
    def create_payment_report(self, nb_failed, nb_injected, payment_logs,
                              payment_cycle, total_attempts):
        logger.info("Processing completed for {} payment items{}.".format(
            len(payment_logs),
            ", {} failed".format(nb_failed) if nb_failed > 0 else ""))

        report_file = payment_report_file_path(self.payments_dir,
                                               payment_cycle, nb_failed)

        CsvPaymentFileParser().write(report_file, payment_logs)

        logger.info("Payment report is created at '{}'".format(report_file))

        for pl in payment_logs:
            logger.debug(
                "Payment done for address %s type %s amount {:>8.2f} paid %s".
                format(pl.amount / MUTEZ), pl.address, pl.type, pl.paid)

        if self.publish_stats and not self.dry_run and (
                not self.args or is_mainnet(self.args.network)):
            stats_dict = self.create_stats_dict(nb_failed, nb_injected,
                                                payment_cycle, payment_logs,
                                                total_attempts)

            # publish
            stat_publish(stats_dict)

        return report_file
Ejemplo n.º 2
0
    def test_retry_failed_payments(self):
        payment_queue = queue.Queue(100)

        retry_producer = RetryProducer(payment_queue, _DummyRpcRewardApi(), _TestPaymentProducer(),
                                       TEST_REPORT_TEMP_DIR)
        retry_producer.retry_failed_payments()

        self.assertEqual(1, len(payment_queue.queue))

        payment_batch = payment_queue.get()

        self.assertEqual(10, payment_batch.cycle)
        self.assertEqual(31, len(payment_batch.batch))
        self.assertEqual(5, len([row for row in payment_batch.batch if row.paid == PaymentStatus.FAIL]))

        nw = dict({'BLOCK_TIME_IN_SEC': 64})
        payment_consumer = self.create_consumer(nw, payment_queue)
        payment_consumer._consume_batch(payment_batch)

        success_report = payment_report_file_path(TEST_REPORT_TEMP_DIR, 10, 0)
        self.assertTrue(os.path.isfile(success_report))

        success_report_rows = CsvPaymentFileParser().parse(success_report, 10)
        nb_success = len([row for row in success_report_rows if row.paid == PaymentStatus.PAID])
        nb_hash_xxx_op_hash = len([row for row in success_report_rows if row.hash == 'xxx_op_hash'])

        self.assertEqual(31, nb_success)
        self.assertEqual(5, nb_hash_xxx_op_hash)
    def test_retry_failed_payments(self):
        """This is a test about retrying failed operations.
        Input is a past payment report which contains 31 payment items,
        26 of them were successful and 5 of them were failed. The final report
        should report 31 successful transactions.
        """
        payment_queue = queue.Queue(100)

        retry_producer = RetryProducer(
            payment_queue,
            _DummyRpcRewardApi(),
            _TestPaymentProducer(),
            TEST_REPORT_TEMP_DIR,
            10,
        )
        retry_producer.retry_failed_payments()

        self.assertEqual(1, len(payment_queue.queue))

        payment_batch = payment_queue.get()

        self.assertEqual(10, payment_batch.cycle)
        self.assertEqual(31, len(payment_batch.batch))
        self.assertEqual(
            5,
            len([
                row for row in payment_batch.batch
                if row.paid == PaymentStatus.FAIL
            ]),
        )

        nw = dict({"MINIMAL_BLOCK_DELAY": 30})
        payment_consumer = self.create_consumer(nw, payment_queue)
        payment_consumer._consume_batch(payment_batch)

        success_report = get_payment_report_file_path(TEST_REPORT_TEMP_DIR, 10,
                                                      0)
        self.assertTrue(os.path.isfile(success_report))

        success_report_rows = CsvPaymentFileParser().parse(success_report, 10)
        success_count = len([row for row in success_report_rows])
        hash_xxx_op_count = len(
            [row for row in success_report_rows if row.hash == "xxx_op_hash"])
        failed_reports_count = len([
            file for file in os.listdir(
                os.path.join(TEST_REPORT_TEMP_DIR, "failed"))
            if os.path.isfile(file)
        ])

        # Success is defined when the transactions are saved in the done folder
        self.assertEqual(31, success_count)
        self.assertEqual(5, hash_xxx_op_count)
        self.assertEqual(0, failed_reports_count)
Ejemplo n.º 4
0
    def create_payment_report(self, nb_failed, payment_logs, payment_cycle,
                              already_paid_items):

        logger.info("Processing completed for {} payment items{}.".format(
            len(payment_logs),
            ", {} failed".format(nb_failed) if nb_failed > 0 else "",
        ))
        logger.debug("Adding {} already paid items to the report".format(
            len(already_paid_items)))

        payouts = already_paid_items + payment_logs

        successful_payouts = [
            payout for payout in payouts if payout.paid != PaymentStatus.FAIL
        ]
        unsuccessful_payouts = [
            payout for payout in payouts if payout.paid == PaymentStatus.FAIL
        ]

        report_file = get_payment_report_file_path(self.payments_dir,
                                                   payment_cycle, 0)
        CsvPaymentFileParser().write(report_file, successful_payouts)
        logger.info("Payment report is created at '{}'".format(report_file))

        if nb_failed > 0:
            report_file = get_payment_report_file_path(self.payments_dir,
                                                       payment_cycle,
                                                       nb_failed)
            CsvPaymentFileParser().write(report_file, unsuccessful_payouts)
            logger.info(
                "Payment report is created at '{}'".format(report_file))

        for pl in payment_logs:
            logger.debug(
                "Payment done for address {:s} type {:s} amount {:<,d} mutez paid {:s}"
                .format(pl.address, pl.type, pl.adjusted_amount, pl.paid))

        return report_file
Ejemplo n.º 5
0
    def create_payment_report(self, nb_failed, payment_logs, payment_cycle):

        logger.info("Processing completed for {} payment items{}."
                    .format(len(payment_logs), ", {} failed".format(nb_failed) if nb_failed > 0 else ""))

        report_file = payment_report_file_path(self.payments_dir, payment_cycle, nb_failed)

        CsvPaymentFileParser().write(report_file, payment_logs)

        logger.info("Payment report is created at '{}'".format(report_file))

        for pl in payment_logs:
            logger.debug("Payment done for address {:s} type {:s} amount {:>10.6f} paid {:s}"
                         .format(pl.address, pl.type, pl.amount / MUTEZ, pl.paid))

        return report_file
    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)
Ejemplo n.º 7
0
    def retry_failed_payments(self):
        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 = [
            join(failed_payments_dir, x) for x in listdir(failed_payments_dir)
            if x.endswith(".csv")
            and int(x.split(".csv")[0]) >= self.initial_payment_cycle
        ]

        if payment_reports_failed:
            payment_reports_failed = sorted(payment_reports_failed,
                                            key=self.get_basename)
            logger.debug("Failed payment files found are: '{}'".format(
                ",".join(payment_reports_failed)))
        else:
            logger.info(
                "No failed payment files found under directory '{}' on or after cycle '{}'"
                .format(failed_payments_dir, self.initial_payment_cycle))

        # 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 - Read csv_report.csv under payments/failed and -if existing- under payments/done
            cycle = int(
                os.path.splitext(
                    os.path.basename(payment_failed_report_file))[0])
            batch = CsvPaymentFileParser().parse(payment_failed_report_file,
                                                 cycle)
            payment_successful_report_file = payment_failed_report_file.replace(
                PAYMENT_FAILED_DIR, PAYMENT_DONE_DIR)
            if os.path.isfile(payment_successful_report_file):
                batch += CsvPaymentFileParser().parse(
                    payment_successful_report_file, cycle)

            # 2.2 Translate batch into a list of dictionaries
            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)))
            nb_avoided = len(
                list(filter(lambda f: f.paid == PaymentStatus.AVOIDED, batch)))

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

            if self.retry_injected:
                self.convert_injected_to_fail(batch)

            # 2.3 - 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. Please wait three minutes.")
                sleep(60 * 3)

            # 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.payment_producer, 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)

        return