from decimal import Decimal as D
from django.core import exceptions
from django.test import TestCase
from oscar.test import factories
from oscar.test.basket import add_product, add_products
from django_redis import get_redis_connection
from oscarbluelight.offer.models import (
    Range,
    Benefit,
    BluelightCountCondition,
    BluelightValueCondition,
    BluelightPercentageDiscountBenefit,
)
from unittest import mock


class TestAPercentageDiscountAppliedWithCountCondition(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

        range = Range.objects.create(name="All products", includes_all_products=True)
        self.condition = BluelightCountCondition(
            range=range,
            proxy_class="oscarbluelight.offer.conditions.BluelightCountCondition",
            value=2,
        )
        self.benefit = BluelightPercentageDiscountBenefit(
            range=range,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
            value=20,
        )
        self.offer = mock.Mock()
        self.basket = factories.create_basket(empty=True)

    def test_applies_correctly_to_empty_basket(self):
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("0.00"), result.discount)
        self.assertEqual(0, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_with_no_discountable_products(self):
        product = factories.create_product(is_discountable=False)
        add_product(self.basket, D("12.00"), 2, product=product)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("0.00"), result.discount)
        self.assertEqual(0, self.basket.num_items_with_discount)
        self.assertEqual(2, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_matches_condition(self):
        add_product(self.basket, D("12.00"), 2)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(2 * D("12.00") * D("0.2"), result.discount)
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition(self):
        add_product(self.basket, D("12.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(3 * D("12.00") * D("0.2"), result.discount)
        self.assertEqual(3, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_obeys_max_discount_setting(self):
        self.benefit.max_discount = D("5.00")
        self.benefit.save()
        add_product(self.basket, D("12.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("5.00"), result.discount)
        self.assertEqual(3, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_records_reason_for_discount_no_voucher(self):
        self.offer.name = "My Offer Name"
        self.offer.description = "My Offer Description"
        self.offer.get_voucher = mock.Mock()
        self.offer.get_voucher.return_value = None

        add_product(self.basket, D("5.00"))
        # Apply benefit twice to simulate how Applicator will actually do it
        self.benefit.apply(self.basket, self.condition, self.offer)
        self.benefit.apply(self.basket, self.condition, self.offer)

        line = self.basket.all_lines()[0]
        descrs = line.get_discount_descriptions()
        self.assertEqual(len(descrs), 1)
        self.assertEqual(descrs[0].amount, D("1.00"))
        self.assertEqual(descrs[0].offer_name, "My Offer Name")
        self.assertEqual(descrs[0].offer_description, "My Offer Description")
        self.assertIsNone(descrs[0].voucher_name)
        self.assertIsNone(descrs[0].voucher_code)

    def test_records_reason_for_discount_with_voucher(self):
        voucher = mock.Mock()
        voucher.name = "My Voucher"
        voucher.code = "SWEETDEAL"
        self.offer.name = "Offer for Voucher"
        self.offer.description = ""
        self.offer.get_voucher = mock.Mock()
        self.offer.get_voucher.return_value = voucher

        add_product(self.basket, D("5.00"))
        # Apply benefit twice to simulate how Applicator will actually do it
        self.benefit.apply(self.basket, self.condition, self.offer)
        self.benefit.apply(self.basket, self.condition, self.offer)

        line = self.basket.all_lines()[0]
        descrs = line.get_discount_descriptions()
        self.assertEqual(len(descrs), 1)
        self.assertEqual(descrs[0].amount, D("1.00"))
        self.assertEqual(descrs[0].offer_name, "Offer for Voucher")
        self.assertEqual(descrs[0].offer_description, "")
        self.assertEqual(descrs[0].voucher_name, "My Voucher")
        self.assertEqual(descrs[0].voucher_code, "SWEETDEAL")


class TestAPercentageDiscountWithMaxItemsSetAppliedWithCountCondition(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

        range = Range.objects.create(name="All products", includes_all_products=True)
        self.condition = BluelightCountCondition(
            range=range,
            proxy_class="oscarbluelight.offer.conditions.BluelightCountCondition",
            value=2,
        )
        self.benefit = BluelightPercentageDiscountBenefit(
            range=range,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
            value=20,
            max_affected_items=1,
        )
        self.offer = mock.Mock()
        self.basket = factories.create_basket(empty=True)

    def test_applies_correctly_to_empty_basket(self):
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("0.00"), result.discount)
        self.assertEqual(0, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_matches_condition(self):
        add_product(self.basket, D("12.00"), 2)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(1 * D("12.00") * D("0.2"), result.discount)
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition(self):
        add_products(self.basket, [(D("12.00"), 2), (D("20.00"), 2)])
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(1 * D("12.00") * D("0.2"), result.discount)
        # Should only consume the condition products
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(2, self.basket.num_items_without_discount)


class TestAPercentageDiscountWithMultipleApplicationsWithCountCondition(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

        self.range_mattresses = Range.objects.create(name="Mattresses")
        self.range_slippers = Range.objects.create(name="Slippers")

        self.mattress = factories.create_product(title="Mattress", price=D("2999.00"))
        self.slipper1 = factories.create_product(title="Slipper", price=D("78.00"))
        self.slipper2 = factories.create_product(title="Slipper", price=D("79.00"))

        self.range_mattresses.add_product(self.mattress)
        self.range_slippers.add_product(self.slipper1)
        self.range_slippers.add_product(self.slipper2)

        self.condition = BluelightCountCondition.objects.create(
            range=self.range_mattresses,
            proxy_class="oscarbluelight.offer.conditions.BluelightCountCondition",
            value=1,
        )

        self.benefit = BluelightPercentageDiscountBenefit.objects.create(
            range=self.range_slippers,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
            value=D("100.00"),
            max_affected_items=1,
        )

        self.offer = mock.Mock()
        self.basket = factories.create_basket(empty=True)

    def test_applies_correctly_to_basket_which_matches_multiple_lines_multiple_times(
        self,
    ):
        # Add two different lines to the basket
        self.basket.add_product(self.mattress, 2)
        self.basket.add_product(self.slipper1, 1)
        self.basket.add_product(self.slipper2, 1)
        # Apply once
        self.assertTrue(self.condition.proxy().is_satisfied(self.offer, self.basket))
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertTrue(result.is_successful)
        self.assertEqual(result.discount, D("78.00"))
        self.assertEqual(self.basket.num_items_with_discount, 2)
        self.assertEqual(self.basket.num_items_without_discount, 2)
        # Apply second time
        self.assertTrue(self.condition.proxy().is_satisfied(self.offer, self.basket))
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertTrue(result.is_successful)
        self.assertEqual(result.discount, D("79.00"))
        self.assertEqual(self.basket.num_items_with_discount, 4)
        self.assertEqual(self.basket.num_items_without_discount, 0)
        # Can't apply a third time because the condition is no longer satisfied
        self.assertFalse(self.condition.proxy().is_satisfied(self.offer, self.basket))


class TestAPercentageDiscountAppliedWithValueCondition(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

        range = Range.objects.create(name="All products", includes_all_products=True)
        self.condition = BluelightValueCondition.objects.create(
            range=range,
            proxy_class="oscarbluelight.offer.conditions.BluelightValueCondition",
            value=D("10.00"),
        )
        self.benefit = BluelightPercentageDiscountBenefit.objects.create(
            range=range,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
            value=20,
        )
        self.offer = mock.Mock()
        self.basket = factories.create_basket(empty=True)

    def test_applies_correctly_to_empty_basket(self):
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("0.00"), result.discount)
        self.assertEqual(0, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_matches_condition(self):
        add_product(self.basket, D("5.00"), 2)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(2 * D("5.00") * D("0.2"), result.discount)
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition_but_matches_on_boundary(
        self,
    ):
        add_product(self.basket, D("5.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(3 * D("5.00") * D("0.2"), result.discount)
        self.assertEqual(3, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition(self):
        add_product(self.basket, D("4.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(3 * D("4.00") * D("0.2"), result.discount)
        self.assertEqual(3, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)


class TestAPercentageDiscountWithMaxItemsSetAppliedWithValueCondition(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

        range = Range.objects.create(name="All products", includes_all_products=True)
        self.condition = BluelightValueCondition.objects.create(
            range=range,
            proxy_class="oscarbluelight.offer.conditions.BluelightValueCondition",
            value=D("10.00"),
        )
        self.benefit = BluelightPercentageDiscountBenefit.objects.create(
            range=range,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
            value=20,
            max_affected_items=1,
        )
        self.offer = mock.Mock()
        self.basket = factories.create_basket(empty=True)

    def test_applies_correctly_to_empty_basket(self):
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(D("0.00"), result.discount)
        self.assertEqual(0, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_matches_condition(self):
        add_product(self.basket, D("5.00"), 2)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(1 * D("5.00") * D("0.2"), result.discount)
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition_but_matches_on_boundary(
        self,
    ):
        add_product(self.basket, D("5.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(1 * D("5.00") * D("0.2"), result.discount)
        self.assertEqual(2, self.basket.num_items_with_discount)
        self.assertEqual(1, self.basket.num_items_without_discount)

    def test_applies_correctly_to_basket_which_exceeds_condition(self):
        add_product(self.basket, D("4.00"), 3)
        result = self.benefit.apply(self.basket, self.condition, self.offer)
        self.assertEqual(1 * D("4.00") * D("0.2"), result.discount)
        self.assertEqual(3, self.basket.num_items_with_discount)
        self.assertEqual(0, self.basket.num_items_without_discount)


class TestAPercentageDiscountBenefit(TestCase):
    def setUp(self):
        # Flush the cache
        conn = get_redis_connection("redis")
        conn.flushall()

    def test_requires_a_benefit_value(self):
        rng = Range.objects.create(name="", includes_all_products=True)
        benefit = Benefit.objects.create(
            range=rng,
            proxy_class="oscarbluelight.offer.benefits.BluelightPercentageDiscountBenefit",
        )
        with self.assertRaises(exceptions.ValidationError):
            benefit.clean()

[(20, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (23, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (24, 'oscarbluelight.offer.models.BluelightCountCondition', 'BluelightCountCondition', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (29, 'oscarbluelight.offer.models.BluelightPercentageDiscountBenefit', 'BluelightPercentageDiscountBenefit', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (34, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (35, 'oscar.test.factories.create_basket', 'factories.create_basket', False, 'from oscar.test import factories\n'), (44, 'oscar.test.factories.create_product', 'factories.create_product', False, 'from oscar.test import factories\n'), (66, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (77, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (95, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (100, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (121, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (124, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (125, 'oscarbluelight.offer.models.BluelightCountCondition', 'BluelightCountCondition', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (130, 'oscarbluelight.offer.models.BluelightPercentageDiscountBenefit', 'BluelightPercentageDiscountBenefit', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (136, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (137, 'oscar.test.factories.create_basket', 'factories.create_basket', False, 'from oscar.test import factories\n'), (164, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (167, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (168, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (178, 'oscarbluelight.offer.models.BluelightCountCondition.objects.create', 'BluelightCountCondition.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (191, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (192, 'oscar.test.factories.create_basket', 'factories.create_basket', False, 'from oscar.test import factories\n'), (222, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (225, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (231, 'oscarbluelight.offer.models.BluelightPercentageDiscountBenefit.objects.create', 'BluelightPercentageDiscountBenefit.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (236, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (237, 'oscar.test.factories.create_basket', 'factories.create_basket', False, 'from oscar.test import factories\n'), (272, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (275, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (281, 'oscarbluelight.offer.models.BluelightPercentageDiscountBenefit.objects.create', 'BluelightPercentageDiscountBenefit.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (287, 'unittest.mock.Mock', 'mock.Mock', False, 'from unittest import mock\n'), (288, 'oscar.test.factories.create_basket', 'factories.create_basket', False, 'from oscar.test import factories\n'), (323, 'django_redis.get_redis_connection', 'get_redis_connection', False, 'from django_redis import get_redis_connection\n'), (327, 'oscarbluelight.offer.models.Range.objects.create', 'Range.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (328, 'oscarbluelight.offer.models.Benefit.objects.create', 'Benefit.objects.create', False, 'from oscarbluelight.offer.models import Range, Benefit, BluelightCountCondition, BluelightValueCondition, BluelightPercentageDiscountBenefit\n'), (39, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (45, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (47, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (52, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (59, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (68, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (70, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (80, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (88, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (103, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (111, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (141, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (146, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (205, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (212, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (241, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (246, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (255, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (262, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (292, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (297, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (306, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (313, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (54, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (61, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (148, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (155, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (170, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (171, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (172, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (187, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (229, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (248, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (257, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (264, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (279, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (299, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (308, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (315, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (54, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (61, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (148, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (153, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (153, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (155, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (248, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (257, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (264, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (299, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (308, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n'), (315, 'decimal.Decimal', 'D', True, 'from decimal import Decimal as D\n')]