262 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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<string, IEnumerable<string>> collection = Nancy.Helpers.HttpUtility.ParseQueryString(query).ToDictionary();
 | |
|                 KeyValuePair<string, WorkItem>? 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<string, object> 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<string, string?> GetKeyValuePairs(IDictionary<string, IEnumerable<string>> collection)
 | |
|     {
 | |
|         Dictionary<string, string?> results = new();
 | |
|         string[] array;
 | |
|         foreach (KeyValuePair<string, IEnumerable<string>> 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<string, object> 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<KeyValuePair<string, WorkItem>>? 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<string, Queue<KeyValuePair<string, WorkItem>>> keyValuePair in FileRead.Queue)
 | |
|                         keyValuePair.Value.Enqueue(new(result.Page, workItem));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     internal static KeyValuePair<string, WorkItem>? GetWorkItem(IDictionary<string, IEnumerable<string>> collection)
 | |
|     {
 | |
|         KeyValuePair<string, WorkItem>? result;
 | |
|         Dictionary<string, string?> keyValuePairs = GetKeyValuePairs(collection);
 | |
|         Notification notification = Notification.Get(keyValuePairs);
 | |
|         if (FileRead.Queue is null)
 | |
|             result = null;
 | |
|         else
 | |
|         {
 | |
|             Queue<KeyValuePair<string, WorkItem>>? 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<int, WorkItem> workItems)
 | |
|     {
 | |
|         string? result = null;
 | |
|         ReadOnlyDictionary<int, WorkItem?> 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<int, WorkItem> 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<int, WorkItem> 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);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| } |