-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #100 from DougSchmidt-AI/feature/Issue-99-PointZil…
…laSavePointsAsCsv Issue-99 Add -SaveCsvPath option to allow saving of the extracted/gen…
- Loading branch information
Showing
9 changed files
with
299 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
TimeSeries/PublicApis/SdkExamples/PointZilla/CsvWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Aquarius.TimeSeries.Client.ServiceModels.Acquisition; | ||
using NodaTime; | ||
using NodaTime.Text; | ||
using ServiceStack.Logging; | ||
|
||
namespace PointZilla | ||
{ | ||
public class CsvWriter | ||
{ | ||
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
|
||
private Context Context { get; } | ||
|
||
public CsvWriter(Context context) | ||
{ | ||
Context = context; | ||
} | ||
|
||
public void WritePoints(List<ReflectedTimeSeriesPoint> points) | ||
{ | ||
var timeSeriesIdentifier = CreateTimeSeriesIdentifier(); | ||
|
||
var csvPath = Directory.Exists(Context.SaveCsvPath) | ||
? Path.Combine(Context.SaveCsvPath, SanitizeFilename($"{timeSeriesIdentifier.Identifier}.{CreatePeriod(Context.SourceQueryFrom, Context.SourceQueryTo)}.csv")) | ||
: Context.SaveCsvPath; | ||
|
||
Log.Info($"Saving {points.Count} extracted points to '{csvPath}' ..."); | ||
|
||
var dir = Path.GetDirectoryName(csvPath); | ||
|
||
if (!string.IsNullOrEmpty(dir)) | ||
{ | ||
Directory.CreateDirectory(dir); | ||
} | ||
|
||
using (var writer = new StreamWriter(csvPath)) | ||
{ | ||
var offsetPattern = OffsetPattern.CreateWithInvariantCulture("m"); | ||
var utcOffsetText = $"UTC{offsetPattern.Format(Context.UtcOffset ?? Offset.Zero)}"; | ||
var period = CreatePeriod(Context.SourceQueryFrom ?? Instant.MinValue, Context.SourceQueryTo ?? Instant.MaxValue); | ||
|
||
writer.WriteLine($"# {Path.GetFileName(csvPath)} generated by {Context.ExecutingFileVersion}"); | ||
writer.WriteLine($"#"); | ||
writer.WriteLine($"# Time series identifier: {timeSeriesIdentifier.Identifier}"); | ||
writer.WriteLine($"# Location: {timeSeriesIdentifier.LocationIdentifier}"); | ||
writer.WriteLine($"# UTC offset: ({utcOffsetText})"); | ||
writer.WriteLine($"# Value units: {Context.Unit}"); | ||
writer.WriteLine($"# Value parameter: {timeSeriesIdentifier.Parameter}"); | ||
writer.WriteLine($"# Interpolation type: {Context.InterpolationType}"); | ||
writer.WriteLine($"# Time series type: {Context.TimeSeriesType}"); | ||
writer.WriteLine($"#"); | ||
writer.WriteLine($"# Export options: Corrected signal from {period.StartText} to {period.EndText}"); | ||
writer.WriteLine($"#"); | ||
writer.WriteLine($"# CSV data starts at line 15."); | ||
writer.WriteLine($"#"); | ||
writer.WriteLine($"ISO 8601 UTC, Value, Grade, Qualifiers"); | ||
|
||
foreach (var point in points) | ||
{ | ||
var time = point.Time ?? Instant.MinValue; | ||
|
||
writer.WriteLine($"{InstantPattern.ExtendedIsoPattern.Format(time)}, {point.Value:G12}, {point.GradeCode}, {FormatQualifiers(point.Qualifiers)}"); | ||
} | ||
} | ||
} | ||
|
||
public static void SetPointZillaCsvFormat(Context context) | ||
{ | ||
// Match PointZilla Export format above | ||
|
||
// # CSV data starts at line 15. | ||
// # | ||
// ISO 8601 UTC, Value, Grade, Qualifiers | ||
// 2015-12-04T00:01:00Z, 3.523200823975, 500, | ||
// 2015-12-04T00:02:00Z, 3.525279357147, 500, | ||
|
||
context.CsvTimeField = 1; | ||
context.CsvValueField = 2; | ||
context.CsvGradeField = 3; | ||
context.CsvQualifiersField = 4; | ||
context.CsvComment = "#"; | ||
context.CsvSkipRows = 0; | ||
context.CsvTimeFormat = null; | ||
context.CsvIgnoreInvalidRows = true; | ||
context.CsvRealign = false; | ||
} | ||
|
||
private TimeSeriesIdentifier CreateTimeSeriesIdentifier() | ||
{ | ||
if (Context.SourceTimeSeries != null) | ||
return Context.SourceTimeSeries; | ||
|
||
if (!string.IsNullOrEmpty(Context.TimeSeries)) | ||
return TimeSeriesIdentifierParser.ParseExtendedIdentifier(Context.TimeSeries); | ||
|
||
string parameter; | ||
var label = "Points"; | ||
var locationIdentifier = "PointZilla"; | ||
|
||
if (Context.Command == CommandType.DeleteAllPoints) | ||
parameter ="Deleted"; | ||
|
||
else if (Context.ManualPoints.Any()) | ||
parameter = "ManuallyEntered"; | ||
else if (Context.CsvFiles.Any()) | ||
parameter = "OtherCsvFile"; | ||
else | ||
parameter = Context.WaveformType.ToString(); | ||
|
||
return new TimeSeriesIdentifier | ||
{ | ||
Parameter = parameter, | ||
Label = label, | ||
LocationIdentifier = locationIdentifier, | ||
Identifier = $"{parameter}.{label}@{locationIdentifier}" | ||
}; | ||
} | ||
|
||
private static string CreatePeriod(Instant? startTime, Instant? endTime) | ||
{ | ||
var start = startTime ?? Instant.MinValue; | ||
var end = endTime ?? Instant.MaxValue; | ||
|
||
if (start == Instant.MinValue && end == Instant.MaxValue) | ||
return "EntireRecord"; | ||
|
||
var period = CreatePeriod(start, end); | ||
|
||
return $"{period.StartText}.{period.EndText}"; | ||
} | ||
|
||
private static (string StartText, string EndText) CreatePeriod(Instant start, Instant end) | ||
{ | ||
return ( | ||
start == Instant.MinValue ? "StartOfRecord" : InstantPattern.ExtendedIsoPattern.Format(start), | ||
end == Instant.MaxValue ? "EndOfRecord" : InstantPattern.ExtendedIsoPattern.Format(end) | ||
); | ||
|
||
} | ||
|
||
private static string FormatQualifiers(List<string> qualifiers) | ||
{ | ||
if (!qualifiers.Any()) | ||
return string.Empty; | ||
|
||
if (qualifiers.Count == 1) | ||
return qualifiers.First(); | ||
|
||
return $"\"{string.Join(",", qualifiers)}\""; | ||
} | ||
|
||
private static string SanitizeFilename(string s) | ||
{ | ||
return Path.GetInvalidFileNameChars().Aggregate(s, (current, ch) => current.Replace(ch, '_')); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.