private List <ApiPlayerBase> GetApiPlayerBases(Feeds.FixtureFeed.ApiFixture feedFixture) { List <ApiPlayerBase> apiPlayerBases = null; // USE LINEUPS AS BASE IF POSSIBLE, INCLUDING MAPPING FROM BOXSCORE BY JERSEY NUMBER AND TEAM if (feedFixture.Lineups != null && feedFixture.Lineups.Count > 0) { if (feedFixture.PlayerBoxscores != null && feedFixture.PlayerBoxscores.Count > 0) { // ALL PLAYERS SHOULD BE IN LINEUP. THERE SHOULD BE A MATCH FROM LINEUP TO BOXSCORE. VERIFY MATCHES var apiBoxscorePlayerMapping = from apiLineupPlayer in feedFixture.AllLineupPlayers join apiBoxscorePlayer in feedFixture.PlayerBoxscores on new { apiLineupPlayer.TeamId, Number = apiLineupPlayer.Number } equals new { apiBoxscorePlayer.TeamId, Number = (int?)apiBoxscorePlayer.Number } into pbx from pbx2 in pbx.DefaultIfEmpty() // pbx2 IS PLAYER BOXSCORES BUT WITH NULL VALUES IF NO MATCH (FORCE LEFT JOIN INSTEAD OF INNER JOIN) select new { TeamId = apiLineupPlayer.TeamId, PlayerName = apiLineupPlayer.PlayerName, JerseyNumber = apiLineupPlayer.Number, LineupPlayerId = apiLineupPlayer.PlayerId, BoxscorePlayerId = pbx2?.PlayerId, IsStarter = apiLineupPlayer.IsStarter }; apiPlayerBases = apiBoxscorePlayerMapping .Where(x => x.LineupPlayerId.HasValue) .Select(x => new ApiPlayerBase(x.LineupPlayerId.Value, x.TeamId, x.PlayerName, x.JerseyNumber, x.LineupPlayerId, x.BoxscorePlayerId, x.IsStarter)).ToList(); } else { apiPlayerBases = feedFixture.AllLineupPlayers? .Where(x => x.PlayerId.HasValue) .Select(x => new ApiPlayerBase(x.PlayerId.Value, x.TeamId, x.PlayerName, x.Number, x.PlayerId.Value, null, x.IsStarter)).ToList(); } } else if (feedFixture.PlayerBoxscores != null && feedFixture.PlayerBoxscores.Count > 0) { apiPlayerBases = feedFixture.PlayerBoxscores.Select(x => new ApiPlayerBase(x.PlayerId.Value, x.TeamId, x.PlayerName, x.Number, null, x.PlayerId, !(x.IsSubstitute ?? true))).ToList(); } if (apiPlayerBases == null || apiPlayerBases.Count == 0) { return(null); } if (apiPlayerBases.GroupBy(x => x.PlayerId).Any(y => y.Count() > 1)) { var groupedApiPlayerBases = apiPlayerBases.GroupBy(x => x.PlayerId); apiPlayerBases = new List <ApiPlayerBase>(); foreach (var groupedApiPlayerBase in groupedApiPlayerBases) { if (groupedApiPlayerBase.Count() == 1) { apiPlayerBases.Add(groupedApiPlayerBase.First()); } else { apiPlayerBases.Add(groupedApiPlayerBase.OrderBy(y => y.BoxscorePlayerId.HasValue ? 0 : 1).ThenBy(y => y.IsStarter ? 0 : 1).First()); } } } return(apiPlayerBases); }
public void Run(SoccerDataContext dbContext) { var dbFixture = dbContext.Fixtures.Single(x => x.ApiFootballId == this.ApiFootballFixtureId); var isFixtureFinal = string.Equals("Match Finished", dbFixture.Status, StringComparison.CurrentCultureIgnoreCase); if (!dbFixture.HomeTeamSeasonId.HasValue || !dbFixture.AwayTeamSeasonId.HasValue || !isFixtureFinal) { return; } var url = Feeds.FixtureFeed.GetFeedUrlByFixtureId(this.ApiFootballFixtureId); var rawJson = JsonUtility.GetRawJsonFromUrl(url); var feed = Feeds.FixtureFeed.FromJson(rawJson); Feeds.FixtureFeed.ApiFixture feedFixture = feed.Result.Fixtures.SingleOrDefault(); if (feedFixture == null) { return; } int dbFixtureId = dbFixture.FixtureId; int dbHomeTeamSeasonId = dbFixture.HomeTeamSeasonId.Value; int dbAwayTeamSeasonId = dbFixture.AwayTeamSeasonId.Value; int apiAwayTeamId = feedFixture.AwayTeam.TeamId; int apiHomeTeamId = feedFixture.HomeTeam.TeamId; int?homeCoachId = null; int?awayCoachId = null; var apiPlayerBases = GetApiPlayerBases(feedFixture); var dbPlayerSeasonDict = GetDbPlayerSeasonDict(dbContext, apiPlayerBases, dbFixture.CompetitionSeasonId); bool hasUpdate = false; Feeds.FixtureFeed.ApiLineup homeLineup = null; Feeds.FixtureFeed.ApiLineup awayLineup = null; #region GET FORMATIONS string homeFormation = null; string awayFormation = null; if (feedFixture.Lineups != null && feedFixture.Lineups.Count == 2) { string homeTeamName = feedFixture.HomeTeam.TeamName; string awayTeamName = feedFixture.AwayTeam.TeamName; // MISMATCH BETWEEN PLAYING TEAM NAMES AND LINEUP DICT KEYS HAS OCCURRED (API fixtureID: 188155) bool hasHomeTeamName = feedFixture.Lineups.Any(x => string.Equals(x.Key, homeTeamName, StringComparison.InvariantCultureIgnoreCase)); bool hasAwayTeamName = feedFixture.Lineups.Any(x => string.Equals(x.Key, awayTeamName, StringComparison.InvariantCultureIgnoreCase)); if (!hasHomeTeamName || !hasAwayTeamName) { if (hasHomeTeamName && !hasAwayTeamName) { awayTeamName = feedFixture.Lineups.Keys.Single(x => !string.Equals(x, homeTeamName, StringComparison.InvariantCultureIgnoreCase)); } else if (!hasHomeTeamName && hasAwayTeamName) { homeTeamName = feedFixture.Lineups.Keys.Single(x => !string.Equals(x, awayTeamName, StringComparison.InvariantCultureIgnoreCase)); } else { throw new KeyNotFoundException("INVALID KEYS FOUND FOR FIXTURE LINEUPS"); } } homeLineup = feedFixture.Lineups.Single(x => string.Equals(x.Key, homeTeamName, StringComparison.InvariantCultureIgnoreCase)).Value; awayLineup = feedFixture.Lineups.Single(x => string.Equals(x.Key, awayTeamName, StringComparison.InvariantCultureIgnoreCase)).Value; homeFormation = homeLineup.Formation; awayFormation = awayLineup.Formation; } #endregion GET FORMATIONS #region ENSURE COACHES EXIST if (this.CheckEntitiesExist) { if (homeLineup != null || awayLineup != null) { var apiCoachIds = new[] { homeLineup.CoachId, awayLineup.CoachId }; var dbCoaches = dbContext.Coaches.Where(x => apiCoachIds.Contains(x.ApiFootballId)).ToDictionary(x => x.ApiFootballId, y => y); if (homeLineup?.CoachId != null) { if (!dbCoaches.TryGetValue(homeLineup.CoachId.Value, out Coach dbHomeCoach)) { dbHomeCoach = new Coach { ApiFootballId = homeLineup.CoachId.Value, CoachName = homeLineup.Coach }; dbContext.Coaches.Add(dbHomeCoach); dbContext.SaveChanges(); dbCoaches.Add(dbHomeCoach.CoachId, dbHomeCoach); // DUE TO BAD DATA, HOME COACH AND AWAY COACH MAY BE THE SAME (API GAME 126635) } homeCoachId = dbHomeCoach.CoachId; } if (awayLineup?.CoachId != null) { if (!dbCoaches.TryGetValue(awayLineup.CoachId.Value, out Coach dbAwayCoach)) { dbAwayCoach = new Coach { ApiFootballId = awayLineup.CoachId.Value, CoachName = awayLineup.Coach }; dbContext.Coaches.Add(dbAwayCoach); dbContext.SaveChanges(); } awayCoachId = dbAwayCoach.CoachId; } } } #endregion ENSURE COACHES EXIST #region ENSURE PLAYERS EXIST if (this.CheckEntitiesExist) { var missingApiPlayerIds = apiPlayerBases?.Select(x => x.PlayerId).Where(x => !dbPlayerSeasonDict.ContainsKey(x)).ToList(); if (missingApiPlayerIds != null && missingApiPlayerIds.Count > 0) { foreach (var missingApiPlayerId in missingApiPlayerIds) { var apiPlayerBase = apiPlayerBases.Single(x => x.PlayerId == missingApiPlayerId); var dbPlayer = dbContext.Players.SingleOrDefault(x => x.ApiFootballId == missingApiPlayerId); if (dbPlayer == null) { dbPlayer = new Player { ApiFootballId = missingApiPlayerId, ApiFootballName = apiPlayerBase.PlayerName, PlayerName = apiPlayerBase.PlayerName }; } var dbPlayerSeason = new PlayerSeason { Player = dbPlayer, CompetitionSeasonId = dbFixture.CompetitionSeasonId }; dbContext.Add(dbPlayerSeason); } dbContext.SaveChanges(); dbPlayerSeasonDict = GetDbPlayerSeasonDict(dbContext, apiPlayerBases, dbFixture.CompetitionSeasonId); } } #endregion ENSURE PLAYERS EXIST #region UPDATE FORAMATION AND COACH IF NECESSARY if (homeCoachId.HasValue && dbFixture.HomeCoachId != homeCoachId) { dbFixture.HomeCoachId = homeCoachId; hasUpdate = true; } if (awayCoachId.HasValue && dbFixture.AwayCoachId != awayCoachId) { dbFixture.AwayCoachId = awayCoachId; hasUpdate = true; } if (!string.IsNullOrEmpty(homeFormation) && dbFixture.HomeFormation != homeFormation) { dbFixture.HomeFormation = homeFormation; hasUpdate = true; } if (!string.IsNullOrEmpty(awayFormation) && dbFixture.AwayFormation != awayFormation) { dbFixture.AwayFormation = awayFormation; hasUpdate = true; } #endregion UPDATE FORAMATION AND COACH IF NECESSARY #region FIXTURE EVENTS // HAVE EACH dbFixtureEvent AVAILABLE. ILookup IS AN IMMUTABLE TYPE, SO A DICTIONARY WITH THE COUNT IS ALSO NEEDED TO TRACK THE NUMBER OF OCCURANCES OF EACH EVENT. // THE ILookup IS JUST TO FIND FIND THE DB REFERENCE FOR EACH EVENT TO MANIPULATE var dbFixtureEventLookup = dbContext.FixtureEvents.Where(x => x.FixtureId == dbFixtureId).ToLookup(x => GetFixtureEventKey(x)); var dbFixtureEventToDeleteCountDict = dbContext.FixtureEvents.Where(x => x.FixtureId == dbFixtureId).ToList().GroupBy(x => GetFixtureEventKey(x)).ToDictionary(x => x.Key, y => y.Count()); var apiFixtureEvents = feedFixture.Events?.Where(x => x.TeamId.HasValue).ToList(); if (apiFixtureEvents != null && apiFixtureEvents.Count > 0) { foreach (var apiFixtureEvent in apiFixtureEvents) { int dbTeamSeasonId = apiFixtureEvent.TeamId == apiAwayTeamId ? dbAwayTeamSeasonId : dbHomeTeamSeasonId; int?dbPlayerSeasonId = null; if (dbPlayerSeasonDict != null && apiFixtureEvent.PlayerId.HasValue && dbPlayerSeasonDict.TryGetValue(apiFixtureEvent.PlayerId.Value, out int intPlayerSeasonId)) { dbPlayerSeasonId = intPlayerSeasonId; } int?dbSecondaryPlayerSeasonId = null; if (dbPlayerSeasonDict != null && apiFixtureEvent.SecondaryPlayerId.HasValue && dbPlayerSeasonDict.TryGetValue(apiFixtureEvent.SecondaryPlayerId.Value, out intPlayerSeasonId)) { dbSecondaryPlayerSeasonId = intPlayerSeasonId; } // IT IS POSSIBLE TO HAVE MULTIPLE IDENTICAL EVENTS IN THE SAME MINUTE // API FIXTURE ID 185030 - 2 GOALS BY SAME PLAYER IN SAME MINUTE // USE LOOKUP TO DETERMINE CORRECT AMOUNT OF EXISTENCE var eventKey = GetFixtureEventKey(apiFixtureEvent.Elapsed, apiFixtureEvent.ElapsedPlus, dbPlayerSeasonId, dbTeamSeasonId, apiFixtureEvent.EventType, apiFixtureEvent.EventDetail); var dbCount = dbFixtureEventToDeleteCountDict.TryGetValue(eventKey, out int tempInt) ? tempInt : 0; FixtureEvent dbFixtureEvent; if (dbCount == 0) { dbFixtureEvent = new FixtureEvent { EventComment = apiFixtureEvent.EventComments, EventDetail = apiFixtureEvent.EventDetail, EventType = apiFixtureEvent.EventType, FixtureId = dbFixtureId, EventTime = apiFixtureEvent.Elapsed, EventTimePlus = apiFixtureEvent.ElapsedPlus, PlayerSeasonId = dbPlayerSeasonId, SecondaryPlayerSeasonId = dbSecondaryPlayerSeasonId, TeamSeasonId = dbTeamSeasonId }; dbContext.FixtureEvents.Add(dbFixtureEvent); hasUpdate = true; } else { dbFixtureEvent = dbFixtureEventLookup[eventKey].Skip(dbCount - 1).First(); // TAKE LAST ENTRY IN LOOKUP. AS THE COUNT IN THE dbFixtureEventCount DICTIONARY IS DECREMENTED, THE SELECTED EVENT WILL MOVE DOWN THE LIST if (dbCount == 1) { dbFixtureEventToDeleteCountDict.Remove(eventKey); } else { dbFixtureEventToDeleteCountDict[eventKey] = dbCount - 1; } if ((!string.IsNullOrEmpty(apiFixtureEvent.EventComments) && dbFixtureEvent.EventComment != apiFixtureEvent.EventComments) || (!string.IsNullOrEmpty(apiFixtureEvent.EventDetail) && dbFixtureEvent.EventDetail != apiFixtureEvent.EventDetail) || (dbSecondaryPlayerSeasonId.HasValue && (!dbFixtureEvent.SecondaryPlayerSeasonId.HasValue || dbFixtureEvent.SecondaryPlayerSeasonId != dbSecondaryPlayerSeasonId)) || (!dbSecondaryPlayerSeasonId.HasValue && dbFixtureEvent.SecondaryPlayerSeasonId.HasValue)) { dbFixtureEvent.EventComment = apiFixtureEvent.EventComments; dbFixtureEvent.EventDetail = apiFixtureEvent.EventDetail; dbFixtureEvent.SecondaryPlayerSeasonId = dbSecondaryPlayerSeasonId; hasUpdate = true; } } } if (dbFixtureEventToDeleteCountDict.Count > 0) { foreach (var dbFixtureEventCountEntry in dbFixtureEventToDeleteCountDict) { var dbFixtureEventLookupEntry = dbFixtureEventLookup[dbFixtureEventCountEntry.Key]; int dbFixtureEventCount = dbFixtureEventLookupEntry.Count(); if (dbFixtureEventCount >= 1) { for (int i = dbFixtureEventCount; i >= 1; i--) { var dbFixtureEvent = dbFixtureEventLookupEntry.Skip(i - 1).First(); dbContext.FixtureEvents.Remove(dbFixtureEvent); } } } hasUpdate = true; } } #endregion FIXTURE EVENTS #region TEAM BOXSCORE var apiTeamStatsDict = feedFixture.TeamStatistics; if (apiTeamStatsDict == null) { if (!dbFixture.HasTeamBoxscores.HasValue || dbFixture.HasTeamBoxscores.Value) { hasUpdate = true; } dbFixture.HasTeamBoxscores = false; } else { var dbTeamBoxscores = dbContext.TeamBoxscores.Where(x => x.FixtureId == dbFixtureId); var dbHomeBoxscore = dbTeamBoxscores?.SingleOrDefault(x => x.TeamSeasonId == dbHomeTeamSeasonId); var dbAwayBoxscore = dbTeamBoxscores?.SingleOrDefault(x => x.TeamSeasonId == dbAwayTeamSeasonId); if (dbHomeBoxscore == null) { dbHomeBoxscore = new TeamBoxscore { FixtureId = dbFixtureId, TeamSeasonId = dbHomeTeamSeasonId, OppTeamSeasonId = dbAwayTeamSeasonId, IsHome = true }; dbContext.TeamBoxscores.Add(dbHomeBoxscore); hasUpdate = true; } if (dbAwayBoxscore == null) { dbAwayBoxscore = new TeamBoxscore { FixtureId = dbFixtureId, TeamSeasonId = dbAwayTeamSeasonId, OppTeamSeasonId = dbHomeTeamSeasonId, IsHome = false, }; dbContext.TeamBoxscores.Add(dbAwayBoxscore); hasUpdate = true; } if (PopulateTeamBoxscore(apiTeamStatsDict, x => x.Home, ref dbHomeBoxscore)) { hasUpdate = true; dbFixture.HasTeamBoxscores = true; } if (PopulateTeamBoxscore(apiTeamStatsDict, x => x.Away, ref dbAwayBoxscore)) { hasUpdate = true; dbFixture.HasTeamBoxscores = true; } if (!dbFixture.HasTeamBoxscores.HasValue) { dbFixture.HasTeamBoxscores = false; } } #endregion TEAM BOXSCORE #region PLAYER BOXSCORE if (apiPlayerBases != null && apiPlayerBases.Count > 0) { var dbPlayerBoxscores = dbContext.PlayerBoxscores .Include(x => x.PlayerSeason) .ThenInclude(y => y.Player) .Where(x => x.FixtureId == dbFixtureId && x.PlayerSeason != null && x.PlayerSeason.Player != null) .ToDictionary(x => x.PlayerSeason.Player.ApiFootballId, y => y); bool hasApiPlayerBoxscores = feedFixture?.PlayerBoxscores != null; bool hasApiLineups = feedFixture?.AllLineupPlayers != null; foreach (var apiPlayerBase in apiPlayerBases) { var dbPlayerSeasonId = dbPlayerSeasonDict[apiPlayerBase.PlayerId]; if (!dbPlayerBoxscores.TryGetValue(apiPlayerBase.PlayerId, out PlayerBoxscore dbPlayerBoxscore)) { dbPlayerBoxscore = new PlayerBoxscore { PlayerSeasonId = dbPlayerSeasonId, IsStarter = apiPlayerBase.IsStarter, FixtureId = dbFixtureId, TeamSeasonId = apiPlayerBase.TeamId == feedFixture.HomeTeam.TeamId ? dbHomeTeamSeasonId : dbAwayTeamSeasonId }; dbContext.PlayerBoxscores.Add(dbPlayerBoxscore); hasUpdate = true; } if (hasApiPlayerBoxscores || hasApiLineups) { Feeds.FixtureFeed.ApiPlayerBoxscore apiPlayerBoxscore = null; if (apiPlayerBase.BoxscorePlayerId.HasValue && apiPlayerBase.JerseyNumber.HasValue) { apiPlayerBoxscore = feedFixture.PlayerBoxscores.Where(x => x.PlayerId.HasValue).FirstOrDefault(x => x.Number == apiPlayerBase.JerseyNumber && x.TeamId == apiPlayerBase.TeamId); } Feeds.FixtureFeed.ApiLineupPlayerWithStarterStatus apiPlayerLineup = null; if (apiPlayerBase.LineupPlayerId.HasValue && apiPlayerBase.JerseyNumber.HasValue) { apiPlayerLineup = feedFixture.AllLineupPlayers.Where(x => x.PlayerId.HasValue).FirstOrDefault(x => x.Number == apiPlayerBase.JerseyNumber && x.TeamId == apiPlayerBase.TeamId); } if (apiPlayerBoxscore != null || apiPlayerLineup != null) { if (PopulatePlayerBoxscore(apiPlayerBoxscore, apiPlayerLineup, ref dbPlayerBoxscore)) { hasUpdate = true; } } } } } #endregion PLAYER BOXSCORE if (hasUpdate) { dbContext.SaveChanges(); } }