23-12-2022

This commit is contained in:
Jonathan Jenne 2022-12-23 13:27:30 +01:00
parent e3f271ac33
commit 7e7eee7299
10 changed files with 130 additions and 36 deletions

View File

@ -1,4 +1,5 @@
using DigitalData.Modules.Logging; using DigitalData.Modules.Logging;
using DigitalData.Modules.Filesystem;
using ECM.JobRunner.Common.JobRunnerReference; using ECM.JobRunner.Common.JobRunnerReference;
using System; using System;
@ -8,15 +9,22 @@ namespace ECM.JobRunner.Web.Data
{ {
private readonly Logger logger; private readonly Logger logger;
private readonly IEDMIServiceChannel channel; private readonly IEDMIServiceChannel channel;
private readonly DigitalData.Modules.Filesystem.File fileEx;
public event EventHandler<string>? PageTitleChanged; 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();
fileEx = new DigitalData.Modules.Filesystem.File(Logging.LogConfig);
channel = Wcf.Channel; channel = Wcf.Channel;
} }
public string GetDateDirectory(DateTime date)
{
return fileEx.GetDateString(date);
}
public void SetPageTitle(string title) public void SetPageTitle(string title)
{ {
PageTitleChanged?.Invoke(this, title); PageTitleChanged?.Invoke(this, title);
@ -61,8 +69,6 @@ namespace ECM.JobRunner.Web.Data
waiting = executingItems.Where(i => i.Successful && i.Waiting == true).Count() waiting = executingItems.Where(i => i.Successful && i.Waiting == true).Count()
}; };
} }
public DateTime GetNextExecutionTime(string pCronExpression) public DateTime GetNextExecutionTime(string pCronExpression)
{ {
Quartz.CronExpression expression = new(pCronExpression); Quartz.CronExpression expression = new(pCronExpression);

View File

@ -58,6 +58,18 @@ namespace ECM.JobRunner.Web.Data
} }
public async Task RunProfile(ImportProfile profile)
{
try
{
await channel.RunJobAsync(new RunJobRunJobRequest() { JobId = profile.JobId });
}
catch (Exception ex)
{
logger.Error(ex);
}
}
public async Task<bool> CreateProfile(ImportProfile profile) => public async Task<bool> CreateProfile(ImportProfile profile) =>
await DoUpdateProfile(profile, UpdateProfileUpdateProfileRequest.UpdateProfileAction.Create); await DoUpdateProfile(profile, UpdateProfileUpdateProfileRequest.UpdateProfileAction.Create);

View File

@ -31,6 +31,9 @@
<Reference Include="DigitalData.Modules.Database"> <Reference Include="DigitalData.Modules.Database">
<HintPath>..\..\DDModules\Database\bin\Debug\DigitalData.Modules.Database.dll</HintPath> <HintPath>..\..\DDModules\Database\bin\Debug\DigitalData.Modules.Database.dll</HintPath>
</Reference> </Reference>
<Reference Include="DigitalData.Modules.Filesystem">
<HintPath>..\..\DDModules\Filesystem\bin\Debug\DigitalData.Modules.Filesystem.dll</HintPath>
</Reference>
<Reference Include="DigitalData.Modules.Logging"> <Reference Include="DigitalData.Modules.Logging">
<HintPath>..\..\DDModules\Logging\bin\Debug\DigitalData.Modules.Logging.dll</HintPath> <HintPath>..\..\DDModules\Logging\bin\Debug\DigitalData.Modules.Logging.dll</HintPath>
</Reference> </Reference>

View File

@ -126,7 +126,7 @@
private List<StatusItem>? filteredEntries; private List<StatusItem>? filteredEntries;
private int dateFilter = 1; private int dateFilter = 1;
private bool showWaiting = true; private bool showWaiting = false;
// TODO: implement more filters // TODO: implement more filters
private bool showSuccessful = true; private bool showSuccessful = true;

View File

