Skip to content

Commit 4be5e38

Browse files
authored
Set task definition schedule (#20)
* Show next due date in Definition grid * Allow capturing CronSchedule in a Task Definition * Allow SmartCronScheduleEditor to set CronSchedule of items * Show loader when performing lookup * Clean up add screen * Adjust showing human readable cron location * WIP on OpenAI hook * Wire up OpenAI API * Almost AI-connected * Use JSON response from OpenAI and add Swagger doc * Clean solution, make component of schedule * Fix string parsing of estimated Cron schedule
1 parent e1acb4e commit 4be5e38

35 files changed

+1443
-224
lines changed

.docs/DATA-DESIGN.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* **`TaskType`**
3535
* **`Asset`(s)**
3636
* Task name
37+
* CronSchedule (CronString AI derived & explained?)
3738
* (Phase1) `TaskInstance`
3839
* An instance of a **`TaskDefinition`**.
3940
* Can be auto-generated when a required lifespan hits.
@@ -48,6 +49,14 @@
4849
* Duration (Self-Recorded)
4950
* DueByDate
5051
* MQTT-enabled (can be _Phase4_)
52+
* (Phase2) Schedule
53+
* Automatically create a **TaskInstance** when a **TaskDefinition** is due.
54+
* Automatically create a **TaskInstance** when a **Part** is due for replacement.
55+
* Properties:
56+
* CronSchedule
57+
* TaskDefinition(s)
58+
* Part
59+
* AutoAssignToUser
5160
* (Phase2) `TaskDefinitionSteps`
5261
* _ie, for 'Clean Room', steps may be 'Organize books', 'Clear dresser', 'Hang clothes', etc. ..._
5362
* `TaskDefinitionStep`s are associated under **`TaskDefinition`s**
@@ -81,9 +90,6 @@
8190
* Notes
8291
* Vendor/Store
8392
* Notification?
84-
* Schedule?
85-
* Automatically create a **TaskInstance** when a **TaskDefinition** is due.
86-
* Automatically create a **TaskInstance** when a **Part** is due for replacement.
8793
* User-Defined Fields
8894
* Installations can define custom fields for:
8995
* **Property**

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ maintenancelogdb.db
88
maintenancelogdb.db-shm
99
maintenancelogdb.db-wal
1010

11+
.mono/
12+
1113
# User-specific files
1214
*.rsuser
1315
*.suo

src/Directory.Packages.props

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
<ItemGroup>
33
<PackageVersion Include="Blazor.QrCodeGen" Version="1.1.3" />
44
<PackageVersion Include="Blazored.LocalStorage" Version="4.5.0" />
5+
<PackageVersion Include="CronExpressionDescriptor" Version="2.33.0" />
6+
<PackageVersion Include="Cronos" Version="0.8.4" />
57
<PackageVersion Include="FluentEmail.Smtp" Version="3.0.2" />
68
<PackageVersion Include="Humanizer" Version="2.14.1" />
79
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
810
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
911
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
1012
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
11-
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
13+
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.0" />
1214
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3" />
1315
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.3" />
1416
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.3" />
@@ -17,5 +19,6 @@
1719
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
1820
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
1921
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3" />
22+
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
2023
</ItemGroup>
2124
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
@using CronExpressionDescriptor
2+
3+
@if (!string.IsNullOrWhiteSpace(Description))
4+
{
5+
<div class="alert alert-info" role="alert">
6+
<strong>Schedule:</strong> @Description
7+
</div>
8+
}
9+
else if (!string.IsNullOrWhiteSpace(ErrorDetails))
10+
{
11+
<div class="alert alert-danger" role="alert">
12+
<strong>Error:</strong> @ErrorDetails
13+
</div>
14+
}
15+
16+
@code
17+
{
18+
[Parameter]
19+
public string? CronSchedule { get; set; }
20+
[Parameter]
21+
public bool ShowErrorDetails { get; set; } = true;
22+
23+
private string? Description;
24+
private string? ErrorDetails;
25+
26+
public override async Task SetParametersAsync(ParameterView parameters)
27+
{
28+
await base.SetParametersAsync(parameters);
29+
SetDescription(CronSchedule);
30+
}
31+
32+
private void SetDescription(string? cronSchedule)
33+
{
34+
if (!string.IsNullOrWhiteSpace(cronSchedule))
35+
{
36+
try
37+
{
38+
Description = ExpressionDescriptor.GetDescription(cronSchedule);
39+
ErrorDetails = null;
40+
}
41+
catch (Exception ex)
42+
{
43+
Description = null;
44+
ErrorDetails = ex.Message;
45+
}
46+
}
47+
else
48+
{
49+
Description = null;
50+
ErrorDetails = null;
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@using MaintenanceLog.Common.Contracts
2+
@using MaintenanceLog.Data.Entities
3+
4+
@inject ISmartScheduleService SmartScheduleService
5+
6+
@typeparam TItem where TItem : IScheduledEntity
7+
8+
@if (isLoading)
9+
{
10+
<div class="spinner-border text-primary" role="status">
11+
<span class="visually-hidden">Loading...</span>
12+
</div>
13+
}
14+
else
15+
{
16+
<button type="button" class="btn btn-primary" @onclick="@OnSmartAssist">
17+
Smart Scheduler
18+
</button>
19+
}
20+
21+
@code
22+
{
23+
[Parameter]
24+
public TItem? Item { get; set; }
25+
[Parameter]
26+
public EventCallback<TItem> DataChangedAsync { get; set; }
27+
28+
private string? aiResultText;
29+
private bool isLoading = false;
30+
31+
protected override async Task OnParametersSetAsync()
32+
{
33+
await base.OnParametersSetAsync();
34+
if (Item is null)
35+
{
36+
throw new ArgumentNullException(nameof(Item));
37+
}
38+
}
39+
40+
protected override async Task OnInitializedAsync()
41+
{
42+
await base.OnInitializedAsync();
43+
}
44+
45+
private async Task OnSmartAssist()
46+
{
47+
isLoading = true;
48+
49+
aiResultText = await SmartScheduleService.EstimateCronScheduleForItem(Item!.Name);
50+
Item.CronSchedule = aiResultText;
51+
52+
isLoading = false;
53+
54+
await InvokeAsync(StateHasChanged);
55+
await DataChangedAsync.InvokeAsync(Item);
56+
57+
return;
58+
}
59+
}

src/MaintenanceLog.Client/Components/TaskDefinitions/TaskDefinitionGrid.razor

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
@using MaintenanceLog.Data.Entities
1+
@using MaintenanceLog.Common.Extensions
2+
@using MaintenanceLog.Data.Entities
3+
@using MaintenanceLog.Data.Extensions
24
@using MaintenanceLog.Data.Services.Contracts
35

46
@inject IJSRuntime JsRuntime
@@ -14,6 +16,7 @@
1416
<tr>
1517
<th>Start</th>
1618
<th>Name</th>
19+
<th>Upcoming</th>
1720
<th>Asset</th>
1821
<th>Area</th>
1922
<th>Actions</th>
@@ -32,6 +35,18 @@
3235
</button>
3336
</td>
3437
<td><a href="@($"/task-definitions/edit/{taskDefinition.Id}")">@taskDefinition.Name</a></td>
38+
<td>
39+
@if (string.IsNullOrWhiteSpace(taskDefinition.CronSchedule))
40+
{
41+
<span class="badge bg-danger">Manual</span>
42+
}
43+
else
44+
{
45+
<span class="badge bg-info">
46+
@taskDefinition.GetNextScheduledDate().ToLocalHumanizedString()
47+
</span>
48+
}
49+
</td>
3550
<td>@taskDefinition.Asset?.Name</td>
3651
<td>@taskDefinition.Area?.Name</td>
3752
<td>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using Blazor.QrCodeGen;
2+
using Blazored.LocalStorage;
3+
using MaintenanceLog.Client.Services;
4+
using MaintenanceLog.Common.Contracts;
5+
using Microsoft.AspNetCore.Components.Authorization;
6+
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
7+
using Microsoft.JSInterop;
8+
9+
namespace MaintenanceLog.Client.Extensions;
10+
11+
public static class ServiceCollectionExtensions
12+
{
13+
public static IServiceCollection AddMaintenanceLogClientServices(this IServiceCollection services, WebAssemblyHostBuilder hostBuilder)
14+
{
15+
services.AddAuthorizationCore();
16+
services.AddCascadingAuthenticationState();
17+
services.AddSingleton<AuthenticationStateProvider, PersistentAuthenticationStateProvider>();
18+
services.AddBlazoredLocalStorage();
19+
20+
services.AddTransient(sp => new ModuleCreator(sp.GetService<IJSRuntime>()));
21+
services.AddScoped(http => new HttpClient { BaseAddress = new Uri(hostBuilder.HostEnvironment.BaseAddress) });
22+
23+
services.AddScoped<ISmartScheduleService, HttpSmartScheduleService>();
24+
25+
return services;
26+
}
27+
}

src/MaintenanceLog.Client/JsonHttpClient.cs

-82
This file was deleted.

src/MaintenanceLog.Client/MaintenanceLog.Client.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<ItemGroup>
1313
<PackageReference Include="Blazor.QrCodeGen" />
1414
<PackageReference Include="Blazored.LocalStorage" />
15+
<PackageReference Include="CronExpressionDescriptor" />
16+
<PackageReference Include="Cronos" />
1517
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
1618
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
1719
</ItemGroup>

0 commit comments

Comments
 (0)