/// <summary> /// Persists a /// </summary> /// <param name="data"></param> /// <returns></returns> public async Task<SeriesDownload> PersistSeriesData(SeriesData data, string urlId, DateTimeOffset requestTime) { SeriesDownload result = new SeriesDownload() { SeriesID = data.SeriesID }; //assumes series is not already in storage using (MemoryStream ms = new MemoryStream()) { //write data to memory stream as csv await WriteDataToMemoryStreamAsCsv(data, ms); if (ms.Length > 0) { // persist memory stream as blob ms.Position = 0; CloudBlockBlob blob = await WriteMemoryStreamToBlobInGuidDirectory(data, ms, csa); //persist table storage record CloudTable tbl = csa.CreateCloudTableClient().GetTableReference(DiscoveryStorageTableNames.SeriesDownloads); await tbl.ExecuteAsync(TableOperation.InsertOrReplace( //optimistic concurrency new DataExportRequest(requestTime, data.myMetadata.StartDate.ToUniversalTime(), data.myMetadata.EndDate.ToUniversalTime(), blob.Uri.AbsoluteUri, data.SeriesID, data.myMetadata.ServURL) )); //fill result object uri result.Uri = blob.Uri.AbsoluteUri; } } return result; }
/// <summary> /// Extracts a Faceted API DataValue object from a HydroDesktop DataValue object /// </summary> /// <param name="v"></param> public DataValue(ServerSideHydroDesktop.ObjectModel.DataValue v, SeriesData sd = null) : this() { UTCTimeStamp = v.DateTimeUTC; UTCOffset = v.UTCOffset; LocalTimeStamp = v.LocalDateTime; Value = v.Value; ValueAccuracy = v.ValueAccuracy; if (v.Qualifier != null) { Qualifier = v.Qualifier.Code; //10-Aug-2015 - BCC - GitHub Issue #33 - Include Qualifier Description in downloaded time series data QualifierDescription = v.Qualifier.Description; } if (v.OffsetType != null) { OffsetDescription = v.OffsetType.Description; OffsetUnit = v.OffsetType.Unit.ToString(); } CensorCode = v.CensorCode; OffsetValue = v.OffsetValue; if (null != v.Series) { //TO DO - LabSampleCode... if (null != v.Series.Method) { MethodCode = v.Series.Method.Code.ToString(); } if (null != v.Series.QualityControlLevel) { QualityControlLevelCode = v.Series.QualityControlLevel.Code.ToString(); } if (null != v.Series.Source) { //TO DO - Does this work for WaterML 1.0 and 1.1? SourceCode = v.Series.Source.OriginId.ToString(); } } SeriesData = sd; }
/// <summary> /// Format: series-<sitename>-<variablename>.csv /// </summary> /// <param name="data"></param> /// <returns></returns> public static string GenerateBlobName(SeriesData data) { return string.Format("series-{0}-{1}.csv", data.myMetadata.SiteName.SanitizeForFilename(), data.myMetadata.VariableName.SanitizeForFilename()); }
/// <summary> /// Generates the filename of the data download version of the SeriesData resource. /// </summary> /// <param name="data"></param> /// <returns></returns> private string GenerateBlobName(SeriesData data) { return string.Format("series-{0}-{1}.csv", data.SeriesID, data.GetOntologyName().SanitizeForFilename()); }
/// <summary> /// Writes data directly to blob storage. Sets Content Type and Content Disposition headers to facilitate in-browser data download. /// </summary> /// <param name="data"></param> /// <param name="ms"></param> /// <param name="csa"></param> /// <returns></returns> private async Task<CloudBlockBlob> WriteMemoryStreamToBlobInGuidDirectory(SeriesData data, MemoryStream ms, CloudStorageAccount csa) { CloudBlobClient bClient = csa.CreateCloudBlobClient(); CloudBlobContainer container = bClient.GetContainerReference(DiscoveryStorageTableNames.SeriesDownloads); string fileName = GenerateBlobName(data); CloudBlockBlob blob = container.GetDirectoryReference(new Guid().ToString()).GetBlockBlobReference(fileName); blob.Properties.ContentType = "text/csv; utf-8"; blob.Properties.ContentDisposition = string.Format("attachment; filename = {0}", fileName); await blob.DeleteIfExistsAsync(); await blob.UploadFromStreamAsync(ms, AccessCondition.GenerateEmptyCondition(), new BlobRequestOptions() { RetryPolicy = new ExponentialRetry() }, null); return blob; }
/// <summary> /// Writes WaterOneFlow data response as CSV file. /// </summary> /// <param name="data">WaterOneFlow data reponse to write.</param> /// <param name="ms">Memory Stream to write to.</param> /// <returns></returns> private async Task WriteDataToMemoryStreamAsCsv(SeriesData data, MemoryStream ms) { using (var csvwrtr = new CsvWriter(ms, Encoding.UTF8, true)) { csvwrtr.ValueSeparator = Char.Parse(","); csvwrtr.WriteRecord(new List<string>() { "TimeStamp" ,"Value","OffsetType","OffsetValue", "ValueAccuracy", "Qualifier","CensorCode" }); foreach (DataValue value in data.values) { List<string> values = new List<string>(); values.Add(value.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss")); values.Add(value.Value.ToString()); values.Add(value.OffsetType); values.Add(value.OffsetValue.ToString()); values.Add(value.ValueAccuracy.ToString()); values.Add(value.Qualifier); values.Add(value.CensorCode); csvwrtr.WriteRecord(values); } await csvwrtr.FlushAsync(); } }
/// <summary> /// Create container for returning data valeus from one or many different series /// </summary> /// <param name="datum"></param> public DataEnvelope(SeriesData datum) { series = new List<SeriesData>() { datum }; }
public async Task<MemoryStream> getCSVResultAsMemoryStream(SeriesData data) { SeriesDownload result = new SeriesDownload() { SeriesID = data.SeriesID }; //assumes series is not already in storage using (MemoryStream ms = new MemoryStream()) { //write data to memory stream as csv await WriteDataToMemoryStreamAsCsv(data, ms); if (ms.Length > 0) { // persist memory stream as blob ms.Position = 0; } return ms; } }