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.
This commit is contained in:
@@ -72,7 +72,7 @@ namespace Disco.Services.Jobs.JobQueues
|
||||
|
||||
return _cache.UpdateQueue(JobQueue);
|
||||
}
|
||||
public static void DeleteJobQueue(DiscoDataContext Database, int JobQueueId, ScheduledTaskStatus Status)
|
||||
public static void DeleteJobQueue(DiscoDataContext Database, int JobQueueId, IScheduledTaskStatus Status)
|
||||
{
|
||||
JobQueue queue = Database.JobQueues.Find(JobQueueId);
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Data.Repository.Monitor;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Authorization;
|
||||
using Disco.Services.Web.Signalling;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.AspNet.SignalR.Hubs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Disco.Services.Users;
|
||||
|
||||
namespace Disco.Services.Jobs
|
||||
{
|
||||
[HubName("jobUpdates"), DiscoHubAuthorize(Claims.Job.Show)]
|
||||
public class JobUpdatesHub : Hub
|
||||
{
|
||||
private const string JobLogsPrefix = "JobLogs_";
|
||||
private const string JobAttachmentsPrefix = "JobAttachments_";
|
||||
public static IHubContext HubContext { get; private set; }
|
||||
|
||||
private static IDisposable RepositoryBeforeSubscription;
|
||||
private static IDisposable RepositoryAfterSubscription;
|
||||
|
||||
static JobUpdatesHub()
|
||||
{
|
||||
HubContext = GlobalHost.ConnectionManager.GetHubContext<JobUpdatesHub>();
|
||||
|
||||
// Subscribe to Repository Monitor for Changes
|
||||
RepositoryBeforeSubscription = RepositoryMonitor.StreamBeforeCommit
|
||||
.Where(e => (
|
||||
e.EntityType == typeof(JobLog) && e.EventType == RepositoryMonitorEventType.Deleted ||
|
||||
e.EntityType == typeof(JobAttachment) && e.EventType == RepositoryMonitorEventType.Deleted
|
||||
))
|
||||
.Subscribe(RepositoryEventBefore);
|
||||
RepositoryAfterSubscription = RepositoryMonitor.StreamAfterCommit
|
||||
.Where(e => (
|
||||
e.EntityType == typeof(JobLog) && e.EventType == RepositoryMonitorEventType.Added ||
|
||||
e.EntityType == typeof(JobAttachment) && e.EventType == RepositoryMonitorEventType.Added
|
||||
))
|
||||
.Subscribe(RepositoryAfterEvent);
|
||||
}
|
||||
|
||||
private static string LogsGroupName(int JobId)
|
||||
{
|
||||
return JobLogsPrefix + JobId.ToString();
|
||||
}
|
||||
private static string AttachmentsGroupName(int JobId)
|
||||
{
|
||||
return JobAttachmentsPrefix + JobId.ToString();
|
||||
}
|
||||
|
||||
public override Task OnConnected()
|
||||
{
|
||||
int jobId;
|
||||
string jobIdParam;
|
||||
|
||||
jobIdParam = Context.QueryString["JobId"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(jobIdParam))
|
||||
throw new ArgumentNullException("JobId");
|
||||
if (!int.TryParse(jobIdParam, out jobId))
|
||||
throw new ArgumentException("An integer was expected", "JobId");
|
||||
|
||||
var userAuth = UserService.GetAuthorization(Context.User.Identity.Name);
|
||||
|
||||
if (userAuth.Has(Claims.Job.ShowLogs))
|
||||
Groups.Add(Context.ConnectionId, LogsGroupName(jobId));
|
||||
if (userAuth.Has(Claims.Job.ShowAttachments))
|
||||
Groups.Add(Context.ConnectionId, AttachmentsGroupName(jobId));
|
||||
|
||||
return base.OnConnected();
|
||||
}
|
||||
|
||||
private static void RepositoryEventBefore(RepositoryMonitorEvent e)
|
||||
{
|
||||
if (e.EventType == RepositoryMonitorEventType.Deleted)
|
||||
{
|
||||
if (e.EntityType == typeof(JobLog))
|
||||
{
|
||||
var repositoryLog = (JobLog)e.Entity;
|
||||
int logJobId;
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
logJobId = Database.JobLogs.Where(l => l.Id == repositoryLog.Id).Select(a => a.JobId).First();
|
||||
|
||||
HubContext.Clients.Group(LogsGroupName(logJobId)).removeLog(repositoryLog.Id);
|
||||
}
|
||||
if (e.EntityType == typeof(JobAttachment))
|
||||
{
|
||||
var repositoryAttachment = (JobAttachment)e.Entity;
|
||||
int attachmentJobId;
|
||||
|
||||
using (DiscoDataContext Database = new DiscoDataContext())
|
||||
attachmentJobId = Database.JobAttachments.Where(a => a.Id == repositoryAttachment.Id).Select(a => a.JobId).First();
|
||||
|
||||
HubContext.Clients.Group(AttachmentsGroupName(attachmentJobId)).removeAttachment(repositoryAttachment.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RepositoryAfterEvent(RepositoryMonitorEvent e)
|
||||
{
|
||||
if (e.EventType == RepositoryMonitorEventType.Added)
|
||||
{
|
||||
if (e.EntityType == typeof(JobLog))
|
||||
{
|
||||
var a = (JobLog)e.Entity;
|
||||
|
||||
HubContext.Clients.Group(LogsGroupName(a.JobId)).addLog(a.Id);
|
||||
}
|
||||
if (e.EntityType == typeof(JobAttachment))
|
||||
{
|
||||
var a = (JobAttachment)e.Entity;
|
||||
|
||||
HubContext.Clients.Group(AttachmentsGroupName(a.JobId)).addAttachment(a.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using Disco.Data.Configuration.Modules;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Jobs.Noticeboards;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Disco.Services.Jobs.Noticeboards
|
||||
{
|
||||
public class HeldDeviceItem : IHeldDeviceItem
|
||||
{
|
||||
public int JobId { get; set; }
|
||||
|
||||
public string DeviceSerialNumber { get; set; }
|
||||
public string DeviceComputerNameFriendly
|
||||
{
|
||||
get
|
||||
{
|
||||
return DeviceComputerName == null ? null : UserExtensions.FriendlyUserId(DeviceComputerName);
|
||||
}
|
||||
}
|
||||
public string DeviceComputerName { get; set; }
|
||||
|
||||
public string DeviceLocation { get; set; }
|
||||
public string DeviceDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(this.DeviceComputerNameFriendly);
|
||||
|
||||
if (UserId != null)
|
||||
sb.Append(" - ").Append(this.UserDisplayName).Append(" (").Append(this.UserIdFriendly).Append(")");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(this.DeviceLocation))
|
||||
sb.Append(" - ").Append(this.DeviceLocation);
|
||||
else if (UserId == null)
|
||||
sb.Append(" - ").Append(this.DeviceSerialNumber);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public int DeviceProfileId { get; set; }
|
||||
public int? DeviceAddressId { get; set; }
|
||||
public string DeviceAddressShortName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DeviceAddressId.HasValue)
|
||||
{
|
||||
var config = new OrganisationAddressesConfiguration(null);
|
||||
var address = config.GetAddress(DeviceAddressId.Value);
|
||||
if (address != null)
|
||||
return address.ShortName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public string UserId { get; set; }
|
||||
public string UserIdFriendly
|
||||
{
|
||||
get
|
||||
{
|
||||
return UserId == null ? null : UserExtensions.FriendlyUserId(UserId);
|
||||
}
|
||||
}
|
||||
public string UserDisplayName { get; set; }
|
||||
|
||||
public bool WaitingForUserAction { get; set; }
|
||||
public DateTime? WaitingForUserActionSince { get; set; }
|
||||
public long? WaitingForUserActionSinceUnixEpoc
|
||||
{
|
||||
get
|
||||
{
|
||||
return WaitingForUserActionSince.ToUnixEpoc();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReadyForReturn { get; set; }
|
||||
public DateTime? EstimatedReturnTime { get; set; }
|
||||
public long? EstimatedReturnTimeUnixEpoc
|
||||
{
|
||||
get
|
||||
{
|
||||
return EstimatedReturnTime.ToUnixEpoc();
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? ReadyForReturnSince { get; set; }
|
||||
public long? ReadyForReturnSinceUnixEpoc
|
||||
{
|
||||
get
|
||||
{
|
||||
return ReadyForReturnSince.ToUnixEpoc();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAlert
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.ReadyForReturn && (this.ReadyForReturnSince.Value < DateTime.Now.AddDays(-3)))
|
||||
return true;
|
||||
|
||||
if (this.WaitingForUserAction && (this.WaitingForUserActionSince.Value < DateTime.Now.AddDays(-6)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<HeldDeviceItem> FromJobs(IQueryable<Job> jobs)
|
||||
{
|
||||
return jobs.Select(j =>
|
||||
new HeldDeviceItem
|
||||
{
|
||||
JobId = j.Id,
|
||||
DeviceSerialNumber = j.DeviceSerialNumber,
|
||||
DeviceComputerName = j.Device.DeviceDomainId,
|
||||
DeviceLocation = j.Device.Location,
|
||||
DeviceProfileId = j.Device.DeviceProfileId,
|
||||
DeviceAddressId = j.Device.DeviceProfile.DefaultOrganisationAddress,
|
||||
UserId = j.Device.AssignedUserId,
|
||||
UserDisplayName = j.Device.AssignedUser.DisplayName,
|
||||
WaitingForUserAction = j.WaitingForUserAction.HasValue || ((j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue || j.JobMetaNonWarranty.AccountingChargeAddedDate.HasValue) && !j.JobMetaNonWarranty.AccountingChargePaidDate.HasValue),
|
||||
WaitingForUserActionSince = j.WaitingForUserAction.HasValue ? j.WaitingForUserAction : (j.JobMetaNonWarranty.AccountingChargeRequiredDate.HasValue ? j.JobMetaNonWarranty.AccountingChargeRequiredDate : j.JobMetaNonWarranty.AccountingChargeAddedDate),
|
||||
ReadyForReturn = j.DeviceReadyForReturn.HasValue && !j.DeviceReturnedDate.HasValue,
|
||||
EstimatedReturnTime = j.ExpectedClosedDate,
|
||||
ReadyForReturnSince = j.DeviceReadyForReturn
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Jobs.Noticeboards;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Jobs.Noticeboards
|
||||
{
|
||||
public class HeldDevicesForUsers
|
||||
{
|
||||
public const string Name = "HeldDevicesForUsers";
|
||||
|
||||
// NOTE: Calculation for updates is performed by HeldDevices to avoid duplication
|
||||
|
||||
internal static void BroadcastUpdates(DiscoDataContext Database, List<string> UserIds)
|
||||
{
|
||||
var jobs = Database.Devices.Where(d => UserIds.Contains(d.AssignedUserId)).SelectMany(d => d.Jobs);
|
||||
|
||||
var items = GetHeldDevicesForUsers(jobs).ToDictionary(i => i.UserId, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (int skipAmount = 0; skipAmount < UserIds.Count; skipAmount = skipAmount + 30)
|
||||
{
|
||||
var updates = UserIds
|
||||
.Skip(skipAmount).Take(30)
|
||||
.ToDictionary(userId => userId,
|
||||
userId =>
|
||||
{
|
||||
IHeldDeviceItem item;
|
||||
items.TryGetValue(userId, out item);
|
||||
return item;
|
||||
});
|
||||
|
||||
NoticeboardUpdatesHub.HubContext.Clients
|
||||
.Group(HeldDevicesForUsers.Name)
|
||||
.updateHeldDeviceForUser(updates);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<IHeldDeviceItem> GetHeldDevicesForUsers(IQueryable<Job> query)
|
||||
{
|
||||
var jobs = query
|
||||
.Where(j =>
|
||||
!j.ClosedDate.HasValue &&
|
||||
j.DeviceSerialNumber != null &&
|
||||
j.Device.AssignedUserId != null &&
|
||||
((j.DeviceHeld.HasValue && !j.DeviceReturnedDate.HasValue) || j.WaitingForUserAction.HasValue)
|
||||
)
|
||||
.SelectHeldDeviceItems()
|
||||
.GroupBy(j => j.UserId);
|
||||
|
||||
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> GetHeldDevicesForUsers(DiscoDataContext Database)
|
||||
{
|
||||
return GetHeldDevicesForUsers(Database.Jobs);
|
||||
}
|
||||
public static IHeldDeviceItem GetHeldDeviceForUsers(DiscoDataContext Database, string UserId)
|
||||
{
|
||||
var split = UserExtensions.SplitUserId(UserId);
|
||||
if (split.Item1 == null)
|
||||
UserId = string.Format(@"{0}\{1}", ActiveDirectory.Context.PrimaryDomain.NetBiosName, UserId);
|
||||
|
||||
return GetHeldDevicesForUsers(Database.Devices.Where(d => d.AssignedUserId == UserId).SelectMany(d => d.Jobs)).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.AspNet.SignalR.Hubs;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services.Jobs.Noticeboards
|
||||
{
|
||||
[HubName("noticeboardUpdates")] // Public
|
||||
public class NoticeboardUpdatesHub : Hub
|
||||
{
|
||||
public static IHubContext HubContext { get; private set; }
|
||||
|
||||
static NoticeboardUpdatesHub()
|
||||
{
|
||||
HubContext = GlobalHost.ConnectionManager.GetHubContext<NoticeboardUpdatesHub>();
|
||||
}
|
||||
|
||||
public override Task OnConnected()
|
||||
{
|
||||
var noticeboardId = Context.QueryString["Noticeboard"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(noticeboardId))
|
||||
throw new ArgumentNullException("Noticeboard");
|
||||
|
||||
switch (noticeboardId)
|
||||
{
|
||||
case HeldDevicesForUsers.Name:
|
||||
Groups.Add(Context.ConnectionId, HeldDevicesForUsers.Name);
|
||||
break;
|
||||
case HeldDevices.Name:
|
||||
Groups.Add(Context.ConnectionId, HeldDevices.Name);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Invalid Noticeboard Specified", "Noticeboard");
|
||||
}
|
||||
|
||||
return base.OnConnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user