Remaining time estimate

This commit is contained in:
2017-02-03 22:26:00 -05:00
parent 77a35e87d3
commit b4440ecd95
27 changed files with 3579 additions and 550 deletions
+9
View File
@@ -30,6 +30,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Moq, Version=4.5.30.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.5.30\lib\net45\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.2.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
@@ -52,6 +60,7 @@
<Compile Include="Remote\PiscalSshClientTests.cs" />
<Compile Include="Utility\MemoizerTests.cs" />
<Compile Include="Utility\StringExtensionsTests.cs" />
<Compile Include="Utility\TimeInProgressEstimaterTests.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Parsers\LeafInputData\LeafInput-valid.csv">
@@ -0,0 +1,38 @@
using System;
using System.Linq;
using LeafWeb.Core.Entities;
using LeafWeb.Core.Utility;
using Moq;
using NUnit.Framework;
namespace LeafWeb.Core.Tests.Utility
{
[TestFixture]
public class TimeInProgressEstimaterTests
{
[Test]
public void EstimateTest()
{
Func<int, int> scaleFunc = i => i*i;
var obervations =
from i in Enumerable.Range(1, 10)
let time = scaleFunc(i)
select Mock.Of<ILeafInput>(li =>
li.InputFiles.Count == i &&
li.TimeInProgress == TimeSpan.FromMinutes(time));
var estimater = new TimeInProgressEstimater(obervations.ToArray());
Action<int> est = i =>
{
var query = Mock.Of<ILeafInput>(li => li.InputFiles.Count == i);
var estimate = estimater.EstimateTimeInProgress(query);
Console.WriteLine($"{i} => {estimate}. {TimeSpan.FromMinutes(scaleFunc(i))}");
};
for (var i = 1; i < 30; i++)
est(i);
}
}
}
+2
View File
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Castle.Core" version="3.3.3" targetFramework="net45" />
<package id="Moq" version="4.5.30" targetFramework="net45" />
<package id="NUnit" version="3.2.1" targetFramework="net45" />
</packages>
+6
View File
@@ -58,6 +58,10 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\References\log4net.dll</HintPath>
</Reference>
<Reference Include="MathNet.Numerics, Version=3.17.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MathNet.Numerics.3.17.0\lib\net40\MathNet.Numerics.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MlkPwgen, Version=0.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MlkPwgen.0.2.0.0\lib\net45\MlkPwgen.dll</HintPath>
<Private>True</Private>
@@ -84,6 +88,7 @@
<Compile Include="DAL\DataService.cs" />
<Compile Include="DAL\LeafWebContext.cs" />
<Compile Include="DAL\LeafWebInitializer.cs" />
<Compile Include="Entities\ILeafInput.cs" />
<Compile Include="Entities\LeafGasComparisonPhotosyntheticInfo.cs" />
<Compile Include="Entities\LeafGasComparisonFittingInfo.cs" />
<Compile Include="Entities\LeafGasComparison.cs" />
@@ -139,6 +144,7 @@
<Compile Include="Utility\BoolTypeConverter.cs" />
<Compile Include="Parsers\CsvParserBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utility\TimeInProgressEstimater.cs" />
<Compile Include="Utility\FileUtility.cs" />
<Compile Include="Utility\Memoizer.cs" />
<Compile Include="Utility\ParseInfoAttribute.cs" />
+13 -2
View File
@@ -57,8 +57,19 @@ namespace LeafWeb.Core.DAL
public IQueryable<LeafInput> GetLeafInputsOrdered()
{
return _db.LeafInputs
.OrderByDescending(li => li.CurrentStatus == LeafInputStatusType.Pending)
.ThenByDescending(li => li.PendingPriority)
// first by in-progress items
.OrderByDescending(li =>
li.CurrentStatus == LeafInputStatusType.Running ||
li.CurrentStatus == LeafInputStatusType.Starting ||
li.CurrentStatus == LeafInputStatusType.Finishing ||
li.CurrentStatus == LeafInputStatusType.Cancelling ||
li.CurrentStatus == LeafInputStatusType.CancelPending)
// then by pending, by priority
.ThenByDescending(li =>
li.CurrentStatus == LeafInputStatusType.Pending
? (int)li.PendingPriority
: int.MinValue)
// then the rest by the order they're added in
.ThenByDescending(li => li.Id);
}
+26
View File
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
namespace LeafWeb.Core.Entities
{
public interface ILeafInput
{
int Id { get; set; }
ICollection<LeafInputFile> InputFiles { get; set; }
ICollection<LeafInputData> LeafInputData { get; set; }
ICollection<LeafOutputFile> OutputFiles { get; set; }
string Name { get; set; }
string Email { get; set; }
string Identifier { get; set; }
string SiteId { get; set; }
string UniqueToken { get; set; }
PhotosynthesisType PhotosynthesisType { get; set; }
DateTime Added { get; set; }
LeafInputStatusType CurrentStatus { get; set; }
ICollection<LeafInputStatus> StatusHistory { get; set; }
Priority PendingPriority { get; set; }
LeafOutputFile OutputErrorMessage { get; }
LeafOutputFile OutputWarningMessage { get; }
TimeSpan TimeInProgress { get; }
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ using System.Linq;
namespace LeafWeb.Core.Entities
{
public class LeafInput
public class LeafInput : ILeafInput
{
public int Id { get; set; }
+53
View File
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using LeafWeb.Core.Entities;
using MathNet.Numerics;
namespace LeafWeb.Core.Utility
{
public class TimeInProgressEstimater
{
private readonly double[] _xData;
private readonly double[] _yData;
public TimeInProgressEstimater(IEnumerable<ILeafInput> leafInputs)
{
var xData = new List<double>();
var yData = new List<double>();
foreach (var leafInput in leafInputs)
{
xData.Add(leafInput.InputFiles.Count);
yData.Add(ConvertFromTimeSpan(leafInput.TimeInProgress));
}
_xData = xData.ToArray();
_yData = yData.ToArray();
}
public TimeSpan EstimateTimeInProgress(ILeafInput leafInput)
{
//var lineFunc = Fit.LineFunc(_xData, _yData);
var lineFunc = Fit.PolynomialFunc(_xData, _yData, 3);
var estimate = lineFunc(leafInput.InputFiles.Count);
return ConvertToTimeSpan(estimate);
}
private static double ConvertFromTimeSpan(TimeSpan timeSpan)
{
return timeSpan.TotalSeconds;
}
private static TimeSpan ConvertToTimeSpan(double estimate)
{
int int32;
try
{
int32 = Convert.ToInt32(estimate);
}
catch (OverflowException)
{
int32 = int.MaxValue;
}
return TimeSpan.FromSeconds(int32);
}
}
}
+2
View File
@@ -25,6 +25,8 @@ namespace LeafWeb.Core.Utility
{
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 > 90)
return formatTime(span.Days/30, "month");
if (span.Duration().Days > 0)
return formatTime(span.Days, "day");
if (span.Duration().Hours > 0)
+1
View File
@@ -5,6 +5,7 @@
<package id="EntityFramework" version="6.1.3" targetFramework="net45" />
<package id="fasterflect" version="2.1.3" targetFramework="net45" />
<package id="log4net" version="1.2.10" targetFramework="net45" />
<package id="MathNet.Numerics" version="3.17.0" targetFramework="net45" />
<package id="MlkPwgen" version="0.2.0.0" targetFramework="net45" />
<package id="SSH.NET" version="2013.4.7" targetFramework="net45" />
</packages>
+1 -1
View File
@@ -1 +1 @@
C:\Users\poprhythm\AppData\Local\Temp\Temporary ASP.NET Files\vs\f80e29bb\faae20bf\App_Web_all.generated.cs.8f9494c4.brz9snlt.dll
C:\Users\poprhythm\AppData\Local\Temp\Temporary ASP.NET Files\vs\f80e29bb\faae20bf\App_Web_all.generated.cs.8f9494c4.ivc2jw58.dll
+675 -10
View File
@@ -1,13 +1,13 @@
/*!
* Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
* Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.2.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');
src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -64,6 +64,19 @@
border: solid 0.08em #eeeeee;
border-radius: .1em;
}
.fa-pull-left {
float: left;
}
.fa-pull-right {
float: right;
}
.fa.fa-pull-left {
margin-right: .3em;
}
.fa.fa-pull-right {
margin-left: .3em;
}
/* Deprecated as of 4.4.0 */
.pull-right {
float: right;
}
@@ -80,6 +93,10 @@
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8);
}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
@@ -101,31 +118,31 @@
}
}
.fa-rotate-90 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.fa-rotate-180 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.fa-rotate-270 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.fa-flip-horizontal {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.fa-flip-vertical {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
@@ -610,6 +627,7 @@
.fa-twitter:before {
content: "\f099";
}
.fa-facebook-f:before,
.fa-facebook:before {
content: "\f09a";
}
@@ -622,6 +640,7 @@
.fa-credit-card:before {
content: "\f09d";
}
.fa-feed:before,
.fa-rss:before {
content: "\f09e";
}
@@ -1259,7 +1278,8 @@
.fa-male:before {
content: "\f183";
}
.fa-gittip:before {
.fa-gittip:before,
.fa-gratipay:before {
content: "\f184";
}
.fa-sun-o:before {
@@ -1363,7 +1383,7 @@
.fa-digg:before {
content: "\f1a6";
}
.fa-pied-piper:before {
.fa-pied-piper-pp:before {
content: "\f1a7";
}
.fa-pied-piper-alt:before {
@@ -1489,6 +1509,7 @@
content: "\f1ce";
}
.fa-ra:before,
.fa-resistance:before,
.fa-rebel:before {
content: "\f1d0";
}
@@ -1502,6 +1523,8 @@
.fa-git:before {
content: "\f1d3";
}
.fa-y-combinator-square:before,
.fa-yc-square:before,
.fa-hacker-news:before {
content: "\f1d4";
}
@@ -1670,3 +1693,645 @@
.fa-meanpath:before {
content: "\f20c";
}
.fa-buysellads:before {
content: "\f20d";
}
.fa-connectdevelop:before {
content: "\f20e";
}
.fa-dashcube:before {
content: "\f210";
}
.fa-forumbee:before {
content: "\f211";
}
.fa-leanpub:before {
content: "\f212";
}
.fa-sellsy:before {
content: "\f213";
}
.fa-shirtsinbulk:before {
content: "\f214";
}
.fa-simplybuilt:before {
content: "\f215";
}
.fa-skyatlas:before {
content: "\f216";
}
.fa-cart-plus:before {
content: "\f217";
}
.fa-cart-arrow-down:before {
content: "\f218";
}
.fa-diamond:before {
content: "\f219";
}
.fa-ship:before {
content: "\f21a";
}
.fa-user-secret:before {
content: "\f21b";
}
.fa-motorcycle:before {
content: "\f21c";
}
.fa-street-view:before {
content: "\f21d";
}
.fa-heartbeat:before {
content: "\f21e";
}
.fa-venus:before {
content: "\f221";
}
.fa-mars:before {
content: "\f222";
}
.fa-mercury:before {
content: "\f223";
}
.fa-intersex:before,
.fa-transgender:before {
content: "\f224";
}
.fa-transgender-alt:before {
content: "\f225";
}
.fa-venus-double:before {
content: "\f226";
}
.fa-mars-double:before {
content: "\f227";
}
.fa-venus-mars:before {
content: "\f228";
}
.fa-mars-stroke:before {
content: "\f229";
}
.fa-mars-stroke-v:before {
content: "\f22a";
}
.fa-mars-stroke-h:before {
content: "\f22b";
}
.fa-neuter:before {
content: "\f22c";
}
.fa-genderless:before {
content: "\f22d";
}
.fa-facebook-official:before {
content: "\f230";
}
.fa-pinterest-p:before {
content: "\f231";
}
.fa-whatsapp:before {
content: "\f232";
}
.fa-server:before {
content: "\f233";
}
.fa-user-plus:before {
content: "\f234";
}
.fa-user-times:before {
content: "\f235";
}
.fa-hotel:before,
.fa-bed:before {
content: "\f236";
}
.fa-viacoin:before {
content: "\f237";
}
.fa-train:before {
content: "\f238";
}
.fa-subway:before {
content: "\f239";
}
.fa-medium:before {
content: "\f23a";
}
.fa-yc:before,
.fa-y-combinator:before {
content: "\f23b";
}
.fa-optin-monster:before {
content: "\f23c";
}
.fa-opencart:before {
content: "\f23d";
}
.fa-expeditedssl:before {
content: "\f23e";
}
.fa-battery-4:before,
.fa-battery:before,
.fa-battery-full:before {
content: "\f240";
}
.fa-battery-3:before,
.fa-battery-three-quarters:before {
content: "\f241";
}
.fa-battery-2:before,
.fa-battery-half:before {
content: "\f242";
}
.fa-battery-1:before,
.fa-battery-quarter:before {
content: "\f243";
}
.fa-battery-0:before,
.fa-battery-empty:before {
content: "\f244";
}
.fa-mouse-pointer:before {
content: "\f245";
}
.fa-i-cursor:before {
content: "\f246";
}
.fa-object-group:before {
content: "\f247";
}
.fa-object-ungroup:before {
content: "\f248";
}
.fa-sticky-note:before {
content: "\f249";
}
.fa-sticky-note-o:before {
content: "\f24a";
}
.fa-cc-jcb:before {
content: "\f24b";
}
.fa-cc-diners-club:before {
content: "\f24c";
}
.fa-clone:before {
content: "\f24d";
}
.fa-balance-scale:before {
content: "\f24e";
}
.fa-hourglass-o:before {
content: "\f250";
}
.fa-hourglass-1:before,
.fa-hourglass-start:before {
content: "\f251";
}
.fa-hourglass-2:before,
.fa-hourglass-half:before {
content: "\f252";
}
.fa-hourglass-3:before,
.fa-hourglass-end:before {
content: "\f253";
}
.fa-hourglass:before {
content: "\f254";
}
.fa-hand-grab-o:before,
.fa-hand-rock-o:before {
content: "\f255";
}
.fa-hand-stop-o:before,
.fa-hand-paper-o:before {
content: "\f256";
}
.fa-hand-scissors-o:before {
content: "\f257";
}
.fa-hand-lizard-o:before {
content: "\f258";
}
.fa-hand-spock-o:before {
content: "\f259";
}
.fa-hand-pointer-o:before {
content: "\f25a";
}
.fa-hand-peace-o:before {
content: "\f25b";
}
.fa-trademark:before {
content: "\f25c";
}
.fa-registered:before {
content: "\f25d";
}
.fa-creative-commons:before {
content: "\f25e";
}
.fa-gg:before {
content: "\f260";
}
.fa-gg-circle:before {
content: "\f261";
}
.fa-tripadvisor:before {
content: "\f262";
}
.fa-odnoklassniki:before {
content: "\f263";
}
.fa-odnoklassniki-square:before {
content: "\f264";
}
.fa-get-pocket:before {
content: "\f265";
}
.fa-wikipedia-w:before {
content: "\f266";
}
.fa-safari:before {
content: "\f267";
}
.fa-chrome:before {
content: "\f268";
}
.fa-firefox:before {
content: "\f269";
}
.fa-opera:before {
content: "\f26a";
}
.fa-internet-explorer:before {
content: "\f26b";
}
.fa-tv:before,
.fa-television:before {
content: "\f26c";
}
.fa-contao:before {
content: "\f26d";
}
.fa-500px:before {
content: "\f26e";
}
.fa-amazon:before {
content: "\f270";
}
.fa-calendar-plus-o:before {
content: "\f271";
}
.fa-calendar-minus-o:before {
content: "\f272";
}
.fa-calendar-times-o:before {
content: "\f273";
}
.fa-calendar-check-o:before {
content: "\f274";
}
.fa-industry:before {
content: "\f275";
}
.fa-map-pin:before {
content: "\f276";
}
.fa-map-signs:before {
content: "\f277";
}
.fa-map-o:before {
content: "\f278";
}
.fa-map:before {
content: "\f279";
}
.fa-commenting:before {
content: "\f27a";
}
.fa-commenting-o:before {
content: "\f27b";
}
.fa-houzz:before {
content: "\f27c";
}
.fa-vimeo:before {
content: "\f27d";
}
.fa-black-tie:before {
content: "\f27e";
}
.fa-fonticons:before {
content: "\f280";
}
.fa-reddit-alien:before {
content: "\f281";
}
.fa-edge:before {
content: "\f282";
}
.fa-credit-card-alt:before {
content: "\f283";
}
.fa-codiepie:before {
content: "\f284";
}
.fa-modx:before {
content: "\f285";
}
.fa-fort-awesome:before {
content: "\f286";
}
.fa-usb:before {
content: "\f287";
}
.fa-product-hunt:before {
content: "\f288";
}
.fa-mixcloud:before {
content: "\f289";
}
.fa-scribd:before {
content: "\f28a";
}
.fa-pause-circle:before {
content: "\f28b";
}
.fa-pause-circle-o:before {
content: "\f28c";
}
.fa-stop-circle:before {
content: "\f28d";
}
.fa-stop-circle-o:before {
content: "\f28e";
}
.fa-shopping-bag:before {
content: "\f290";
}
.fa-shopping-basket:before {
content: "\f291";
}
.fa-hashtag:before {
content: "\f292";
}
.fa-bluetooth:before {
content: "\f293";
}
.fa-bluetooth-b:before {
content: "\f294";
}
.fa-percent:before {
content: "\f295";
}
.fa-gitlab:before {
content: "\f296";
}
.fa-wpbeginner:before {
content: "\f297";
}
.fa-wpforms:before {
content: "\f298";
}
.fa-envira:before {
content: "\f299";
}
.fa-universal-access:before {
content: "\f29a";
}
.fa-wheelchair-alt:before {
content: "\f29b";
}
.fa-question-circle-o:before {
content: "\f29c";
}
.fa-blind:before {
content: "\f29d";
}
.fa-audio-description:before {
content: "\f29e";
}
.fa-volume-control-phone:before {
content: "\f2a0";
}
.fa-braille:before {
content: "\f2a1";
}
.fa-assistive-listening-systems:before {
content: "\f2a2";
}
.fa-asl-interpreting:before,
.fa-american-sign-language-interpreting:before {
content: "\f2a3";
}
.fa-deafness:before,
.fa-hard-of-hearing:before,
.fa-deaf:before {
content: "\f2a4";
}
.fa-glide:before {
content: "\f2a5";
}
.fa-glide-g:before {
content: "\f2a6";
}
.fa-signing:before,
.fa-sign-language:before {
content: "\f2a7";
}
.fa-low-vision:before {
content: "\f2a8";
}
.fa-viadeo:before {
content: "\f2a9";
}
.fa-viadeo-square:before {
content: "\f2aa";
}
.fa-snapchat:before {
content: "\f2ab";
}
.fa-snapchat-ghost:before {
content: "\f2ac";
}
.fa-snapchat-square:before {
content: "\f2ad";
}
.fa-pied-piper:before {
content: "\f2ae";
}
.fa-first-order:before {
content: "\f2b0";
}
.fa-yoast:before {
content: "\f2b1";
}
.fa-themeisle:before {
content: "\f2b2";
}
.fa-google-plus-circle:before,
.fa-google-plus-official:before {
content: "\f2b3";
}
.fa-fa:before,
.fa-font-awesome:before {
content: "\f2b4";
}
.fa-handshake-o:before {
content: "\f2b5";
}
.fa-envelope-open:before {
content: "\f2b6";
}
.fa-envelope-open-o:before {
content: "\f2b7";
}
.fa-linode:before {
content: "\f2b8";
}
.fa-address-book:before {
content: "\f2b9";
}
.fa-address-book-o:before {
content: "\f2ba";
}
.fa-vcard:before,
.fa-address-card:before {
content: "\f2bb";
}
.fa-vcard-o:before,
.fa-address-card-o:before {
content: "\f2bc";
}
.fa-user-circle:before {
content: "\f2bd";
}
.fa-user-circle-o:before {
content: "\f2be";
}
.fa-user-o:before {
content: "\f2c0";
}
.fa-id-badge:before {
content: "\f2c1";
}
.fa-drivers-license:before,
.fa-id-card:before {
content: "\f2c2";
}
.fa-drivers-license-o:before,
.fa-id-card-o:before {
content: "\f2c3";
}
.fa-quora:before {
content: "\f2c4";
}
.fa-free-code-camp:before {
content: "\f2c5";
}
.fa-telegram:before {
content: "\f2c6";
}
.fa-thermometer-4:before,
.fa-thermometer:before,
.fa-thermometer-full:before {
content: "\f2c7";
}
.fa-thermometer-3:before,
.fa-thermometer-three-quarters:before {
content: "\f2c8";
}
.fa-thermometer-2:before,
.fa-thermometer-half:before {
content: "\f2c9";
}
.fa-thermometer-1:before,
.fa-thermometer-quarter:before {
content: "\f2ca";
}
.fa-thermometer-0:before,
.fa-thermometer-empty:before {
content: "\f2cb";
}
.fa-shower:before {
content: "\f2cc";
}
.fa-bathtub:before,
.fa-s15:before,
.fa-bath:before {
content: "\f2cd";
}
.fa-podcast:before {
content: "\f2ce";
}
.fa-window-maximize:before {
content: "\f2d0";
}
.fa-window-minimize:before {
content: "\f2d1";
}
.fa-window-restore:before {
content: "\f2d2";
}
.fa-times-rectangle:before,
.fa-window-close:before {
content: "\f2d3";
}
.fa-times-rectangle-o:before,
.fa-window-close-o:before {
content: "\f2d4";
}
.fa-bandcamp:before {
content: "\f2d5";
}
.fa-grav:before {
content: "\f2d6";
}
.fa-etsy:before {
content: "\f2d7";
}
.fa-imdb:before {
content: "\f2d8";
}
.fa-ravelry:before {
content: "\f2d9";
}
.fa-eercast:before {
content: "\f2da";
}
.fa-microchip:before {
content: "\f2db";
}
.fa-snowflake-o:before {
content: "\f2dc";
}
.fa-superpowers:before {
content: "\f2dd";
}
.fa-wpexplorer:before {
content: "\f2de";
}
.fa-meetup:before {
content: "\f2e0";
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
File diff suppressed because one or more lines are too long
+3 -1
View File
@@ -33,11 +33,13 @@ namespace LeafWeb.WebCms.Controllers
}
}
var completedLeafInput = DataService.GetLeafInputsOrdered().Where(li => li.CurrentStatus == LeafInputStatusType.Complete);
var serviceDescription = ServiceDescription();
var queueViewModel = new QueueViewModel
{
Items = resultItems, ServerDescription = serviceDescription, Query = query
Items = resultItems, ServerDescription = serviceDescription, Query = query, CompletedLeafInput = completedLeafInput
};
return View(queueViewModel);
+2
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using LeafWeb.Core.Entities;
namespace LeafWeb.WebCms.Models
@@ -9,5 +10,6 @@ namespace LeafWeb.WebCms.Models
public string ServerStatus { get; set; }
public IEnumerable<LeafInput> Items { get; set; }
public string Query { get; set; }
public IQueryable<LeafInput> CompletedLeafInput { get; set; }
}
}
+58 -13
View File
@@ -30,10 +30,9 @@
grid.Column("Identifier", "Identifier"),
grid.Column("SiteId", "Site Id"),
grid.Column("Name", "Submitted By"),
//grid.Column("FileCount", "Input files"),
grid.Column("TimeInProgress", "Time In Progress", item => TimeInProgress(item.TimeInProgress)),
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("TimeInProgress", "Time In Progress", item => TimeInProgress(item.Value)),
grid.Column("CurrentStatus", "Status", item => Status(item.Value)),
grid.Column("Total Results: " + Model.Items.Count(), format: item => Btns(item.Value))),
htmlAttributes: new {@class = "table table-striped table-bordered table-hover table-condensed"}
)
@grid.BootstrapPager()
@@ -43,7 +42,24 @@ else
<p>No results.</p>
}
@helper Btns(dynamic item)
@helper Status(LeafInput leafInput)
{
if (leafInput.IsPending)
{
<span class="text-nowrap">
<span class="text-muted" title="@leafInput.PendingPriority.ToString() Priority">
@Html.Partial("DisplayTemplates/PriorityIcon", leafInput.PendingPriority)
</span>
@Html.Partial("DisplayTemplates/_LeafInputStatus", leafInput.CurrentStatus.ToString())
</span>
}
else
{
@Html.Partial("DisplayTemplates/_LeafInputStatus", leafInput.CurrentStatus.ToString())
}
}
@helper Btns(LeafInput item)
{
<div class="btn-group text-nowrap" role="group">
<div class="btn-group" role="group">
@@ -61,12 +77,12 @@ else
<li @DisableItem(item.PendingPriority == Priority.High)>@PriorityForm(item, Priority.High)</li>
<li @DisableItem(item.PendingPriority == Priority.Normal)>@PriorityForm(item, Priority.Normal)</li>
<li @DisableItem(item.PendingPriority == Priority.Low)>@PriorityForm(item, Priority.Low)</li>
}
}
@if (item.IsCancellable)
{
<li role="separator" class="divider"></li>
<li>@CancelLink(item)</li>
}
}
<li role="separator" class="divider"></li>
<li class="dropdown-header">Download</li>
<li>@DownloadInput(item)</li>
@@ -100,16 +116,45 @@ else
<span class="glyphicon glyphicon-download"></span> ToUser
</a>
}
@helper TimeInProgress(TimeSpan timeSpan)
@helper TimeInProgress(LeafInput leafInput)
{
if (timeSpan > TimeSpan.Zero)
var summary = new List<string> { "Added Time: " + leafInput.Added };
if (leafInput.StartTime.HasValue)
{
@timeSpan.ToRoundedReadableString()
summary.Add("Start Time: " + leafInput.StartTime.Value);
}
}
@helper DeleteLink(dynamic item)
if (leafInput.EndTime.HasValue)
{
@Html.Partial("DisplayTemplates/_DeleteForm", (Tuple<int, string, bool>)Tuple.Create(item.Id, item.Identifier, item.IsDeletable))
summary.Add("End Time: " + leafInput.EndTime.Value);
}
var summaryText = string.Join(Environment.NewLine, summary);
<span class="text-nowrap" title="@summaryText">
@if (leafInput.TimeInProgress > TimeSpan.Zero)
{
<text>
<i class="fa fa-clock-o"></i> @leafInput.TimeInProgress.ToRoundedReadableString()
</text>
}
else if (leafInput.IsPending)
{
<text>
<i class="fa fa-hourglass-start"></i> @Html.Partial("DisplayTemplates/_TimeRemaining", Tuple.Create(leafInput, Model.CompletedLeafInput)) est.
</text>
}
@if (leafInput.IsRunning)
{
<text>
<br />
<i class="fa fa-hourglass-half"></i> @Html.Partial("DisplayTemplates/_TimeRemaining", Tuple.Create(leafInput, Model.CompletedLeafInput)) left
</text>
}
<br />
<i class="fa fa-file-o"></i> @leafInput.InputFiles.Count input files
</span>
}
@helper DeleteLink(LeafInput item)
{
@Html.Partial("DisplayTemplates/_DeleteForm", Tuple.Create(item.Id, item.Identifier, item.IsDeletable))
}
@helper CancelLink(dynamic item)
@@ -8,7 +8,7 @@
<a href="@url?leafInputId=@Model"
class="btn btn-default @{if (xs) {<text>btn-xs</text>}} @{if (disabled) {<text>disabled</text>}}"
role="button"
@{if (disabled) {<text>title="No chart has been generated"</text>}} />
@{if (disabled) {<text>title="No chart has been generated"</text>}} >
<span class="glyphicon glyphicon-stats"></span>
Chart
</a>
@@ -0,0 +1,14 @@
@using LeafWeb.Core.Entities
@model Tuple<LeafInput,IQueryable<LeafInput>>
@{
var leafInput = Model.Item1;
var observations = Model.Item2.Take(20);
var estimater = new TimeInProgressEstimater(observations);
var totalTimeEstimate = estimater.EstimateTimeInProgress(leafInput);
var remaining = totalTimeEstimate - leafInput.TimeInProgress;
if (remaining > TimeSpan.Zero)
{
<text>@remaining.ToRoundedReadableString()</text>
}
}
@@ -4,7 +4,7 @@
<div class="form-group@(Html.ValidationErrorFor(m => m, " has-error"))">
@Html.LabelFor(m => m, new { @class = "control-label" })
<div class="input-group">
<span class="input-group-addon">&euro;</span>@Html.TextBox(
<span class="input-group-addon">&euro;@Html.TextBox(
"",
Model == null ? "" : String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0:F2}", ViewData.ModelMetadata.Model),
ViewBag.ClearTextField == true ? new { @class = "form-control clear-text-field input-block-level" } : new { @class = "form-control input-block-level" })</span>
+2
View File
@@ -781,6 +781,7 @@
<Content Include="Config\applications.config" />
<Content Include="Config\404handlers.config" />
<Content Include="fonts\FontAwesome.otf" />
<Content Include="fonts\fontawesome-webfont.woff2" />
<Content Include="fonts\fontawesome-webfont.woff" />
<Content Include="fonts\fontawesome-webfont.ttf" />
<Content Include="fonts\fontawesome-webfont.eot" />
@@ -891,6 +892,7 @@
<Content Include="Views\Shared\DisplayTemplates\TimeSpan.cshtml" />
<Content Include="Views\Shared\DisplayTemplates\_PriorityForm.cshtml" />
<Content Include="Views\Shared\DisplayTemplates\PriorityIcon.cshtml" />
<Content Include="Views\Shared\DisplayTemplates\_TimeRemaining.cshtml" />
<None Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</None>
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -9,7 +9,7 @@
<package id="ClientDependency-Mvc5" version="1.8.0.0" targetFramework="net452" />
<package id="EntityFramework" version="6.1.3" targetFramework="net452" />
<package id="Examine" version="0.1.70.0" targetFramework="net452" />
<package id="FontAwesome" version="4.2.0" targetFramework="net452" />
<package id="FontAwesome" version="4.7.0" targetFramework="net452" />
<package id="Hangfire" version="1.6.7" targetFramework="net452" />
<package id="Hangfire.Core" version="1.6.7" targetFramework="net452" />
<package id="Hangfire.SqlServer" version="1.6.7" targetFramework="net452" />