21-12-2022

This commit is contained in:
Jonathan Jenne 2022-12-21 16:09:49 +01:00
parent 6bca1f53f4
commit e3f271ac33
13 changed files with 139 additions and 80 deletions

View File

@ -6,31 +6,5 @@ namespace ECM.JobRunner.Web.Data
{ {
public DateTime heartbeat = DateTime.MinValue; public DateTime heartbeat = DateTime.MinValue;
public List<StatusItem> jobStatus = new(); public List<StatusItem> jobStatus = new();
public class JobHistory
{
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

@ -1,5 +1,6 @@
using DigitalData.Modules.Logging; using DigitalData.Modules.Logging;
using ECM.JobRunner.Common.JobRunnerReference; using ECM.JobRunner.Common.JobRunnerReference;
using System;
namespace ECM.JobRunner.Web.Data namespace ECM.JobRunner.Web.Data
{ {
@ -8,13 +9,59 @@ namespace ECM.JobRunner.Web.Data
private readonly Logger logger; private readonly Logger logger;
private readonly IEDMIServiceChannel channel; private readonly IEDMIServiceChannel channel;
public string Title = "No Title"; public event EventHandler<string>? PageTitleChanged;
public HelperService(LoggingService Logging, WcfService Wcf) public HelperService(LoggingService Logging, WcfService Wcf)
{ {
logger = Logging.LogConfig.GetLogger(); logger = Logging.LogConfig.GetLogger();
channel = Wcf.Channel; channel = Wcf.Channel;
} }
public void SetPageTitle(string title)
{
PageTitleChanged?.Invoke(this, title);
}
public List<StatusItem> GetItemsForTimespan(List<StatusItem> items, TimeSpan timespan)
{
return items.
Where(s => (DateTime.Now - s.CompleteTime) < timespan).
ToList();
}
public List<StatusItem> GetItemsForLastMinutes(List<StatusItem> items, int minutes) =>
GetItemsForTimespan(items, new TimeSpan(0, minutes, 0));
public List<StatusItem> GetItemsForLastHours(List<StatusItem> items, int hours) =>
GetItemsForTimespan(items, new TimeSpan(hours, 0, 0));
public List<StatusItem> GetItemsForLastSeconds(List<StatusItem> items, int seconds) =>
GetItemsForTimespan(items, new TimeSpan(0, 0, seconds));
public class JobHistory
{
public List<StatusItem> items;
public int total;
public int success;
public int failed;
public int waiting;
}
public JobHistory GetJobHistory(List<StatusItem> items, int sinceMinutes)
{
var filteredItems = GetItemsForLastMinutes(items, sinceMinutes);
var executingItems = filteredItems.Where(s => s.Executing == false);
return new JobHistory()
{
items = executingItems.ToList(),
total = executingItems.Count(),
success = executingItems.Where(i => i.Successful && i.Waiting == false).Count(),
failed = executingItems.Where(i => i.Successful == false).Count(),
waiting = executingItems.Where(i => i.Successful && i.Waiting == true).Count()
};
}
public DateTime GetNextExecutionTime(string pCronExpression) public DateTime GetNextExecutionTime(string pCronExpression)
{ {

View File

@ -5,8 +5,6 @@
<PageTitle>History</PageTitle> <PageTitle>History</PageTitle>
<h3>Job History</h3>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-2"> <div class="col-2">
@ -15,6 +13,17 @@
<form> <form>
<legend>Filter</legend> <legend>Filter</legend>
<select class="form-select" aria-label="Default select example" value="@dateFilter" @oninput="DateFilterChanged">
<option selected>Please select a value..</option>
<option value="1">1 hour</option>
<option value="3">3 hours</option>
<option value="6">6 hours</option>
<option value="12">12 hours</option>
<option value="24">24 hours</option>
<option value="72">3 days</option>
<option value="168">1 week</option>
</select>
<div class="mb-3 form-check"> <div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="showWaiting" checked="@showWaiting" @oninput="CheckboxChanged"> <input type="checkbox" class="form-check-input" id="showWaiting" checked="@showWaiting" @oninput="CheckboxChanged">
<label class="form-check-label" for="exampleCheck1">Waiting</label> <label class="form-check-label" for="exampleCheck1">Waiting</label>
@ -37,7 +46,7 @@
else if (filteredEntries.Count == 0) else if (filteredEntries.Count == 0)
{ {
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item">No Job History yet.</li> <li class="list-group-item">No Job History.</li>
</ul> </ul>
} }
else else
@ -116,12 +125,16 @@
private List<StatusItem>? statusEntries; private List<StatusItem>? statusEntries;
private List<StatusItem>? filteredEntries; private List<StatusItem>? filteredEntries;
private int dateFilter = 1;
private bool showWaiting = true;
// TODO: implement more filters
private bool showSuccessful = true; private bool showSuccessful = true;
private bool showFailed = true; private bool showFailed = true;
private bool showWaiting = true;
protected async override void OnInitialized() protected async override void OnInitialized()
{ {
Helper.SetPageTitle("History");
DashboardResponse data = await Api.GetData(); DashboardResponse data = await Api.GetData();
UpdateData(data); UpdateData(data);
@ -137,6 +150,14 @@
InvokeAsync(StateHasChanged); InvokeAsync(StateHasChanged);
} }
private void DateFilterChanged(ChangeEventArgs e)
{
var dateFilterString = e.Value.ToString();
dateFilter = int.Parse(dateFilterString);
UpdateEntries(statusEntries);
InvokeAsync(StateHasChanged);
}
protected void Api_DataUpdated(object? sender, DashboardResponse e) protected void Api_DataUpdated(object? sender, DashboardResponse e)
{ {
UpdateData(e); UpdateData(e);
@ -146,26 +167,29 @@
{ {
today = response.heartbeat; today = response.heartbeat;
statusEntries = response.jobStatus; statusEntries = response.jobStatus;
UpdateEntries(response.jobStatus); filteredEntries = UpdateEntries(response.jobStatus);
InvokeAsync(StateHasChanged); InvokeAsync(StateHasChanged);
} }
protected void UpdateEntries(List<StatusItem>? entries) protected List<StatusItem>? UpdateEntries(List<StatusItem>? entries)
{ {
if (entries == null) if (entries == null)
{ {
return; return null;
} }
var filtered = entries. var filtered = entries.
Where(s => s.Executing == false); Where(s => s.Executing == false).
ToList();
if (!showWaiting) if (!showWaiting)
{ {
filtered = filtered. filtered = filtered.
Where(e => e.Waiting == false); Where(e => e.Waiting == false).ToList();
} }
filteredEntries = filtered.ToList(); filtered = Helper.GetItemsForLastHours(filtered, dateFilter);
return filtered;
} }
} }

View File

@ -7,8 +7,6 @@
<PageTitle>Job bearbeiten</PageTitle> <PageTitle>Job bearbeiten</PageTitle>
<h3>Job bearbeiten</h3>
<ProfileForm ProfileId="ProfileId" OnValidSubmit="OnFormSubmit" /> <ProfileForm ProfileId="ProfileId" OnValidSubmit="OnFormSubmit" />
@code { @code {
@ -21,6 +19,7 @@
private async void OnFormSubmit(EditContext ctx) private async void OnFormSubmit(EditContext ctx)
{ {
ImportProfile profile = (ImportProfile)ctx.Model; ImportProfile profile = (ImportProfile)ctx.Model;
Helper.SetPageTitle(profile.Job.Name);
bool result = await Profile.UpdateProfile(profile); bool result = await Profile.UpdateProfile(profile);
if (result == true) if (result == true)

View File

@ -6,8 +6,6 @@
<PageTitle>Import Profiles</PageTitle> <PageTitle>Import Profiles</PageTitle>
<h3>Import Profiles</h3>
@if (filteredProfiles == null) @if (filteredProfiles == null)
{ {
<ul class="list-group"> <ul class="list-group">
@ -95,6 +93,7 @@ else
protected async override void OnInitialized() protected async override void OnInitialized()
{ {
Helper.SetPageTitle("Import Profiles");
profiles = await Import.GetProfiles(); profiles = await Import.GetProfiles();
filteredProfiles = profiles; filteredProfiles = profiles;

View File

@ -19,25 +19,18 @@
} }
else else
{ {
<h3>
@if (profile.Active)
{
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-play-circle text-success me-1" 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="M6.271 5.055a.5.5 0 0 1 .52.038l3.5 2.5a.5.5 0 0 1 0 .814l-3.5 2.5A.5.5 0 0 1 6 10.5v-5a.5.5 0 0 1 .271-.445z" />
</svg>
}
else
{
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-stop-circle text-danger me-1" 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="M5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3z" />
</svg>
}
@profile.Job.Name
</h3>
<ul class="list-group mb-3"> <ul class="list-group mb-3">
<li class="list-group-item 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" 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> Aktiv
</div>
@profile.Active
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start"> <li class="list-group-item d-flex justify-content-between align-items-start">
<div class="me-auto"> <div class="me-auto">
<div class="fw-bold"> <div class="fw-bold">
@ -111,6 +104,7 @@ else
if (profile != null) if (profile != null)
{ {
Helper.SetPageTitle(profile.Job.Name);
nextExecution = Helper.GetNextExecutionTime(profile.Job.CronSchedule); nextExecution = Helper.GetNextExecutionTime(profile.Job.CronSchedule);
StateHasChanged(); StateHasChanged();
} }

View File

@ -149,7 +149,7 @@ else
protected string GetBackUrl() protected string GetBackUrl()
{ {
return $"/profiles/import/{ProfileId}/steps/{StepId}"; return StepId < 0 ? $"/profiles/import/{ProfileId}/steps" : $"/profiles/import/{ProfileId}/steps/{StepId}";
} }
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()

View File

@ -1,5 +1,4 @@
@using ECM.JobRunner.Web.Data; @using ECM.JobRunner.Web.Data;
@inject HelperService Helper;
@page "/" @page "/"
<PageTitle>Index</PageTitle> <PageTitle>Index</PageTitle>
@ -9,6 +8,6 @@
@code { @code {
protected override void OnInitialized() protected override void OnInitialized()
{ {
Helper.Title = "Start"; Helper.SetPageTitle("Index");
} }
} }

View File

@ -2,8 +2,6 @@
<PageTitle>Profile</PageTitle> <PageTitle>Profile</PageTitle>
<h3>Profile</h3>
<div class="list-group"> <div class="list-group">
<a href="profiles/import" class="list-group-item list-group-item-action d-flex justify-content-between align-items-start"> <a href="profiles/import" class="list-group-item list-group-item-action d-flex justify-content-between align-items-start">
<div class="me-auto"> <div class="me-auto">
@ -26,3 +24,11 @@
</div> </div>
</a> </a>
</div> </div>
@code {
protected override void OnInitialized()
{
base.OnInitialized();
Helper.SetPageTitle("Profile");
}
}

View File

@ -19,7 +19,7 @@
</svg> Last 5 Minutes </svg> Last 5 Minutes
</h5> </h5>
<h6 class="card-subtitle mb-2"> <h6 class="card-subtitle mb-2">
<span>@last5MinutesItems.total jobs executed</span>, <span>@last5MinutesItems.total jobs executed</span>,
<span class="text-success">@last5MinutesItems.success Successful</span>, <span class="text-success">@last5MinutesItems.success Successful</span>,
<span class="text-danger">@last5MinutesItems.failed Failed</span>, <span class="text-danger">@last5MinutesItems.failed Failed</span>,
<span class="text-muted">@last5MinutesItems.waiting Waiting</span> <span class="text-muted">@last5MinutesItems.waiting Waiting</span>
@ -112,12 +112,13 @@
private List<StatusItem>? executingEntries; private List<StatusItem>? executingEntries;
private List<StatusItem>? completedEntries; private List<StatusItem>? completedEntries;
private DashboardResponse.JobHistory? last5MinutesItems; private HelperService.JobHistory last5MinutesItems;
private DashboardResponse.JobHistory? lastHourItems; private HelperService.JobHistory lastHourItems;
private DashboardResponse.JobHistory? last12HoursItems; private HelperService.JobHistory last12HoursItems;
protected async override void OnInitialized() protected async override void OnInitialized()
{ {
Helper.SetPageTitle("Status");
DashboardResponse data = await Dashboard.GetData(); DashboardResponse data = await Dashboard.GetData();
UpdateData(data); UpdateData(data);
@ -143,12 +144,14 @@
Where(s => s.StartTime.AddMinutes(10) > DateTime.Now). Where(s => s.StartTime.AddMinutes(10) > DateTime.Now).
ToList(); ToList();
last5MinutesItems = response.GetHistoryForLastMinutes(5); last5MinutesItems = Helper.GetJobHistory(response.jobStatus, 5);
lastHourItems = response.GetHistoryForLastMinutes(60); lastHourItems = Helper.GetJobHistory(response.jobStatus, 60);
last12HoursItems = response.GetHistoryForLastMinutes(60 * 12); last12HoursItems = Helper.GetJobHistory(response.jobStatus, 60 * 12);
InvokeAsync(StateHasChanged); InvokeAsync(StateHasChanged);
} }
} }

View File

@ -10,9 +10,11 @@ builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
builder.Services.AddTransient<LoggingService>(); builder.Services.AddTransient<LoggingService>();
builder.Services.AddTransient<DatabaseService>(); builder.Services.AddTransient<DatabaseService>();
builder.Services.AddSingleton<WcfService>(); builder.Services.AddSingleton<WcfService>();
builder.Services.AddSingleton<HelperService>();
builder.Services.AddTransient<DashboardService>(); builder.Services.AddTransient<DashboardService>();
builder.Services.AddTransient<HelperService>();
builder.Services.AddTransient<ImportProfileService>(); builder.Services.AddTransient<ImportProfileService>();
var app = builder.Build(); var app = builder.Build();

View File

@ -5,15 +5,16 @@
<PageTitle>ECM.JobRunner.Web</PageTitle> <PageTitle>ECM.JobRunner.Web</PageTitle>
<div class=@(connected ? "page connected" : "page disconnected")> <div class=@(Connected ? "page connected" : "page disconnected")>
<div class="sidebar"> <div class="sidebar">
<NavMenu connected="connected" /> <NavMenu connected="Connected" />
</div> </div>
<main> <main>
<div class="top-row px-4 d-flex align-content-between justify-content-between"> <div class="top-row navbar navbar-light">
<strong>@Helper.Title</strong> <div class="container-fluid ps-0">
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> <span class="navbar-brand">@Title</span>
</div>
</div> </div>
<article class="content px-4"> <article class="content px-4">
@ -23,17 +24,25 @@
</div> </div>
@code { @code {
private bool connected = true; private bool Connected = true;
private string Title = "";
protected override Task OnInitializedAsync() protected override Task OnInitializedAsync()
{ {
Helper.PageTitleChanged += Helper_PageTitleChanged;
Wcf.ConnectedChanged += Wcf_ConnectedChanged; Wcf.ConnectedChanged += Wcf_ConnectedChanged;
return Task.FromResult(true); return Task.FromResult(true);
} }
public void Helper_PageTitleChanged(object? sender, string title)
{
Title = title;
InvokeAsync(StateHasChanged);
}
public void Wcf_ConnectedChanged(object? sender, bool status) public void Wcf_ConnectedChanged(object? sender, bool status)
{ {
connected = status; Connected = status;
InvokeAsync(StateHasChanged); InvokeAsync(StateHasChanged);
} }
} }

View File

@ -7,4 +7,7 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using ECM.JobRunner.Web @using ECM.JobRunner.Web
@using ECM.JobRunner.Web.Data
@using ECM.JobRunner.Web.Shared @using ECM.JobRunner.Web.Shared
@inject HelperService Helper