Source: site.view [edit]
Function name: findWineRecommendation
Arguments: foodType,style,ingredient,appellation,varietal,vineyard
Description:
Page type: webl
Render function:  
Module: global

Page source:

// ************************************************************
// WineCom.webl
//   Encapsulates Wine.com's APIs
//
// Implemented Functions:
//     findWineRecommendation(FoodClassification)
//
// Author: Adam Cheyer
// ************************************************************

// ------------------------------------------------------------

// Structure representing foodType, Ingredients, and Styles, with some keywords to help guessing
var winePairingData = `foodType, 3008, Meat, meat
    ingredient, 3015, Beef, steak
    style, 3019, Herbs, herb
    style, 3020, Hot Spices, spice|chili powder|chili
    style, 3021, Mushroom, mushrooms
    style, 3022, Stew, stew
    style, 3023, BBQ, barbecue
    style, 3024, Burgers, hamburger|burger
    ingredient, 3016, Lamb, lamb
    style, 3025, Herbs, herb
    style, 3026, Hot Spices, spice|chili powder|chili
    ingredient, 3017, Pork, pig
    style, 3027, Herbs, herb
    style, 3028, Hot Spices, spice|chili powder|chili
    style, 3029, Mushroom, mushrooms
    style, 3030, Sausage, sausage
    style, 3031, Tenderloin, loin
    style, 3032, BBQ, barbecue
    style, 3033, Breaded, breadcrumbs
    style, 3034, Fruit, fruit|apple
    ingredient, 3018, Veal, veal
    style, 3035, Herbs, herb
    style, 3036, Lemon/Citrus, lemon|orange
    style, 3037, Mushroom, mushrooms
    style, 3038, Breaded, breadcrumbs
    style, 3039, Chops, chop
    foodType, 3009, Cheese, frommage
    ingredient, 3040, Blue, blue
    ingredient, 3041, Cheddar, cheddar
    ingredient, 3042, Creamy, creamcheese
    ingredient, 3043, Goat, goat
    ingredient, 3044, Hard, firm
    ingredient, 3045, Semi-Firm, semifirm
    ingredient, 3046, Stinky, smelly
    foodType, 3010, Dessert, dessert
    ingredient, 3047, Berries, strawberries|raspberries
    ingredient, 3048, Chocolate, chocolate
    ingredient, 3049, Cream or Custard, cream|custard
    ingredient, 3050, Lemon, lemon|lime
    foodType, 3012, Pasta & Grains, pasta|noodles|spaghetti|penne|lasagna|lasagne
    ingredient, 3055, Lasagne, lasagna|lasagne|italian
    style, 3060, Meat, meat sauce|bolognese|hamburger|sausage
    style, 3061, Vegetable, vegetables
    ingredient, 3056, Paella, paella
    ingredient, 3057, Pasta,pasta|noodles|spaghetti|penne|italian
    style, 3067, Meat, meat sauce|bolognese
    style, 3068, Mushroom, mushrooms
    style, 3069, Pesto, pesto
    style, 3070, Tomato-base, tomato|bolognese
    style, 3071, Vegetable, vegetables
    style, 3072, White Sauce/Seafood, flour|seafood|fish
    style, 3073, Cream-Based, cream
    ingredient, 3058, Pizza, pizza
    style, 3062, Meat, sausage|hamburger|american
    style, 3063, Vegetable, vegetables|onions|pepper
    ingredient, 3059, Risotto, risotta
    style, 3064, Mushroom, mushrooms
    style, 3065, Plain, plain
    style, 3066, Primavera, primavera
    foodType, 3013, Poultry, poultry
    ingredient, 3074, Cassoulet, cassoulet
    ingredient, 3075, Chicken, chicken
    style, 3078, Herbs, herb
    style, 3079, Lemon/Citrus, lemon|citrus
    style, 3080, Mushroom, mushrooms
    style, 3081, Mustard, mustard
    style, 3082, Spicy, spice
    style, 3083, BBQ, barbecue
    style, 3084, Cream-Based, cream
    style, 3085, Fried, fried
    ingredient, 3076, Duck, canard|french
    style, 3086, Seared, sear
    style, 3087, Confit, confit
    style, 3088, Fois Gras, fois gras
    style, 3089, Fruit, fruit
    ingredient, 3077, Turkey, turkey
    style, 3090, Lemon/Citrus, lemon|citrus
    style, 3091, Roasted, roast
    style, 3092, Breaded, breadcrumbs
    foodType, 3014, Seafood, seafood|fish
    ingredient, 3093, Crab, crab
    style, 3102, Soft Shelled, shell|shelled
    style, 3103, Spicy, spice
    style, 3104, Butter, butter
    style, 3105, Crab Cakes, crab cakes
    ingredient, 3094, Lobster, lobster
    style, 3106, Butter, butter
    ingredient, 3095, Oysters, oyster
    ingredient, 3096, Salmon, salmon
    style, 3107, Herbs, herb
    style, 3108, Mustard, mustard
    style, 3109, Grilled, grill
    ingredient, 3097, Scallops, scallop
    style, 3110, Herbs, herb
    style, 3111, Lemon/Citrus, lemon|citrus
    style, 3112, Butter, butter
    ingredient, 3098, Shrimp, shrimp
    style, 3113, Lemon/Citrus, lemon|citrus
    style, 3114, Herbs, herb
    ingredient, 3099, Sushi, sushi
    ingredient, 3100, Tuna, tuna|ahi
    style, 3115, Seared, seared
    style, 3116, Spicy, spice
    ingredient, 3101, White Fish, white fish
    style, 3117, Herbs, herb
    style, 3118, Hot Spices, spice
    style, 3119, Lemon/Citrus, lemon|citrus
    style, 3120, Stew, stew
    style, 3121, Fish Tacos, tacos|mexican
    style, 3122, Fried, fried
`;

