Update Scheduler, Add config for GetTimeFormat Config, Add Logger

This commit is contained in:
Mira 2023-05-30 22:01:19 +02:00
parent d810b82011
commit 96040d2d8a
Signed by untrusted user who does not match committer: Xorog
GPG key ID: 983798ED9C3E7C36
5 changed files with 174 additions and 104 deletions

View file

@ -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;

View file

@ -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
View 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; }
}

View file

@ -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);

View file

@ -36,4 +36,8 @@
<None Remove="UniversalExtensions.cs~RF2ac278e.TMP" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>