20-12-2022

This commit is contained in:
Jonathan Jenne 2022-12-20 15:29:29 +01:00
parent 3e2c4a9ab0
commit 1e925242bc
12 changed files with 291 additions and 179 deletions

View File

@ -25,6 +25,7 @@
<xs:element minOccurs="0" name="SuccessMessage" nillable="true" type="xs:string" />
<xs:element minOccurs="0" name="Successful" type="xs:boolean" />
<xs:element minOccurs="0" name="UpdateTime" type="xs:dateTime" />
<xs:element minOccurs="0" name="Waiting" type="xs:boolean" />
</xs:sequence>
</xs:complexType>
<xs:element name="StatusItem" nillable="true" type="tns:StatusItem" />

View File

@ -322,6 +322,9 @@ Namespace JobRunnerReference
<System.Runtime.Serialization.OptionalFieldAttribute()> _
Private UpdateTimeField As Date
<System.Runtime.Serialization.OptionalFieldAttribute()> _
Private WaitingField As Boolean
<Global.System.ComponentModel.BrowsableAttribute(false)> _
Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData
Get
@ -527,6 +530,19 @@ Namespace JobRunnerReference
End Set
End Property
<System.Runtime.Serialization.DataMemberAttribute()> _
Public Property Waiting() As Boolean
Get
Return Me.WaitingField
End Get
Set
If (Me.WaitingField.Equals(value) <> true) Then
Me.WaitingField = value
Me.RaisePropertyChanged("Waiting")
End If
End Set
End Property
Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
Protected Sub RaisePropertyChanged(ByVal propertyName As String)

View File