var lines = Str_Split(winePairingData, "\n");
var count = 0;


// ------------------------------------------------------------
// Parse the ingredient data and build a representative structure
// ------------------------------------------------------------
var buildStructure = fun(itemType, exitList)
   var res = [];
   var last = nil;
   var obj = nil;
   while (count < Size(lines)) do
      var cols = Str_Split(lines[count], ",");
      last = obj;
      obj = [. type=Str_Trim(cols[0]), id=Str_Trim(cols[1]), name=Str_Trim(cols[2]), synonyms=(Str_Split(Str_Trim(cols[3]), "|")?nil) .];
      count = count + 1;

      if (obj.type == itemType) then
         res = res + [obj]
      else
         if (obj.type member exitList) then
            count = count - 1;
            return res
         end;
         count = count - 1;
         last.children := buildStructure(obj.type, [itemType] + exitList);
      end;
   end;
   return res
end;


// ------------------------------------------------------------
// Format the food classification structure in an indented printout
// ------------------------------------------------------------
var PrettyFormat = fun(pre, struc)
   var res = "";
   every obj in struc do
       res = res + pre + obj.type + ":" + obj.name + "\n";
       var c = obj.children ? nil;
       if c != nil then
          res = res + PrettyFormat(pre + "   ", c)
       end
   end;
   return res
end;

// ------------------------------------------------------------
// Finds a matching object
// ------------------------------------------------------------
var getMatchingObj = fun(path, lst)
    var n = First(path);
    every obj in lst do
       if (obj.name == n) then
          if Rest(path) == [] then
             return obj
          else
             return getMatchingObj(Rest(path), obj.children)
          end
       end
    end;
    return nil
end;


// ------------------------------------------------------------
// Build the correct query link using foodType, ingredient, and style
// ------------------------------------------------------------
var ConstructLink = fun(structure, foodType, ingredient, style)
   var obj = getMatchingObj([ foodType ], structure);
   var args = "N=7155+" + obj.id;
   var name = "/V6/Pairing-Wine-With-" + foodType;  // this.ActiveFoodType.UrlName;

   PrintLn("FoodType: " + ToString(foodType) + "  Ingredient: " + ToString(ingredient) + "  Style: " + ToString(style));

   if (ingredient != nil) then
      obj = getMatchingObj([ foodType, ingredient ], structure);
      args = "N=7155+" + obj.id;
      var c = obj.children ? nil;
      PrintLn("MAT OBJ: " + ToString(obj));

      if (c != nil) then
         name = "/V6/Pairing-Wine-With-" + ingredient;   // this.ActiveIngredient.UrlName;
      else
         name = "/V6/Pairing-Wine-With-" + ingredient + "-" + foodType;
      end;

      if (style != nil) then
         obj = getMatchingObj([foodType, ingredient, style], structure);

         PrintLn("MAT OBJ2: " + ToString(obj));

         args = "N=7155+" + obj.id;
         if (obj.type == "style") then
            name = "/V6/Pairing-Wine-With-" + "-" + ingredient + "-" + style;
         else
            name = "/V6/Pairing-Wine-With-" + "-" + ingredient + "-" + foodType;
         end
      end;
   end;

   return "http://www.wine.com" + name + "/winefoodpairing.aspx?" + args + "&show=true";

