b78ce003a7
The noticeboard listens for updates to user assignments (including who was previously assigned the device), but was requesting data which no longer existed (as updates are delayed and buffered for up to 500ms).
264 lines
10 KiB
C#
264 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.Concurrency;
|
|
using System.Reactive.Linq;
|
|
using System.Reactive.Subjects;
|
|
|
|
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"
|
|
};
|
|
|
|
private static Subject<Tuple<List<string>, List<string>>> BufferedUpdateStream;
|
|
|
|
static HeldDevices()
|
|
{
|
|
BufferedUpdateStream = new Subject<Tuple<List<string>, List<string>>>();
|
|
|
|
BufferedUpdateStream
|
|
.DelayBuffer(TimeSpan.FromMilliseconds(500))
|
|
.SubscribeOn(TaskPoolScheduler.Default)
|
|
.Subscribe(ProcessUpdates);
|
|
|
|
// Subscribe to Repository Notifications
|
|
RepositoryMonitor.StreamBeforeCommit.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)))
|
|
)
|
|
)
|
|
.Subscribe(RepositoryEvent);
|
|
}
|
|
|
|
private static void RepositoryEvent(RepositoryMonitorEvent i)
|
|
{
|
|
List<string> deviceSerialNumbers = new List<string>();
|
|
List<string> userIds = new List<string>();
|
|
|
|
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 = i.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(
|
|
i.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(
|
|
i.Database.Jobs
|
|
.Where(j => !j.ClosedDate.HasValue && j.Device.AssignedUserId == u.UserId)
|
|
.Select(j => j.DeviceSerialNumber)
|
|
);
|
|
}
|
|
|
|
if (deviceSerialNumbers.Count > 0 || userIds.Count > 0)
|
|
{
|
|
i.ExecuteAfterCommit(e =>
|
|
{
|
|
BufferedUpdateStream.OnNext(Tuple.Create(deviceSerialNumbers, userIds));
|
|
});
|
|
}
|
|
}
|
|
|
|
private static void ProcessUpdates(IEnumerable<Tuple<List<string>, List<string>>> e)
|
|
{
|
|
using (DiscoDataContext Database = new DiscoDataContext())
|
|
{
|
|
var deviceSerialNumbers = e.SelectMany(i => i.Item1).Distinct().ToList();
|
|
var userIds = e.SelectMany(i => i.Item2).Distinct().ToList();
|
|
|
|
// Determine Held Devices for Users
|
|
if (deviceSerialNumbers.Count > 0)
|
|
{
|
|
userIds.AddRange(
|
|
Database.Devices
|
|
.Where(d => d.AssignedUserId != null && deviceSerialNumbers.Contains(d.SerialNumber))
|
|
.Select(d => d.AssignedUserId)
|
|
);
|
|
}
|
|
if (userIds.Count > 0)
|
|
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);
|
|
}
|
|
}
|
|
}
|