Skip to content

Commit

Permalink
Merge branch 'master' into dependabot/npm_and_yarn/web/http-cache-sem…
Browse files Browse the repository at this point in the history
…antics-and-vue-resource-4.1.1
  • Loading branch information
marzmehr committed Feb 11, 2024
2 parents 01ff620 + 9ec1a63 commit 94afae6
Show file tree
Hide file tree
Showing 131 changed files with 21,381 additions and 978 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"mapster.tool": {
"version": "6.5.0",
"version": "8.0.0",
"commands": [
"dotnet-mapster"
]
Expand Down
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# the repo. Unless a later match takes precedence,
# @global-owner1 and @global-owner2 will be requested for
# review when someone opens a pull request.
* @devinleighsmith @WadeBarnes @seeker25 @marzmehr
* @devinleighsmith @WadeBarnes @marzmehr

# Order is important; the last matching pattern takes the most
# precedence. When someone opens a pull request that only
Expand Down
8 changes: 8 additions & 0 deletions api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
using SS.Api.services.ef;
using SS.Db.models;
using Microsoft.Extensions.Logging;
using Quartz;
using SS.Api.cronjobs;

namespace SS.Api
{
Expand Down Expand Up @@ -137,6 +139,12 @@ public void ConfigureServices(IServiceCollection services)
});

services.AddSwaggerGenNewtonsoftSupport();

services.AddQuartz(q =>
{
q.AddJobAndTrigger<TrainingNotification>(Configuration);
});
services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
Expand Down
11 changes: 6 additions & 5 deletions api/api.csproj
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>SS.Api</RootNamespace>
<UserSecretsId>de959767-ede6-4f8a-b6b9-d36aed70339C</UserSecretsId>
<ProjectGuid>6224c484-3e23-4f06-a749-195c1e478110</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>bin\$(Configuration)\netcoreapp3.1\api.xml</DocumentationFile>
<DocumentationFile>bin\$(Configuration)\net5.0\api.xml</DocumentationFile>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
<DocumentationFile>bin\$(Configuration)\netcoreapp3.1\api.xml</DocumentationFile>
<DocumentationFile>bin\$(Configuration)\net5.0\api.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="3.1.0" />
<PackageReference Include="IdentityModel" Version="4.4.0" />
<PackageReference Include="Mapster" Version="6.5.0" />
<PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.18" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.9" />
Expand All @@ -34,10 +33,12 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.2.8" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.8.0" />
<PackageReference Include="NodaTime" Version="3.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.7.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="5.6.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.6.3" />
Expand Down
3 changes: 2 additions & 1 deletion api/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"WebBaseHref": "/sheriff-scheduling/",
"PdfUrl": "http://localhost:5001",
"PdfUrl": "http://localhost:5001",
"TrainingNotification": "0 30 5 * * ?",
// Hint: Override these in secrets when doing local development. ByPassAuthAndUseImpersonatedUser - only works in development mode.
"ByPassAuthAndUseImpersonatedUser": "true",
"ImpersonateUser": {
Expand Down
35 changes: 34 additions & 1 deletion api/controllers/usermanagement/SheriffController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ public class SheriffController : UserController
private ShiftService ShiftService { get; }
private DutyRosterService DutyRosterService { get; }
private SheriffDbContext Db { get; }
private TrainingService TrainingService { get; }

// ReSharper disable once InconsistentNaming
private readonly long _uploadPhotoSizeLimitKB;

public SheriffController(SheriffService sheriffService, DutyRosterService dutyRosterService, ShiftService shiftService, UserService userUserService, IConfiguration configuration, SheriffDbContext db) : base(userUserService)
public SheriffController(SheriffService sheriffService, DutyRosterService dutyRosterService, ShiftService shiftService, UserService userUserService,TrainingService trainingService, IConfiguration configuration, SheriffDbContext db) : base(userUserService)
{
SheriffService = sheriffService;
ShiftService = shiftService;
DutyRosterService = dutyRosterService;
TrainingService = trainingService;
Db = db;
_uploadPhotoSizeLimitKB = Convert.ToInt32(configuration.GetNonEmptyValue("UploadPhotoSizeLimitKB"));
}
Expand Down Expand Up @@ -155,6 +157,15 @@ public async Task<ActionResult<SheriffDto>> UploadPhoto(Guid? id, string badgeNu
return Ok(sheriff.Adapt<SheriffDto>());
}

[HttpPut]
[Route("updateExcused")]
[PermissionClaimAuthorize(perm: Permission.GenerateReports)]
public async Task<ActionResult<SheriffDto>> UpdateExcused(Sheriff excusedSheriff)
{
var sheriff = await SheriffService.UpdateSheriffExcused(excusedSheriff);
return Ok(sheriff.Adapt<SheriffDto>());
}

#endregion Sheriff

#region SheriffAwayLocation
Expand Down Expand Up @@ -268,6 +279,28 @@ public async Task<ActionResult> RemoveSheriffLeave(int id, string expiryReason)

#endregion SheriffLeave

#region SheriffTrainingReports

[HttpPost]
[Route("training/reports")]
[PermissionClaimAuthorize(perm: Permission.GenerateReports)]
public async Task<ActionResult<TrainingReportDto>> GetSheriffsTrainingReports(TrainingReportSearchDto trainingReportSearch)
{
var sheriffs = await TrainingService.GetSheriffsTrainingReports(trainingReportSearch);
return Ok(sheriffs.Adapt<List<TrainingReportDto>>());
}

[HttpGet]
[Route("training/adjust-expiry")]
[PermissionClaimAuthorize(perm: Permission.AdjustTrainingExpiry)]
public async Task<ActionResult> TrainingExpiryAdjustment()
{
await TrainingService.TrainingExpiryAdjustment();
return Ok(new { result = "success"});
}

#endregion SheriffTrainingReports

#region SheriffTraining

[HttpGet]
Expand Down
34 changes: 34 additions & 0 deletions api/cronjobs/QuartzConfigService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using Microsoft.Extensions.Configuration;
using Quartz;

namespace SS.Api.cronjobs
{
public static class QuartzConfigService
{
public static void AddJobAndTrigger<T>(
this IServiceCollectionQuartzConfigurator quartz,
IConfiguration config)
where T : IJob
{

string jobName = typeof(T).Name;
var configKey = $"{jobName}";
var cronSchedule = config[configKey];

if (string.IsNullOrEmpty(cronSchedule))
{
throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
}

var jobKey = new JobKey(jobName);
quartz.AddJob<T>(opts => opts.WithIdentity(jobKey));

quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(jobName + "-trigger")
.WithCronSchedule(cronSchedule));

}
}
}
89 changes: 89 additions & 0 deletions api/cronjobs/TrainingNotification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@


