20-12-2022
This commit is contained in:
parent
3e2c4a9ab0
commit
1e925242bc
@ -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" />
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 = ""
|
||||
|
||||
|
||||
@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -39,8 +39,4 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Components\Profile\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -7,58 +7,114 @@
|
||||
|
||||
<h3>Job History</h3>
|
||||
|
||||
@if (historyEntries == null)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Loading Job History..</li>
|
||||
</ul>
|
||||
}
|
||||
else if (historyEntries.Count == 0)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">No Job History yet.</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var entry in historyEntries)
|
||||
{
|
||||
<ul class="list-group mb-3">
|
||||
<li class="list-group-item bg-light bg-gradient 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>
|
||||
</div>
|
||||
<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>
|
||||
</li>
|
||||
@foreach (var step in entry.Steps)
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-10">
|
||||
@if (filteredEntries == null)
|
||||
{
|
||||
<li class="list-group-item">
|
||||
<strong>@step.Created.ToString("HH:mm")</strong> @step.Message
|
||||
</li>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Loading Job History..</li>
|
||||
</ul>
|
||||
}
|
||||
<li class="list-group-item">
|
||||
<strong>@entry.CreatedAt.ToString("HH:mm")</strong> @entry.Message
|
||||
</li>
|
||||
@if (entry.Successful == false)
|
||||
else if (filteredEntries.Count == 0)
|
||||
{
|
||||
<li class="list-group-item text-danger">Job Failed</li>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">No Job History yet.</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="list-group-item text-success">Job Succeeded</li>
|
||||
@foreach (var entry in filteredEntries)
|
||||
{
|
||||
<ul class="list-group mb-3">
|
||||
<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">
|
||||
<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:ss")</strong> @step.Message
|
||||
</li>
|
||||
}
|
||||
@if (entry.Successful == false)
|
||||
{
|
||||
<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>
|
||||
}
|
||||
|
||||
@if (entry.Successful == true && entry.Waiting == false)
|
||||
{
|
||||
<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>
|
||||
}
|
||||
}
|
||||
</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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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.ProgressCurrent = oStatus.ProgressTotal
|
||||
oStatus.ExecutionTime = pJob.JobRunTime
|
||||
oStatus.Executing = False
|
||||
oStatus.CompleteTime = Date.Now
|
||||
oStatus.SuccessMessage = pMessage
|
||||
oStatus.Successful = False
|
||||
oStatus.Steps = pSteps
|
||||
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.Steps = pSteps
|
||||
|
||||
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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
'-------------------------------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user