Files
Disco/Disco.Services/Jobs/Noticeboards/HeldDevices.cs
T
Gary Sharp 4cd57f4a90 Update: SignalR 2.0.3 Migration; Noticeboards
Migrate all SignalR 1.x Persistent Connections to SignalR 2.x Hubs.
Abstracts ScheduledTaskStatus with core interface and adds a Mock for
optional status reporting. Noticeboards rewritten (with new theme) to be
more resilient and accurate.
2014-06-01 23:27:07 +10:00

240 lines
10 KiB
C#

using Disco.Data.Repository;
using Disco.Data.Repository.Monitor;
using Disco.Models.Repository;
using Disco.Models.Services.Jobs.Noticeboards;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
namespace Disco.Services.Jobs.Noticeboards
{
public static class HeldDevices
{
public const string Name = "HeldDevices";
private readonly static List<string> MonitorJobProperties = new List<string>() {
"DeviceSerialNumber",
"UserId",
"ExpectedClosedDate",
"ClosedDate",
"WaitingForUserAction",
"DeviceHeld",
"DeviceReadyForReturn",
"DeviceReturnedDate"
};
private readonly static List<string> MonitorJobMetaNonWarrantyProperties = new List<string>(){
"AccountingChargeRequiredDate",
"AccountingChargeAddedDate",
"AccountingChargePaidDate"
};
private readonly static List<string> MonitorDeviceProperties = new List<string>(){
"Location",
"DeviceProfileId",
"DeviceDomainId",
"AssignedUserId",
};
private readonly static List<string> MonitorDeviceProfileProperties = new List<string>(){
"DefaultOrganisationAddress"
};
private readonly static List<string> MonitorUserProperties = new List<string>(){
"DisplayName"
};
static HeldDevices()
{
// Subscribe to Repository Notifications
RepositoryMonitor.StreamAfterCommit.Where(e =>
(e.EntityType == typeof(Job) &&
(e.EventType == RepositoryMonitorEventType.Added ||
e.EventType == RepositoryMonitorEventType.Deleted ||
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorJobProperties.Contains(p))))
) ||
(e.EntityType == typeof(JobMetaNonWarranty) &&
(e.EventType == RepositoryMonitorEventType.Added ||
e.EventType == RepositoryMonitorEventType.Deleted ||
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorJobMetaNonWarrantyProperties.Contains(p))))
) ||
(e.EntityType == typeof(Device) &&
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorDeviceProperties.Contains(p)))
) ||
(e.EntityType == typeof(DeviceProfile) &&
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorDeviceProfileProperties.Contains(p)))
) ||
(e.EntityType == typeof(User) &&
(e.EventType == RepositoryMonitorEventType.Modified && e.ModifiedProperties.Any(p => MonitorUserProperties.Contains(p)))
)
)
.DelayBuffer(TimeSpan.FromMilliseconds(500))
.Subscribe(RepositoryEvent);
}
private static void RepositoryEvent(IEnumerable<RepositoryMonitorEvent> e)
{
List<string> deviceSerialNumbers = new List<string>();
List<string> userIds = new List<string>();
using (DiscoDataContext Database = new DiscoDataContext())
{
foreach (var i in e)
{
if (i.EntityType == typeof(Job))
{
if (i.EventType == RepositoryMonitorEventType.Modified &&
i.ModifiedProperties.Contains("DeviceSerialNumber"))
{
var p = i.GetPreviousPropertyValue<string>("DeviceSerialNumber");
if (p != null)
deviceSerialNumbers.Add(p);
}
var j = (Job)i.Entity;
if (j.DeviceSerialNumber != null)
deviceSerialNumbers.Add(j.DeviceSerialNumber);
}
else if (i.EntityType == typeof(JobMetaNonWarranty))
{
var jmnw = (JobMetaNonWarranty)i.Entity;
if (jmnw.Job != null)
{
if (jmnw.Job.DeviceSerialNumber != null)
deviceSerialNumbers.Add(jmnw.Job.DeviceSerialNumber);
}
else
{
var sn = Database.Jobs.Where(j => j.Id == jmnw.JobId).Select(j => j.DeviceSerialNumber).FirstOrDefault();
if (sn != null)
deviceSerialNumbers.Add(sn);
}
}
else if (i.EntityType == typeof(Device))
{
var d = (Device)i.Entity;
deviceSerialNumbers.Add(d.SerialNumber);
if (i.EventType == RepositoryMonitorEventType.Modified &&
i.ModifiedProperties.Contains("AssignedUserId"))
{
var p = i.GetPreviousPropertyValue<string>("AssignedUserId");
if (p != null)
userIds.Add(p);
}
}
else if (i.EntityType == typeof(DeviceProfile))
{
var dp = (DeviceProfile)i.Entity;
deviceSerialNumbers.AddRange(
Database.Jobs
.Where(j => !j.ClosedDate.HasValue && j.Device.DeviceProfileId == dp.Id)
.Select(j => j.DeviceSerialNumber)
);
}
else if (i.EntityType == typeof(User))
{
var u = (User)i.Entity;
deviceSerialNumbers.AddRange(
Database.Jobs
.Where(j => !j.ClosedDate.HasValue && j.Device.AssignedUserId == u.UserId)
.Select(j => j.DeviceSerialNumber)
);
}
}
deviceSerialNumbers = deviceSerialNumbers.Distinct().ToList();
// Determine Held Devices for Users
userIds.AddRange(
Database.Devices
.Where(d => d.AssignedUserId != null && deviceSerialNumbers.Contains(d.SerialNumber))
.Select(d => d.AssignedUserId)
);
userIds = userIds.Distinct().ToList();
// Notify Held Devices
HeldDevices.BroadcastUpdates(Database, deviceSerialNumbers);
// Notify Held Devices for Users
HeldDevicesForUsers.BroadcastUpdates(Database, userIds);
}
}
internal static void BroadcastUpdates(DiscoDataContext Database, List<string> DeviceSerialNumbers)
{
var jobs = Database.Jobs.Where(j => DeviceSerialNumbers.Contains(j.DeviceSerialNumber));
var items = GetHeldDevices(jobs).ToDictionary(i => i.DeviceSerialNumber, StringComparer.OrdinalIgnoreCase);
for (int skipAmount = 0; skipAmount < DeviceSerialNumbers.Count; skipAmount = skipAmount + 30)
{
var updates = DeviceSerialNumbers
.Skip(skipAmount).Take(30)
.ToDictionary(dsn => dsn,
dsn => {
IHeldDeviceItem item;
items.TryGetValue(dsn, out item);
return item;
});
NoticeboardUpdatesHub.HubContext.Clients
.Group(HeldDevices.Name)
.updateHeldDevice(updates);
}
}
private static IEnumerable<IHeldDeviceItem> GetHeldDevices(IQueryable<Job> query)
{
var jobs = query
.Where(j =>
!j.ClosedDate.HasValue &&
j.DeviceSerialNumber != null &&
((j.DeviceHeld.HasValue && !j.DeviceReturnedDate.HasValue) || j.WaitingForUserAction.HasValue)
)
.SelectHeldDeviceItems()
.GroupBy(j => j.DeviceSerialNumber);
foreach (var job in jobs.ToList())
{
if (job.Any(j => j.WaitingForUserAction))
{
var item = job.Where(j => j.WaitingForUserAction).OrderBy(j => j.WaitingForUserActionSince).First();
yield return item;
}
else
{
if (job.All(j => j.ReadyForReturn))
{
var item = job.OrderByDescending(j => j.ReadyForReturnSince).First();
yield return item;
}
else
{
var item = job.Where(j => !j.ReadyForReturn).OrderByDescending(j => j.EstimatedReturnTime).First();
yield return item;
}
}
}
}
public static IEnumerable<IHeldDeviceItem> GetHeldDevices(DiscoDataContext Database)
{
return GetHeldDevices(Database.Jobs);
}
public static IHeldDeviceItem GetHeldDevice(DiscoDataContext Database, string DeviceSerialNumber)
{
return GetHeldDevices(Database.Jobs.Where(j => j.DeviceSerialNumber == DeviceSerialNumber)).FirstOrDefault();
}
internal static IEnumerable<HeldDeviceItem> SelectHeldDeviceItems(this IQueryable<Job> jobs)
{
return HeldDeviceItem.FromJobs(jobs);
}
}
}