public async Task AddForecastingTaskRecord(string entityName, List <ForecastingTaskFieldValue> fields)
        {
            entityName = entityName?.Trim();
            if (!await DoesForecastingTaskEntityExist(entityName))
            {
                throw new DomainErrorException($"Forecasting task with name {entityName} doesn't exist!");
            }

            var taskEntityDeclaration = await _forecastingTasksRepository.GetForecastingTaskFieldsDeclaration(entityName);

            if (taskEntityDeclaration.Count != fields.Select(x => x.FieldId).Distinct().Count())
            {
                throw new DomainErrorException($"Forecasting task with name {entityName} and the request have a different count of fields!");
            }

            for (int i = 0; i < fields.Count; i++)
            {
                if (!taskEntityDeclaration.Any(x => x.Id == fields[i].FieldId))
                {
                    throw new DomainErrorException($"Column {fields[i].FieldId} doesn't exist in forecasting task with name {entityName}!");
                }

                fields[i].Value = fields[i].Value?.Trim();
                var fieldDeclaration = taskEntityDeclaration.Single(x => x.Id == fields[i].FieldId);
                if (fieldDeclaration.Type != FieldType.InformationField && !float.TryParse(fields[i].Value, out _))
                {
                    throw new DomainErrorException($"Field {fieldDeclaration.Name} must to be filled with a number! But was filled with value: {fields[i].Value}");
                }
            }

            await _forecastingTasksRepository.AddForecastingTaskRecord(entityName, fields);
        }
        public async Task AddForecastingTaskRecordsViaCsv(string entityName, string csv)
        {
            entityName = entityName?.Trim();
            if (!await DoesForecastingTaskEntityExist(entityName))
            {
                throw new DomainErrorException($"Forecasting task with name {entityName} doesn't exist!");
            }

            var taskEntityDeclaration = await _forecastingTasksRepository.GetForecastingTaskFieldsDeclaration(entityName);

            var rows        = csv.Split("\r\n");
            var fieldsOrder = new Dictionary <int, ForecastingTaskFieldDeclaration>();

            // Checking csv header
            var headerColumns = rows.First().Split(',');

            if (taskEntityDeclaration.Count != headerColumns.Count())
            {
                throw new DomainErrorException($"Forecasting task with name {entityName} and csv file have a different count of columns!");
            }

            for (int i = 0; i < headerColumns.Length; i++)
            {
                if (!taskEntityDeclaration.Any(x => x.Name == headerColumns[i].Trim()))
                {
                    throw new DomainErrorException($"Column {headerColumns[i].Trim()} doesn't exist in forecasting task with name {entityName}!");
                }

                fieldsOrder.Add(i, taskEntityDeclaration.Single(x => x.Name == headerColumns[i].Trim()));
            }

            //Add fields values
            var fieldsValues = new List <List <ForecastingTaskFieldValue> >();

            foreach (var row in rows.Skip(1))
            {
                var factorsValue = new List <ForecastingTaskFieldValue>();
                var columns      = row.Split(',');
                for (int i = 0; i < columns.Length; i++)
                {
                    columns[i] = columns[i]?.Trim();
                    if (fieldsOrder[i].Type != FieldType.InformationField && !float.TryParse(columns[i], out _))
                    {
                        throw new DomainErrorException($"Field {fieldsOrder[i].Name} must to be filled with a number! But was filled with value: {columns[i]}");
                    }

                    factorsValue.Add(new ForecastingTaskFieldValue
                    {
                        FieldId = fieldsOrder[i].Id,
                        Value   = columns[i]
                    });
                }
                fieldsValues.Add(factorsValue);
            }

            await _forecastingTasksRepository.AddBatchOfForecastingTaskRecords(entityName, fieldsValues);
        }
        public async Task <float> PredictValueByFactors(string entityName, List <ForecastingTaskFieldValue> factors, bool isValidationNeeded = true)
        {
            entityName = entityName?.Trim();
            if (isValidationNeeded)
            {
                if (factors.Count == 0)
                {
                    throw new DomainErrorException($"Factor list is empty!");
                }
                if (!DoMLModelFilesExist(entityName))
                {
                    throw new DomainErrorException("You didn't train the model! Please, do!");
                }
                if (!await DoesForecastingTaskEntityExist(entityName))
                {
                    throw new DomainErrorException($"Forecasting task with name {entityName} doesn't exist!");
                }
            }
            var taskEntityDeclaration = await _forecastingTasksRepository.GetForecastingTaskFieldsDeclaration(entityName);

            var nonInformationFields = taskEntityDeclaration.Where(x => x.Type != FieldType.InformationField).ToList();
            var predictionValueId    = taskEntityDeclaration.Single(x => x.Type == FieldType.PredictionField).Id;

            if (factors.Any(x => x.FieldId == predictionValueId))
            {
                throw new DomainErrorException($"FieldId {predictionValueId} is incorrect! This is the prediction value!");
            }

            var entity          = new ClassBuilder(entityName, GetFieldsType(nonInformationFields));
            var myClassInstance = entity.CreateObject();

            foreach (var value in factors)
            {
                value.Value = value.Value?.Trim();
                if (!nonInformationFields.Any(x => x.Id == value.FieldId))
                {
                    throw new DomainErrorException($"FieldId {value.FieldId} is incorrect!");
                }

                var name = nonInformationFields.Single(x => x.Id == value.FieldId).Name;
                entity.SetPropertyValue(myClassInstance, name, float.Parse(value.Value));
            }

            var predicatedValue = ForecastingTaskConsumeModel.Predict(myClassInstance, entityName, entity.Type);

            return((float)Math.Round(predicatedValue, 3, MidpointRounding.AwayFromZero));
        }