using log4net; using Nancy; using Nancy.Extensions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text.Json; #nullable enable #pragma warning disable CA1822 namespace Adaptation.FileHandlers.Priority; public class WeightedShortestJobFirstModule : NancyModule { public WeightedShortestJobFirstModule() { Get("/api/v1/ado/", _ => { string json; ILog log = LogManager.GetLogger(typeof(WeightedShortestJobFirstModule)); log.Info($"Enter-{nameof(GetKeyValuePairs)}"); try { string query = Request.Url.Query; IDictionary> collection = Nancy.Helpers.HttpUtility.ParseQueryString(query).ToDictionary(); KeyValuePair? workItem = GetWorkItem(collection); json = workItem is null ? string.Empty : JsonSerializer.Serialize(workItem, KeyValuePairStringWorkItemSourceGenerationContext.Default.KeyValuePairStringWorkItem); } catch (Exception ex) { log.Fatal($"Exception-{nameof(GetKeyValuePairs)}{Environment.NewLine}{ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}"); throw; } log.Info($"Return-{nameof(GetKeyValuePairs)}"); return json; }); base.Post("/api/v1/ado/", _ => { Notification notification; ILog log = LogManager.GetLogger(typeof(WeightedShortestJobFirstModule)); log.Info($"Enter-{nameof(Post)}"); try { string body = Request.Body.AsString(); DynamicDictionary form = Request.Form; Dictionary keyValuePairs = form.ToDictionary(); notification = GetNotification(body, keyValuePairs); } catch (Exception ex) { log.Fatal($"Exception-{nameof(Post)}{Environment.NewLine}{ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}"); throw; } log.Info($"Return-{nameof(Post)}"); return notification.Time.ToString(); }); } private static Dictionary GetKeyValuePairs(IDictionary> collection) { Dictionary results = new(); string[] array; foreach (KeyValuePair> keyValuePair in collection) { array = keyValuePair.Value.ToArray(); if (array.Length != 1) continue; if (array.Length == 1 && array[0] == "null") results.Add(keyValuePair.Key, null); else results.Add(keyValuePair.Key, array[0]); } return results; } internal static Notification GetNotification(string body, Dictionary keyValuePairs) { Notification result; if (FileRead.Queue is null) throw new NullReferenceException(nameof(FileRead.Queue)); if (FileRead.Settings is null) throw new NullReferenceException(nameof(FileRead.Settings)); if (FileRead.WorkItems is null) throw new NullReferenceException(nameof(FileRead.WorkItems)); string? json; if (!string.IsNullOrEmpty(body) && body[0] == '{') { File.WriteAllText(".json", body); result = JsonSerializer.Deserialize(body, NotificationSourceGenerationContext.Default.Notification) ?? throw new NullReferenceException(); } else { json = JsonSerializer.Serialize(keyValuePairs); File.WriteAllText(".json", json); result = JsonSerializer.Deserialize(json, NotificationSourceGenerationContext.Default.Notification) ?? throw new NullReferenceException(); } if (!string.IsNullOrEmpty(result.Id)) FileWriteAllText(FileRead.Settings, result); json = PopulatedWorkItemsAndGetJson(FileRead.Settings, FileRead.WorkItems); if (!string.IsNullOrEmpty(json)) WriteJson(FileRead.Settings, json); if (!string.IsNullOrEmpty(result.SessionId)) { string key = GetKey(result); Queue>? queue; lock (FileRead.Queue) { if (!FileRead.Queue.TryGetValue(key, out queue)) { FileRead.Queue.Add(key, new()); if (!FileRead.Queue.TryGetValue(key, out queue)) throw new Exception(); } } WorkItem? workItem = GetWorkItem(FileRead.WorkItems, result); if (workItem is not null) { lock (FileRead.Queue) { foreach (KeyValuePair>> keyValuePair in FileRead.Queue) keyValuePair.Value.Enqueue(new(result.Page, workItem)); } } } return result; } internal static KeyValuePair? GetWorkItem(IDictionary> collection) { KeyValuePair? result; Dictionary keyValuePairs = GetKeyValuePairs(collection); Notification notification = Notification.Get(keyValuePairs); if (FileRead.Queue is null) result = null; else { Queue>? queue; string key = GetKey(notification); lock (FileRead.Queue) { if (!FileRead.Queue.TryGetValue(key, out queue)) { FileRead.Queue.Add(key, new()); if (!FileRead.Queue.TryGetValue(key, out queue)) throw new Exception(); } result = queue.Count == 0 ? null : queue.Dequeue(); } } return result; } private static string GetKey(Notification notification) => $"{notification.SessionId}-{notification.MachineId}-{notification.Username}"; private static void FileWriteAllText(Settings settings, Notification notification) { if (string.IsNullOrEmpty(notification.Id)) throw new NullReferenceException(nameof(notification.Id)); string json = JsonSerializer.Serialize(notification, NotificationSourceGenerationContext.Default.Notification); string directory = Path.Combine(settings.SourceFileLocation, notification.Page, notification.Id.ToString()); if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); string checkFile = Path.Combine(directory, $"{notification.Time}.json"); File.WriteAllText(checkFile, json); } internal static string? PopulatedWorkItemsAndGetJson(Settings settings, Dictionary workItems) { string? result = null; ReadOnlyDictionary keyValuePairs = WorkItem.GetKeyValuePairs(settings); int useCount = (from l in keyValuePairs where l.Value.CostOfDelay is not null select true).Count(); double prioritySize = useCount / settings.Priorities; double priorityGroupSize = useCount / settings.PriorityGroups; WorkItem[] sorted = (from l in keyValuePairs where l.Value is not null orderby l.Value.Site is not null, l.Value.Site descending, l.Value.CostOfDelay is not null, l.Value.CostOfDelay descending, l.Value.BusinessValue?.FibonacciAverage is not null, l.Value.BusinessValue?.FibonacciAverage descending, l.Key select l.Value).ToArray(); lock (workItems) { int j = 0; WorkItem w; double value; int lastId = -1; int? sortBeforeId; WorkItem workItem; int? sortPriority; workItems.Clear(); int? sortPriorityGroup; for (int i = 0; i < sorted.Length; i++) { w = sorted[i]; if (w.CostOfDelay is null) { sortBeforeId = null; sortPriority = null; sortPriorityGroup = null; } else { j += 1; sortBeforeId = lastId; value = (j / prioritySize) + 1; sortPriority = (int)Math.Floor(value); if (sortPriority > settings.Priorities) sortPriority = settings.Priorities; value = (j / priorityGroupSize) + 1; sortPriorityGroup = (int)Math.Floor(value); if (sortPriorityGroup > settings.PriorityGroups) sortPriorityGroup = settings.PriorityGroups; } workItem = WorkItem.GetWorkItem(w, i, sortBeforeId, sortPriority, sortPriorityGroup); workItems.Add(workItem.Id, workItem); lastId = w.Id; } result = JsonSerializer.Serialize(workItems, WorkItemDictionarySourceGenerationContext.Default.DictionaryInt32WorkItem); } return result; } private static WorkItem? GetWorkItem(Dictionary workItems, Notification notification) { WorkItem? result; if (string.IsNullOrEmpty(notification.Id) || !int.TryParse(notification.Id, out int id)) result = null; else { lock (workItems) { if (!workItems.TryGetValue(id, out result)) throw new Exception(); } } return result; } internal static void WriteJson(Settings settings, string json) { string jsonFile = Path.Combine(settings.ParentDirectory, "{}.json"); string jsonFileWith = Path.Combine(settings.ParentDirectory, "{[]}.json"); string jsonOld = File.Exists(jsonFileWith) ? File.ReadAllText(jsonFileWith) : string.Empty; if (json != jsonOld) { File.WriteAllText(jsonFileWith, json); Dictionary w = JsonSerializer.Deserialize(json.Replace($"\"{nameof(Aggregation.Notifications)}\":", "\"ignore\":"), WorkItemDictionarySourceGenerationContext.Default.DictionaryInt32WorkItem) ?? throw new Exception(); json = JsonSerializer.Serialize(w, WorkItemDictionarySourceGenerationContext.Default.DictionaryInt32WorkItem); File.WriteAllText(jsonFile, json); } } }