<commit_before>﻿using Bossr.Api.Mappers;
using Bossr.Api.Repositories;
using Bossr.Lib.Models.Entities;
using NodaTime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Bossr.Api.Services
{
    public interface IStateCalculator
    {
        Task<IEnumerable<StateDto>> GetStatesByWorldId(int worldId);
    }

    public class StateCalculator : IStateCalculator
    {
        private readonly IStatisticRepository statisticRepository;
        private readonly IScrapeRepository scrapeRepository;
        private readonly IRaidRepository raidRepository;
        private readonly ISpawnRepository spawnRepository;
        private readonly ICreatureRepository creatureRepository;
        private readonly IPositionRepository positionRepository;
        private readonly IRaidMapper raidMapper;
        private readonly IScrapeMapper scrapeMapper;

        public StateCalculator(
            IStatisticRepository statisticRepository,
            IScrapeRepository scrapeRepository,
            IRaidRepository raidRepository,
            ISpawnRepository spawnRepository,
            ICreatureRepository creatureRepository,
            IPositionRepository positionRepository,
            IRaidMapper raidMapper,
            IScrapeMapper scrapeMapper)
        {
            this.statisticRepository = statisticRepository;
            this.scrapeRepository = scrapeRepository;
            this.raidRepository = raidRepository;
            this.spawnRepository = spawnRepository;
            this.creatureRepository = creatureRepository;
            this.positionRepository = positionRepository;
            this.raidMapper = raidMapper;
            this.scrapeMapper = scrapeMapper;
        }

        public async Task<IEnumerable<StateDto>> GetStatesByWorldId(int worldId)
        {
            var raids = await raidRepository.ReadAllAsync();
            var spawns = await spawnRepository.ReadAllAsync();
            var creatures = await creatureRepository.ReadAllAsync();
            var positions = await positionRepository.ReadAllAsync();

            raidMapper.MapRelations(raids, spawns, creatures, positions);

            var statistics = (await statisticRepository.ReadAllByWorldIdAsync(worldId)) // Todo: Only get latest X stats (X = amount of spawnpoints)
                .ToList();

            var scrapes = (await scrapeRepository.ReadAllAsync())
                .OrderByDescending(x => x.Date)
                .ToList();

            scrapeMapper.MapRelations(scrapes, statistics);

            var currentTimeUtc = DateTime.UtcNow;
            var states = new List<StateDto>();
            foreach (var raid in raids)
            {
                var spawn = raid.Spawns.First();
                var totalAmount = GetTotalAmountOfSpawnsByCreature(spawns, spawn.CreatureId);

                var latestOccurances = GetAllOccurances(scrapes, spawn)
                    .Take(totalAmount)
                    .ToList();

                if (!latestOccurances.Any())
                    continue;

                var expectedMin = GetMin(latestOccurances.Min(x => x.Date)).Plus(raid.FrequencyMin);
                var expectedMax = GetMax(latestOccurances.Max(x => x.Date)).Plus(raid.FrequencyMax);

                var missedRaids = 0;
                var currentInstant = Instant.FromDateTimeUtc(currentTimeUtc);
                while (currentInstant > expectedMax)
                {
                    expectedMin = expectedMin.Plus(raid.FrequencyMin);
                    expectedMax = expectedMax.Plus(raid.FrequencyMax);
                    missedRaids++;
                }

                var state = CreateState(raid, expectedMin, expectedMax, missedRaids);
                states.Add(state);
            }

            return states;
        }

        private StateDto CreateState(IRaid raid, Instant expectedMin, Instant expectedMax, int missedRaids)
        {
            return new StateDto
            {
                Raid = raidMapper.MapToRaidDto(raid),
                ExpectedMin = expectedMin.ToDateTimeUtc(),
                ExpectedMax = expectedMax.ToDateTimeUtc(),
                MissedRaids = missedRaids
            };
        }

        private int GetTotalAmountOfSpawnsByCreature(IEnumerable<ISpawn> spawns, int creatureId)
        {
            return spawns
                .Where(x => x.CreatureId == creatureId)
                .Sum(x => x.Amount);
        }

        private IEnumerable<IScrape> GetAllOccurances(IEnumerable<IScrape> scrapes, ISpawn spawn)
        {
            return scrapes.Where(x => x.Statistics.Any(y => y.CreatureId == spawn.CreatureId));
        }

        private Instant GetMin(LocalDate date)
        {
            return Instant
                .FromDateTimeUtc(DateTime.SpecifyKind(date.ToDateTimeUnspecified(), DateTimeKind.Utc))
                .Plus(Duration.FromHours(2));
        }

        private Instant GetMax(LocalDate date)
        {
            return Instant
                .FromDateTimeUtc(DateTime.SpecifyKind(date.ToDateTimeUnspecified(), DateTimeKind.Utc))
                .Plus(Duration.FromDays(1))
                .Plus(Duration.FromHours(2));
        }
    }
}
<commit_msg>Updated calculator to count the amount of raids containing spawns with certain creature instead of counting all spawns<commit_after>﻿using Bossr.Api.Mappers;
using Bossr.Api.Repositories;
using Bossr.Lib.Models.Entities;
using NodaTime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Bossr.Api.Services
{
    public interface IStateCalculator
    {
        Task<IEnumerable<StateDto>> GetStatesByWorldId(int worldId);
    }

    public class StateCalculator : IStateCalculator
    {
        private readonly IStatisticRepository statisticRepository;
        private readonly IScrapeRepository scrapeRepository;
        private readonly IRaidRepository raidRepository;
        private readonly ISpawnRepository spawnRepository;
        private readonly ICreatureRepository creatureRepository;
        private readonly IPositionRepository positionRepository;
        private readonly IRaidMapper raidMapper;
        private readonly IScrapeMapper scrapeMapper;

        public StateCalculator(
            IStatisticRepository statisticRepository,
            IScrapeRepository scrapeRepository,
            IRaidRepository raidRepository,
            ISpawnRepository spawnRepository,
            ICreatureRepository creatureRepository,
            IPositionRepository positionRepository,
            IRaidMapper raidMapper,
            IScrapeMapper scrapeMapper)
        {
            this.statisticRepository = statisticRepository;
            this.scrapeRepository = scrapeRepository;
            this.raidRepository = raidRepository;
            this.spawnRepository = spawnRepository;
            this.creatureRepository = creatureRepository;
            this.positionRepository = positionRepository;
            this.raidMapper = raidMapper;
            this.scrapeMapper = scrapeMapper;
        }

        public async Task<IEnumerable<StateDto>> GetStatesByWorldId(int worldId)
        {
            var raids = await raidRepository.ReadAllAsync();
            var spawns = await spawnRepository.ReadAllAsync();
            var creatures = await creatureRepository.ReadAllAsync();
            var positions = await positionRepository.ReadAllAsync();

            raidMapper.MapRelations(raids, spawns, creatures, positions);

            var statistics = (await statisticRepository.ReadAllByWorldIdAsync(worldId)) // Todo: Only get latest X stats (X = amount of spawnpoints)
                .ToList();

            var scrapes = (await scrapeRepository.ReadAllAsync())
                .OrderByDescending(x => x.Date)
                .ToList();

            scrapeMapper.MapRelations(scrapes, statistics);

            var currentTimeUtc = DateTime.UtcNow;
            var states = new List<StateDto>();
            foreach (var raid in raids)
            {
                var spawn = raid.Spawns.First();
                var totalAmount = GetTotalAmountOfRaidsByCreature(raids, spawn);

                var latestOccurances = GetAllOccurances(scrapes, spawn)
                    .Take(totalAmount)
                    .ToList();

                if (!latestOccurances.Any())
                    continue;

                var expectedMin = GetExpectedMin(latestOccurances.Min(x => x.Date)).Plus(raid.FrequencyMin);
                var expectedMax = GetExpectedMax(latestOccurances.Max(x => x.Date)).Plus(raid.FrequencyMax);

                var missedRaids = 0;
                var currentInstant = Instant.FromDateTimeUtc(currentTimeUtc);
                while (currentInstant > expectedMax)
                {
                    expectedMin = expectedMin.Plus(raid.FrequencyMin);
                    expectedMax = expectedMax.Plus(raid.FrequencyMax);
                    missedRaids++;
                }

                var state = CreateState(raid, expectedMin, expectedMax, missedRaids);
                states.Add(state);
            }

            return states;
        }

        private StateDto CreateState(IRaid raid, Instant expectedMin, Instant expectedMax, int missedRaids)
        {
            return new StateDto
            {
                Raid = raidMapper.MapToRaidDto(raid),
                ExpectedMin = expectedMin.ToDateTimeUtc(),
                ExpectedMax = expectedMax.ToDateTimeUtc(),
                MissedRaids = missedRaids
            };
        }

        private int GetTotalAmountOfRaidsByCreature(IEnumerable<IRaid> raids, ISpawn spawn)
        {
            return raids
                .Where(x => x.Spawns.Any(y => y.CreatureId == spawn.CreatureId))
                .Count();
        }

        private IEnumerable<IScrape> GetAllOccurances(IEnumerable<IScrape> scrapes, ISpawn spawn)
        {
            return scrapes.Where(x => x.Statistics.Any(y => y.CreatureId == spawn.CreatureId));
        }

        private Instant GetExpectedMin(LocalDate date)
        {
            return Instant
                .FromDateTimeUtc(DateTime.SpecifyKind(date.ToDateTimeUnspecified(), DateTimeKind.Utc))
                .Plus(Duration.FromHours(2));
        }

        private Instant GetExpectedMax(LocalDate date)
        {
            return Instant
                .FromDateTimeUtc(DateTime.SpecifyKind(date.ToDateTimeUnspecified(), DateTimeKind.Utc))
                .Plus(Duration.FromDays(1))
                .Plus(Duration.FromHours(2));
        }
    }
}

{'fraction_non_alphanumeric': 0.07343514328808447, 'fraction_numerical': 0.0007541478129713424, 'mean_word_length': 2.106588579795022, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '973941', 'n_tokens_mistral': 2724, 'n_tokens_neox': 2536, 'n_words': 549}