Update Scheduler, Add config for GetTimeFormat Config, Add Logger
This commit is contained in:
parent
d810b82011
commit
96040d2d8a
5 changed files with 174 additions and 104 deletions
|
|
@ -6,7 +6,9 @@ global using System.Collections.Generic;
|
|||
global using System.Text;
|
||||
global using System.Threading.Tasks;
|
||||
global using System.Drawing;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
|
||||
global using static Xorog.UniversalExtensions.UniversalExtensionsEnums;
|
||||
global using static Xorog.UniversalExtensions.Internal;
|
||||
global using static Xorog.UniversalExtensions.InternalSheduler;
|
||||
global using static Xorog.UniversalExtensions.InternalSheduler;
|
||||
global using static Xorog.UniversalExtensions.Log;
|
||||
|
|
@ -6,71 +6,64 @@ internal static class Internal
|
|||
{
|
||||
switch (timeFormat)
|
||||
{
|
||||
case TimeFormat.HOURS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" +
|
||||
$"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" +
|
||||
$"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
|
||||
return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
case TimeFormat.DAYS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{(Math.Floor(_timespan.TotalDays).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalDays)}" : Math.Floor(_timespan.TotalDays))}" +
|
||||
$"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" +
|
||||
$"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
return $"{Math.Floor(_timespan.TotalDays).ToString().PadLeft(2, '0')}:{_timespan.Hours.ToString().PadLeft(2, '0')}:{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" +
|
||||
$"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
return $"{Math.Floor(_timespan.TotalHours).ToString().PadLeft(2, '0')}:{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
case TimeFormat.HOURS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{Math.Floor(_timespan.TotalHours).ToString().PadLeft(2, '0')}:" +
|
||||
$"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{_timespan.Hours.ToString().PadLeft(2, '0')}:" +
|
||||
$"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
case TimeFormat.MINUTES:
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{(Math.Floor(_timespan.TotalMinutes).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalMinutes)}" : Math.Floor(_timespan.TotalMinutes))}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
return $"{Math.Floor(_timespan.TotalMinutes).ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" +
|
||||
$"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}";
|
||||
return $"{_timespan.Minutes.ToString().PadLeft(2, '0')}:{_timespan.Seconds.ToString().PadLeft(2, '0')}";
|
||||
|
||||
default:
|
||||
return _timespan.ToString();
|
||||
}
|
||||
}
|
||||
internal static string GetTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat)
|
||||
internal static string GetTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat, HumanReadableTimeFormatConfig? config = null)
|
||||
{
|
||||
config ??= new();
|
||||
|
||||
switch (timeFormat)
|
||||
{
|
||||
case TimeFormat.HOURS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{Math.Floor(_timespan.TotalHours)} hours, {_timespan.Minutes} minutes";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes";
|
||||
|
||||
return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds";
|
||||
case TimeFormat.DAYS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{Math.Floor(_timespan.TotalDays)} days, {_timespan.Hours} hours";
|
||||
return $"{Math.Floor(_timespan.TotalDays)} {config.DaysString}, {_timespan.Hours} {config.HoursString}";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes";
|
||||
return $"{_timespan.Hours} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}";
|
||||
|
||||
return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds";
|
||||
return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}";
|
||||
|
||||
case TimeFormat.HOURS:
|
||||
if (_timespan.TotalDays >= 1)
|
||||
return $"{Math.Floor(_timespan.TotalHours)} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}";
|
||||
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{_timespan.Hours} {config.HoursString}, {_timespan.Minutes} {config.MinutesString}";
|
||||
|
||||
return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}";
|
||||
|
||||
case TimeFormat.MINUTES:
|
||||
if (_timespan.TotalHours >= 1)
|
||||
return $"{Math.Floor(_timespan.TotalMinutes)} minutes, {_timespan.Seconds} seconds";
|
||||
return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds";
|
||||
return $"{Math.Floor(_timespan.TotalMinutes)} {config.MinutesString}, {_timespan.Seconds} {config.SecondsString}";
|
||||
return $"{_timespan.Minutes} {config.MinutesString}, {_timespan.Seconds} {config.MinutesString}";
|
||||
|
||||
default:
|
||||
return _timespan.ToString();
|
||||
|
|
@ -88,12 +81,66 @@ internal static class Internal
|
|||
|
||||
public class InternalSheduler
|
||||
{
|
||||
public static Dictionary<string, taskInfo> registeredScheduledTasks { get; internal set; } = new Dictionary<string, taskInfo>();
|
||||
public static Dictionary<string, ScheduledTask> RegisteredScheduledTasks { get; internal set; } = new Dictionary<string, ScheduledTask>();
|
||||
|
||||
public class taskInfo
|
||||
public class ScheduledTask
|
||||
{
|
||||
public string customId { get; internal set; } = "";
|
||||
internal CancellationTokenSource? tokenSource { get; set; }
|
||||
public DateTime? runTime { get; internal set; }
|
||||
public ScheduledTask()
|
||||
{
|
||||
this.Uid = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of this task.
|
||||
/// </summary>
|
||||
public string Uid { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The custom data asscociated with this task.
|
||||
/// </summary>
|
||||
public object CustomData { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time this task will run.
|
||||
/// </summary>
|
||||
public DateTime? RunTime { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CancellationTokenSource"/> to prematurely dequeue this task.
|
||||
/// </summary>
|
||||
internal CancellationTokenSource? TokenSource { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Delete this task.
|
||||
/// </summary>
|
||||
public void Delete() =>
|
||||
UniversalExtensions.DeleteScheduledTask(Uid);
|
||||
}
|
||||
}
|
||||
|
||||
public class HumanReadableTimeFormatConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The string used for days.
|
||||
/// Defaults to: "days".
|
||||
/// </summary>
|
||||
public string DaysString { get; set; } = "days";
|
||||
|
||||
/// <summary>
|
||||
/// The string used for hours.
|
||||
/// Defaults to: "hours".
|
||||
/// </summary>
|
||||
public string HoursString { get; set; } = "hours";
|
||||
|
||||
/// <summary>
|
||||
/// The string used for minutes.
|
||||
/// Defaults to: "minutes".
|
||||
/// </summary>
|
||||
public string MinutesString { get; set; } = "minutes";
|
||||
|
||||
/// <summary>
|
||||
/// The string used for seconds.
|
||||
/// Defaults to: "seconds".
|
||||
/// </summary>
|
||||
public string SecondsString { get; set; } = "seconds";
|
||||
}
|
||||
15
Log.cs
Normal file
15
Log.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// Project Makoto
|
||||
// Copyright (C) 2023 Fortunevale
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
|
||||
namespace Xorog.UniversalExtensions;
|
||||
|
||||
internal class Log
|
||||
{
|
||||
internal static ILogger? _logger { get; set; }
|
||||
}
|
||||
|
|
@ -2,6 +2,16 @@
|
|||
|
||||
public static class UniversalExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Attaches a logger to UniversalExtensions. Used for Debug purposes.
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
public static void AttachLogger(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for string.IsNullOrWhiteSpace
|
||||
/// </summary>
|
||||
|
|
@ -161,6 +171,8 @@ public static class UniversalExtensions
|
|||
/// <returns>The URL the redirect leads to</returns>
|
||||
public static async Task<string> UnshortenUrl(string url, bool UseHeadMethod = true)
|
||||
{
|
||||
_logger?.LogDebug("Unshortening Url '{Url}', using head method: {UseHeadMethod}", url, UseHeadMethod);
|
||||
|
||||
HttpClient client = new(new HttpClientHandler()
|
||||
{
|
||||
AllowAutoRedirect = false,
|
||||
|
|
@ -194,14 +206,20 @@ public static class UniversalExtensions
|
|||
if (!request_task.IsCompleted)
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
if (UseHeadMethod && request_task.IsFaulted && request_task.Exception.InnerException.GetType().FullName == "System.Net.Http.HttpRequestException")
|
||||
if (UseHeadMethod && request_task.IsFaulted && request_task.Exception.InnerException.GetType() == typeof(HttpRequestException))
|
||||
{
|
||||
_logger?.LogWarning("Unshortening Url '{Url}' failed, falling back to non-head method", url);
|
||||
return await UnshortenUrl(url, false);
|
||||
}
|
||||
|
||||
var statuscode = request_task.Result.StatusCode;
|
||||
var header = request_task.Result.Headers;
|
||||
|
||||
if (UseHeadMethod && statuscode is HttpStatusCode.NotFound or HttpStatusCode.InternalServerError)
|
||||
{
|
||||
_logger?.LogWarning("Unshortening Url '{Url}' failed, falling back to non-head method", url);
|
||||
return await UnshortenUrl(url, false);
|
||||
}
|
||||
|
||||
if (statuscode is HttpStatusCode.Found
|
||||
or HttpStatusCode.Redirect
|
||||
|
|
@ -291,9 +309,10 @@ public static class UniversalExtensions
|
|||
/// </summary>
|
||||
/// <param name="task">The task to run</param>
|
||||
/// <param name="runTime">The time to run the task</param>
|
||||
/// <param name="customData">Any custom data you wish to provide.</param>
|
||||
/// <returns>An unique identifier of the task</returns>
|
||||
|
||||
public static string CreateScheduleTask(this Task task, DateTime runTime, string CustomId = "")
|
||||
public static string CreateScheduledTask(this Task task, DateTime runTime, object? customData = null)
|
||||
{
|
||||
string UID = Guid.NewGuid().ToString();
|
||||
CancellationTokenSource CancellationToken = new CancellationTokenSource();
|
||||
|
|
@ -303,28 +322,28 @@ public static class UniversalExtensions
|
|||
|
||||
_ = LongDelay(runTime.GetTimespanUntil(), CancellationToken).ContinueWith(x =>
|
||||
{
|
||||
if (registeredScheduledTasks.ContainsKey(UID))
|
||||
registeredScheduledTasks.Remove(UID);
|
||||
lock (RegisteredScheduledTasks)
|
||||
{
|
||||
RegisteredScheduledTasks.Remove(UID);
|
||||
}
|
||||
|
||||
_logger?.LogDebug("Running scheduled task with UID '{UID}'", UID, runTime.GetTimespanUntil().GetHumanReadable());
|
||||
|
||||
if (x.IsCompletedSuccessfully)
|
||||
task.Start();
|
||||
});
|
||||
|
||||
if (registeredScheduledTasks is null)
|
||||
registeredScheduledTasks = new();
|
||||
lock (RegisteredScheduledTasks)
|
||||
{
|
||||
_logger?.LogDebug("Creating scheduled task with UID '{UID}' running in {RunTime}", UID, runTime.GetTimespanUntil().GetHumanReadable());
|
||||
|
||||
try
|
||||
{
|
||||
registeredScheduledTasks.Add(UID, new taskInfo { tokenSource = CancellationToken, customId = CustomId, runTime = runTime });
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
registeredScheduledTasks = new();
|
||||
registeredScheduledTasks.Add(UID, new taskInfo { tokenSource = CancellationToken, customId = CustomId, runTime = runTime });
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
RegisteredScheduledTasks.Add(UID, new ScheduledTask
|
||||
{
|
||||
Uid = UID,
|
||||
RunTime = runTime,
|
||||
TokenSource = CancellationToken,
|
||||
CustomData = customData,
|
||||
});
|
||||
}
|
||||
return UID;
|
||||
}
|
||||
|
|
@ -335,17 +354,22 @@ public static class UniversalExtensions
|
|||
/// Deletes a scheduled task
|
||||
/// </summary>
|
||||
/// <param name="UID">The task's unique identifier</param>
|
||||
/// <exception cref="Exception">Throws if the task hasn't been found or if an internal error occurred</exception>
|
||||
public static void DeleteScheduleTask(string UID)
|
||||
/// <exception cref="KeyNotFoundException">Throws if the task hasn't been found or if an internal error occurred</exception>
|
||||
public static void DeleteScheduledTask(string UID)
|
||||
{
|
||||
if (!registeredScheduledTasks.ContainsKey(UID))
|
||||
throw new Exception($"No scheduled task has been found with UID '{UID}'");
|
||||
if (!RegisteredScheduledTasks.ContainsKey(UID))
|
||||
throw new KeyNotFoundException($"No scheduled task has been found with UID '{UID}'");
|
||||
|
||||
if (registeredScheduledTasks[ UID ].tokenSource is null)
|
||||
if (RegisteredScheduledTasks[UID].TokenSource is null)
|
||||
throw new Exception($"Internal: There is no token source registered the specified task.");
|
||||
|
||||
registeredScheduledTasks[ UID ].tokenSource?.Cancel();
|
||||
registeredScheduledTasks.Remove(UID);
|
||||
_logger?.LogDebug("Deleting scheduled task with UID '{UID}'", UID);
|
||||
|
||||
lock (RegisteredScheduledTasks)
|
||||
{
|
||||
RegisteredScheduledTasks[UID].TokenSource?.Cancel();
|
||||
RegisteredScheduledTasks.Remove(UID);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -355,15 +379,8 @@ public static class UniversalExtensions
|
|||
/// Gets a list of all registered tasks
|
||||
/// </summary>
|
||||
/// <returns>A list of all registered tasks</returns>
|
||||
public static IReadOnlyDictionary<string, taskInfo>? GetScheduleTasks()
|
||||
{
|
||||
if (registeredScheduledTasks is null)
|
||||
registeredScheduledTasks = new();
|
||||
|
||||
return registeredScheduledTasks as IReadOnlyDictionary<string, taskInfo>;
|
||||
}
|
||||
|
||||
|
||||
public static IReadOnlyList<ScheduledTask>? GetScheduledTasks()
|
||||
=> RegisteredScheduledTasks.Select(x => x.Value).ToList().AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific task
|
||||
|
|
@ -371,13 +388,8 @@ public static class UniversalExtensions
|
|||
/// <param name="UID">The unique identifier of what task to get</param>
|
||||
/// <returns>The task</returns>
|
||||
/// <exception cref="Exception">Throws if the task has not been found</exception>
|
||||
public static taskInfo GetScheduleTask(string UID)
|
||||
{
|
||||
if (!registeredScheduledTasks.ContainsKey(UID))
|
||||
throw new Exception($"The specified task doesn't exist.");
|
||||
|
||||
return registeredScheduledTasks[UID];
|
||||
}
|
||||
public static ScheduledTask GetScheduledTask(string UID)
|
||||
=> RegisteredScheduledTasks[UID];
|
||||
|
||||
|
||||
|
||||
|
|
@ -513,33 +525,23 @@ public static class UniversalExtensions
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Get a human readable string for the given amount of seconds
|
||||
/// Get a human readable string for the given amount of time.
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <param name="timeFormat"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) =>
|
||||
TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat);
|
||||
public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) =>
|
||||
TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat,config);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="UniversalExtensions.GetHumanReadable(int, TimeFormat)"/>
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <param name="timeFormat"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) =>
|
||||
TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat);
|
||||
public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) =>
|
||||
TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat, config);
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc cref="UniversalExtensions.GetHumanReadable(int, TimeFormat)"/>
|
||||
/// </summary>
|
||||
/// <param name="timeSpan"></param>
|
||||
/// <param name="timeFormat"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetHumanReadable(this TimeSpan timeSpan, TimeFormat timeFormat = TimeFormat.DAYS) =>
|
||||
timeSpan.GetTimeFormat(timeFormat);
|
||||
public static string GetHumanReadable(this TimeSpan timeSpan, TimeFormat timeFormat = TimeFormat.DAYS, HumanReadableTimeFormatConfig? config = null) =>
|
||||
timeSpan.GetTimeFormat(timeFormat, config);
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,4 +36,8 @@
|
|||
<None Remove="UniversalExtensions.cs~RF2ac278e.TMP" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue