public void TestCreateMultiNodeTreeFromScratch()
        {
            ProductPartitionTree tree =
                ProductPartitionTree.CreateAdGroupTree(new List <AdGroupCriterion>());

            ProductPartitionNode rootNode = tree.Root.AsSubdivision();
            ProductPartitionNode brand1   = rootNode.AddChild(ProductDimensions.CreateBrand("google"))
                                            .AsSubdivision();
            ProductPartitionNode brand1Offer1 =
                brand1.AddChild(ProductDimensions.CreateOfferId("A"));

            brand1Offer1.AsBiddableUnit().CpcBid = 1000000L;
            ProductPartitionNode brand1Offer2    =
                brand1.AddChild(ProductDimensions.CreateOfferId()).AsExcludedUnit();
            ProductPartitionNode brand2 =
                rootNode.AddChild(ProductDimensions.CreateBrand()).AsExcludedUnit();

            ProductPartitionNode[] nodes = new ProductPartitionNode[]
            {
                rootNode,
                brand1,
                brand1Offer1,
                brand1Offer2,
                brand2
            };

            AdGroupCriterionOperation[] mutateOperations = tree.GetMutateOperations();

            for (int i = 0; i < nodes.Length; i++)
            {
                List <AdGroupCriterionOperation> nodeOperations =
                    shoppingTestUtils.GetOperationsForNode(nodes[i], mutateOperations);
                Assert.That(nodeOperations.Count == 1);
                Assert.That(nodeOperations[0].@operator == Operator.ADD);
                ProductPartition partition = (ProductPartition)nodeOperations[0].operand.criterion;
                Assert.That(partition.id == nodes[i].ProductPartitionId);
                if (nodes[i].Parent == null)
                {
                    Assert.That(partition.parentCriterionIdSpecified == false);
                }
                else
                {
                    Assert.That(partition.parentCriterionId == nodes[i].Parent.ProductPartitionId);
                }

                Assert.That(partition.caseValue == nodes[i].Dimension);
            }
        }
    public void TestSetBidOnUnit() {
      rootNode = rootNode.AsSubdivision();
      ProductBrand childDimension = ProductDimensions.CreateBrand("google");
      ProductPartitionNode childNode = rootNode.AddChild(childDimension);

      Assert.That(childNode.CpcBidSpecified == false, "Bid should be null by default.");

      childNode.CpcBid = 1L;

      Assert.AreEqual(1L, childNode.CpcBid, "Bid does not reflect setBid.");
      Assert.True(childNode.IsBiddableUnit, "Node should be a biddable unit.");

      childNode = childNode.AsExcludedUnit();
      Assert.True(childNode.IsExcludedUnit, "Node should be an excluded unit.");
      Assert.False(childNode.IsBiddableUnit, "Node should not be a biddable unit.");
      Assert.False(childNode.CpcBidSpecified, "Excluded unit should have a null bid");

      // Set back to biddable.
      childNode = childNode.AsBiddableUnit();
      Assert.True(childNode.IsBiddableUnit, "Node should be a biddable unit.");
    }
        public void TestCreateUltimatelyEmptyTree()
        {
            ProductPartitionTree tree =
                ProductPartitionTree.CreateAdGroupTree(new List <AdGroupCriterion>());

            ProductPartitionNode rootNode = tree.Root.AsSubdivision();
            ProductPartitionNode brand1   = rootNode.AddChild(ProductDimensions.CreateBrand("google"))
                                            .AsSubdivision();
            ProductPartitionNode offerNode = brand1.AddChild(ProductDimensions.CreateOfferId("A"));

            offerNode.AsBiddableUnit().CpcBid = 1000000L;

            brand1.AddChild(ProductDimensions.CreateOfferId()).AsExcludedUnit();
            ProductPartitionNode brand2 =
                rootNode.AddChild(ProductDimensions.CreateBrand()).AsExcludedUnit();

            // Now remove the two child nodes under the root and set the root back
            // to a UNIT. This should result in operations that simply create the
            // root node.
            rootNode.RemoveChild(brand1.Dimension);
            rootNode.RemoveChild(brand2.Dimension);
            rootNode = rootNode.AsBiddableUnit();

            AdGroupCriterionOperation[] mutateOperations = tree.GetMutateOperations();

            Assert.AreEqual(mutateOperations.Count(), 1, "Number of operations is incorrect.");
            AdGroupCriterionOperation operation = mutateOperations[0];

            Assert.AreEqual(Operator.ADD, operation.@operator,
                            "Should have a single operation to ADD the root node.");
            BiddableAdGroupCriterion adGroupCriterion =
                (BiddableAdGroupCriterion)operation.operand;

            Assert.Null(((ProductPartition)adGroupCriterion.criterion).caseValue,
                        "Product dimension of operation's operand should be null.");
            Assert.True(adGroupCriterion.criterion.id < 0L,
                        "Partition ID of the operand should be negative.");
        }
        public void Init()
        {
            CAMPAIGN_ID = utils.CreateShoppingCampaign(user, BiddingStrategyType.MANUAL_CPC);
            ADGROUP_ID  = utils.CreateAdGroup(user, CAMPAIGN_ID);

            tree = ProductPartitionTree.CreateAdGroupTree(ADGROUP_ID,
                                                          new List <AdGroupCriterion>());
            ProductPartitionNode root = tree.Root.AsSubdivision();

            ProductPartitionNode clothing = root.AddChild(ProductDimensions.CreateType(
                                                              ProductDimensionType.PRODUCT_TYPE_L1, "clothing"));

            clothing.AsBiddableUnit().CpcBid = 200000;
            ProductPartitionNode shoes       = root.AddChild(ProductDimensions.CreateType(
                                                                 ProductDimensionType.PRODUCT_TYPE_L1, "shoes"));

            shoes.AsBiddableUnit().CpcBid  = 400000;
            ProductPartitionNode otherNode = root.AddChild(ProductDimensions.CreateType(
                                                               ProductDimensionType.PRODUCT_TYPE_L1));

            otherNode.AsBiddableUnit().CpcBid = 300000;

            tree = ExecuteTreeOperations();
        }
        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="adGroupId">The ad group to which product partition is
        /// added.</param>
        public void Run(AdWordsUser user, long adGroupId)
        {
            using (AdGroupCriterionService adGroupCriterionService =
                       (AdGroupCriterionService)user.GetService(
                           AdWordsService.v201802.AdGroupCriterionService)) {
                // Build a new ProductPartitionTree using the ad group's current set of criteria.
                ProductPartitionTree partitionTree =
                    ProductPartitionTree.DownloadAdGroupTree(user, adGroupId);

                Console.WriteLine("Original tree: {0}", partitionTree);

                // Clear out any existing criteria.
                ProductPartitionNode rootNode = partitionTree.Root.RemoveAllChildren();

                // Make the root node a subdivision.
                rootNode = rootNode.AsSubdivision();

                // Add a unit node for condition = NEW.
                ProductPartitionNode newConditionNode = rootNode.AddChild(
                    ProductDimensions.CreateCanonicalCondition(ProductCanonicalConditionCondition.NEW));
                newConditionNode.AsBiddableUnit().CpcBid = 200000;

                ProductPartitionNode usedConditionNode = rootNode.AddChild(
                    ProductDimensions.CreateCanonicalCondition(ProductCanonicalConditionCondition.USED));
                usedConditionNode.AsBiddableUnit().CpcBid = 100000;

                // Add a subdivision node for condition = null (everything else).
                ProductPartitionNode otherConditionNode =
                    rootNode.AddChild(ProductDimensions.CreateCanonicalCondition()).AsSubdivision();

                // Add a unit node under condition = null for brand = "CoolBrand".
                ProductPartitionNode coolBrandNode = otherConditionNode.AddChild(
                    ProductDimensions.CreateBrand("CoolBrand"));
                coolBrandNode.AsBiddableUnit().CpcBid = 900000L;

                // Add a unit node under condition = null for brand = "CheapBrand".
                ProductPartitionNode cheapBrandNode = otherConditionNode.AddChild(
                    ProductDimensions.CreateBrand("CheapBrand"));
                cheapBrandNode.AsBiddableUnit().CpcBid = 10000L;

                // Add a subdivision node under condition = null for brand = null (everything else).
                ProductPartitionNode otherBrandNode = otherConditionNode.AddChild(
                    ProductDimensions.CreateBrand(null)).AsSubdivision();

                // Add unit nodes under condition = null/brand = null.
                // The value for each bidding category is a fixed ID for a specific
                // category. You can retrieve IDs for categories from the ConstantDataService.
                // See the 'GetProductCategoryTaxonomy' example for more details.

                // Add a unit node under condition = null/brand = null for product type
                // level 1 = 'Luggage & Bags'.
                ProductPartitionNode luggageAndBagNode = otherBrandNode.AddChild(
                    ProductDimensions.CreateBiddingCategory(ProductDimensionType.BIDDING_CATEGORY_L1,
                                                            -5914235892932915235L));
                luggageAndBagNode.AsBiddableUnit().CpcBid = 750000L;

                // Add a unit node under condition = null/brand = null for product type
                // level 1 = null (everything else).
                ProductPartitionNode everythingElseNode = otherBrandNode.AddChild(
                    ProductDimensions.CreateBiddingCategory(ProductDimensionType.BIDDING_CATEGORY_L1));
                everythingElseNode.AsBiddableUnit().CpcBid = 110000L;

                try {
                    // Make the mutate request, using the operations returned by the ProductPartitionTree.
                    AdGroupCriterionOperation[] mutateOperations = partitionTree.GetMutateOperations();

                    if (mutateOperations.Length == 0)
                    {
                        Console.WriteLine("Skipping the mutate call because the original tree and the " +
                                          "current tree are logically identical.");
                    }
                    else
                    {
                        adGroupCriterionService.mutate(mutateOperations);
                    }

                    // The request was successful, so create a new ProductPartitionTree based on the updated
                    // state of the ad group.
                    partitionTree = ProductPartitionTree.DownloadAdGroupTree(user, adGroupId);

                    Console.WriteLine("Final tree: {0}", partitionTree);
                } catch (Exception e) {
                    throw new System.ApplicationException("Failed to set shopping product partition.", e);
                }
            }
        }
        public void TestMutateMultiNodeTree()
        {
            ProductPartitionTree tree = shoppingTestUtils.CreateTestTreeForTransformation(ADGROUP_ID);

            Assert.AreEqual(ADGROUP_ID, tree.AdGroupId, "ad group ID is incorrect");

            // Change the bids on leaf nodes.
            ProductPartitionNode brandGoogleNode = tree.Root.GetChild(shoppingTestUtils.BRAND_GOOGLE);
            ProductPartitionNode offerANode      = brandGoogleNode.GetChild(shoppingTestUtils.OFFER_A);

            // This should produce 1 SET operation.
            offerANode.CpcBid = offerANode.CpcBid * 10;

            // Offer B is changed from Exclude to Biddable. This should produce 1
            // REMOVE operation + 1 ADD operation.
            ProductPartitionNode offerBNode = brandGoogleNode.GetChild(shoppingTestUtils.OFFER_B);

            offerBNode.AsBiddableUnit().CpcBid = 5000000L;

            // Other Brand node is changed from Exclude to Biddable. This should
            // produce 1 REMOVE operation + 1 ADD operation.
            ProductPartitionNode brandOtherNode = tree.Root.GetChild(shoppingTestUtils.BRAND_OTHER);

            brandOtherNode = brandOtherNode.AsBiddableUnit();

            // Add an offer C node. This should produce 1 ADD operation.
            ProductPartitionNode offerCNode = brandGoogleNode.AddChild(shoppingTestUtils.OFFER_C);

            offerCNode.AsBiddableUnit().CpcBid = 1500000L;

            // Remove the brand Motorola node. This should produce 1 REMOVE operation.
            ProductPartitionNode brandMotorolaNode = tree.Root.GetChild(
                shoppingTestUtils.BRAND_MOTOROLA);

            tree.Root.RemoveChild(shoppingTestUtils.BRAND_MOTOROLA);

            // Get the mutate operations generated by the modifications made to the tree.
            AdGroupCriterionOperation[] mutateOperations = tree.GetMutateOperations();
            Assert.AreEqual(7, mutateOperations.Length);

            List <AdGroupCriterionOperation> operations = null;

            // Since Offer A node only has modified attributes, there should only be
            // one SET operation.
            operations = shoppingTestUtils.GetOperationsForNode(offerANode, mutateOperations);
            Assert.That(operations.Count == 1);
            Assert.That(operations[0].@operator == Operator.SET);

            // Since Offer B node is being converted from Exclude to Biddable node,
            // there should be one REMOVE operation, and another ADD operation.
            operations = shoppingTestUtils.GetOperationsForNode(offerBNode, mutateOperations);
            Assert.That(operations.Count == 2);
            Assert.That(operations[0].@operator == Operator.REMOVE);
            Assert.That(operations[1].@operator == Operator.ADD);

            // Since Offer C node is being added, there should be one ADD operation.
            operations = shoppingTestUtils.GetOperationsForNode(offerCNode, mutateOperations);
            Assert.That(operations.Count == 1);
            Assert.That(operations[0].@operator == Operator.ADD);

            // Since Other Brand node is being converted from Exclude to Biddable node,
            // there should be one REMOVE operation, and another ADD operation.
            operations = shoppingTestUtils.GetOperationsForNode(brandOtherNode, mutateOperations);
            Assert.That(operations.Count == 2);
            Assert.That(operations[0].@operator == Operator.REMOVE);
            Assert.That(operations[1].@operator == Operator.ADD);

            // Since Offer B node is being removed, there should be one REMOVE
            // operation.
            operations = shoppingTestUtils.GetOperationsForNode(brandMotorolaNode, mutateOperations);
            Assert.That(operations.Count == 1);
            Assert.That(operations[0].@operator == Operator.REMOVE);
        }
        /// <summary>
        /// Returns a new tree based on a non-empty collection of ad group criteria.
        /// </summary>
        /// <param name="adGroupId">The ad group ID.</param>
        /// <param name="parentIdMap">The multimap from parent product partition ID
        /// to child criteria.</param>
        /// <returns>a new product partition tree.</returns>
        private static ProductPartitionTree CreateAdGroupTree(long adGroupId,
        Dictionary<long, List<AdGroupCriterion>> parentIdMap)
        {
            ProductPartitionNode rootNode = null;

              if (parentIdMap.Count == 0) {
            rootNode = new ProductPartitionNode(null, null, NEW_ROOT_ID);
              } else {
            PreconditionUtilities.CheckState(parentIdMap.ContainsKey(ROOT_PARENT_ID),
            string.Format(ShoppingMessages.RootCriteriaNotFoundInCriteriaList, adGroupId));

            PreconditionUtilities.CheckState(parentIdMap[ROOT_PARENT_ID].Count == 1,
            string.Format(ShoppingMessages.MoreThanOneRootFound, adGroupId));

            AdGroupCriterion rootCriterion = parentIdMap[ROOT_PARENT_ID][0];

            PreconditionUtilities.CheckState(rootCriterion is BiddableAdGroupCriterion,
            string.Format(ShoppingMessages.RootCriterionIsNotBiddable, adGroupId));

            BiddableAdGroupCriterion biddableRootCriterion = (BiddableAdGroupCriterion) rootCriterion;

            rootNode = new ProductPartitionNode(null, null, rootCriterion.criterion.id,
            new ProductDimensionEqualityComparer());

            // Set the root's bid if a bid exists on the BiddableAdGroupCriterion.
            Money rootNodeBid = GetBid(biddableRootCriterion);

            if (rootNodeBid != null) {
              rootNode.AsBiddableUnit().CpcBid = rootNodeBid.microAmount;
            }

            AddChildNodes(rootNode, parentIdMap);
              }
              return new ProductPartitionTree(adGroupId, rootNode);
        }