diff --git a/Samples/DotNetSdk/ObservationReportExporter/Exporter.cs b/Samples/DotNetSdk/ObservationReportExporter/Exporter.cs index 6b6b717..269b5b8 100644 --- a/Samples/DotNetSdk/ObservationReportExporter/Exporter.cs +++ b/Samples/DotNetSdk/ObservationReportExporter/Exporter.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using Aquarius.Samples.Client; using Aquarius.Samples.Client.ServiceModel; using Aquarius.TimeSeries.Client; using Aquarius.TimeSeries.Client.ServiceModels.Provisioning; using Humanizer; using log4net; +using NodaTime; +using ServiceStack; using GetTags = Aquarius.TimeSeries.Client.ServiceModels.Provisioning.GetTags; namespace ObservationReportExporter @@ -89,18 +92,202 @@ private void ValidateOnceConnected() } private SpreadsheetTemplate ExportTemplate { get; set; } + + private Dictionary TimeSeriesLocationAliases { get; set; } = + new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private void ValidateSamplesConfiguration() { ExportTemplate = Samples .Get(new GetSpreadsheetTemplates()) .DomainObjects - .FirstOrDefault(t => t.CustomId.Equals(Context.ExportTemplateName, StringComparison.InvariantCultureIgnoreCase)); + .FirstOrDefault(t => t.CustomId.Equals(Context.ExportTemplateName, StringComparison.InvariantCultureIgnoreCase) && t.Type == SpreadsheetTemplateType.OBSERVATION_EXPORT); if (ExportTemplate == null) - throw new ExpectedException($"'{Context.ExportTemplateName}' is not a known spreadsheet template"); + throw new ExpectedException($"'{Context.ExportTemplateName}' is not a known Observation Export spreadsheet template"); + + var exchangeConfiguration = Samples + .Get(new GetExchangeConfigurations()) + .DomainObjects + .FirstOrDefault(e => e.Type == "AQUARIUS_TIMESERIES"); + + if (exchangeConfiguration != null) + { + TimeSeriesLocationAliases.Clear(); + + foreach (var mapping in exchangeConfiguration.SamplingLocationMappings) + { + if (mapping.SamplingLocation.CustomId.Equals(mapping.ExternalLocation, StringComparison.InvariantCultureIgnoreCase)) + break; + + TimeSeriesLocationAliases.Add(mapping.SamplingLocation.CustomId, mapping.ExternalLocation); + } + } + + var clauses = new List(); + var builder = new StringBuilder(); + + var startObservedTime = (Instant?)null; + var endObservedTime = (Instant?)null; + + if (Context.StartTime.HasValue) + { + clauses.Add($"after {Context.StartTime:O}"); + startObservedTime = Instant.FromDateTimeOffset(Context.StartTime.Value); + } + + if (Context.EndTime.HasValue) + { + clauses.Add($"before {Context.EndTime:O}"); + endObservedTime = Instant.FromDateTimeOffset(Context.EndTime.Value); + } + + if (clauses.Any()) + { + builder.Append(string.Join(" and ", clauses)); + clauses.Clear(); + } + + var analyticalGroupIds = new List(); + var observedPropertyIds = new List(); + var samplingLocationIds = new List(); + var samplingLocationGroupIds = new List(); + + var locationClauses = new List(); + var locationGroupClauses = new List(); + var analyticalGroupClauses = new List(); + var observedPropertyClauses = new List(); + + if (Context.LocationIds.Any()) + { + Log.Info($"Resolving {"sampling location ID".ToQuantity(Context.LocationIds.Count)} ..."); + + samplingLocationIds = + GetSpecificPaginatedItemIds( + "location", + locationClauses, + Context.LocationIds, + item => item.CustomId, + item => item.Id, + name => new GetSamplingLocations { CustomId = name }); + } + + if (Context.LocationGroupIds.Any()) + { + Log.Info($"Resolving {"sampling location group ID".ToQuantity(Context.LocationGroupIds.Count)} ..."); + + samplingLocationGroupIds = + GetItemIds( + "location group", + locationGroupClauses, + Context.LocationGroupIds, + item => item.Name, + item => item.Id); + } + + if (Context.AnalyticalGroupIds.Any()) + { + Log.Info($"Resolving {"analytical group ID".ToQuantity(Context.AnalyticalGroupIds.Count)} ..."); + + analyticalGroupIds = + GetItemIds( + "analytical group", + analyticalGroupClauses, + Context.AnalyticalGroupIds, + item => item.Name, + item => item.Id); + } + + if (Context.ObservedPropertyIds.Any()) + { + Log.Info($"Resolving {"observed property ID".ToQuantity(Context.ObservedPropertyIds.Count)} ..."); + + observedPropertyIds = + GetItemIds( + "observed property", + observedPropertyClauses, + Context.ObservedPropertyIds, + item => item.CustomId, + item => item.Id); + } + + clauses.AddRange(locationClauses); + clauses.AddRange(locationGroupClauses); + clauses.AddRange(analyticalGroupClauses); + clauses.AddRange(observedPropertyClauses); + + if (clauses.Any()) + { + if (builder.Length > 0) + builder.Append(' '); + + builder.Append($"with {string.Join(" and ", clauses)}"); + } + + var summary = builder.ToString(); + + Log.Info($"Exporting observations for {summary}."); + } + + private List GetSpecificPaginatedItemIds(string type, List clauses, List names, Func nameSelector, Func idSelector, Func requestFunc) + where TRequest : IPaginatedRequest, IReturn, new() + where TResponse : IPaginatedResponse + { + var items = names + .SelectMany(name => Samples.LazyGet(requestFunc(name)).DomainObjects) + .ToList(); + + return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector); + } + + + private List GetPaginatedItemIds(string type, List clauses, List names, Func nameSelector, Func idSelector) + where TRequest : IPaginatedRequest, IReturn, new() + where TResponse : IPaginatedResponse + { + var response = Samples.LazyGet(new TRequest()); + + var items = response.DomainObjects.ToList(); + + return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector); } - private Dictionary LocationTags { get; set; } + private List GetItemIds(string type, List clauses, List names, Func nameSelector, Func idSelector) + where TRequest : IReturn, new() + where TResponse : IPaginatedResponse + { + var response = Samples.Get(new TRequest()); + + var items = response.DomainObjects.ToList(); + + return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector); + } + + private List MapNamesToIds(string type, List clauses, List names, List items, Func nameSelector, Func idSelector) + { + var unmatchedNames = names + .Where(name => items.All(item => !nameSelector(item).Equals(name, StringComparison.InvariantCultureIgnoreCase))) + .Distinct() + .ToList(); + + if (unmatchedNames.Any()) + throw new ExpectedException($"{$"unknown {type}".ToQuantity(unmatchedNames.Count)}: {string.Join(", ", unmatchedNames)}"); + + clauses.Add(names.Count == 1 + ? $"{type} '{names.First()}'" + : $"{type.ToQuantity(names.Count)} in ({string.Join(", ", names)})"); + + var nameSet = new HashSet(names.Distinct(), StringComparer.InvariantCultureIgnoreCase); + + return items + .Where(item => nameSet.Contains(nameSelector(item))) + .Select(idSelector) + .Distinct() + .ToList(); + } + + + private Dictionary LocationTags { get; set; } = new Dictionary(); private void ValidateTimeSeriesConfiguration() { @@ -114,13 +301,10 @@ private void ValidateTimeSeriesConfiguration() .Where(t => t.AppliesToLocations) .ToDictionary(t => t.Key, t => t, StringComparer.InvariantCultureIgnoreCase); - foreach (var kvp in Context.AttachmentTags) + foreach (var key in Context.AttachmentTags.Keys) { - var key = kvp.Key; - var value = kvp.Value; - - if (!LocationTags.TryGetValue(key, out var locationTag)) - throw new ExpectedException($"'{key}' is not an existing tag with {nameof(locationTag.AppliesToLocations)}=true"); + if (!LocationTags.TryGetValue(key, out _)) + throw new ExpectedException($"'{key}' is not an existing tag with {nameof(Tag.AppliesToLocations)}=true"); } } diff --git a/Samples/DotNetSdk/ObservationReportExporter/GetExchangeConfigurations.cs b/Samples/DotNetSdk/ObservationReportExporter/GetExchangeConfigurations.cs new file mode 100644 index 0000000..359bb9d --- /dev/null +++ b/Samples/DotNetSdk/ObservationReportExporter/GetExchangeConfigurations.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Aquarius.Samples.Client.ServiceModel; +using ServiceStack; + +namespace ObservationReportExporter +{ + [Route("/v1/exchangeconfigurations", HttpMethods.Get)] + public class GetExchangeConfigurations : IReturn + { + } + + public class SearchResultsExchangeConfigurations + { + public int TotalCount { get; set; } + public List DomainObjects { get; set; } = new List(); + + } + + public class ExchangeConfiguration + { + public string Id { get; set; } + public string CustomId { get; set; } + public string Type { get; set; } + public string Description { get; set; } + + public List SamplingLocationMappings { get; set; } = new List(); + } + + public class SamplingLocationMapping + { + public string Id { get; set; } + public SamplingLocation SamplingLocation { get; set; } + public string ExternalLocation { get; set; } + } +} diff --git a/Samples/DotNetSdk/ObservationReportExporter/ObservationReportExporter.csproj b/Samples/DotNetSdk/ObservationReportExporter/ObservationReportExporter.csproj index 37f1349..9d2c523 100644 --- a/Samples/DotNetSdk/ObservationReportExporter/ObservationReportExporter.csproj +++ b/Samples/DotNetSdk/ObservationReportExporter/ObservationReportExporter.csproj @@ -242,6 +242,7 @@ +