Skip to content

Commit

Permalink
PF-1365 - Added validation of named AQS things to IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
Doug Schmidt committed Feb 3, 2022
1 parent e9ac9de commit cbb51cb
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 9 deletions.
202 changes: 193 additions & 9 deletions Samples/DotNetSdk/ObservationReportExporter/Exporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -89,18 +92,202 @@ private void ValidateOnceConnected()
}

private SpreadsheetTemplate ExportTemplate { get; set; }

private Dictionary<string, string> TimeSeriesLocationAliases { get; set; } =
new Dictionary<string, string>(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<string>();
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<string>();
var observedPropertyIds = new List<string>();
var samplingLocationIds = new List<string>();
var samplingLocationGroupIds = new List<string>();

var locationClauses = new List<string>();
var locationGroupClauses = new List<string>();
var analyticalGroupClauses = new List<string>();
var observedPropertyClauses = new List<string>();

if (Context.LocationIds.Any())
{
Log.Info($"Resolving {"sampling location ID".ToQuantity(Context.LocationIds.Count)} ...");

samplingLocationIds =
GetSpecificPaginatedItemIds<SamplingLocation, GetSamplingLocations, SearchResultSamplingLocation>(
"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<SamplingLocationGroup, GetSamplingLocationGroups, SearchResultSamplingLocationGroup>(
"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<AnalyticalGroup, GetAnalyticalGroups, SearchResultAnalyticalGroup>(
"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<ObservedProperty, GetObservedProperties, SearchResultObservedProperty>(
"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<string> GetSpecificPaginatedItemIds<TDomainObject, TRequest, TResponse>(string type, List<string> clauses, List<string> names, Func<TDomainObject, string> nameSelector, Func<TDomainObject, string> idSelector, Func<string, TRequest> requestFunc)
where TRequest : IPaginatedRequest, IReturn<TResponse>, new()
where TResponse : IPaginatedResponse<TDomainObject>
{
var items = names
.SelectMany(name => Samples.LazyGet<TDomainObject, TRequest, TResponse>(requestFunc(name)).DomainObjects)
.ToList();

return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector);
}


private List<string> GetPaginatedItemIds<TDomainObject, TRequest, TResponse>(string type, List<string> clauses, List<string> names, Func<TDomainObject, string> nameSelector, Func<TDomainObject, string> idSelector)
where TRequest : IPaginatedRequest, IReturn<TResponse>, new()
where TResponse : IPaginatedResponse<TDomainObject>
{
var response = Samples.LazyGet<TDomainObject, TRequest, TResponse>(new TRequest());

var items = response.DomainObjects.ToList();

return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector);
}

private Dictionary<string, Tag> LocationTags { get; set; }
private List<string> GetItemIds<TDomainObject, TRequest, TResponse>(string type, List<string> clauses, List<string> names, Func<TDomainObject, string> nameSelector, Func<TDomainObject, string> idSelector)
where TRequest : IReturn<TResponse>, new()
where TResponse : IPaginatedResponse<TDomainObject>
{
var response = Samples.Get(new TRequest());

var items = response.DomainObjects.ToList();

return MapNamesToIds(type, clauses, names, items, nameSelector, idSelector);
}

private List<string> MapNamesToIds<TDomainObject>(string type, List<string> clauses, List<string> names, List<TDomainObject> items, Func<TDomainObject, string> nameSelector, Func<TDomainObject, string> 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<string>(names.Distinct(), StringComparer.InvariantCultureIgnoreCase);

return items
.Where(item => nameSet.Contains(nameSelector(item)))
.Select(idSelector)
.Distinct()
.ToList();
}


private Dictionary<string, Tag> LocationTags { get; set; } = new Dictionary<string, Tag>();

private void ValidateTimeSeriesConfiguration()
{
Expand All @@ -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");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<SearchResultsExchangeConfigurations>
{
}

public class SearchResultsExchangeConfigurations
{
public int TotalCount { get; set; }
public List<ExchangeConfiguration> DomainObjects { get; set; } = new List<ExchangeConfiguration>();

}

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<SamplingLocationMapping> SamplingLocationMappings { get; set; } = new List<SamplingLocationMapping>();
}

public class SamplingLocationMapping
{
public string Id { get; set; }
public SamplingLocation SamplingLocation { get; set; }
public string ExternalLocation { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
<Compile Include="ExeHelper.cs" />
<Compile Include="ExpectedException.cs" />
<Compile Include="Exporter.cs" />
<Compile Include="GetExchangeConfigurations.cs" />
<Compile Include="Option.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand Down

0 comments on commit cbb51cb

Please sign in to comment.