using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Quartz;
using SS.Api.services;
using SS.Api.services.usermanagement;
using SS.Db.models.sheriff;

namespace SS.Api.cronjobs
{
[DisallowConcurrentExecution]
public class TrainingNotification: IJob
{
private readonly ILogger<TrainingNotification> Logger;
public IServiceProvider Services { get; }

public TrainingNotification(ILogger<TrainingNotification> logger, IServiceProvider services, ManageTypesService manageTypesService)
{
Logger = logger;
Services = services;
}

public async void ProcessTrainings()
{
using var scope = Services.CreateScope();
var TrainingService = scope.ServiceProvider.GetRequiredService<TrainingService>();
var ChesEmailService = scope.ServiceProvider.GetRequiredService<ChesEmailService>();

var trainings = await TrainingService.GetTrainings();
foreach(var training in trainings)
{
var noticeDate = DateTimeOffset.UtcNow.AddDays(training.TrainingType.AdvanceNotice);

Logger.LogInformation(training.TrainingCertificationExpiry.ToString());
Logger.LogInformation((training.TrainingCertificationExpiry < noticeDate).ToString());
Logger.LogInformation(training.Sheriff.Email);

if(training.TrainingCertificationExpiry < noticeDate){

bool rotatingTraining = TrainingService.IsRotatingTraining(training.TrainingType);
var emailBody = rotatingTraining? GetEmailRotatingBody(training) : GetEmailEndOfYearBody(training);
var emailTitle = rotatingTraining? "Training Expiry Notice" : "Training Requalification Notice";

var emailSent = await ChesEmailService.SendEmail(
emailBody,
emailTitle,
training.Sheriff.Email
);
if(emailSent)
await TrainingService.UpdateTraining(training.Id);
}
}
Logger.LogInformation("CronJob Done");
}

public string GetEmailRotatingBody(SheriffTraining training)
{
var expiryDate = training.TrainingCertificationExpiry.Value.ToString("MMMM dd, yyyy");
var emailBody =
$"Dear {training.Sheriff.FirstName} {training.Sheriff.LastName}, \n\n"+
$"Your \'{training.TrainingType.Code}\' certification will expire on \'{expiryDate}\'. \n"+
"Please ensure your certification is renewed before this date.";

return emailBody;
}

public string GetEmailEndOfYearBody(SheriffTraining training)
{
var expiryYear = training.TrainingCertificationExpiry.Value.AddDays(-1).AddYears(1).ToString("yyyy");
var emailBody =
$"Dear {training.Sheriff.FirstName} {training.Sheriff.LastName}, \n\n"+
$"Your \'{training.TrainingType.Code}\' certification will require renewal for the calendar year \'{expiryYear}\'. \n"+
$"Please ensure you renew your certification between January 1st and December 31, {expiryYear}. \n\n" +
"It is recommended to schedule training early in the year to ensure end of year compliance.";

return emailBody;
}

public Task Execute(IJobExecutionContext context)
{
Logger.LogInformation("___Running CronJob___");
ProcessTrainings();
return Task.CompletedTask;
}
}
}
8 changes: 8 additions & 0 deletions api/helpers/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,13 @@ public static string GetNonEmptyValue(this IConfiguration configuration, string
? throw new ConfigurationException($"Configuration '{key}' is invalid or missing.")
: configurationValue;
}

