public async Task <IActionResult> Index() { var actions = GetActions(); var timeOfDayFeature = GetTimeOfDay(); var userOsFeature = GetUserOs(Request.Headers["User-Agent"]); var currentContext = new List <object> { new { time = timeOfDayFeature }, new { userOs = userOsFeature } }; var request = new RankRequest(actions, currentContext); var response = await _personalizerClient.RankAsync(request); return(View(new PersonalizerModel { PersonalizerEventId = response.EventId, PersonalizerEventStartTime = DateTime.UtcNow, Action = response.RewardActionId, TimeOfDay = timeOfDayFeature, UserOs = userOsFeature, Ranking = response.Ranking })); }
private async Task RankWithNoOptions(PersonalizerClient client) { IList <object> contextFeatures = new List <object>() { new { Features = new { day = "tuesday", time = "night", weather = "rainy" } }, new { Features = new { userId = "1234", payingUser = true, favoriteGenre = "documentary", hoursOnSite = 0.12, lastwatchedType = "movie" } } }; IList <PersonalizerRankableAction> actions = new List <PersonalizerRankableAction>(); actions.Add( new PersonalizerRankableAction( id: "Person1", features: new List <object>() { new { videoType = "documentary", videoLength = 35, director = "CarlSagan" }, new { mostWatchedByAge = "30-35" } } )); actions.Add( new PersonalizerRankableAction( id: "Person2", features: new List <object>() { new { videoType = "documentary", videoLength = 35, director = "CarlSagan" }, new { mostWatchedByAge = "40-45" } } )); // Action PersonalizerRankResult response = await client.RankAsync(actions, contextFeatures); Assert.AreEqual(actions.Count, response.Ranking.Count); }
public async Task RankNullParameters() { PersonalizerClient client = GetPersonalizerClient(); IList <PersonalizerRankableAction> actions = new List <PersonalizerRankableAction>(); actions.Add (new PersonalizerRankableAction( id: "Person", features: new List <object>() { new { videoType = "documentary", videoLength = 35, director = "CarlSagan" }, new { mostWatchedByAge = "30-35" } } )); var request = new PersonalizerRankOptions(actions); // Action PersonalizerRankResult response = await client.RankAsync(request); // Assert Assert.AreEqual(actions.Count, response.Ranking.Count); for (int i = 0; i < response.Ranking.Count; i++) { Assert.AreEqual(actions[i].Id, response.Ranking[i].Id); } }
public async Task RankServerFeatures() { PersonalizerClient client = GetPersonalizerClient(); IList <object> contextFeatures = new List <object>() { new { Features = new { day = "tuesday", time = "night", weather = "rainy" } }, new { Features = new { userId = "1234", payingUser = true, favoriteGenre = "documentary", hoursOnSite = 0.12, lastwatchedType = "movie" } } }; IList <PersonalizerRankableAction> actions = new List <PersonalizerRankableAction>(); actions.Add( new PersonalizerRankableAction( id: "Person1", features: new List <object>() { new { videoType = "documentary", videoLength = 35, director = "CarlSagan" }, new { mostWatchedByAge = "30-35" } } )); actions.Add( new PersonalizerRankableAction( id: "Person2", features: new List <object>() { new { videoType = "documentary", videoLength = 35, director = "CarlSagan" }, new { mostWatchedByAge = "40-45" } } )); IList <string> excludeActions = new List <string> { "Person1" }; string eventId = "123456789"; var request = new PersonalizerRankOptions(actions, contextFeatures, excludeActions, eventId); // Action PersonalizerRankResult response = await client.RankAsync(request); // Assert Assert.AreEqual(eventId, response.EventId); Assert.AreEqual(actions.Count, response.Ranking.Count); for (int i = 0; i < response.Ranking.Count; i++) { Assert.AreEqual(actions[i].Id, response.Ranking[i].Id); } }
public static async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, ILogger log) { try { string requestBody = String.Empty; using (StreamReader sReader = new StreamReader(req.Body)) { requestBody = await sReader.ReadToEndAsync(); } var recommendationRequest = JsonConvert.DeserializeObject <RecommendationRequest>(requestBody); var personalizerEndpoint = Environment.GetEnvironmentVariable("AzurePersonalizerEndpoint", EnvironmentVariableTarget.Process); var personalizerKey = Environment.GetEnvironmentVariable("AzurePersonalizerKey", EnvironmentVariableTarget.Process); if (!string.IsNullOrWhiteSpace(personalizerEndpoint) || !string.IsNullOrWhiteSpace(personalizerKey)) { var personalizerClient = new PersonalizerClient( new ApiKeyServiceClientCredentials(personalizerKey)) { Endpoint = personalizerEndpoint }; var actions = GetRankableActions(recommendationRequest.Catalogitems); var contextFeatures = GetContextFeatures(recommendationRequest.UserName); var excludeActions = new List <string>(); var request = new RankRequest(actions, contextFeatures, excludeActions, Guid.NewGuid().ToString()); var response = await personalizerClient.RankAsync(request); var recommendationReponse = new RecommendationResponse { eventId = response.EventId, PrefferedItemId = Convert.ToInt32(response.RewardActionId) }; return(new OkObjectResult(recommendationReponse)); } return(new BadRequestObjectResult("No endpoint or key configured")); } catch (Exception ex) { Console.WriteLine(ex); return(new BadRequestObjectResult(ex)); } }
private async Task <RankResponse> ChooseRankAsync(ITurnContext turnContext, string eventId, CancellationToken cancellationToken) { IList <object> contextFeature = new List <object> { new { weather = _rlFeaturesManager.RLFeatures.Weather.ToString() }, new { dayofweek = _rlFeaturesManager.RLFeatures.DayOfWeek.ToString() }, }; Random rand = new Random(DateTime.UtcNow.Millisecond); IList <RankableAction> actions = new List <RankableAction>(); var coffees = Enum.GetValues(typeof(Coffees)); var beansOrigin = Enum.GetValues(typeof(CoffeeBeansOrigin)); var organic = Enum.GetValues(typeof(Organic)); var roast = Enum.GetValues(typeof(CoffeeRoast)); var teas = Enum.GetValues(typeof(Teas)); foreach (var coffee in coffees) { actions.Add(new RankableAction { Id = coffee.ToString(), Features = new List <object>() { new { BeansOrigin = beansOrigin.GetValue(rand.Next(0, beansOrigin.Length)).ToString() }, new { Organic = organic.GetValue(rand.Next(0, organic.Length)).ToString() }, new { Roast = roast.GetValue(rand.Next(0, roast.Length)).ToString() }, }, }); } foreach (var tea in teas) { actions.Add(new RankableAction { Id = tea.ToString(), Features = new List <object>() { new { Organic = organic.GetValue(rand.Next(0, organic.Length)).ToString() }, }, }); } var request = new RankRequest(actions, contextFeature, null, eventId); await turnContext.SendActivityAsync( "===== DEBUG MESSAGE CALL TO RANK =====\n" + "This is what is getting sent to Rank:\n" + $"{JsonConvert.SerializeObject(request, Formatting.Indented)}\n", cancellationToken : cancellationToken); var response = await _personalizerClient.RankAsync(request, cancellationToken); await turnContext.SendActivityAsync( $"===== DEBUG MESSAGE RETURN FROM RANK =====\n" + "This is what Rank returned:\n" + $"{JsonConvert.SerializeObject(response, Formatting.Indented)}\n", cancellationToken : cancellationToken); return(response); }
public async Task Init() { News.IsVisible = false; Loading.IsVisible = true; var feeds = new List <System.Threading.Tasks.Task <Feed> >() { FeedReader.ReadAsync("https://feeds.expressen.se/sport/"), FeedReader.ReadAsync("https://www.aftonbladet.se/sportbladet/rss.xml"), }; await Task.WhenAll(feeds); var items = new List <FeedItem>(); foreach (var feed in feeds) { var result = feed.Result; items.AddRange(result.Items); } var actions = new List <RankableAction>(); foreach (var item in items.OrderByDescending(x => x.PublishingDate).Take(50)) { string source = null; foreach (var feed in feeds) { if (feed.Result.Items.Contains(item)) { source = feed.Result.Link; break; } } var features = new List <object>() { new { item.Title }, new { item.Author }, new { item.Description }, new { source } }; var action = new RankableAction(item.Id, features); actions.Add(action); } string timeOfDay = null; if (DateTime.Now.Hour > 22 || DateTime.Now.Hour < 5) { timeOfDay = "night"; } else if (DateTime.Now.Hour >= 5 && DateTime.Now.Hour < 12) { timeOfDay = "morning"; } else if (DateTime.Now.Hour >= 12 && DateTime.Now.Hour < 17) { timeOfDay = "afternoon"; } else { timeOfDay = "evening"; } var context = new List <object>() { new { timeOfDay } }; eventId = Guid.NewGuid().ToString(); var rankRequest = new RankRequest() { Actions = actions, ContextFeatures = context, ExcludedActions = new List <string>(), EventId = eventId }; try { var rankResult = await client.RankAsync(rankRequest); source = new List <Item>(); foreach (var ranked in rankResult.Ranking.OrderByDescending(x => x.Probability)) { var feedItem = items.Single(x => x.Id == ranked.Id); var urls = Regex.Matches(feedItem.Description, @"(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?"); var itm = new Item() { FeedItem = feedItem, Image = urls.FirstOrDefault()?.Value }; foreach (var feed in feeds) { if (feed.Result.Items.Contains(feedItem)) { itm.Source = feed.Result.Link; break; } } source.Add(itm); } News.ItemsSource = source; } catch (Exception ex) { source = new List <Item>(); foreach (var feedItem in items) { var urls = Regex.Matches(feedItem.Description, @"(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?"); var itm = new Item() { FeedItem = feedItem, Image = urls.FirstOrDefault()?.Value }; foreach (var feed in feeds) { if (feed.Result.Items.Contains(feedItem)) { itm.Source = feed.Result.Link; break; } } source.Add(itm); } News.ItemsSource = source; } News.ScrollTo(source.First(), ScrollToPosition.Start, false); News.IsVisible = true; Loading.IsVisible = false; }
/// <summary> /// Approach /// 1. Generate actions /// 2. Generate user contexts /// For each user context /// Get actions /// For each action, calculate a score for the user /// If the top action recommended by the service also has the top like score, send 1 back to the API. /// Variant a: ScoreHalfRewards = false, send back 0 to the API otherwise. /// Variant b: ScoreHalfRewards = true, if the top action recommended by the service has the second-highest like score, send 0.5 back to the API. /// </summary> /// <param name="endpoint"></param> /// <param name="apiKey"></param> /// <param name="scoreHalfRewards"></param> /// <returns></returns> public async Task <List <SegmentScore> > ProcessAsync(string endpoint, string apiKey, bool scoreHalfRewards, int howManyActions, int howManyUserContexts, int howManyUsersPerSegment, int segmentPauseMilliseconds) { this.HowManyActions = howManyActions; this.HowManyUserContexts = howManyUserContexts; this.HowManyUsersPerSegment = howManyUsersPerSegment; this.SegmentPauseMilliseconds = segmentPauseMilliseconds; List <SegmentScore> result = new List <SegmentScore>(); int overallCounter = 0; SegmentScore segmentScore = new SegmentScore(); segmentScore.Segment = 1; using (PersonalizerClient client = InitializePersonalizerClient(endpoint, apiKey)) { // Iterate through each context - i.e. each interaction where we want to get a ranked list of choices foreach (var context in UserContexts) { overallCounter++; segmentScore.Count++; // Each interaction (context + choices -> ranked choices -> user behavior -> send feedback) requires a unique ID to correlate throughout string eventId = Guid.NewGuid().ToString(); var rankingRequest = new RankRequest(this.Actions, context, null, eventId, false); RankResponse response = await client.RankAsync(rankingRequest); // These are our calcs for points, for THIS user/context, for each of the actions IDictionary <string, int> actionScoresForContext = this.GetActionScoresForContext(context); // Using THIS user/context's points for each action, calculate a reward based on whether the API "got it right" double reward = CalculateReward(actionScoresForContext, response.RewardActionId, scoreHalfRewards); Console.WriteLine($"Iteration {overallCounter} = reward {reward}"); // Send feedback to the API - for this event (ranking interaction), what is the reward we calculated await client.RewardAsync(response.EventId, new RewardRequest(reward)); // We are tracking segments of users so we can more clearly track reward accumulation / API performance // Manage this segment's rewards here segmentScore.TotalReward += reward; if (reward > 0) { if (reward == 1) { segmentScore.CountRewardFull++; } else if (reward < 1) { segmentScore.CountRewardHalf++; } } if (segmentScore.Count % this.HowManyUsersPerSegment == 0) { result.Add(segmentScore); int newSegment = segmentScore.Segment + 1; segmentScore = new SegmentScore() { Segment = newSegment }; // As we complete each segment of users let's pause for a time to allow the API to retrain // This is artificial so we can watch the API's performance improve in this contrived app // In the real world we wouldn't do this since ongoing usage would be far lengthier than a quick sample app like this if (overallCounter < this.HowManyUserContexts) { Console.WriteLine(); Console.WriteLine("Sleeping for service training..."); Thread.Sleep(this.SegmentPauseMilliseconds); Console.WriteLine("Completed sleep, continuing"); Console.WriteLine(); } } } } return(result); }
public async Task <ActionResult <RankedRecipes> > GetRecommendations() { try { if (!HttpContext.User.Identity.IsAuthenticated) { throw new Exception(); } var apiKey = (await _context.AppSettings.FirstOrDefaultAsync(setting => setting.EnumCode == (int)Enums.AppSetting.PersonalizerApiKey)).Value; var serviceEndpoint = (await _context.AppSettings.FirstOrDefaultAsync(setting => setting.EnumCode == (int)Enums.AppSetting.PersonalizerServiceEndpoint)).Value; PersonalizerClient client = new PersonalizerClient(new ApiKeyServiceClientCredentials(apiKey)) { Endpoint = serviceEndpoint }; var idUser = GetUserId(); var userRecipeTags = await _context.AspNetUserRecipeTag.Where(userTag => userTag.IdUser == idUser) .Join( _context.RecipeTags, userTag => userTag.IdRecipeTag, tag => tag.Id, (userTag, tag) => tag) .Join( _context.RecipeTagTypes, tag => tag.IdRecipeTagType, type => type.Id, (tag, type) => new { tag, type }).ToListAsync(); var userRecipeTagDictionary = userRecipeTags.GroupBy(recipe => recipe.type).ToDictionary(mapping => mapping.Key.EnumCode, mapping => userRecipeTags.Where(tag => tag.type.Id == mapping.Key.Id).Select(tag => tag.tag.Name)); IList <object> currentContext = new List <object>() { new { Cuisine = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Cuisine) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Cuisine]) : string.Empty }, new { Dietary = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Dietary) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Dietary]) : string.Empty }, new { Occasion = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Occasion) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Occasion]) : string.Empty }, new { Meal = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Meal) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Meal]) : string.Empty }, new { Seasonal = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Seasonal) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Seasonal]) : string.Empty }, new { Holiday = userRecipeTagDictionary.ContainsKey((int)Shared.Enums.Recipe.RecipeTagType.Holiday) ? string.Join(',', userRecipeTagDictionary[(int)Shared.Enums.Recipe.RecipeTagType.Holiday]) : string.Empty } }; (IEnumerable <Recipe> recipes, string selectedSearchCriteria) = await GetUserRecipes(); if (recipes != null) { var recipeWithTags = recipes.Join(_context.Recipe_RecipeTags, recipe => recipe.Id, recipeTagMapping => recipeTagMapping.IdRecipe, (recipe, recipeTagMapping) => new { recipe, recipeTagMapping }) .Join(_context.RecipeTags, recipeTagMapping => recipeTagMapping.recipeTagMapping.IdRecipeTag, recipeTag => recipeTag.Id, (recipeTagMapping, recipeTag) => new { recipeTagMapping.recipe, recipeTag }) .Join(_context.RecipeTagTypes, tag => tag.recipeTag.IdRecipeTagType, type => type.Id, (tag, type) => new { tag.recipe, tag.recipeTag, type }).ToList(); var recipeWithTagsDictionary = recipeWithTags.GroupBy(recipe => recipe.recipe).ToDictionary(group => group.Key, group => recipeWithTags.Where(item => item.recipe == group.Key).Select(item => item)); IList <RankableAction> actions = recipeWithTagsDictionary.Select(recipe => new RankableAction() { Id = recipe.Key.Id.ToString(), Features = new List <object>() { new { Cuisine = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Cuisine).Select(item => item.recipeTag.Name)) }, new { Dietary = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Dietary).Select(item => item.recipeTag.Name)) }, new { Occasion = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Occasion).Select(item => item.recipeTag.Name)) }, new { Meal = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Meal).Select(item => item.recipeTag.Name)) }, new { Seasonal = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Seasonal).Select(item => item.recipeTag.Name)) }, new { Holiday = string.Join(',', recipe.Value.Where(item => item.type.EnumCode == (int)Shared.Enums.Recipe.RecipeTagType.Holiday).Select(item => item.recipeTag.Name)) } } }).Take(50).ToList(); IList <string> excludeActions = new List <string>(); var eventId = Guid.NewGuid().ToString(); // Rank the actions var request = new RankRequest(actions, currentContext, excludeActions, eventId); RankResponse response = await client.RankAsync(request); var rankedRecipes = await GetRecipes(response.Ranking, Convert.ToInt64(response.RewardActionId)); return(new RankedRecipes() { EventId = response.EventId, RecommendedRecipeId = Convert.ToInt64(response.RewardActionId), Recipes = rankedRecipes, RecipeSearchCriteria = selectedSearchCriteria }); } return(null); } catch (Exception) { return(StatusCode(StatusCodes.Status500InternalServerError, "Error retrieving recipes")); } }