using DD.Persistence.Extensions;
using DD.Persistence.Models;
using DD.Persistence.Repositories;
using DD.Persistence.Services.Interfaces;
using System.Text.Json;
namespace DD.Persistence.Services;
///
public class TimestampedValuesService : ITimestampedValuesService
{
private readonly ITimestampedValuesRepository timestampedValuesRepository;
private readonly ISchemePropertyRepository dataSchemeRepository;
///
public TimestampedValuesService(ITimestampedValuesRepository timestampedValuesRepository, ISchemePropertyRepository relatedDataRepository)
{
this.timestampedValuesRepository = timestampedValuesRepository;
this.dataSchemeRepository = relatedDataRepository;
}
///
public async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token)
{
// ToDo: реализовать без foreach
foreach (var dto in dtos)
{
await CreateDataSchemeIfNotExist(discriminatorId, dto, token);
}
var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token);
return result;
}
///
public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token)
{
var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token);
var dtos = await BindingToDataScheme(result, token);
if (!columnNames.IsNullOrEmpty())
{
dtos = ReduceSetColumnsByNames(dtos, columnNames!).ToList();
}
return dtos;
}
///
public async Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
{
var result = await timestampedValuesRepository.GetFirst(discriminatorId, takeCount, token);
var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) }
.ToDictionary();
var dtos = await BindingToDataScheme(resultBeforeBinding, token);
return dtos;
}
///
public async Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
{
var result = await timestampedValuesRepository.GetLast(discriminatorId, takeCount, token);
var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) }
.ToDictionary();
var dtos = await BindingToDataScheme(resultBeforeBinding, token);
return dtos;
}
///
public async Task> GetResampledData(
Guid discriminatorId,
DateTimeOffset beginTimestamp,
double intervalSec = 600d,
int approxPointsCount = 1024,
CancellationToken token = default)
{
var result = await timestampedValuesRepository.GetResampledData(discriminatorId, beginTimestamp, intervalSec, approxPointsCount, token);
var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) }
.ToDictionary();
var dtos = await BindingToDataScheme(resultBeforeBinding, token);
return dtos;
}
///
public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token)
{
var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token);
var resultBeforeBinding = new[] { KeyValuePair.Create(discriminatorId, result) }
.ToDictionary();
var dtos = await BindingToDataScheme(resultBeforeBinding, token);
return dtos;
}
// ToDo: рефакторинг, переименовать (текущее название не отражает суть)
///
/// Преобразовать результат запроса в набор dto
///
///
///
///
private async Task> BindingToDataScheme(IDictionary> queryResult, CancellationToken token)
{
IEnumerable result = [];
foreach (var keyValuePair in queryResult)
{
var dataScheme = await dataSchemeRepository.Get(keyValuePair.Key, token);
if (dataScheme is null)
continue;
foreach (var tuple in keyValuePair.Value)
{
var dto = new TimestampedValuesDto()
{
Timestamp = tuple.Timestamp.ToUniversalTime(),
Values = dataScheme
.ToDictionary(k => k.PropertyName, v => tuple.Values[v.Index])
};
result = result.Append(dto);
}
}
return result;
}
///
/// Создать схему данных, при отсутствии таковой
///
/// Дискриминатор схемы
/// Набор данных, по образу которого будет создана соответствующая схема
///
///
/// Некорректный набор наименований полей
private async Task CreateDataSchemeIfNotExist(Guid discriminatorId, TimestampedValuesDto dto, CancellationToken token)
{
var valuesList = dto.Values.ToList();
var properties = valuesList.ToList().Select(e => new SchemePropertyDto() {
Index = valuesList.IndexOf(e),
PropertyName = e.Key,
PropertyKind = ((JsonElement) e.Value).ValueKind
}).ToArray();
var dataScheme = await dataSchemeRepository.Get(discriminatorId, token);
if (dataScheme is null)
{
dataScheme = new DataSchemeDto(discriminatorId, properties);
await dataSchemeRepository.AddRange(dataScheme, token);
return;
}
if (!dataScheme.Equals(properties))
{
throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " +
$"был передан нехарактерный набор данных");
}
}
///
/// Отсеить лишние поля в соответствии с заданным фильтром
///
///
/// Поля, которые необходимо оставить
///
private IEnumerable ReduceSetColumnsByNames(IEnumerable dtos, IEnumerable fieldNames)
{
var result = dtos.Select(dto =>
{
var reducedValues = dto.Values
.Where(v => fieldNames.Contains(v.Key))
.ToDictionary();
dto.Values = reducedValues;
return dto;
});
return result;
}
}