public static string GetBoolValue(this IConfiguration configuration, string key)
{
var configurationValue = configuration.GetValue<string>(key);
return string.IsNullOrEmpty(configurationValue)
? "false"
: configurationValue;
}
}
}
1 change: 1 addition & 0 deletions api/infrastructure/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static IServiceCollection AddSSServices(this IServiceCollection services,
services.AddScoped<UserService>();
services.AddScoped<SheriffService>();
services.AddScoped<ShiftService>();
services.AddScoped<TrainingService>();
services.AddScoped<DutyRosterService>();
services.AddScoped<AssignmentService>();
services.AddScoped<DistributeScheduleService>();
Expand Down
5 changes: 5 additions & 0 deletions api/models/dto/AddLookupCodeDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public class AddLookupCodeDto
public DateTimeOffset? EffectiveDate { get; set; }
public DateTimeOffset? ExpiryDate { get; set; }
public int? LocationId { get; set; }
public bool? Mandatory { get; set; }
public bool? Rotating { get; set; }
public int? ValidityPeriod { get; set; }
public string? Category { get; set; }
public int? AdvanceNotice { get; set; }
public AddLookupSortOrderDto SortOrderForLocation { get; set; }
}
}
16 changes: 16 additions & 0 deletions api/models/dto/TrainingReportDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace SS.Api.models.dto
{
public class TrainingReportDto
{
public string name { get; set; }
public string trainingType { get; set; }
public DateTimeOffset? end { get; set; }
public DateTimeOffset? expiryDate { get; set; }
public bool excluded { get; set; }
public Guid sheriffId { get; set; }
public string status { get; set; }
public string _rowVariant { get; set; }
}
}
14 changes: 14 additions & 0 deletions api/models/dto/TrainingReportSearchDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;


namespace SS.Api.models.dto
{
public class TrainingReportSearchDto
{
public int? regionId { get; set; }
public int? locationId { get; set; }
public int? reportSubtypeId { get; set; }
public DateTimeOffset? startDate { get; set; }
public DateTimeOffset? endDate { get; set; }
}
}
5 changes: 5 additions & 0 deletions api/models/dto/generated/LookupCodeDto.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public partial class LookupCodeDto
public DateTimeOffset? ExpiryDate { get; set; }
public LocationDto Location { get; set; }
public int? LocationId { get; set; }
public bool Mandatory { get; set; }
public int ValidityPeriod { get; set; }
public string Category { get; set; }
public int AdvanceNotice { get; set; }
public bool Rotating { get; set; }
public uint ConcurrencyToken { get; set; }
public LookupSortOrderDto SortOrderForLocation { get; set; }
}
Expand Down
1 change: 1 addition & 0 deletions api/models/dto/generated/SheriffDto.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public partial class SheriffDto
public List<SheriffTrainingDto> Training { get; set; }
public string PhotoUrl { get; set; }
public DateTimeOffset LastPhotoUpdate { get; set; }
public bool Excused { get; set; }
public Guid Id { get; set; }
public bool IsEnabled { get; set; }
public string FirstName { get; set; }
Expand Down
1 change: 1 addition & 0 deletions api/models/dto/generated/SheriffTrainingDto.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public partial class SheriffTrainingDto
public int? TrainingTypeId { get; set; }
public DateTimeOffset? TrainingCertificationExpiry { get; set; }
public string Note { get; set; }
public bool FirstNotice { get; set; }
public int Id { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
Expand Down
8 changes: 8 additions & 0 deletions api/models/types/TrainingStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace SS.Api.models.types
{
public class TrainingStatus
{
public string status;
public string rowType;
}
}
10 changes: 10 additions & 0 deletions api/models/types/TrainingStatusTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace SS.Api.models.types
{
public static class TrainingStatusTypes
{
public static readonly string danger = "Not Taken";
public static readonly string alert = "Expired";
public static readonly string warning = "Requalification";
public static readonly string notify = "Notified";
}
}
Loading

0 comments on commit 94afae6

Please sign in to comment.