end;


// ------------------------------------------------------------
// Pulls out textual recommendation description from webpage,
//  along with a recommended varietal and appellation
// ------------------------------------------------------------
var getRecommendation = fun(P)
                                                                                               
   if (P == nil) then
      return nil
   end;
                                                                                               
   var recommendation = nil;
   var varietal = nil;
   var appellation = nil;
   var vintage = nil;
   var wineName = nil;

   //  Pull out description text
   var i = 0;
   var D = Elem(P, "div");
   while (i < Size(D)) do
      var id = D[i].id ? nil;
      if id == "ctl00_BodyContent_ctrResults_pnlMessage" then
         recommendation = Str_Trim(Text(D[i]));
         i = 10000
      end;
      i = i + 1;
   end;

   // Look for appellation and varietal from one of the recommended bottles of wine
   i = 0;
   var A = Elem(P, "a");
   while (i < Size(A)) do
      var id = A[i].id ? nil;
      if Str_EndsWith(ToString(id),"ctrProduct_hlName") then
         wineName = Str_Trim(Text(A[i])) ? nil;
         var m = Str_Match(wineName, `.*(\d\d\d\d)`) ? nil;
         vintage = (m[1] ? nil);
         if (wineName != nil and recommendation != nil) then
            recommendation = recommendation + ".\n\nExemplar: " + wineName;
         end
      end;
      if Str_EndsWith(ToString(id),"ctrProduct_lnkVarietal") then
         varietal = Str_Trim(Text(A[i]));
      end;
      if Str_EndsWith(ToString(id),"lnkAppellation") then
         appellation = Str_Trim(Text(A[i]));
      end;
      if (varietal != nil and appellation != nil) then
         i = 10000
      end;
      i = i + 1;
   end;

   if (recommendation != nil) then
      return [. recommendation = recommendation, varietal = varietal, appellation = appellation,
                vintage=vintage, wineItemName = wineName .]
   else
      return nil
   end
end;

                                                                                               
// ------------------------------------------------------------
//  Check if an incoming command line argument (string) is nil
// ------------------------------------------------------------
var checkNil = fun(arg) 
   if arg == "nil" or arg == "" then
      arg = nil
   end;
   return arg;
end;

// ------------------------------------------------------------
// Implements findWineRecommendation function
//   Inputs:  FoodClassification (foodType, ingredient, style)
//   Outputs: wineRecommendation (description: string)
// ------------------------------------------------------------
var findWineRecommendation = fun(foodType, style, ingredient, appellation, varietal, vineyard)
                                                                                               
    foodType = checkNil(foodType);                                                                                           
    style = checkNil(style);                                                                                           
    ingredient = checkNil(ingredient);                                                                                           
    appellation = checkNil(appellation);                                                                                           
    varietal = checkNil(varietal);                                                                                           
    vineyard = checkNil(vineyard);                                                                                           

    if (foodType != nil) then                                                                                               
       var structure = buildStructure("foodType", []);
       // PrettyFormat("", structure);

       var link = ConstructLink(structure, foodType, ingredient, style);
       //PrintLn("Getting URL for wine recommendation: " + link);
       link = Wub_ReplaceAll(link, " ", "%20");
       var P = GetURL(link) ? nil;
       var rec = getRecommendation(P);
       //PrintLn("Recommendation: " + ToString(rec));
       if (rec != nil) then
          var showIt = true;
          if (appellation != nil and (Str_ToLowerCase(appellation) != Str_ToLowerCase(rec.appellation))) then
             showIt = false;
             //PrintLn("FAILED COMPARISON: '" + varietal + "' and '" + rec.varietal + "'");
          end;
          if (varietal != nil and (Str_ToLowerCase(varietal) != Str_ToLowerCase(rec.varietal))) then
             showIt = false;
             //PrintLn("FAILED COMPARISON: '" + varietal + "' and '" + rec.varietal + "'");
          end;
          if (vineyard != nil) then
             showIt = false;
             //PrintLn("FAILED COMPARISON: '" + vineyard + "'");
          end;
          if (showIt) then
             return [[. description=rec.recommendation, appellation=rec.appellation, varietal=rec.varietal,
                     wineItemName=rec.wineItemName, vintage=rec.vintage .]];
          else
             return []
          end
       else
          return []
       end
    else 
       return []
    end                                                                                               
end;


var res = findWineRecommendation(foodType, style, ingredient, appellation, varietal, vineyard);
NewPage(WubCall("toJSON", [res]), "text/plain");