using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using MoreLinq; using Nancy; using Sony.Filtr.AdminAPI.Utility; using Sony.Filtr.AdminAPI.ViewModels.Graph; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Definitions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Core.EditorialPlaylists; using Sony.Filtr.Core.SpotifyBrowse; using Sony.Filtr.Core.SpotifyBrowse.Data; using Sony.Filtr.Playlists.Spotify; using Sony.Filtr.SpotifyAnalytics; using Sony.Filtr.SpotifyAnalytics.Data; using Sony.Filtr.Utility; using Sony.Filtr.Utility.Extensions; namespace Sony.Filtr.AdminAPI.Modules { public class PlaylistGraphModule : BaseModule { private readonly EditorialPlaylistStatsManager _editorialPlaylistStatsManager; private readonly SpotifyBrowseManager _spotifyBrowseManager; private readonly SpotifyAnalyticsManager _spotifyAnalyticsManager; private readonly SpotifyPlaylistManager _spotifyPlaylistManager; private readonly SpotifyPlaylistHistoricTrackListManager _historicTrackListManager; public PlaylistGraphModule(IApplicationInstanceManager applicationInstanceManager, EditorialPlaylistStatsManager editorialPlaylistStatsManager, SpotifyBrowseManager spotifyBrowseManager, SpotifyAnalyticsManager spotifyAnalyticsManager, SpotifyPlaylistManager spotifyPlaylistManager, SpotifyPlaylistHistoricTrackListManager historicTrackListManager) : base(applicationInstanceManager) { _editorialPlaylistStatsManager = editorialPlaylistStatsManager; _spotifyBrowseManager = spotifyBrowseManager; _spotifyAnalyticsManager = spotifyAnalyticsManager; _spotifyPlaylistManager = spotifyPlaylistManager; _historicTrackListManager = historicTrackListManager; Get["PlaylistStreamsGraph", "/playlistGraph/streams", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); var legacyPlaylistUri = await spotifyPlaylistManager.GetLegacyUriAsync(playlistId); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var playlistStreamsData = await _spotifyAnalyticsManager.GetPlaylistStreamDataAsync(playlistId, legacyPlaylistUri, startDate, endDate); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsGraphViewModel { Series = playlistStreamsData.GroupBy(p => p.Market).Select(p => new StreamsSeriesViewModel() { CountryCode = p.Key, MarketName = apps.GetValueOrDefault(p.Key), Entries = p.GroupBy(d=> d.Date).Select(e => new GraphEntry() { Date = e.Key, Value = e.Sum(t=> t.TotalStreams) }).OrderBy(e => e.Date).ToList() }).ToList(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, //CountryCode = null, //TODO: Do we need filtering? PlaylistId = playlistId, PlaylistUri = legacyPlaylistUri, } }; return Response.AsJson(viewModel); }; Get["PlaylistUsersGraph", "/playlistGraph/users", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); var legacyPlaylistUri = await spotifyPlaylistManager.GetLegacyUriAsync(playlistId); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var playlistStreamsData = await _spotifyAnalyticsManager.GetPlaylistStreamDataAsync(playlistId, legacyPlaylistUri, SpotifyAnalyticsAccount.Sony, startDate, endDate); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsGraphViewModel { Series = playlistStreamsData.GroupBy(p => p.Market ).Select(p => new StreamsSeriesViewModel() { CountryCode = p.Key, MarketName = apps.GetValueOrDefault(p.Key), Entries = p.GroupBy(d=> d.Date).Select(e => new GraphEntry() { Date = e.Key, Value = e.Sum(t=> t.UniqueUsers) }).OrderBy(e => e.Date).ToList() }).ToList(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, //CountryCode = null, //TODO: Do we need filtering? PlaylistUri = legacyPlaylistUri, } }; return Response.AsJson(viewModel); }; Get["PlaylistStreamsPerUsersGraph", "/playlistGraph/streamsPerUser", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); var legacyPlaylistUri = await spotifyPlaylistManager.GetLegacyUriAsync(playlistId); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var playlistStreamsData = await _spotifyAnalyticsManager.GetPlaylistStreamDataAsync(playlistId, legacyPlaylistUri, SpotifyAnalyticsAccount.Sony, startDate, endDate); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsPerUserGraphViewModel() { Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, //CountryCode = null, //TODO: Do we need filtering? PlaylistUri = legacyPlaylistUri, } }; var marketSeries = playlistStreamsData.GroupBy(p => p.Market).Select(p => new StreamsPerUserSeriesViewModel() { CountryCode = p.Key, MarketName = apps.GetValueOrDefault(p.Key), Entries = p.GroupBy(d => d.Date).Select(e => new GraphEntry() { Date = e.Key, Value = (double)e.Sum(t => t.TotalStreams) / (double)e.Sum(t => t.UniqueUsers) }).OrderBy(e => e.Date).ToList() }).ToList(); viewModel.Series.AddRange(marketSeries); var totalSeries = new StreamsPerUserSeriesViewModel() { CountryCode = "total", MarketName = "total", Entries = playlistStreamsData.GroupBy(d => d.Date).Select(e => new GraphEntry() { Date = e.Key, Value = (double)e.Sum(t => t.TotalStreams) / (double)e.Sum(t => t.UniqueUsers) }).OrderBy(e => e.Date).ToList() }; viewModel.Series.Add(totalSeries); var totalStreams = (double)playlistStreamsData.SumSafe(t => t.TotalStreams); var uniqueUsers = (double)playlistStreamsData.SumSafe(t => t.UniqueUsers); if (uniqueUsers != 0) { viewModel.TotalStreamsPerUser = totalStreams / uniqueUsers; } return Response.AsJson(viewModel); }; Get["PlaylistFollowersGraph", "/playlistGraph/followers", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; //We fetch one extra day so we can calculate the change from the previous value. var stats = await _editorialPlaylistStatsManager.GetPlaylistStatsAsync(new List { playlistId }, startDate.AddDays(-1), endDate, ignoreCache: true); stats = stats.OrderBy(p => p.Date).ToList(); var total = stats.Skip(1).Select(s => new GraphEntry() { Date = s.Date, Value = (int)s.Subscribers }).OrderBy(e => e.Date).ToList(); var change = BuildDailyChange(stats).ToList(); List series = new List(); series.Add(new FollowerSeriesViewModel { FollowerType = FollowerStatType.Total.ToString(), Entries = total }); series.Add(new FollowerSeriesViewModel { FollowerType = FollowerStatType.DailyChange.ToString(), Entries = change }); var viewModel = new FollowersGraphViewModel { Series = series, Filter = new FollowersFilter() { StartDate = startDate, EndDate = endDate, PlaylistId = playlistId, } }; return Response.AsJson(viewModel); }; Get["PlaylistBrowseGraph", "/playlistGraph/browse", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); var legacyPlaylistUri = await spotifyPlaylistManager.GetLegacyUriAsync(playlistId); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var playlistStats = await _spotifyBrowseManager.GetSpotifyBrowsePlaylistsAsync(legacyPlaylistUri, startDate, endDate); var uniqueRegions = playlistStats.Select(p => p.Market).Distinct().ToList(); var categories = await GetBrowseCategories(uniqueRegions); var viewModel = new BrowseGraphViewModel() { Series = playlistStats.GroupBy(p => new { p.Market, p.CategoryId}).Select(c => new BrowseSeriesViewModel() { CountryCode = c.Key.Market, CategoryId = c.Key.CategoryId, CategoryName = categories.FirstOrDefault(n=> n.CategoryId == c.Key.CategoryId && n.Region == c.Key.Market)?.CategoryName, Entries = c.Select(d => new GraphEntry() { Date = d.Date, Value = d.Position }).OrderBy(e => e.Date).ToList() }).ToList(), Filter = new BrowseFilter() { StartDate = startDate, EndDate = endDate, PlaylistId = playlistId, PlaylistUri = legacyPlaylistUri, } }; return Response.AsJson(viewModel); }; Get["PlaylistSonyPercentage", "/playlistGraph/sonyPercentage", true] = async (parameters, cx) => { var playlistId = Request.GetRequiredPlaylistId(); DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var percentages = await _historicTrackListManager.GetHistoricPlaylistTrackCount(playlistId, startDate, endDate); var viewModel = new SonyPercentageGraphViewModel() { Filter = new SonyPercentageFilter() { EndDate = endDate, StartDate = startDate, PlaylistId = playlistId }, SonyTracksSeries = percentages.Select(p => new TrackAmountSeriesViewModel() { CountryCode = p.Market, Entries = p.TrackCounts.Select(t => new GraphEntry() { Date = t.Date, Value = t.SonyTracks }).ToList() }).ToList(), TotalTracksSeries = percentages.Select(p => new TrackAmountSeriesViewModel() { CountryCode = p.Market, Entries = p.TrackCounts.Select(t => new GraphEntry() { Date = t.Date, Value = t.TotalTracks }).ToList() }).ToList(), SonyPercentageSeries = percentages.Select(p => new TrackPercentageSeriesViewModel() { CountryCode = p.Market, Entries = p.TrackCounts.Select(t => new GraphEntry() { Date = t.Date, Value = Math.Round(t.SonyTracks / (double)t.TotalTracks * 100, 2) }).ToList() }).ToList() }; return Response.AsJson(viewModel); }; Get["MarketStreamsGraph", "/{countryCode}/playlistGraph/marketStreams", true] = async (parameters, cx) => { DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var buzzCategoryId = GetBuzzCategoryFromRequest(Request); if (CurrentApplication == null) return HttpStatusCode.BadRequest; var market = CurrentApplication.SpotifyRegionCode; var marketStreams = await _spotifyAnalyticsManager.GetMarketPlaylistStreamsAsync(market, startDate, endDate, (int)buzzCategoryId); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsGraphViewModel { Series = new List(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, } }; var marketStreamsSeries = marketStreams.GroupBy(p => p.Market).Select(p => new StreamsSeriesViewModel() { CountryCode = p.Key, Entries = p.GroupBy(d=> d.Date).Select(m => new GraphEntry() { Date = m.Key.Date, Value = m.Sum(t=> t.LocalListStreams) }).ToList(), MarketName = apps.GetValueOrDefault(market) }); viewModel.Series.AddRange(marketStreamsSeries); var marketTotalSeries = new StreamsSeriesViewModel() { CountryCode = "total", MarketName = "total", Entries = marketStreams.GroupBy(d=> d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = m.Sum(t=> t.TotalListStreams) }).ToList(), }; viewModel.Series.Add(marketTotalSeries); return Response.AsJson(viewModel); }; Get["MarketUsersGraph", "/{countryCode}/playlistGraph/marketUsers", true] = async (parameters, cx) => { DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var buzzCategoryId = GetBuzzCategoryFromRequest(Request); if (CurrentApplication == null) return HttpStatusCode.BadRequest; var market = CurrentApplication.SpotifyRegionCode; var marketStreams = await _spotifyAnalyticsManager.GetMarketPlaylistStreamsAsync(SpotifyAnalyticsAccount.Sony, market, startDate, endDate, (int)buzzCategoryId); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsGraphViewModel { Series = new List(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, } }; var marketSeries = marketStreams.GroupBy(p => p.Market).Select(p => new StreamsSeriesViewModel() { CountryCode = p.Key, Entries = p.GroupBy(d=> d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = m.Sum(t=> t.LocalListListeners) }).ToList(), MarketName = apps.GetValueOrDefault(market) }); viewModel.Series.AddRange(marketSeries); var marketTotalSeries = new StreamsSeriesViewModel() { CountryCode = "total", MarketName = "total", Entries = marketStreams.GroupBy(d=> d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = m.Sum(t=> t.TotalListListeners) }).ToList(), }; viewModel.Series.Add(marketTotalSeries); return Response.AsJson(viewModel); }; Get["MarketStreamsPerUserGraph", "/{countryCode}/playlistGraph/marketStreamsPerUser", true] = async (parameters, cx) => { DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var buzzCategoryId = GetBuzzCategoryFromRequest(Request); if (CurrentApplication == null) return HttpStatusCode.BadRequest; var market = CurrentApplication.SpotifyRegionCode; var marketStreams = await _spotifyAnalyticsManager.GetMarketPlaylistStreamsAsync(SpotifyAnalyticsAccount.Sony, market, startDate, endDate, (int)buzzCategoryId); var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var viewModel = new StreamsPerUserGraphViewModel { Series = new List(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, } }; var marketSeries = marketStreams.GroupBy(p => p.Market).Select(p => new StreamsPerUserSeriesViewModel() { CountryCode = p.Key, Entries = p.GroupBy(d=> d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = CalculateStreamsPerListener(m) }).ToList(), MarketName = apps.GetValueOrDefault(market) }); viewModel.Series.AddRange(marketSeries); var totalSeries = new StreamsPerUserSeriesViewModel() { CountryCode = "total", MarketName = "total", Entries = marketStreams.GroupBy(d => d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = Math.Round(m.Sum(t => (long)t.TotalListStreams) / (double)m.Sum(t => (long)t.TotalListListeners), 2) }).ToList(), }; viewModel.Series.Add(totalSeries); var otherSeries = new StreamsPerUserSeriesViewModel() { CountryCode = "other", MarketName = "other", Entries = marketStreams.GroupBy(d => d.Date).Select(m => new GraphEntry() { Date = m.Key, Value = Math.Round((m.Sum(t => t.TotalListStreams) - m.Sum(t=> (long)t.LocalListStreams)) / (double)(m.Sum(t => (long)t.TotalListListeners) - m.Sum(t => (long)t.LocalListListeners)), 2) } ).ToList(), }; viewModel.Series.Add(otherSeries); if (marketStreams.Any()) { long totalListListeners = (long)marketStreams.Sum(t => (long)t.TotalListListeners); if (totalListListeners > 0) { viewModel.TotalStreamsPerUser = (long)marketStreams.Sum(t => (long)t.TotalListStreams) / totalListListeners; } } return Response.AsJson(viewModel); }; Get["MarketFollowersGraph", "/{countryCode}/playlistGraph/marketFollowers", true] = async (parameters, cx) => { DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var buzzCategoryId = GetBuzzCategoryFromRequest(Request); if (CurrentApplication == null) return HttpStatusCode.BadRequest; var market = CurrentApplication.SpotifyRegionCode; var playlists = await _spotifyPlaylistManager.GetPlaylistsForMarketAsync(market, (int)buzzCategoryId); var playlistLinks = playlists.Select(p => new SpotifyLink(p.Playlist.PlaylistUri)).ToList(); var stats = await _editorialPlaylistStatsManager.GetCombinedEditorialPlaylistSubscriptionStatisticAsync(playlistLinks, startDate.AddDays(-1), endDate); var total = stats.Skip(1).Select(s => new GraphEntry() { Date = s.Key, Value = (int)s.Value }).OrderBy(e => e.Date).ToList(); var change = BuildDailyChange(stats).ToList(); List series = new List(); series.Add(new FollowerSeriesViewModel { FollowerType = FollowerStatType.Total.ToString(), Entries = total }); series.Add(new FollowerSeriesViewModel { FollowerType = FollowerStatType.DailyChange.ToString(), Entries = change }); var viewModel = new FollowersGraphViewModel { Series = series, Filter = new FollowersFilter() { StartDate = startDate, EndDate = endDate } }; return Response.AsJson(viewModel); }; Get["MarketFiltrStreamsPercentage", "/{countryCode}/playlistGraph/marketFiltrStreamsPercentage", true] = async (parameters, cx) => { DateTime startDate = GetDate((string)Request.Query.startDate) ?? DateTime.Today.AddDays(-30); DateTime endDate = GetDate((string)Request.Query.endDate) ?? DateTime.Today; var buzzCategoryId = GetBuzzCategoryFromRequest(Request); if (CurrentApplication == null) return HttpStatusCode.BadRequest; var market = CurrentApplication.SpotifyRegionCode; var apps = applicationInstanceManager.GetApplications().DistinctBy(a => a.SpotifyRegionCode).ToDictionary(a => a.SpotifyRegionCode, b => b.Name); var marketStreams = await _spotifyAnalyticsManager.GetMarketPlaylistStreamsAsync(SpotifyAnalyticsAccount.Sony, market, startDate, endDate, (int)buzzCategoryId); var streamSummary = spotifyAnalyticsManager.GetStreamSummary(SpotifyAnalyticsAccount.Sony, startDate, endDate); var viewModel = new StreamsPercentageGraphViewModel { Series = new List(), Filter = new StreamsFilter() { StartDate = startDate, EndDate = endDate, } }; StreamPercentageSeriesViewModel marketSeries = new StreamPercentageSeriesViewModel() { CountryCode = market, MarketName = apps.GetValueOrDefault(market), Entries = new List>() }; foreach(var marketStreamsDate in marketStreams.GroupBy(p=> p.Date)) { int? sourcePlaylist = null; int? totalStreams = null; int localListStreams = 0; int totalListStreams = 0; if (market.Equals("_gl", StringComparison.InvariantCultureIgnoreCase)) { var globalSonyStreamSummaries = streamSummary.Where(p => p.Date == marketStreamsDate.Key).ToList(); if (globalSonyStreamSummaries.Any()) { sourcePlaylist = globalSonyStreamSummaries.Sum(t => t.SourcePlaylist); totalStreams = globalSonyStreamSummaries.Sum(t => t.TotalStreams); } localListStreams = marketStreamsDate.Sum(t=> t.LocalListStreams); totalListStreams = marketStreamsDate.Sum(t => t.TotalListStreams); } else { var localSonyStreamSummaries = streamSummary.FirstOrDefault(p => p.Date == marketStreamsDate.Key && p.Market == market); sourcePlaylist = localSonyStreamSummaries?.SourcePlaylist; totalStreams = localSonyStreamSummaries?.TotalStreams; var localMarketStreams = marketStreamsDate.FirstOrDefault(); if (localMarketStreams != null) { localListStreams = localMarketStreams.LocalListStreams; totalListStreams = localMarketStreams.TotalListStreams; } } if (sourcePlaylist != null && totalStreams != null && sourcePlaylist.Value > 0 && totalStreams.Value > 0) { marketSeries.Entries.Add(new GraphEntry() { Date = marketStreamsDate.Key, Value = new StreamPercentage() { TotalStreams = totalStreams.Value, PlaylistSourceStreams = sourcePlaylist.Value, LocalListStreams = localListStreams, TotalListStreams = totalListStreams, LocalListStreamsOfPlaylistStreams = 100* (1.0 * localListStreams / sourcePlaylist) ?? 0, TotalListStreamsOfPlaylistStreams = 100 * (1.0 * totalListStreams / sourcePlaylist) ?? 0, LocalListStreamsOfTotalStreams = 100 * (1.0 * localListStreams / totalStreams) ?? 0, TotalListStreamsOfTotalStreams = 100 * (1.0 * totalListStreams / totalStreams) ?? 0, } }); } } if (marketSeries.Entries.Any()) { viewModel.Series.Add(marketSeries); } var sumTotalListStreams = viewModel.Series.SelectMany(p => p.Entries).Sum(p => p.Value.TotalListStreams); var sumLocalListStreams = viewModel.Series.SelectMany(p => p.Entries).Sum(p => p.Value.LocalListStreams); var sumTotalStreams = viewModel.Series.SelectMany(p => p.Entries).Sum(p => p.Value.TotalStreams); if (sumTotalStreams > 0) { viewModel.LocalFiltrStreamsPercentageOfAllStreams = 100 * (1.0 * sumLocalListStreams / sumTotalStreams); viewModel.TotalFiltrStreamsPercentageOfAllStreams = 100 * (1.0 * sumTotalListStreams / sumTotalStreams); } return Response.AsJson(viewModel); }; } private StaticBuzzCategory GetBuzzCategoryFromRequest(Request request) { var buzzCategoryId = Maybe.ToEnumOrDefault((string)request.Query.buzzCategoryId) ?? StaticBuzzCategory.SonyMusic; return buzzCategoryId; } private double CalculateStreamsPerListener(IGrouping marketPlaylistCategoryStreamSummaries) { if (marketPlaylistCategoryStreamSummaries.Sum(t=> t.LocalListListeners) == 0) return 0; return Math.Round(marketPlaylistCategoryStreamSummaries.Sum(t=> t.LocalListStreams) / (double)marketPlaylistCategoryStreamSummaries.Sum(t=> t.LocalListListeners), 2); } private async Task> GetBrowseCategories(List regions) { List allCategories = new List(); foreach(var region in regions) { var regionCategories = await _spotifyBrowseManager.GetSpotifyBrowseCategoriesAsync(region); allCategories.AddRange(regionCategories); } return allCategories; } private IEnumerable> BuildDailyChange(Dictionary stats) { return stats.OrderBy(e => e.Key).SelectWithPrevious((previous, current) => { if (previous.Key.Date == current.Key.Date.AddDays(-1)) { return new GraphEntry() { Date = current.Key, Value = (int)current.Value - (int)previous.Value }; } return null; }).Where(s => s != null); } private IEnumerable> BuildDailyChange(List stats) { return stats.OrderBy(e => e.Date).SelectWithPrevious((previous, current) => { if (previous.Date.Date == current.Date.Date.AddDays(-1)) { return new GraphEntry() { Date = current.Date, Value = (int)current.Subscribers - (int)previous.Subscribers }; } return null; }).Where(s=> s != null); } private DateTime? GetDate(string startDate) { DateTime tempdate; if (DateTime.TryParse(startDate, out tempdate)) return tempdate; return null; } } }