Files
LeafWeb/Core/Entities/LeafInput.cs
T
2020-07-11 12:43:18 -04:00

240 lines
8.0 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.IO;
using System.IO.Compression;
using System.Linq;
using LeafWeb.Core.Utility;
namespace LeafWeb.Core.Entities
{
public class LeafInput : ILeafInput
{
public int Id { get; set; }
public virtual ICollection<LeafInputFile> InputFiles { get; set; }
public virtual ICollection<LeafInputData> LeafInputData { get; set; }
public virtual ICollection<LeafOutputFile> OutputFiles { get; set; }
[Required(ErrorMessage = "Name required")]
public string Name { get; set; }
[Required(ErrorMessage = "An email address is required")]
public string Email { get; set; }
[Required(ErrorMessage = "A unique identifier is required")]
public string Identifier { get; set; }
[Required(ErrorMessage = "Site Id required")]
public string SiteId { get; set; }
[Required]
[Column(TypeName = "VARCHAR")]
[StringLength(12)]
[Index("IX_UniqueToken", 1, IsUnique = true)]
public string UniqueToken { get; set; }
// [Required(ErrorMessage = "PhotosynthesisType required")]
// http://stackoverflow.com/questions/6038541/ef-validation-failing-on-update-when-using-lazy-loaded-required-properties
public virtual PhotosynthesisType PhotosynthesisType { get; set; }
[DataType(DataType.Date)]
[Required]
public DateTime Added { get; set; }
public LeafInputStatusType CurrentStatus { get; set; }
public virtual ICollection<LeafInputStatus> StatusHistory { get; set; }
public Priority PendingPriority { get; set; }
#region Calculated properties
public LeafOutputFile OutputErrorMessage => OutputFiles?.FirstOrDefault(f => f.IsErrorMessage);
public LeafOutputFile OutputWarningMessage => OutputFiles?.FirstOrDefault(f => f.IsWarningMessage);
public bool HasOutputFiles => OutputFiles.Any();
public bool HasLeafChart => OutputFiles.Any(f => f.IsLeafChartFile);
public bool IsPending => CurrentStatus == LeafInputStatusType.Pending;
public bool IsStarting => CurrentStatus == LeafInputStatusType.Starting;
public bool IsRunning => CurrentStatus == LeafInputStatusType.Running;
public bool IsFinishing => CurrentStatus == LeafInputStatusType.Finishing;
public bool IsComplete => CurrentStatus == LeafInputStatusType.Complete;
public bool IsException => CurrentStatus == LeafInputStatusType.Exception;
public bool IsCancelPending => CurrentStatus == LeafInputStatusType.CancelPending;
public bool IsCancelling => CurrentStatus == LeafInputStatusType.Cancelling;
public bool IsCancelled => CurrentStatus == LeafInputStatusType.Cancelled;
public bool IsUnresponsive => CurrentStatus == LeafInputStatusType.Unresponsive;
public bool IsInProgress => IsStarting
|| IsRunning
|| IsFinishing
|| IsCancelPending
|| IsCancelling
|| IsUnresponsive;
public bool IsCancellable =>
IsRunning
|| IsPending
|| IsUnresponsive;
public bool IsDeletable => !IsInProgress;
public bool IsAtEndState => IsComplete || IsException || IsCancelled;
public DateTime? StartTime
{
get
{
if (IsPending)
{
return null;
}
return StatusHistory.FirstOrDefault(s => s.Status == LeafInputStatusType.Starting)?.DateTime;
}
}
public DateTime? EndTime
{
get
{
if (!IsAtEndState)
{
return null;
}
return StatusHistory.FirstOrDefault(
s =>
s.Status == LeafInputStatusType.Complete
|| s.Status == LeafInputStatusType.Exception
|| s.Status == LeafInputStatusType.Cancelled)?.DateTime;
}
}
public DateTime LastStatusChange => StatusHistory.Last().DateTime;
public TimeSpan TimeInProgress
{
get
{
var start = StartTime;
if (!start.HasValue)
{
return TimeSpan.Zero;
}
var end = EndTime;
if (!end.HasValue)
{
return DateTime.Now - start.Value;
}
return end.Value - start.Value;
}
}
#endregion
public override string ToString()
{
return $"{Id}_{Identifier}";
}
/// <summary>
/// Contains all output files in a zip
/// </summary>
public byte[] GetOutputFileZip(LeafOutputFileType? fileType)
{
return NewMemoryZipArchive((archive, stream) =>
CompressOutputFiles(
OutputFiles.Where(f => !fileType.HasValue || f.FileType == fileType.Value),
archive));
}
/// <summary>
/// Contains all input files in a zip
/// </summary>
public byte[] GetInputFileZip() =>
NewMemoryZipArchive((archive, stream) => CompressInputFiles(InputFiles, archive));
/// <summary>
/// Contains all input files in a zip, ordered into directories
/// </summary>
public static byte[] GetInputFilesZip(IEnumerable<LeafInput> leafInputs) =>
NewMemoryZipArchive((archive, stream) =>
{
var inputs = leafInputs.ToList();
foreach (var leafInput in inputs)
{
if (stream.Length > 2560000L)
continue;
var directory = $"{leafInput.Added:yyyy-dd-MM--HH-mm}_{leafInput.Identifier.FilterValidFilename()}";
CompressInputFiles(leafInput.InputFiles,
archive, directory);
}
});
/// <summary>
/// Contains all input files in a zip, ordered into directories
/// </summary>
public static byte[] GetOutputFilesZip_ToUser(IEnumerable<LeafInput> leafInputs) =>
NewMemoryZipArchive((archive, stream) =>
{
var inputs = leafInputs.ToList();
foreach (var leafInput in inputs)
{
if (stream.Length > 2560000L)
continue;
var directory = $"{leafInput.Added:yyyy-dd-MM--HH-mm}_{leafInput.Identifier.FilterValidFilename()}";
CompressOutputFiles(leafInput.OutputFiles
.Where(f => f.FileType == LeafOutputFileType.ToUser),
archive, directory);
}
});
private static byte[] NewMemoryZipArchive(Action<ZipArchive, MemoryStream> addFiles)
{
using (var compressedFileStream = new MemoryStream())
using (var archive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, true))
{
addFiles(archive, compressedFileStream);
return compressedFileStream.ToArray();
}
}
private static long CompressInputFiles(IEnumerable<LeafInputFile> inputFiles, ZipArchive archive, string prefix = "")
{
var contentsLength = 0L;
foreach (var inputFile in inputFiles)
{
contentsLength += inputFile.Contents.LongLength;
var entry = archive.CreateEntry(Path.Combine(prefix, inputFile.Filename));
using (var originalFileStream = new MemoryStream(inputFile.Contents))
using (var entryStream = entry.Open())
originalFileStream.CopyTo(entryStream);
}
return contentsLength;
}
private static long CompressOutputFiles(IEnumerable<LeafOutputFile> outputFiles, ZipArchive archive, string prefix = "")
{
var contentsLength = 0L;
foreach (var outputFile in outputFiles)
{
contentsLength += outputFile.FileContents.Contents.LongLength;
var entry = archive.CreateEntry(Path.Combine(prefix, outputFile.Filename));
using (var originalFileStream = new MemoryStream(outputFile.FileContents.Contents))
using (var entryStream = entry.Open())
originalFileStream.CopyTo(entryStream);
}
return contentsLength;
}
public int GetOutputFileSizeSum()
{
return OutputFiles.Sum(o => o.FileContents.Contents.Length);
}
}
}