/ Framework / OverwatchTranscript / TranscriptWriter.cs
TranscriptWriter.cs
  1  using Logging;
  2  using Newtonsoft.Json;
  3  using System.IO.Compression;
  4  
  5  namespace OverwatchTranscript
  6  {
  7      public interface ITranscriptWriter
  8      {
  9          void AddHeader(string key, object value);
 10          void Add(DateTime utc, object payload);
 11          void IncludeArtifact(string filePath);
 12          void Write(string outputFilename);
 13      }
 14  
 15      public class TranscriptWriter : ITranscriptWriter
 16      {
 17          private readonly object _lock = new object();
 18          private readonly MomentReferenceBuilder builder;
 19          private readonly string transcriptFile;
 20          private readonly string artifactsFolder;
 21          private readonly Dictionary<string, string> header = new Dictionary<string, string>();
 22          private readonly BucketSet bucketSet;
 23          private readonly ILog log;
 24          private readonly string workingDir;
 25          private bool closed;
 26  
 27          public TranscriptWriter(ILog log, string workingDir)
 28          {
 29              closed = false;
 30              this.log = log;
 31              this.workingDir = workingDir;
 32              bucketSet = new BucketSet(log, workingDir);
 33              builder = new MomentReferenceBuilder(log, workingDir);
 34              transcriptFile = Path.Combine(workingDir, TranscriptConstants.TranscriptFilename);
 35              artifactsFolder = Path.Combine(workingDir, TranscriptConstants.ArtifactFolderName);
 36  
 37              if (!Directory.Exists(workingDir)) Directory.CreateDirectory(workingDir);
 38              if (File.Exists(transcriptFile) || Directory.Exists(artifactsFolder)) throw new Exception("workingdir not clean");
 39          }
 40  
 41          public void Add(DateTime utc, object payload)
 42          {
 43              CheckClosed();
 44              bucketSet.Add(utc, payload);
 45          }
 46  
 47          public void AddHeader(string key, object value)
 48          {
 49              CheckClosed();
 50              lock (_lock)
 51              {
 52                  header.Add(key, Json.Serialize(value));
 53              }
 54          }
 55  
 56          public void IncludeArtifact(string filePath)
 57          {
 58              CheckClosed();
 59              if (!File.Exists(filePath)) throw new Exception("File not found: " + filePath);
 60              if (!Directory.Exists(artifactsFolder)) Directory.CreateDirectory(artifactsFolder);
 61              var name = Path.GetFileName(filePath);
 62              File.Copy(filePath, Path.Combine(artifactsFolder, name), overwrite: false);
 63          }
 64  
 65          public void Write(string outputFilename)
 66          {
 67              CheckClosed();
 68              closed = true;
 69  
 70              var momentReferences = builder.Build(bucketSet.FinalizeBuckets());
 71              var model = CreateModel(momentReferences);
 72  
 73              File.WriteAllText(transcriptFile, Json.Serialize(model, Formatting.Indented));
 74  
 75              ZipFile.CreateFromDirectory(workingDir, outputFilename);
 76              log.Debug($"Transcript written to {outputFilename}");
 77              log.Debug($"Common header: {Json.Serialize(model.Header.Common, Formatting.Indented)}");
 78  
 79              Directory.Delete(workingDir, true);
 80              log.Debug($"Workdir {workingDir} deleted");
 81          }
 82  
 83          private OverwatchTranscript CreateModel(OverwatchMomentReference[] momentReferences)
 84          {
 85              lock (_lock)
 86              {
 87                  var model = new OverwatchTranscript
 88                  {
 89                      Header = new OverwatchHeader
 90                      {
 91                          Common = CreateCommonHeader(momentReferences),
 92                          Entries = header.Select(h =>
 93                          {
 94                              return new OverwatchHeaderEntry
 95                              {
 96                                  Key = h.Key,
 97                                  Value = h.Value
 98                              };
 99                          }).ToArray()
100                      },
101                      MomentReferences = momentReferences
102                  };
103  
104                  header.Clear();
105                  return model;
106              }
107          }
108  
109          private OverwatchCommonHeader CreateCommonHeader(OverwatchMomentReference[] momentReferences)
110          {
111              var moments = momentReferences.Sum(m => m.NumberOfMoments);
112              var events = momentReferences.Sum(m => m.NumberOfEvents);
113              var earliest = momentReferences.Min(m => m.EarliestUtc);
114              var latest = momentReferences.Max(m => m.LatestUtc);
115  
116              return new OverwatchCommonHeader
117              {
118                  NumberOfMoments = moments,
119                  NumberOfEvents = events,
120                  EarliestUtc = earliest,
121                  LatestUtc = latest
122              };
123          }
124  
125          private void CheckClosed()
126          {
127              if (closed) throw new Exception("Transcript has already been written. Cannot modify or write again.");
128          }
129      }
130  }