@ -1,7 +1,6 @@
@using ECM.JobRunner.Common.JobRunnerReference; @using ECM.JobRunner.Common.JobRunnerReference;
@using ECM.JobRunner.Web.Data; @using ECM.JobRunner.Web.Data;
@inject ImportProfileService Import; @inject ImportProfileService Import;
@inject HelperService Jobs;
@inject WcfService Service; @inject WcfService Service;
@if (profile == null) @if (profile == null)
@ -144,9 +143,10 @@ else
</label> </label>
<select class="form-select" aria-label="Job type selection" id="jobType" @bind="profile.SubfolderDateFormat"> <select class="form-select" aria-label="Job type selection" id="jobType" @bind="profile.SubfolderDateFormat">
<option selected value="">Keine Unterordner</option> <option selected value="">Keine Unterordner</option>
<option value="YYYY">YYYY (@DateTime.Now.ToString("yyyy"))</option> @foreach (var dateString in GetSubdirectoryDateStrings())
<option value="YYYY-MM">YYYY-MM (@DateTime.Now.ToString("yyyy-MM"))</option> {
<option value="YYYY-MM-DD">YYYY-MM-DD (@DateTime.Now.ToString("yyyy-MM-dd"))</option> <option value=@dateString.formatString>@dateString.prettyName (@dateString.exampleValue)</option>
}
</select> </select>
<div id="cronHelp" class="form-text">Der Windream Objekttyp, dem das Profil zugeordnet wird. Bestimmt u.a., welche Indexe zur Verfügung stehen.</div> <div id="cronHelp" class="form-text">Der Windream Objekttyp, dem das Profil zugeordnet wird. Bestimmt u.a., welche Indexe zur Verfügung stehen.</div>
</div> </div>
@ -195,6 +195,24 @@ else
return ProfileId == Constants.ENTITY_ID_NEW ? "profiles/import" : $"profiles/import/{ProfileId}"; return ProfileId == Constants.ENTITY_ID_NEW ? "profiles/import" : $"profiles/import/{ProfileId}";
} }
private List<SubdirectoryDateString> GetSubdirectoryDateStrings()
{
return new()
{
new() {prettyName = $"YYYY", exampleValue = $"{DateTime.Now:yyyy}", formatString = "yyyy" },
new() {prettyName = $"YYYY\\MM", exampleValue = $"{DateTime.Now:yyyy\\\\MM}", formatString = "yyyy\\MM" },
new() {prettyName = $"YYYY\\MM\\DD", exampleValue = $"{DateTime.Now:yyyy\\\\MM\\\\dd}", formatString = "yyyy\\MM\\dd" }
};
}
private class SubdirectoryDateString
{
public string? prettyName;
public string? exampleValue;
public string? formatString;
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
jobTypes = await Service.GetJobTypes(); jobTypes = await Service.GetJobTypes();

View File

@ -84,9 +84,21 @@ else
</svg> Delete </svg> Delete
</a> </a>
</div> </div>
<div class="btn-group" role="group">
<a class="btn btn-warning" @onclick="RunProfile">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-play-fill" viewBox="0 0 16 16">
<path d="m11.596 8.697-6.363 3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 3.692a.802.802 0 0 1 0 1.393z" />
</svg> Profil manuell starten
</a>
</div>
<div class="flex-column flex-fill"></div> <div class="flex-column flex-fill"></div>
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<a class="btn btn-secondary" href="profiles/import/@profile.Id/steps">Profilschritte bearbeiten (@profile.Steps.Length)</a> <a class="btn btn-primary" href="profiles/import/@profile.Id/steps">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z" />
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z" />
</svg> Profilschritte bearbeiten (@profile.Steps.Length)
</a>
</div> </div>
</div> </div>
} }
@ -110,6 +122,18 @@ else
} }
} }
protected async void RunProfile()
{
if (profile != null)
{
bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Are you sure?");
if (confirmed)
{
await Import.RunProfile(profile);
}
}
}
protected async void DeleteJob() protected async void DeleteJob()
{ {
if (profile != null) if (profile != null)

View File

@ -1,4 +1,5 @@
Imports System.Collections.Specialized Imports System.Collections.Specialized
Imports System.Runtime.InteropServices
Imports DigitalData.Modules.Database Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Windream Imports DigitalData.Modules.Windream
@ -88,11 +89,20 @@ Namespace Scheduler
If oJob IsNot Nothing Then If oJob IsNot Nothing Then
Logger.Info("Scheduling Job [{0}] manually!", oJob.Name) Logger.Info("Scheduling Job [{0}] manually!", oJob.Name)
Await PrepareScheduleJob(oJob) Await PrepareScheduleJob(oJob, pStartManually:=True)
End If End If
End Function End Function
Private Async Function PrepareScheduleJob(pJob As JobDefinition) As Task Private Async Sub ScheduleJobs()
Logger.Info("Loading [{0}] Job Definitions..", State.JobDefinitions.Count)
For Each oJob In State.JobDefinitions
Await PrepareScheduleJob(oJob, pStartManually:=False)
Next
End Sub
Private Async Function PrepareScheduleJob(pJob As JobDefinition, pStartManually As Boolean) As Task
Logger.Debug("Loading Job Definition [{0}]", pJob.Name) Logger.Debug("Loading Job Definition [{0}]", pJob.Name)
Logger.Debug("Job Type is [{0}]", pJob.Type.Name) Logger.Debug("Job Type is [{0}]", pJob.Type.Name)
@ -110,15 +120,6 @@ Namespace Scheduler
End Select End Select
End Function End Function
Private Async Sub ScheduleJobs()
Logger.Info("Loading [{0}] Job Definitions..", State.JobDefinitions.Count)
For Each oJob In State.JobDefinitions
Await PrepareScheduleJob(oJob)
Next
End Sub
Private Function BuildJobConfig(Of TJob As IJob)(pJob As JobDefinition) As JobConfig Private Function BuildJobConfig(Of TJob As IJob)(pJob As JobDefinition) As JobConfig
Return New JobConfig With { Return New JobConfig With {
.Name = pJob.Name, .Name = pJob.Name,
@ -131,8 +132,11 @@ Namespace Scheduler
} }
End Function End Function
Private Async Function ScheduleJob(Of T As IJob)(pJobConfig As JobConfig) As Task Private Async Function ScheduleJob(Of T As IJob)(pJobConfig As JobConfig, pStartManually As Boolean) As Task
If Await Scheduler.CheckExists(New JobKey(GetJobName(pJobConfig))) Then If pStartManually = True Then
Logger.Debug("Manual run, scheduling..")
Await DoScheduleJobWithoutDelay(Of T)(pJobConfig)
ElseIf Await Scheduler.CheckExists(New JobKey(GetJobName(pJobConfig))) Then
Logger.Debug("Job already exists, rescheduling..") Logger.Debug("Job already exists, rescheduling..")
Await DoRescheduleJob(Of T)(pJobConfig) Await DoRescheduleJob(Of T)(pJobConfig)
Else Else
@ -156,12 +160,12 @@ Namespace Scheduler
End Function End Function
Private Async Function DoScheduleJob(Of T As IJob)(pJobConfig As JobConfig) As Task Private Async Function DoScheduleJob(Of T As IJob)(pJobConfig As JobConfig) As Task
Dim oJobName As String = GetJobName(pJobConfig)
Dim oTriggerName As String = GetTriggerName(pJobConfig) Dim oTriggerName As String = GetTriggerName(pJobConfig)
Dim oTrigger As ITrigger = BuildTrigger(pJobConfig)
Dim oJobName As String = GetJobName(pJobConfig)
Dim oJobData As JobDataMap = BuildJobData(pJobConfig) Dim oJobData As JobDataMap = BuildJobData(pJobConfig)
Dim oJob As IJobDetail = BuildJob(Of T)(pJobConfig, oJobData) Dim oJob As IJobDetail = BuildJob(Of T)(pJobConfig, oJobData)
Dim oTrigger As ITrigger = BuildTrigger(pJobConfig)
If pJobConfig.Enabled Then If pJobConfig.Enabled Then
Await Scheduler.ScheduleJob(oJob, oTrigger) Await Scheduler.ScheduleJob(oJob, oTrigger)
@ -172,6 +176,16 @@ Namespace Scheduler
End If End If
If pJobConfig.StartWithoutDelay Then If pJobConfig.StartWithoutDelay Then
Await DoScheduleJobWithoutDelay(Of T)(pJobConfig)
End If
End Function
Private Async Function DoScheduleJobWithoutDelay(Of T As IJob)(pJobConfig As JobConfig) As Task
Dim oJobName As String = GetJobName(pJobConfig)
Dim oJobData As JobDataMap = BuildJobData(pJobConfig)
Dim oJob As IJobDetail = BuildJob(Of T)(pJobConfig, oJobData)
Dim oTriggerName As String = GetTriggerName(pJobConfig)
Dim oNoDelayTrigger = TriggerBuilder.Create(). Dim oNoDelayTrigger = TriggerBuilder.Create().
WithIdentity(oTriggerName & "-NO-DELAY"). WithIdentity(oTriggerName & "-NO-DELAY").
StartAt(DateBuilder.FutureDate(10, IntervalUnit.Second)). StartAt(DateBuilder.FutureDate(10, IntervalUnit.Second)).
@ -179,7 +193,6 @@ Namespace Scheduler
Logger.Info("Job {0} will start in 10 Seconds.", oJobName) Logger.Info("Job {0} will start in 10 Seconds.", oJobName)
Await Scheduler.ScheduleJob(oJob, oNoDelayTrigger) Await Scheduler.ScheduleJob(oJob, oNoDelayTrigger)
End If
End Function End Function
Private Function BuildJob(Of T As IJob)(pJobConfig As JobConfig, pJobData As JobDataMap) As IJobDetail Private Function BuildJob(Of T As IJob)(pJobConfig As JobConfig, pJobData As JobDataMap) As IJobDetail

View File

@ -46,7 +46,7 @@ Namespace Scheduler.Jobs
State.JobStatus.Update(ctx, pCurrentValue, pTotalValue) State.JobStatus.Update(ctx, pCurrentValue, pTotalValue)
End Sub End Sub
Public Sub LogDebug(pMessage As String, ParamArray pArgs As String()) Public Sub LogDebug(pMessage As String, ParamArray pArgs As Object())
Logger.Debug(pMessage, pArgs) Logger.Debug(pMessage, pArgs)
JobSteps.Add(New StatusItem.HistoryStep With { JobSteps.Add(New StatusItem.HistoryStep With {
.Message = String.Format(pMessage, pArgs), .Message = String.Format(pMessage, pArgs),
@ -54,15 +54,16 @@ Namespace Scheduler.Jobs
}) })
End Sub End Sub
Public Sub LogInfo(pMessage As String, ParamArray pArgs As String()) Public Sub LogInfo(pMessage As String, ParamArray pArgs As Object())
Logger.Info(pMessage, pArgs) Logger.Info(pMessage, pArgs)
JobSteps.Add(New StatusItem.HistoryStep With { JobSteps.Add(New StatusItem.HistoryStep With {
.Message = String.Format(pMessage, pArgs), .Message = String.Format(pMessage, pArgs),
.Level = StatusItem.STEP_INFO .Level = StatusItem.STEP_INFO
}) })
End Sub End Sub
Public Sub LogWarning(pMessage As String, ParamArray pArgs As String()) Public Sub LogWarning(pMessage As String, ParamArray pArgs As Object())
Logger.Warn(pMessage, pArgs) Logger.Warn(pMessage, pArgs)
JobSteps.Add(New StatusItem.HistoryStep With { JobSteps.Add(New StatusItem.HistoryStep With {
.Message = String.Format(pMessage, pArgs), .Message = String.Format(pMessage, pArgs),
@ -70,15 +71,13 @@ Namespace Scheduler.Jobs
}) })
End Sub End Sub
Public Sub LogError(pMessage As String, ParamArray pArgs As String()) Public Sub LogError(pMessage As String, ParamArray pArgs As Object())
Logger.Error(pMessage, pArgs) Logger.Error(pMessage, pArgs)
JobSteps.Add(New StatusItem.HistoryStep With { JobSteps.Add(New StatusItem.HistoryStep With {
.Message = String.Format(pMessage, pArgs), .Message = String.Format(pMessage, pArgs),
.Level = StatusItem.STEP_ERROR .Level = StatusItem.STEP_ERROR
}) })
End Sub End Sub
Public Function CompleteJob(pMessage As String) As Task(Of Boolean) Public Function CompleteJob(pMessage As String) As Task(Of Boolean)
ctx.Result = State.JobStatus.CompleteWithSuccess(ctx, JobSteps, pMessage) ctx.Result = State.JobStatus.CompleteWithSuccess(ctx, JobSteps, pMessage)
Return Task.FromResult(True) Return Task.FromResult(True)

View File

@ -3,6 +3,7 @@ Imports Quartz
Imports System.Text.RegularExpressions Imports System.Text.RegularExpressions
Imports DigitalData.Modules.Filesystem Imports DigitalData.Modules.Filesystem
Imports ECM.JobRunner.Common Imports ECM.JobRunner.Common
Imports FxResources.System
Namespace Scheduler.Jobs Namespace Scheduler.Jobs
@ -176,11 +177,28 @@ Namespace Scheduler.Jobs
'Check if target folder exists 'Check if target folder exists
If Windream.TestFolderExists(pProfile.TargetFolder) = False Then If Windream.TestFolderExists(pProfile.TargetFolder) = False Then
If Windream.NewFolder(pProfile.TargetFolder) = False Then If Windream.NewFolder(pProfile.TargetFolder) = False Then
Logger.Warn("Folder [{0}] could not be created!", pProfile.TargetFolder) Logger.Warn("Folder [{0}] could not be created! Exiting.", pProfile.TargetFolder)
Return Nothing Return Nothing
End If End If
End If End If
Dim oFinalDirectoryPath = pProfile.TargetFolder
If pProfile.SubfolderDateFormat <> String.Empty Then
' ToString formatter needs the backslashes escaped again.
Dim oSubfolders = Now.ToString(pProfile.SubfolderDateFormat.Replace())
Dim oFullPath = IO.Path.Combine(pProfile.TargetFolder, oSubfolders)
Logger.Debug("Creating subfolder [{0}] in Target path [{1}]", oSubfolders, pProfile.TargetFolder)
If Windream.NewFolder(oFullPath) = False Then
Logger.Warn("Folder [{0}] could not be created! Exiting.", oFullPath)
Return Nothing
End If
oFinalDirectoryPath = oFullPath
End If
' Generate new filepath and stream file ' Generate new filepath and stream file
Dim oFileName = IO.Path.GetFileName(pFile.FilePath) Dim oFileName = IO.Path.GetFileName(pFile.FilePath)
Dim oNewFilePath As String = IO.Path.Combine(pProfile.TargetFolder, oFileName) Dim oNewFilePath As String = IO.Path.Combine(pProfile.TargetFolder, oFileName)

View File

@ -44,7 +44,8 @@ Namespace WCF
End Function End Function
Public Function GetJobHistory() As GetJobStatus.GetJobStatusResponse Implements IJobRunner.GetJobStatus Public Function GetJobHistory() As GetJobStatus.GetJobStatusResponse Implements IJobRunner.GetJobStatus
Logger.Info("Calling Method [GetJobHistory]") ' No, we will not log this call because it will be called *A LOT*.
' Nobody needs to see this in a log.
Dim oMethod As New GetJobStatus.GetJobStatusMethod(LogConfig, Database, State, Scheduler) Dim oMethod As New GetJobStatus.GetJobStatusMethod(LogConfig, Database, State, Scheduler)
Return oMethod.Run() Return oMethod.Run()
End Function End Function