@ -9,35 +9,66 @@ Public Class StatusItem
Public Const STEP_WARNING = "WARNING"
Public Const STEP_ERROR = "ERROR"
' Unique Job Run Id, GUID
''' <summary>
''' Is the job currently executing
''' </summary>
Public Executing As Boolean = False
''' <summary>
''' Did the job complete without an error
''' </summary>
Public Successful As Boolean = False
''' <summary>
''' Did the job do some work or is/was just waiting for some input
''' </summary>
Public Waiting As Boolean = False
''' <summary>
''' Unique Job Run Id, GUID
''' </summary>
Public Id As String
' Job Id, corresponds to Job Schedule in DB
''' <summary>
''' Job Id, corresponds to Job Schedule in DB
''' </summary>
Public JobId As String
' Job Name, corresponds to Job Schedule Key from Quartz
''' <summary>
''' Job Name, corresponds to Job Schedule Key from Quartz
''' </summary>
Public Name As String = "Unnamed"
Public Steps As List(Of HistoryStep)
' Runtime Variables
' Progress Counter
''' <summary>
''' Progress Counter
''' </summary>
Public ProgressCurrent As Integer = 0
' Total Progress
''' <summary>
''' Total Progress
''' </summary>
Public ProgressTotal As Integer = 0
' Flag to determin if the job is currently executing/working
Public Executing As Boolean = False
' Creation time of job, set by Constructor
''' <summary>
''' Creation time of job, set by Constructor
''' </summary>
Public CreationTime As Date = Date.Now
' Start time of execution, set by JobStatus.Start
''' <summary>
''' Start time of execution, set by JobStatus.Start
''' </summary>
Public StartTime As Date
' End time of execution, set by JobStatus.Complete
''' <summary>
''' End time of execution, set by JobStatus.Complete
''' </summary>
Public CompleteTime As Date
' Time of last Progress Update
''' <summary>
''' Time of last Progress Update
''' </summary>
Public UpdateTime As Date
' Total execution time, calculated by JobStatus.Complete
''' <summary>
''' Total execution time, calculated by JobStatus.Complete
''' </summary>
Public ExecutionTime As TimeSpan
' Completion/Failure Messages
Public Successful As Boolean = False
Public SuccessMessage As String = ""
Public FailureMessage As String = ""

View File

@ -5,14 +5,32 @@ namespace ECM.JobRunner.Web.Data
public class DashboardResponse
{
public DateTime heartbeat = DateTime.MinValue;
public List<HistoryItem> jobHistory = new();
public List<StatusItem> jobStatus = new();
public List<HistoryItem> GetHistoryForLastMinutes(int pMinutes)
public class JobHistory
{
return jobHistory.
Where(h => (DateTime.Now - h.CreatedAt) < new TimeSpan(0, pMinutes, 0)).
public List<StatusItem> items;
public int total;
public int success;
public int failed;
public int waiting;
}
public JobHistory GetHistoryForLastMinutes(int pMinutes)
{
var items = jobStatus.
Where(s => (DateTime.Now - s.CompleteTime) < new TimeSpan(0, pMinutes, 0)).
Where(s => s.Executing == false).
ToList();
return new JobHistory()
{
items = items,
total = items.Count,
success = items.Where(i => i.Successful && i.Waiting == false).Count(),
failed = items.Where(i => i.Successful == false).Count(),
waiting = items.Where(i => i.Successful && i.Waiting == true).Count()
};
}
}
}

View File

@ -17,7 +17,9 @@ namespace ECM.JobRunner.Web.Data
{
logger = Logging.LogConfig.GetLogger();
channel = Wcf.Channel;
pollingTimer.Interval = 1000;
pollingTimer.Elapsed += PollingTimer_Elapsed;
pollingTimer.Enabled = true;
}
protected virtual void OnDataUpdated(DashboardResponse e)
@ -32,39 +34,14 @@ namespace ECM.JobRunner.Web.Data
public async Task<DashboardResponse> GetData()
{
List<Common.JobRunnerReference.HistoryItem> jobHistory = await GetHistoryItems();
List<Common.JobRunnerReference.StatusItem> jobStatus = await GetStatusItems();
return new DashboardResponse()
{
jobHistory = jobHistory.OrderByDescending(e => e.CreatedAt).ToList(),
jobStatus = jobStatus.OrderByDescending(e => e.StartTime).ToList()
jobStatus = jobStatus.OrderByDescending(e => e.CompleteTime).ToList()
};
}
private async Task<List<Common.JobRunnerReference.HistoryItem>> GetHistoryItems()
{
try
{
var oResponse = await channel.GetJobStatusAsync();
if (oResponse.OK)
{
return oResponse.HistoryItems.ToList();
}
else
{
return new();
}
}
catch (Exception e)
{
logger.Error(e);
return new();
}
}
private async Task<List<Common.JobRunnerReference.StatusItem>> GetStatusItems()
{
try

View File

@ -39,8 +39,4 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Folder Include="Components\Profile\" />
</ItemGroup>
</Project>

View File

@ -7,58 +7,114 @@
<h3>Job History</h3>
@if (historyEntries == null)
{
<div class="container-fluid">
<div class="row">
<div class="col-2">
<div class="card">
<div class="card-body">
<form>
<legend>Filter</legend>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="showWaiting" checked="@showWaiting" @oninput="CheckboxChanged">
<label class="form-check-label" for="exampleCheck1">Waiting</label>
</div>
</form>
</div>
</div>
</div>
<div class="col-10">
@if (filteredEntries == null)
{
<ul class="list-group">
<li class="list-group-item">Loading Job History..</li>
</ul>
}
else if (historyEntries.Count == 0)
{
}
else if (filteredEntries.Count == 0)
{
<ul class="list-group">
<li class="list-group-item">No Job History yet.</li>
</ul>
}
else
{
@foreach (var entry in historyEntries)
}
else
{
@foreach (var entry in filteredEntries)
{
<ul class="list-group mb-3">
<li class="list-group-item bg-light bg-gradient d-flex justify-content-between align-items-start">
<li class="list-group-item list-group-item-secondary d-flex justify-content-between align-items-start">
<div class="me-auto">
<div class="fw-bold">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle text-success" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
</svg>
<span>@entry.JobName</span>
<span>@entry.Name</span>
</div>
</div>
<div>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar-check" viewBox="0 0 16 16">
<path d="M10.854 7.146a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 1 1 .708-.708L7.5 9.793l2.646-2.647a.5.5 0 0 1 .708 0z" />
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z" />
</svg> @entry.CompleteTime.ToString("HH:mm:ss")
</div>
</li>
@foreach (var step in entry.Steps)
{
<li class="list-group-item">
<strong>@step.Created.ToString("HH:mm")</strong> @step.Message
<strong>@step.Created.ToString("HH:mm:ss")</strong> @step.Message
</li>
}
<li class="list-group-item">
<strong>@entry.CreatedAt.ToString("HH:mm")</strong> @entry.Message
</li>
@if (entry.Successful == false)
{
<li class="list-group-item text-danger">Job Failed</li>
<li class="list-group-item list-group-item-danger d-flex justify-content-between align-items-start">
<span>@entry.FailureMessage</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stopwatch" viewBox="0 0 16 16">
<path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5V5.6z" />
<path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64a.715.715 0 0 1 .012-.013l.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354a.512.512 0 0 1-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5zM8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3z" />
</svg> @entry.ExecutionTime.ToString("mm':'ss")
</span>
</li>
}
else
@if (entry.Successful == true && entry.Waiting == false)
{
<li class="list-group-item text-success">Job Succeeded</li>
<li class="list-group-item list-group-item-success d-flex justify-content-between align-items-start">
<span>@entry.SuccessMessage</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stopwatch" viewBox="0 0 16 16">
<path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5V5.6z" />
<path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64a.715.715 0 0 1 .012-.013l.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354a.512.512 0 0 1-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5zM8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3z" />
</svg> @entry.ExecutionTime.ToString("mm':'ss")
</span>
</li>
}
@if (entry.Successful == true && entry.Waiting == true)
{
<li class="list-group-item list-group-item-info d-flex justify-content-between align-items-start">
<span>@entry.SuccessMessage</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stopwatch" viewBox="0 0 16 16">
<path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5V5.6z" />
<path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64a.715.715 0 0 1 .012-.013l.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354a.512.512 0 0 1-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5zM8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3z" />
</svg> @entry.ExecutionTime.ToString("mm':'ss")
</span>
</li>
}
</ul>
}
}
}
</div>
</div>
</div>
@code {
private DateTime today;
private List<HistoryItem>? historyEntries;
private List<StatusItem>? statusEntries;
private List<StatusItem>? filteredEntries;
private bool showSuccessful = true;
private bool showFailed = true;
private bool showWaiting = true;
protected async override void OnInitialized()
{
@ -68,7 +124,16 @@ else
Api.DataUpdated += Api_DataUpdated;
}
protected void Api_DataUpdated(object sender, DashboardResponse e)
private void CheckboxChanged(ChangeEventArgs e)
{
showWaiting = (bool)e.Value;
UpdateEntries(statusEntries);
InvokeAsync(StateHasChanged);
}
protected void Api_DataUpdated(object? sender, DashboardResponse e)
{
UpdateData(e);
}
@ -76,8 +141,27 @@ else
protected void UpdateData(DashboardResponse response)
{
today = response.heartbeat;
historyEntries = response.jobHistory;
statusEntries = response.jobStatus;
UpdateEntries(response.jobStatus);
InvokeAsync(StateHasChanged);
}
protected void UpdateEntries(List<StatusItem>? entries)
{
if (entries == null)
{
return;
}
var filtered = entries.
Where(s => s.Executing == false);
if (!showWaiting)
{
filtered = filtered.
Where(e => e.Waiting == false);
}
filteredEntries = filtered.ToList();
}
}

View File

@ -6,8 +6,7 @@
<PageTitle>Status</PageTitle>
<section class="mb-5">
<section class="mb-4">
<div class="row row-cols-1 row-cols-md-3 g-4">
@if (last5MinutesItems != null)
{
@ -19,7 +18,12 @@
<path d="M2 14.5a.5.5 0 0 0 .5.5h11a.5.5 0 1 0 0-1h-1v-1a4.5 4.5 0 0 0-2.557-4.06c-.29-.139-.443-.377-.443-.59v-.7c0-.213.154-.451.443-.59A4.5 4.5 0 0 0 12.5 3V2h1a.5.5 0 0 0 0-1h-11a.5.5 0 0 0 0 1h1v1a4.5 4.5 0 0 0 2.557 4.06c.29.139.443.377.443.59v.7c0 .213-.154.451-.443.59A4.5 4.5 0 0 0 3.5 13v1h-1a.5.5 0 0 0-.5.5zm2.5-.5v-1a3.5 3.5 0 0 1 1.989-3.158c.533-.256 1.011-.79 1.011-1.491v-.702s.18.101.5.101.5-.1.5-.1v.7c0 .701.478 1.236 1.011 1.492A3.5 3.5 0 0 1 11.5 13v1h-7z" />
</svg> Last 5 Minutes
</h5>
<h6 class="card-subtitle mb-2 text-muted">@last5MinutesItems.Count jobs executed.</h6>
<h6 class="card-subtitle mb-2">
<span>@last5MinutesItems.total jobs executed</span>,
<span class="text-success">@last5MinutesItems.success Successful</span>,
<span class="text-danger">@last5MinutesItems.failed Failed</span>,
<span class="text-muted">@last5MinutesItems.waiting Waiting</span>
</h6>
</div>
</div>
</div>
@ -34,7 +38,7 @@
<path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2h-7zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48V8.35zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z" />
</svg> Last Hour
</h5>
<h6 class="card-subtitle mb-2 text-muted">@lastHourItems.Count jobs executed.</h6>
<h6 class="card-subtitle mb-2">@lastHourItems.total jobs executed, @lastHourItems.success Successful, @lastHourItems.failed Failed, @lastHourItems.waiting Waiting</h6>
</div>
</div>
</div>
@ -43,14 +47,14 @@
@if (last12HoursItems != null)
{
<div class="col">
<div class="card">
<div class="card text-bg-success">
<div class="card-body">
<h5 class="card-title">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-hourglass-bottom" viewBox="0 0 16 16">
<path d="M2 1.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1-.5-.5zm2.5.5v1a3.5 3.5 0 0 0 1.989 3.158c.533.256 1.011.791 1.011 1.491v.702s.18.149.5.149.5-.15.5-.15v-.7c0-.701.478-1.236 1.011-1.492A3.5 3.5 0 0 0 11.5 3V2h-7z" />
</svg> Last 12 Hours
</h5>
<h6 class="card-subtitle mb-2 text-muted">@last12HoursItems.Count jobs executed.</h6>
<h6 class="card-subtitle mb-2">@last12HoursItems.total jobs executed, @last12HoursItems.success Successful, @last12HoursItems.failed Failed, @last12HoursItems.waiting Waiting</h6>
</div>
</div>
</div>
@ -58,12 +62,9 @@
</div>
</section>
<h3 class="mb-3">Job Status</h3>
<section class="mb-5">
<section class="mb-3">
@if (executingEntries == null)
{
<h4>Executing</h4>
<ul class="list-group">
<li class="list-group-item">Loading Job Status..</li>
@ -71,7 +72,6 @@
}
else if (executingEntries.Count == 0)
{
<h4>Executing (0)</h4>
<ul class="list-group">
<li class="list-group-item">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cup-hot" viewBox="0 0 16 16">
@ -83,8 +83,6 @@
}
else
{
<h4>Executing (@executingEntries.Count)</h4>
<ul class="list-group">
@foreach (var entry in executingEntries)
{
@ -109,64 +107,14 @@
</section>
@if (completedEntries == null)
{
<h4>Completed</h4>
<ul class="list-group">
<li class="list-group-item">Loading Job Status..</li>
</ul>
}
else if (completedEntries.Count == 0)
{
<h4>Completed (0)</h4>
<ul class="list-group">
<li class="list-group-item">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cup-hot" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M.5 6a.5.5 0 0 0-.488.608l1.652 7.434A2.5 2.5 0 0 0 4.104 16h5.792a2.5 2.5 0 0 0 2.44-1.958l.131-.59a3 3 0 0 0 1.3-5.854l.221-.99A.5.5 0 0 0 13.5 6H.5ZM13 12.5a2.01 2.01 0 0 1-.316-.025l.867-3.898A2.001 2.001 0 0 1 13 12.5ZM2.64 13.825 1.123 7h11.754l-1.517 6.825A1.5 1.5 0 0 1 9.896 15H4.104a1.5 1.5 0 0 1-1.464-1.175Z" />
<path d="m4.4.8-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 3.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 3.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 3 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 4.4.8Zm3 0-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 6.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 6.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 6 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 7.4.8Zm3 0-.003.004-.014.019a4.077 4.077 0 0 0-.204.31 2.337 2.337 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.198 3.198 0 0 1-.202.388 5.385 5.385 0 0 1-.252.382l-.019.025-.005.008-.002.002A.5.5 0 0 1 9.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 9.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 9 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 10.4.8Z" />
</svg> No completed jobs yet.
</li>
</ul>
}
else
{
<h4>Completed (@completedEntries.Count)</h4>
<ul class="list-group">
@foreach (var entry in completedEntries)
{
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto w-100">
<div class="fw-bold">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check-circle text-success" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z" />
</svg>
<span>@entry.Name</span>
</div>
<ul>
<li>Execution Time: @((int)entry.ExecutionTime.TotalSeconds)s</li>
<li>Started: @entry.StartTime.ToLongTimeString()</li>
<li>Completed: @entry.CompleteTime.ToLongTimeString()</li>
</ul>
</div>
<span class="badge bg-success rounded-pill">Completed @entry.CompleteTime.ToLongTimeString()</span>
</li>
}
</ul>
}
@code {
private DateTime today;
private List<StatusItem>? executingEntries;
private List<StatusItem>? completedEntries;
private List<HistoryItem>? last5MinutesItems;
private List<HistoryItem>? lastHourItems;
private List<HistoryItem>? last12HoursItems;
private DashboardResponse.JobHistory? last5MinutesItems;
private DashboardResponse.JobHistory? lastHourItems;
private DashboardResponse.JobHistory? last12HoursItems;
protected async override void OnInitialized()
{
@ -201,4 +149,6 @@ else
InvokeAsync(StateHasChanged);
}
}

View File

@ -10,7 +10,8 @@
</div>
<main>
<div class="top-row px-4">
<div class="top-row px-4 d-flex align-content-between justify-content-between">
<strong>Page Title</strong>
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

View File

@ -47,16 +47,26 @@ Public Class JobStatus
Logger.Info("Completing Job [{0}] with Error", oStatus.Id)
If oStatus IsNot Nothing Then
oStatus.ProgressCurrent = oStatus.ProgressTotal
oStatus.ExecutionTime = pJob.JobRunTime
oStatus.Executing = False
oStatus.CompleteTime = Date.Now
oStatus.FailureMessage = pMessage
oStatus.Successful = False
oStatus.Steps = pSteps
oStatus.Waiting = False
End If
Return oStatus
Return DoComplete(pJob, pSteps)
End Function
Public Function CompleteWithWaiting(pJob As Quartz.IJobExecutionContext, pSteps As List(Of StatusItem.HistoryStep), pMessage As String) As StatusItem
Dim oStatus = GetJobStatus(pJob)
Logger.Info("Completing Job [{0}] with Waiting", oStatus.Id)
If oStatus IsNot Nothing Then
oStatus.SuccessMessage = pMessage
oStatus.Successful = True
oStatus.Waiting = True
End If
Return DoComplete(pJob, pSteps)
End Function
Public Function CompleteWithSuccess(pJob As Quartz.IJobExecutionContext, pSteps As List(Of StatusItem.HistoryStep), pMessage As String) As StatusItem
@ -65,23 +75,35 @@ Public Class JobStatus
Logger.Info("Completing Job [{0}] with Success", oStatus.Id)
If oStatus IsNot Nothing Then
oStatus.SuccessMessage = pMessage
oStatus.Successful = True
oStatus.Waiting = False
End If
Return DoComplete(pJob, pSteps)
End Function
Private Function DoComplete(pJob As Quartz.IJobExecutionContext, pSteps As List(Of StatusItem.HistoryStep))
Dim oStatus = GetJobStatus(pJob)
oStatus.ProgressCurrent = oStatus.ProgressTotal
oStatus.ExecutionTime = pJob.JobRunTime
oStatus.Executing = False
oStatus.CompleteTime = Date.Now
oStatus.SuccessMessage = pMessage
oStatus.Successful = False
oStatus.Steps = pSteps
End If
Return oStatus
End Function
Private Function GetJobStatus(pJob As Quartz.IJobExecutionContext) As StatusItem
Dim oJobId = GetJobId(pJob)
Dim oJobId As String = GetJobId(pJob)
Logger.Debug("Getting status for job id [{0}]", oJobId)
Dim oExists = Entries.Where(Function(e) e.JobId = oJobId).Any()
Logger.Debug("Job exists: [{0}]", oExists)
If Not oExists Then
Logger.Debug("Creating status for job id [{0}]", oJobId)
Entries.Add(New StatusItem With {
.JobId = oJobId,
.Id = Guid.NewGuid.ToString(),
@ -89,7 +111,7 @@ Public Class JobStatus
})
End If
Return Entries.Where(Function(e) e.Id = oJobId).SingleOrDefault()
Return Entries.Where(Function(e) e.JobId = oJobId).Single()
End Function
Private Function GetJobId(pJob As Quartz.IJobExecutionContext) As String

View File

@ -29,11 +29,12 @@ Namespace Scheduler.Jobs
Database = oJobData.Item(Constants.Scheduler.JOB_CONFIG_DATABASE)
State = oJobData.Item(Constants.Scheduler.JOB_CONFIG_STATE)
Windream = oJobData.Item(Constants.Scheduler.JOB_CONFIG_WINDREAM)
Logger = LogConfig.GetLogger()
ExecutionId = Guid.NewGuid.ToString()
Id = Integer.Parse(oArgs.Item("Id"))
Name = oArgs.Item("Name")
Logger = LogConfig.GetLogger(Name)
State.JobStatus.Start(ctx)
Logger.Info("Job [{0}] is starting!", Id)
@ -83,6 +84,11 @@ Namespace Scheduler.Jobs
Return Task.FromResult(True)
End Function
Public Function CompleteJobWithWaiting(pMessage As String) As Task(Of Boolean)
ctx.Result = State.JobStatus.CompleteWithWaiting(ctx, JobSteps, pMessage)
Return Task.FromResult(True)
End Function
Public Function CompleteJobWithError(pException As Exception) As Task(Of Boolean)
ctx.Result = State.JobStatus.CompleteWithError(ctx, JobSteps, pException)

View File

@ -29,7 +29,7 @@ Namespace Scheduler.Jobs
If IO.Directory.Exists(oProfile.SourceFolder) = False Then
LogError("Source directory [{0}] does not exist!", oProfile.SourceFolder)
Return Task.FromResult(False)
Return CompleteJobWithError(New IO.DirectoryNotFoundException($"Source directory [{oProfile.SourceFolder}] does not exist!"))
End If
Dim oRecursive As Boolean = oProfile.IncludeSubfolders
@ -40,24 +40,24 @@ Namespace Scheduler.Jobs
If oFileNames.Count = 0 Then
Logger.Info("No Files for Profile [{0}]", Name)
Return CompleteJob("No files for profile")
Return CompleteJobWithWaiting("No files for profile")
End If
LogInfo("{0} files found in source directory {1}", oFileNames.Count, oProfile.SourceFolder)
LogInfo("{0} files found in source directory {1}", oFileNames.Count.ToString, oProfile.SourceFolder)
' - [ ] Process Rules, build list of files and indexes
' - [x] Process Regex to filter out files
' - [x] Check time to filter out files
' - [ ] (Check if files can be accessed)
' - [ ] Check if backup is needed and backup files
' - [ ] Import into windream
' - [x] Import into windream
' - [ ] Create original subfolder structure
' - [ ] Create DateTime Subfolders
' - [x] Check if file exists and version
' - [x] Do import
' - [ ] Check for filesize 0
' - [ ] Write indexes (using data from getimportfile)
' - [ ] Check if orig file should be deleted
' - [x] Write indexes (using data from getimportfile)
' - [x] Check if orig file should be deleted
' - [ ] (delete subdirectories in source path)
Dim oFiles = oFileNames.
@ -71,12 +71,22 @@ Namespace Scheduler.Jobs
LogDebug("{0} Files filtered out for being too new.", oDateFilteredCount)
oFilteredFiles = oDateFilteredFiles
If oFilteredFiles.Count = 0 Then
Logger.Info("No Files for Profile [{0}]", Name)
Return CompleteJobWithWaiting("No files for profile")
End If
' Process Regex to filter out files
Dim oRegexFilteredFiles = oFilteredFiles.Where(Function(f) Not FileMatchesRegex(f, oRegexList))
Dim oRegexFilteredCount = oFilteredFiles.Except(oRegexFilteredFiles).Count()
LogDebug("{0} Files filtered out for matching exclusion Regex.", oRegexFilteredCount)
oFilteredFiles = oDateFilteredFiles
If oFilteredFiles.Count = 0 Then
Logger.Info("No Files for Profile [{0}]", Name)
Return CompleteJobWithWaiting("No files for profile")
End If
'-------------------------------------------------------------------------------------------------
' After this point, files are treated as processed and are being deleted before finishing the job
'-------------------------------------------------------------------------------------------------