Add run time to queue
This commit is contained in:
@@ -144,6 +144,7 @@
|
|||||||
<Compile Include="Utility\ParseInfoPropertyMatcherWithCache.cs" />
|
<Compile Include="Utility\ParseInfoPropertyMatcherWithCache.cs" />
|
||||||
<Compile Include="Utility\ReflectionExtensions.cs" />
|
<Compile Include="Utility\ReflectionExtensions.cs" />
|
||||||
<Compile Include="Utility\StringExtensions.cs" />
|
<Compile Include="Utility\StringExtensions.cs" />
|
||||||
|
<Compile Include="Utility\TimeSpanExtensions.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace LeafWeb.Core.Entities
|
namespace LeafWeb.Core.Entities
|
||||||
{
|
{
|
||||||
public class LeafInput
|
public abstract class LeafInputBase
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public virtual ICollection<LeafInputFile> InputFiles { get; set; }
|
|
||||||
public virtual ICollection<LeafOutputFile> OutputFiles { get; set; }
|
public virtual ICollection<LeafOutputFile> OutputFiles { get; set; }
|
||||||
public LeafOutputFile OutputErrorMessage => OutputFiles?.FirstOrDefault(f => f.IsErrorMessage);
|
public LeafOutputFile OutputErrorMessage => OutputFiles?.FirstOrDefault(f => f.IsErrorMessage);
|
||||||
public LeafOutputFile OutputWarningMessage => OutputFiles?.FirstOrDefault(f => f.IsWarningMessage);
|
public LeafOutputFile OutputWarningMessage => OutputFiles?.FirstOrDefault(f => f.IsWarningMessage);
|
||||||
public virtual ICollection<LeafInputData> LeafInputData { get; set; }
|
public bool HasOutputFiles => OutputFiles.Any();
|
||||||
|
public bool HasLeafChart => OutputFiles.Any(f => f.IsLeafChartFile);
|
||||||
|
|
||||||
public LeafInputStatusType CurrentStatus { get; set; }
|
public LeafInputStatusType CurrentStatus { get; set; }
|
||||||
public virtual ICollection<LeafInputStatus> StatusHistory { get; set; }
|
public virtual ICollection<LeafInputStatus> StatusHistory { get; set; }
|
||||||
@@ -36,12 +34,71 @@ namespace LeafWeb.Core.Entities
|
|||||||
|| IsFinishing
|
|| IsFinishing
|
||||||
|| IsCancelPending
|
|| IsCancelPending
|
||||||
|| IsCancelling;
|
|| IsCancelling;
|
||||||
public bool IsDeletable => !IsInProgress;
|
|
||||||
public bool HasOutputFiles => OutputFiles.Any();
|
|
||||||
public bool IsCancellable =>
|
public bool IsCancellable =>
|
||||||
IsRunning
|
IsRunning
|
||||||
|| IsPending;
|
|| IsPending;
|
||||||
|
|
||||||
|
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 TimeSpan TotalInProgressTime
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LeafInput : LeafInputBase
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<LeafInputFile> InputFiles { get; set; }
|
||||||
|
public virtual ICollection<LeafInputData> LeafInputData { get; set; }
|
||||||
|
|
||||||
[Required(ErrorMessage = "Name required")]
|
[Required(ErrorMessage = "Name required")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Remoting.Messaging;
|
||||||
|
|
||||||
|
namespace LeafWeb.Core.Utility
|
||||||
|
{
|
||||||
|
public static class TimeSpanExtensions
|
||||||
|
{
|
||||||
|
public static string ToReadableString(this TimeSpan span)
|
||||||
|
{
|
||||||
|
Func<int, string> pluralize = i => i > 1 ? "s" : string.Empty;
|
||||||
|
var formatted = string.Format("{0}{1}{2}{3}",
|
||||||
|
span.Duration().Days > 0 ? $"{span.Days:0} day{pluralize(span.Days)}, " : string.Empty,
|
||||||
|
span.Duration().Hours > 0 ? $"{span.Hours:0} hour{pluralize(span.Hours)}, " : string.Empty,
|
||||||
|
span.Duration().Minutes > 0 ? $"{span.Minutes:0} minute{pluralize(span.Minutes)}, " : string.Empty,
|
||||||
|
span.Duration().Seconds > 0 ? $"{span.Seconds:0} second{pluralize(span.Seconds)}" : string.Empty);
|
||||||
|
|
||||||
|
if (formatted.EndsWith(", ")) formatted = formatted.Substring(0, formatted.Length - 2);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(formatted)) formatted = "0 seconds";
|
||||||
|
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToRoundedReadableString(this TimeSpan span)
|
||||||
|
{
|
||||||
|
Func<int, string> pluralize = i => i > 1 ? "s" : string.Empty;
|
||||||
|
Func<int, string, string> formatTime = (i, s) => $"{i:0} {s}{pluralize(i)}";
|
||||||
|
if (span.Duration().Days > 0)
|
||||||
|
return formatTime(span.Days, "day");
|
||||||
|
if (span.Duration().Hours > 0)
|
||||||
|
return formatTime(span.Hours, "hour");
|
||||||
|
if (span.Duration().Minutes > 0)
|
||||||
|
return formatTime(span.Minutes, "minute");
|
||||||
|
if (span.Duration().Seconds > 0)
|
||||||
|
return formatTime(span.Seconds, "second");
|
||||||
|
|
||||||
|
return "0 seconds";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using System;
|
|
||||||
using LeafWeb.Core.Entities;
|
|
||||||
using LeafWeb.WebCms.Models;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.Tests.Models
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class ResultStatusViewModelTests
|
|
||||||
{
|
|
||||||
private LeafInput GetLeafInput()
|
|
||||||
{
|
|
||||||
return new LeafInput
|
|
||||||
{
|
|
||||||
CurrentStatus = LeafInputStatusType.Complete,
|
|
||||||
OutputFiles = new[] {new LeafOutputFile {Filename = "OutputFilename.txt"}},
|
|
||||||
Added = DateTime.Today,
|
|
||||||
Email = "test@email.com",
|
|
||||||
Identifier = "Ident I Fier",
|
|
||||||
Name = "My Name",
|
|
||||||
PhotosynthesisType = new PhotosynthesisType {Id = "1", Name = "1", SortOrder = 1},
|
|
||||||
InputFiles = new[]
|
|
||||||
{
|
|
||||||
new LeafInputFile
|
|
||||||
{
|
|
||||||
Filename = "MyFilename.ext",
|
|
||||||
Id = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void CanConstructFromLeafInputFile()
|
|
||||||
{
|
|
||||||
var leafInput = GetLeafInput();
|
|
||||||
var viewModel = new QueueItemViewModel(leafInput);
|
|
||||||
|
|
||||||
Assert.That(viewModel.CurrentStatus, Is.EqualTo(leafInput.CurrentStatus.ToString()));
|
|
||||||
Assert.That(viewModel.LeafInputId, Is.EqualTo(leafInput.Id));
|
|
||||||
//Assert.That(viewModel.LeafOutputFilenames, Has.Length.EqualTo(1));
|
|
||||||
Assert.That(viewModel.LeafInputIdentifier, Is.EqualTo(leafInput.Identifier));
|
|
||||||
Assert.That(viewModel.LeafInputSiteId, Is.EqualTo(leafInput.SiteId));
|
|
||||||
Assert.That(viewModel.LeafInputPhotosynthesisType, Is.EqualTo(leafInput.PhotosynthesisType.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void CanConstructFromLeafInputFile_Running()
|
|
||||||
{
|
|
||||||
var leafInput = GetLeafInput();
|
|
||||||
leafInput.CurrentStatus = LeafInputStatusType.Running;
|
|
||||||
leafInput.OutputFiles = new LeafOutputFile[0];
|
|
||||||
var viewModel = new QueueItemViewModel(leafInput);
|
|
||||||
|
|
||||||
Assert.That(viewModel.CurrentStatus, Is.EqualTo(LeafInputStatusType.Running.ToString()));
|
|
||||||
//Assert.That(viewModel.LeafOutputFilenames, Has.Length.EqualTo(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void CanConstructFromLeafInputFile_Error()
|
|
||||||
{
|
|
||||||
var leafInput = GetLeafInput();
|
|
||||||
leafInput.CurrentStatus = LeafInputStatusType.Exception;
|
|
||||||
leafInput.StatusHistory = new []
|
|
||||||
{
|
|
||||||
new LeafInputStatus
|
|
||||||
{
|
|
||||||
DateTime = DateTime.Today,
|
|
||||||
LeafInput = leafInput,
|
|
||||||
Description = "My Error",
|
|
||||||
Status = LeafInputStatusType.Exception
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var viewModel = new QueueItemViewModel(leafInput);
|
|
||||||
|
|
||||||
Assert.That(viewModel.CurrentStatus, Is.EqualTo(LeafInputStatusType.Exception.ToString()));
|
|
||||||
//Assert.That(viewModel.ErrorMessages[0], Is.EqualTo(leafInput.StatusHistory.First().Description));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -46,7 +46,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Models\LeafInputDetailsTests.cs" />
|
<Compile Include="Models\LeafInputDetailsTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Models\ResultStatusViewModelTests.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
C:\Users\poprhythm\AppData\Local\Temp\Temporary ASP.NET Files\vs\f80e29bb\faae20bf\App_Web_all.generated.cs.8f9494c4._retrxtu.dll
|
C:\Users\poprhythm\AppData\Local\Temp\Temporary ASP.NET Files\vs\f80e29bb\faae20bf\App_Web_all.generated.cs.8f9494c4.tezx-cyz.dll
|
||||||
@@ -3,6 +3,7 @@ using System.Web.Optimization;
|
|||||||
using System.Web.Routing;
|
using System.Web.Routing;
|
||||||
using Backload.Bundles;
|
using Backload.Bundles;
|
||||||
using LeafWeb.Core.DAL;
|
using LeafWeb.Core.DAL;
|
||||||
|
using LeafWeb.WebCms.Models;
|
||||||
using Umbraco.Core;
|
using Umbraco.Core;
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.App_Start
|
namespace LeafWeb.WebCms.App_Start
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ a.banner-link:hover {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
background: rgba(0, 0, 0, 0.6); }
|
background: rgba(0, 0, 0, 0.6); }
|
||||||
a.banner-link:hover .glyphicon {
|
a.banner-link:hover .glyphicon {
|
||||||
color: #a8ed4e; }
|
color: #a8ed4f; }
|
||||||
|
|
||||||
.headline-icon {
|
.headline-icon {
|
||||||
/* http://glyphicons.bootstrapcheatsheets.com/ */ }
|
/* http://glyphicons.bootstrapcheatsheets.com/ */ }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
h1{padding:24px 0 12px 0;}p{padding:12px 0;}footer{margin-top:24px !important;}.row-no-padding [class*="col-"]{padding-left:0 !important;padding-right:0 !important;}.home .dark .row:first-child .column:first-child h1{padding-top:0;}.home .blogarchive{padding-top:20px;}.top-buffer{margin-top:20px;}.detail-actions>a,.detail-actions>form>button{margin-top:20px;float:left;clear:left;}.banner-link{white-space:normal;padding:20px;background:#000;background:rgba(0,0,0,.5);-moz-border-radius:10px;border-radius:10px;}.banner-link .glyphicon{color:#8cc641;}a.banner-link:hover{text-decoration:none;background:rgba(0,0,0,.6);}a.banner-link:hover .glyphicon{color:#a8ed4e;}.headline-icon h1:after{color:rgba(172,214,118,.8);font-family:"Glyphicons Halflings";font-size:.8em;padding-left:10px;}.headline-icon.headline-icon-file h1:after{content:"";}.headline-icon.headline-icon-leaf h1:after{content:"";}.headline-icon.headline-icon-question h1:after{content:"";}.headline-icon.headline-icon-stats h1:after{content:"";}.headline-icon.headline-icon-user h1:after{content:"";}.headline-icon.headline-icon-list h1:after{content:"";}.status{white-space:nowrap;}.status:after{font-family:"Glyphicons Halflings";font-size:.8em;padding-left:5px;}.status.status-pending{color:#f0ad4e;}.status.status-pending:after{content:"";}.status.status-complete{color:#337ab7;}.status.status-complete:after{content:"";}.status.status-exception{color:#a94442;}.status.status-exception:after{content:"";}.status.status-running,.status.status-starting,.status.status-finishing{color:#3c763d;}.status.status-running:after{content:"";}.status.status-starting:after{content:"";}.status.status-finishing:after{content:"";}.status.status-cancelpending,.status.status-cancelling{color:#f0ad4e;}.status.status-cancelled{color:#ec971f;}.status.status-cancelpending:after{content:"";}.status.status-cancelling:after{content:"";}.status.status-cancelled:after{content:"";}#chart{padding-top:20px;}.btn-file{position:relative;overflow:hidden;}.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;outline:none;background:#fff;cursor:inherit;display:block;}form .validation-summary-errors ul{list-style-type:none;}.autocomplete-suggestions{border:1px solid #999;background:#fff;overflow:auto;}.autocomplete-suggestion{padding:2px 5px;white-space:nowrap;overflow:hidden;}.autocomplete-selected{background:#f0f0f0;}.autocomplete-suggestions strong{font-weight:normal;color:#39f;}.autocomplete-group{padding:2px 5px;}.autocomplete-group strong{display:block;border-bottom:1px solid #000;}.toggle{width:15px;}.dropdown-menu li form .btn-link{display:block;color:#333;clear:both;float:left;font-size:1rem;font-weight:normal;line-height:1.42857;min-width:160px;padding:3px 20px;text-align:left;white-space:nowrap;}.dropdown-menu li form .btn-link:focus,.dropdown-menu li form .btn-link:hover{text-decoration:none;color:#262626;background-color:#f5f5f5;}.divider-right{border-right:1px dashed #333;}
|
h1{padding:24px 0 12px 0;}p{padding:12px 0;}footer{margin-top:24px !important;}.row-no-padding [class*="col-"]{padding-left:0 !important;padding-right:0 !important;}.home .dark .row:first-child .column:first-child h1{padding-top:0;}.home .blogarchive{padding-top:20px;}.top-buffer{margin-top:20px;}.detail-actions>a,.detail-actions>form>button{margin-top:20px;float:left;clear:left;}.banner-link{white-space:normal;padding:20px;background:#000;background:rgba(0,0,0,.5);-moz-border-radius:10px;border-radius:10px;}.banner-link .glyphicon{color:#8cc641;}a.banner-link:hover{text-decoration:none;background:rgba(0,0,0,.6);}a.banner-link:hover .glyphicon{color:#a8ed4f;}.headline-icon h1:after{color:rgba(172,214,118,.8);font-family:"Glyphicons Halflings";font-size:.8em;padding-left:10px;}.headline-icon.headline-icon-file h1:after{content:"";}.headline-icon.headline-icon-leaf h1:after{content:"";}.headline-icon.headline-icon-question h1:after{content:"";}.headline-icon.headline-icon-stats h1:after{content:"";}.headline-icon.headline-icon-user h1:after{content:"";}.headline-icon.headline-icon-list h1:after{content:"";}.status{white-space:nowrap;}.status:after{font-family:"Glyphicons Halflings";font-size:.8em;padding-left:5px;}.status.status-pending{color:#f0ad4e;}.status.status-pending:after{content:"";}.status.status-complete{color:#337ab7;}.status.status-complete:after{content:"";}.status.status-exception{color:#a94442;}.status.status-exception:after{content:"";}.status.status-running,.status.status-starting,.status.status-finishing{color:#3c763d;}.status.status-running:after{content:"";}.status.status-starting:after{content:"";}.status.status-finishing:after{content:"";}.status.status-cancelpending,.status.status-cancelling{color:#f0ad4e;}.status.status-cancelled{color:#ec971f;}.status.status-cancelpending:after{content:"";}.status.status-cancelling:after{content:"";}.status.status-cancelled:after{content:"";}#chart{padding-top:20px;}.btn-file{position:relative;overflow:hidden;}.btn-file input[type=file]{position:absolute;top:0;right:0;min-width:100%;min-height:100%;font-size:100px;text-align:right;filter:alpha(opacity=0);opacity:0;outline:none;background:#fff;cursor:inherit;display:block;}form .validation-summary-errors ul{list-style-type:none;}.autocomplete-suggestions{border:1px solid #999;background:#fff;overflow:auto;}.autocomplete-suggestion{padding:2px 5px;white-space:nowrap;overflow:hidden;}.autocomplete-selected{background:#f0f0f0;}.autocomplete-suggestions strong{font-weight:normal;color:#39f;}.autocomplete-group{padding:2px 5px;}.autocomplete-group strong{display:block;border-bottom:1px solid #000;}.toggle{width:15px;}.dropdown-menu li form .btn-link{display:block;color:#333;clear:both;float:left;font-size:1rem;font-weight:normal;line-height:1.42857;min-width:160px;padding:3px 20px;text-align:left;white-space:nowrap;}.dropdown-menu li form .btn-link:focus,.dropdown-menu li form .btn-link:hover{text-decoration:none;color:#262626;background-color:#f5f5f5;}.divider-right{border-right:1px dashed #333;}
|
||||||
@@ -61,7 +61,7 @@ a.banner-link:hover {
|
|||||||
background: rgba(0, 0, 0, 0.6);
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
.glyphicon {
|
.glyphicon {
|
||||||
color: #a8ed4e;
|
color: #a8ed4f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ a.banner-link:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.status-running,&.status-starting,&.status-finishing {
|
&.status-running, &.status-starting, &.status-finishing {
|
||||||
color: #3c763d;
|
color: #3c763d;
|
||||||
}
|
}
|
||||||
&.status-running:after {
|
&.status-running:after {
|
||||||
@@ -139,7 +139,7 @@ a.banner-link:hover {
|
|||||||
content:"\e027";
|
content:"\e027";
|
||||||
}
|
}
|
||||||
|
|
||||||
&.status-cancelpending,&.status-cancelling {
|
&.status-cancelpending, &.status-cancelling {
|
||||||
color: #f0ad4e;
|
color: #f0ad4e;
|
||||||
}
|
}
|
||||||
&.status-cancelled {
|
&.status-cancelled {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ namespace LeafWeb.WebCms.Controllers
|
|||||||
if (!files.Any())
|
if (!files.Any())
|
||||||
ModelState.AddModelError("Files", "Must select at least one file");
|
ModelState.AddModelError("Files", "Must select at least one file");
|
||||||
|
|
||||||
|
// TODO: this keeps randomly not being mapable because string->bool binding fails. WHY
|
||||||
|
if (ModelState.ContainsKey("TermsOfService"))
|
||||||
|
ModelState["TermsOfService"].Errors.Clear();
|
||||||
|
|
||||||
if (ModelState.IsValid) // HttpParamMatch indicates it's backing out from Confirm
|
if (ModelState.IsValid) // HttpParamMatch indicates it's backing out from Confirm
|
||||||
{
|
{
|
||||||
// convert viewModel into Model
|
// convert viewModel into Model
|
||||||
|
|||||||
@@ -2,25 +2,23 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Web.Mvc;
|
using System.Web.Mvc;
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using log4net;
|
|
||||||
using LeafWeb.Core.Entities;
|
using LeafWeb.Core.Entities;
|
||||||
using LeafWeb.Core.Utility;
|
using LeafWeb.Core.Utility;
|
||||||
using LeafWeb.WebCms.App_Start;
|
|
||||||
using LeafWeb.WebCms.Models;
|
using LeafWeb.WebCms.Models;
|
||||||
using LeafWeb.WebCms.Services;
|
using LeafWeb.WebCms.Services;
|
||||||
using LeafWeb.WebCms.Services.PiscalQueue;
|
using LeafWeb.WebCms.Services.PiscalQueue;
|
||||||
|
using Umbraco.Web.Mvc;
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.Controllers
|
namespace LeafWeb.WebCms.Controllers
|
||||||
{
|
{
|
||||||
|
[MemberAuthorize]
|
||||||
public class QueueController : BaseController
|
public class QueueController : BaseController
|
||||||
{
|
{
|
||||||
public ActionResult Index()
|
public ActionResult Index()
|
||||||
{
|
{
|
||||||
var resultItems =
|
var resultItems =
|
||||||
DataService.GetLeafInputs()
|
DataService.GetLeafInputs()
|
||||||
.OrderByDescending(f => f.Id)
|
.OrderByDescending(f => f.Id);
|
||||||
.ToList()
|
|
||||||
.Select(leafInput => new QueueItemViewModel(leafInput));
|
|
||||||
|
|
||||||
string serviceDescription;
|
string serviceDescription;
|
||||||
try
|
try
|
||||||
@@ -134,27 +132,16 @@ namespace LeafWeb.WebCms.Controllers
|
|||||||
return RedirectToUmbracoPage(LeafWebPageIds.ManageQueue);
|
return RedirectToUmbracoPage(LeafWebPageIds.ManageQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leafInput.IsPending)
|
var cancelPendingSuccess = new PiscalQueueManager().CancelPending(id);
|
||||||
{
|
|
||||||
LogManager.GetLogger(LoggerName(RouteData)).DebugFormat("LeafInput: {0}, Set Cancelled from Pending", leafInput.Id);
|
|
||||||
DataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Cancelled,
|
|
||||||
"Emailing cancellation notification to user",
|
|
||||||
$"Email: \'{leafInput.Email}\'");
|
|
||||||
|
|
||||||
// send notification immediately
|
if (cancelPendingSuccess)
|
||||||
BackgroundJob.Enqueue<EmailNotificationService>(email => email.SendLeafWebCancelled(leafInput.Id));
|
|
||||||
SetStatusMessage($"Cancelling LeafInput '{leafInput.Identifier}'", StatusType.Success);
|
|
||||||
}
|
|
||||||
else if (leafInput.IsRunning)
|
|
||||||
{
|
{
|
||||||
DataService.SetLeafInputStatus(leafInput, LeafInputStatusType.CancelPending);
|
|
||||||
SetStatusMessage($"Cancelling LeafInput '{leafInput.Identifier}'", StatusType.Success);
|
SetStatusMessage($"Cancelling LeafInput '{leafInput.Identifier}'", StatusType.Success);
|
||||||
HangfireStartup.TriggerPiscalProcessQueue();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// don't allow to be cancelled if it isn't currently running
|
// don't allow to be cancelled if it isn't currently running
|
||||||
SetStatusMessage($"LeafInput '{leafInput.Identifier}' is not currently running!", StatusType.Error);
|
SetStatusMessage($"Couldn't cancel '{leafInput.Identifier}', is it currently running?", StatusType.Error);
|
||||||
}
|
}
|
||||||
return RedirectToCurrentUmbracoUrl();
|
return RedirectToCurrentUmbracoUrl();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ namespace LeafWeb.WebCms.Controllers
|
|||||||
where li.Added >= dateThreshold
|
where li.Added >= dateThreshold
|
||||||
orderby li.Id descending
|
orderby li.Id descending
|
||||||
select li
|
select li
|
||||||
).ToList()
|
);
|
||||||
.Select(leafInput => new QueueItemViewModel(leafInput));
|
|
||||||
return View(viewModel);
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using AutoMapper;
|
|
||||||
using LeafWeb.Core.Entities;
|
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.Models
|
|
||||||
{
|
|
||||||
public class QueueItemViewModel
|
|
||||||
{
|
|
||||||
public int LeafInputId { get; set; }
|
|
||||||
public string LeafInputName { get; set; }
|
|
||||||
public string LeafInputIdentifier { get; set; }
|
|
||||||
public string LeafInputSiteId { get; set; }
|
|
||||||
public string LeafInputPhotosynthesisType { get; set; }
|
|
||||||
public bool HasLeafChart { get; set; }
|
|
||||||
public bool IsRunning { get; set; }
|
|
||||||
public bool IsComplete { get; set; }
|
|
||||||
public bool IsDeletable { get; set; }
|
|
||||||
public bool IsCancellable { get; set; }
|
|
||||||
public bool IsPending { get; set; }
|
|
||||||
public bool HasOutputFiles { get; set; }
|
|
||||||
public string CurrentStatus { get; set; }
|
|
||||||
|
|
||||||
//public string[] ErrorMessages { get; set; }
|
|
||||||
//public string[] LeafOutputFilenames { get; set; }
|
|
||||||
//public bool HasLeafChartOutputFile { get; set; }
|
|
||||||
|
|
||||||
static QueueItemViewModel()
|
|
||||||
{
|
|
||||||
Mapper.CreateMap<LeafInput, QueueItemViewModel>()
|
|
||||||
.ForMember(dest => dest.LeafInputId, opt => opt.MapFrom(src => src.Id))
|
|
||||||
.ForMember(dest => dest.HasLeafChart, opt => opt.ResolveUsing(src => src.OutputFiles.Any(o => o.IsLeafChartFile)))
|
|
||||||
//.ForMember(dest => dest.LeafOutputFilenames,
|
|
||||||
// opt => opt.ResolveUsing(
|
|
||||||
// src =>
|
|
||||||
// src.OutputFiles?
|
|
||||||
// .Select(o => o.Filename)
|
|
||||||
// .ToArray()
|
|
||||||
// ?? new string[] {}))
|
|
||||||
//.ForMember(dest => dest.HasLeafChartOutputFile,
|
|
||||||
// opt => opt.ResolveUsing(
|
|
||||||
// file => file.OutputFiles?.Any(o => o.IsLeafChartFile)))
|
|
||||||
.ForMember(dest => dest.LeafInputName, opt => opt.MapFrom(src => src.Name))
|
|
||||||
.ForMember(dest => dest.LeafInputIdentifier, opt => opt.MapFrom(src => src.Identifier))
|
|
||||||
.ForMember(dest => dest.LeafInputSiteId, opt => opt.MapFrom(src => src.SiteId))
|
|
||||||
.ForMember(dest => dest.LeafInputPhotosynthesisType, opt => opt.MapFrom(src => src.PhotosynthesisType.Name))
|
|
||||||
//.ForMember(dest => dest.ErrorMessages,
|
|
||||||
// opt => opt.ResolveUsing(
|
|
||||||
// src =>
|
|
||||||
// src.StatusHistory?
|
|
||||||
// .Where(sh => sh.Status == LeafInputStatusType.Exception)
|
|
||||||
// .Select(sh => sh.Description)
|
|
||||||
// .ToArray()
|
|
||||||
// ?? new string[] {}))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueueItemViewModel(LeafInput leafInput)
|
|
||||||
{
|
|
||||||
Mapper.Map(leafInput, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using LeafWeb.Core.Entities;
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.Models
|
namespace LeafWeb.WebCms.Models
|
||||||
{
|
{
|
||||||
@@ -6,6 +7,6 @@ namespace LeafWeb.WebCms.Models
|
|||||||
{
|
{
|
||||||
public string ServerDescription { get; set; }
|
public string ServerDescription { get; set; }
|
||||||
public string ServerStatus { get; set; }
|
public string ServerStatus { get; set; }
|
||||||
public IEnumerable<QueueItemViewModel> Items { get; set; }
|
public IEnumerable<LeafInput> Items { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,17 +3,18 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using LeafWeb.Core.Entities;
|
using LeafWeb.Core.Entities;
|
||||||
using LeafWeb.Core.Remote;
|
using LeafWeb.Core.Remote;
|
||||||
|
using LeafWeb.WebCms.App_Start;
|
||||||
|
|
||||||
namespace LeafWeb.WebCms.Services.PiscalQueue
|
namespace LeafWeb.WebCms.Services.PiscalQueue
|
||||||
{
|
{
|
||||||
public class PiscalQueueManager : PiscalQueueBase
|
public class PiscalQueueManager : PiscalQueueBase
|
||||||
{
|
{
|
||||||
private static readonly object ProcessQueueLock = new object();
|
private static readonly object Lock = new object();
|
||||||
|
|
||||||
public void ProcessQueue()
|
public void ProcessQueue()
|
||||||
{
|
{
|
||||||
// prevent multiple entry into processing the queue
|
// prevent multiple entry into processing the queue
|
||||||
if (Monitor.TryEnter(ProcessQueueLock))
|
if (Monitor.TryEnter(Lock))
|
||||||
{
|
{
|
||||||
Logger.DebugFormat("ProcessQueue entered");
|
Logger.DebugFormat("ProcessQueue entered");
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ namespace LeafWeb.WebCms.Services.PiscalQueue
|
|||||||
{
|
{
|
||||||
Logger.DebugFormat("ProcessQueue exit");
|
Logger.DebugFormat("ProcessQueue exit");
|
||||||
|
|
||||||
Monitor.Exit(ProcessQueueLock);
|
Monitor.Exit(Lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -40,6 +41,46 @@ namespace LeafWeb.WebCms.Services.PiscalQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CancelPending(int leafInputId)
|
||||||
|
{
|
||||||
|
if (Monitor.TryEnter(Lock, TimeSpan.FromSeconds(1)))
|
||||||
|
{
|
||||||
|
Logger.DebugFormat("CancelLeafInput entered");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var leafInput = DataService.GetLeafInput(leafInputId);
|
||||||
|
if (!leafInput.IsCancellable)
|
||||||
|
return false;
|
||||||
|
if (leafInput.IsPending)
|
||||||
|
{
|
||||||
|
Logger.DebugFormat("LeafInput: {0}, Set Cancelled from Pending", leafInput.Id);
|
||||||
|
DataService.SetLeafInputStatus(leafInput, LeafInputStatusType.Cancelled,
|
||||||
|
"Emailing cancellation notification to user",
|
||||||
|
$"Email: \'{leafInput.Email}\'");
|
||||||
|
|
||||||
|
// send notification immediately
|
||||||
|
BackgroundJobEnqueueRetry<EmailNotificationService>(email => email.SendLeafWebCancelled(leafInput.Id));
|
||||||
|
}
|
||||||
|
else if (leafInput.IsRunning)
|
||||||
|
{
|
||||||
|
DataService.SetLeafInputStatus(leafInput, LeafInputStatusType.CancelPending);
|
||||||
|
HangfireStartup.TriggerPiscalProcessQueue();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Logger.DebugFormat("CancelLeafInput exit");
|
||||||
|
|
||||||
|
Monitor.Exit(Lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.DebugFormat("CancelLeafInput locked");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void StartCancelPending()
|
private void StartCancelPending()
|
||||||
{
|
{
|
||||||
var cancelPendingLeafInputs =
|
var cancelPendingLeafInputs =
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
<a href="@page.Url">
|
<a href="@page.Url">
|
||||||
@page.Name
|
@page.Name
|
||||||
@if (library.IsProtected(page.id, page.path)) {
|
@if (library.IsProtected(page.id, page.path)) {
|
||||||
<i class="fa fa-minus-circle fa-fw text-danger" aria-hidden="true"></i>
|
<i class="fa fa-lock fa-fw text-danger" aria-hidden="true"></i>
|
||||||
}
|
}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,14 @@
|
|||||||
<dd>@Model.ServerDescription</dd>
|
<dd>@Model.ServerDescription</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
|
||||||
@grid.Table(columns:
|
@grid.Table(columns:
|
||||||
grid.Columns(
|
grid.Columns(
|
||||||
grid.Column("LeafInputIdentifier", "Identifier"),
|
grid.Column("Identifier", "Identifier"),
|
||||||
grid.Column("LeafInputSiteId", "Site Id"),
|
grid.Column("SiteId", "Site Id"),
|
||||||
grid.Column("LeafInputName", "Submitted By"),
|
grid.Column("Name", "Submitted By"),
|
||||||
grid.Column("CurrentStatus", "Status", item => Html.Partial("DisplayTemplates/_LeafInputStatus", (string)item.CurrentStatus)),
|
//grid.Column("FileCount", "Input files"),
|
||||||
|
grid.Column("TotalInProgressTime", "In-Progress Time", item => ((TimeSpan)item.TotalInProgressTime).ToRoundedReadableString()),
|
||||||
|
grid.Column("CurrentStatus", "Status", item => Html.Partial("DisplayTemplates/_LeafInputStatus", (string)item.CurrentStatus.ToString())),
|
||||||
grid.Column("Total Results: " + Model.Items.Count(), format: item => Btns(item))),
|
grid.Column("Total Results: " + Model.Items.Count(), format: item => Btns(item))),
|
||||||
htmlAttributes: new { @class = "table table-striped table-bordered table-hover table-condensed" }
|
htmlAttributes: new { @class = "table table-striped table-bordered table-hover table-condensed" }
|
||||||
)
|
)
|
||||||
@@ -54,47 +55,47 @@
|
|||||||
|
|
||||||
@helper DetailsLink(dynamic item)
|
@helper DetailsLink(dynamic item)
|
||||||
{
|
{
|
||||||
@Html.Partial("DisplayTemplates/_DetailsLink", (int)item.LeafInputId)
|
@Html.Partial("DisplayTemplates/_DetailsLink", (int)item.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@helper ChartLink(dynamic item)
|
@helper ChartLink(dynamic item)
|
||||||
{
|
{
|
||||||
@Html.Partial("DisplayTemplates/_ChartLink", (int)item.LeafInputId)
|
@Html.Partial("DisplayTemplates/_ChartLink", (int)item.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@helper SetPriorityHigh(dynamic item, string label)
|
@helper SetPriorityHigh(dynamic item, string label)
|
||||||
{
|
{
|
||||||
<a href="@Url.Action("SetPriorityHigh", "Queue", new {id = item.LeafInputId})">
|
<a href="@Url.Action("SetPriorityHigh", "Queue", new {id = item.Id})">
|
||||||
<span class="glyphicon glyphicon-arrow-up"></span> @label
|
<span class="glyphicon glyphicon-arrow-up"></span> @label
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@helper SetPriorityLow(dynamic item, string label)
|
@helper SetPriorityLow(dynamic item, string label)
|
||||||
{
|
{
|
||||||
<a href="@Url.Action("SetPriorityLow", "Queue", new {id = item.LeafInputId})">
|
<a href="@Url.Action("SetPriorityLow", "Queue", new {id = item.Id})">
|
||||||
<span class="glyphicon glyphicon-arrow-down"></span> @label
|
<span class="glyphicon glyphicon-arrow-down"></span> @label
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
@helper DownloadInput(dynamic item)
|
@helper DownloadInput(dynamic item)
|
||||||
{
|
{
|
||||||
<a href="@Url.Action("DownloadInput", "Queue", new {id = item.LeafInputId})">
|
<a href="@Url.Action("DownloadInput", "Queue", new {id = item.Id})">
|
||||||
<span class="glyphicon glyphicon-download"></span> Input
|
<span class="glyphicon glyphicon-download"></span> Input
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@helper DownloadOutputToUser(dynamic item)
|
@helper DownloadOutputToUser(dynamic item)
|
||||||
{
|
{
|
||||||
<a href="@Url.Action("DownloadOutputToUser", "Queue", new {id = item.LeafInputId})">
|
<a href="@Url.Action("DownloadOutputToUser", "Queue", new {id = item.Id})">
|
||||||
<span class="glyphicon glyphicon-download"></span> ToUser
|
<span class="glyphicon glyphicon-download"></span> ToUser
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@helper DeleteLink(dynamic item)
|
@helper DeleteLink(dynamic item)
|
||||||
{
|
{
|
||||||
@Html.Partial("DisplayTemplates/_DeleteForm", (Tuple<int, string, bool>)Tuple.Create(item.LeafInputId, item.LeafInputIdentifier, item.IsDeletable))
|
@Html.Partial("DisplayTemplates/_DeleteForm", (Tuple<int, string, bool>)Tuple.Create(item.Id, item.Identifier, item.IsDeletable))
|
||||||
}
|
}
|
||||||
|
|
||||||
@helper CancelLink(dynamic item)
|
@helper CancelLink(dynamic item)
|
||||||
{
|
{
|
||||||
@Html.Partial("DisplayTemplates/_CancelForm", (Tuple<int, string>)Tuple.Create(item.LeafInputId, item.LeafInputIdentifier))
|
@Html.Partial("DisplayTemplates/_CancelForm", (Tuple<int, string>)Tuple.Create(item.Id, item.Identifier))
|
||||||
}
|
}
|
||||||
|
|
||||||
@helper DisableItem(bool disabled)
|
@helper DisableItem(bool disabled)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<QueueItemViewModel>
|
@model IOrderedQueryable<LeafWeb.Core.Entities.LeafInput>
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var grid = new WebGrid(Model, rowsPerPage: 45);
|
var grid = new WebGrid(Model, rowsPerPage: 45);
|
||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
@grid.Table(columns:
|
@grid.Table(columns:
|
||||||
grid.Columns(
|
grid.Columns(
|
||||||
grid.Column("LeafInputIdentifier", "Identifier"),
|
grid.Column("Identifier", "Identifier"),
|
||||||
grid.Column("LeafInputSiteId", "Site Id"),
|
grid.Column("SiteId", "Site Id"),
|
||||||
//grid.Column("LeafInputName", "Submitted By"),
|
//grid.Column("Name", "Submitted By"),
|
||||||
grid.Column("CurrentStatus", "Status", item => Html.Partial("DisplayTemplates/_LeafInputStatus", (string)item.CurrentStatus)),
|
grid.Column("CurrentStatus", "Status", item => Html.Partial("DisplayTemplates/_LeafInputStatus", (string)item.CurrentStatus.ToString())),
|
||||||
grid.Column("", "", item => ChartLink(item))
|
grid.Column("", "", item => ChartLink(item))
|
||||||
),
|
),
|
||||||
htmlAttributes: new { @class = "table table-striped table-bordered table-hover table-condensed" }
|
htmlAttributes: new { @class = "table table-striped table-bordered table-hover table-condensed" }
|
||||||
@@ -18,5 +18,5 @@
|
|||||||
|
|
||||||
@helper ChartLink(dynamic item)
|
@helper ChartLink(dynamic item)
|
||||||
{
|
{
|
||||||
@Html.Partial("DisplayTemplates/_ChartButton", (int)item.LeafInputId, new ViewDataDictionary { { "Disabled", !item.HasLeafChart }, {"xs", true} })
|
@Html.Partial("DisplayTemplates/_ChartButton", (int)item.Id, new ViewDataDictionary { { "Disabled", !item.HasLeafChart }, {"xs", true} })
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<LeafWeb.WebCms.Models.LeafInputStatusViewModel>
|
@model IEnumerable<LeafInputStatusViewModel>
|
||||||
@{
|
@{
|
||||||
Layout = "~/Views/Shared/DisplayTemplates/_FieldLayout.cshtml";
|
Layout = "~/Views/Shared/DisplayTemplates/_FieldLayout.cshtml";
|
||||||
var grid = new WebGrid(Model, rowsPerPage: 45)
|
var grid = new WebGrid(Model, rowsPerPage: 45)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
var identifier = Model.Item2;
|
var identifier = Model.Item2;
|
||||||
}
|
}
|
||||||
@using (Html.BeginUmbracoForm<QueueController>("Cancel", null,
|
@using (Html.BeginUmbracoForm<QueueController>("Cancel", null,
|
||||||
new { @class = "confirm", confirm_msg = "Cancelling cannot be undone! Confirm cancelling '" + identifier + "'." }))
|
new { @class = "confirm clearfix", confirm_msg = "Cancelling cannot be undone! Confirm cancelling '" + identifier + "'." }))
|
||||||
{
|
{
|
||||||
<input type="hidden" name="id" value="@leafInputId"/>
|
<input type="hidden" name="id" value="@leafInputId"/>
|
||||||
<button type="submit" class="btn btn-link">
|
<button type="submit" class="btn btn-link">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
var isDeletable = Model.Item3;
|
var isDeletable = Model.Item3;
|
||||||
}
|
}
|
||||||
@using (Html.BeginUmbracoForm<QueueController>("Delete", null,
|
@using (Html.BeginUmbracoForm<QueueController>("Delete", null,
|
||||||
new { @class = "confirm", confirm_msg = "Deletion cannot be undone! Confirm deleting '" + identifier + "'." }))
|
new { @class = "confirm clearfix", confirm_msg = "Deletion cannot be undone! Confirm deleting '" + identifier + "'." }))
|
||||||
{
|
{
|
||||||
<input type="hidden" name="id" value="@leafInputId"/>
|
<input type="hidden" name="id" value="@leafInputId"/>
|
||||||
<button type="submit" class="btn btn-link" @{if (!isDeletable) { <text> disabled="disabled" </text> }}>
|
<button type="submit" class="btn btn-link" @{if (!isDeletable) { <text> disabled="disabled" </text> }}>
|
||||||
|
|||||||
@@ -23,8 +23,9 @@
|
|||||||
<add namespace="umbraco" />
|
<add namespace="umbraco" />
|
||||||
<add namespace="Examine" />
|
<add namespace="Examine" />
|
||||||
<add namespace="WebGridBootstrapPager" />
|
<add namespace="WebGridBootstrapPager" />
|
||||||
<add namespace="LeafWeb.WebCms.Models" />
|
<add namespace="LeafWeb.Core.Utility" />
|
||||||
<add namespace="Umbraco.Web.PublishedContentModels" />
|
<add namespace="LeafWeb.WebCms.Models" />
|
||||||
|
<add namespace="Umbraco.Web.PublishedContentModels" />
|
||||||
</namespaces>
|
</namespaces>
|
||||||
</pages>
|
</pages>
|
||||||
</system.web.webPages.razor>
|
</system.web.webPages.razor>
|
||||||
|
|||||||
@@ -924,7 +924,6 @@
|
|||||||
<Compile Include="Models\LeafInputDetails.cs" />
|
<Compile Include="Models\LeafInputDetails.cs" />
|
||||||
<Compile Include="Models\LeafInputCreate.cs" />
|
<Compile Include="Models\LeafInputCreate.cs" />
|
||||||
<Compile Include="Models\LeafInputStatusViewModel.cs" />
|
<Compile Include="Models\LeafInputStatusViewModel.cs" />
|
||||||
<Compile Include="Models\QueueItemViewModel.cs" />
|
|
||||||
<Compile Include="Models\QueueViewModel.cs" />
|
<Compile Include="Models\QueueViewModel.cs" />
|
||||||
<Compile Include="Models\SelectListViewModel.cs" />
|
<Compile Include="Models\SelectListViewModel.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user