From dff47c0e8badfdd881014ca7a3252be654925ab8 Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Sun, 4 Dec 2022 12:05:54 +1100 Subject: [PATCH] feature: export device custom details --- .../Devices/Exporting/DeviceExportOptions.cs | 4 ++ .../Devices/Exporting/DeviceExportRecord.cs | 3 + .../Devices/Exporting/DeviceExport.cs | 67 ++++++++++++++----- 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/Disco.Models/Services/Devices/Exporting/DeviceExportOptions.cs b/Disco.Models/Services/Devices/Exporting/DeviceExportOptions.cs index 73310c73..46846510 100644 --- a/Disco.Models/Services/Devices/Exporting/DeviceExportOptions.cs +++ b/Disco.Models/Services/Devices/Exporting/DeviceExportOptions.cs @@ -88,6 +88,8 @@ namespace Disco.Models.Services.Devices.Exporting public bool AssignedUserPhoneNumber { get; set; } [Display(ShortName = "Assigned User", Name = "Email Address", Description = "The email address of the user assigned to the device")] public bool AssignedUserEmailAddress { get; set; } + [Display(ShortName = "Assigned User", Name = "Custom Details", Description = "The custom details provided by plugins for the user assigned to the device")] + public bool AssignedUserDetailCustom { get; set; } // Jobs [Display(ShortName = "Jobs", Name = "Count", Description = "The total number of jobs associated with the device")] @@ -128,6 +130,8 @@ namespace Disco.Models.Services.Devices.Exporting public bool DetailBattery { get; set; } [Display(ShortName = "Details", Name = "Keyboard", Description = "The Keyboard associated with the device")] public bool DetailKeyboard { get; set; } + [Display(ShortName = "Details", Name = "Custom Details", Description = "Custom details provided by plugins")] + public bool DetailCustom { get; set; } public static DeviceExportOptions DefaultOptions() { diff --git a/Disco.Models/Services/Devices/Exporting/DeviceExportRecord.cs b/Disco.Models/Services/Devices/Exporting/DeviceExportRecord.cs index 40c3e751..8e5c5cd3 100644 --- a/Disco.Models/Services/Devices/Exporting/DeviceExportRecord.cs +++ b/Disco.Models/Services/Devices/Exporting/DeviceExportRecord.cs @@ -21,6 +21,7 @@ namespace Disco.Models.Services.Devices.Exporting public List DeviceDetailLanMacAddresses { get; set; } public List DeviceDetailWlanMacAddresses { get; set; } public List DeviceDetailBatteries { get; set; } + public Dictionary DeviceDetailCustom { get; set; } // Model public int? ModelId { get; set; } @@ -48,6 +49,8 @@ namespace Disco.Models.Services.Devices.Exporting // User public DeviceUserAssignment DeviceUserAssignment { get; set; } public User AssignedUser { get; set; } + public IList AssignedUserDetails { get; set; } + public Dictionary AssignedUserCustomDetails { get; set; } // Jobs public int JobsTotalCount { get; set; } diff --git a/Disco.Services/Devices/Exporting/DeviceExport.cs b/Disco.Services/Devices/Exporting/DeviceExport.cs index 544f625b..9add2332 100644 --- a/Disco.Services/Devices/Exporting/DeviceExport.cs +++ b/Disco.Services/Devices/Exporting/DeviceExport.cs @@ -2,12 +2,14 @@ using Disco.Data.Repository; using Disco.Models.Repository; using Disco.Models.Services.Devices.Exporting; +using Disco.Services.Plugins.Features.DetailsProvider; using Disco.Services.Tasks; using Disco.Services.Users; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data; +using System.Data.Entity; using System.IO; using System.Linq; using System.Text; @@ -17,12 +19,18 @@ namespace Disco.Services.Devices.Exporting public static class DeviceExport { - public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable Devices, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus) + public static DeviceExportResult GenerateExport(DiscoDataContext Database, Func, IQueryable> Filter, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus) { TaskStatus.UpdateStatus(15, "Extracting records from the database"); - var records = BuildRecords(Devices).ToList(); + var devices = Database.Devices + .Include(d => d.AssignedUser.UserDetails) + .Include(d => d.DeviceDetails); + if (Filter != null) + devices = Filter(devices); + + var records = BuildRecords(devices).ToList(); // materialize device details records.ForEach(r => { @@ -49,6 +57,12 @@ namespace Disco.Services.Devices.Exporting } if (Options.DetailBatteries) r.DeviceDetailBatteries = r.DeviceDetails.Batteries(); + + var detailsService = new DetailsProviderService(Database); + if (Options.DetailCustom) + r.DeviceDetailCustom = detailsService.GetDetails(r.Device).Details; + if (Options.AssignedUserDetailCustom && r.AssignedUser != null) + r.AssignedUserCustomDetails = detailsService.GetDetails(r.AssignedUser).Details; }); TaskStatus.UpdateStatus(40, "Building metadata and database query"); @@ -65,7 +79,7 @@ namespace Disco.Services.Devices.Exporting Options.AssignedUserEmailAddress) { TaskStatus.UpdateStatus(45, "Updating Assigned User details"); - var users = Devices.Where(d => d.AssignedUserId != null).Select(d => d.AssignedUser).Distinct().ToList(); + var users = records.Where(d => d.AssignedUser != null).Select(d => d.AssignedUser).Distinct().ToList(); users.Select((user, index) => { @@ -119,26 +133,21 @@ namespace Disco.Services.Devices.Exporting }; } - public static DeviceExportResult GenerateExport(DiscoDataContext Database, IQueryable Devices, DeviceExportOptions Options) - { - return GenerateExport(Database, Devices, Options, ScheduledTaskMockStatus.Create("Device Export")); - } - public static DeviceExportResult GenerateExport(DiscoDataContext Database, DeviceExportOptions Options, IScheduledTaskStatus TaskStatus) { switch (Options.ExportType) { case DeviceExportTypes.All: - return GenerateExport(Database, Database.Devices, Options, TaskStatus); + return GenerateExport(Database, null, Options, TaskStatus); case DeviceExportTypes.Batch: if (Options.ExportTypeTargetId.HasValue && Options.ExportTypeTargetId.Value > 0) - return GenerateExport(Database, Database.Devices.Where(d => d.DeviceBatchId == Options.ExportTypeTargetId), Options, TaskStatus); + return GenerateExport(Database, devices => devices.Where(d => d.DeviceBatchId == Options.ExportTypeTargetId), Options, TaskStatus); else - return GenerateExport(Database, Database.Devices.Where(d => d.DeviceBatchId == null), Options, TaskStatus); + return GenerateExport(Database, devices => devices.Where(d => d.DeviceBatchId == null), Options, TaskStatus); case DeviceExportTypes.Model: - return GenerateExport(Database, Database.Devices.Where(d => d.DeviceModelId == Options.ExportTypeTargetId), Options, TaskStatus); + return GenerateExport(Database, devices => devices.Where(d => d.DeviceModelId == Options.ExportTypeTargetId), Options, TaskStatus); case DeviceExportTypes.Profile: - return GenerateExport(Database, Database.Devices.Where(d => d.DeviceProfileId == Options.ExportTypeTargetId), Options, TaskStatus); + return GenerateExport(Database, devices => devices.Where(d => d.DeviceProfileId == Options.ExportTypeTargetId), Options, TaskStatus); default: throw new ArgumentException(string.Format("Unknown Device Export Type", Options.ExportType.ToString()), "Options"); } @@ -229,6 +238,7 @@ namespace Disco.Services.Devices.Exporting DeviceUserAssignment = d.DeviceUserAssignments.Where(dua => dua.UnassignedDate == null).FirstOrDefault(), AssignedUser = d.AssignedUser, + AssignedUserDetails = d.AssignedUser.UserDetails, JobsTotalCount = d.Jobs.Count(), JobsOpenCount = d.Jobs.Count(j => j.ClosedDate == null), @@ -249,7 +259,14 @@ namespace Disco.Services.Devices.Exporting var certificateMaxCount = Math.Max(1, records.Max(r => r.DeviceCertificates?.Count() ?? 0)); var batteriesMaxCount = Math.Max(1, records.Max(r => r.DeviceDetailBatteries?.Count ?? 0)); - var allAssessors = BuildRecordAccessors(processorMaxCount, memoryMaxCount, diskDriveMaxCount, lanAdapterMaxCount, wlanAdapterMaxCount, certificateMaxCount, batteriesMaxCount); + IEnumerable deviceDetailCustomKeys = null; + IEnumerable assignedUserDetailCustomKeys = null; + if (options.DetailCustom) + deviceDetailCustomKeys = records.Where(r => r.DeviceDetailCustom != null).SelectMany(r => r.DeviceDetailCustom.Keys).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + if (options.AssignedUserDetailCustom) + assignedUserDetailCustomKeys = records.Where(r => r.AssignedUserCustomDetails != null).SelectMany(r => r.AssignedUserCustomDetails.Keys).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + + var allAssessors = BuildRecordAccessors(processorMaxCount, memoryMaxCount, diskDriveMaxCount, lanAdapterMaxCount, wlanAdapterMaxCount, certificateMaxCount, batteriesMaxCount, deviceDetailCustomKeys, assignedUserDetailCustomKeys); return typeof(DeviceExportOptions).GetProperties() .Where(p => p.PropertyType == typeof(bool)) @@ -271,7 +288,7 @@ namespace Disco.Services.Devices.Exporting }).ToList(); } - private static Dictionary> BuildRecordAccessors(int processorMaxCount, int memoryMaxCount, int diskDriveMaxCount, int lanAdapterMaxCount, int wlanAdapterMaxCount, int certificateMaxCount, int batteriesMaxCount) + private static Dictionary> BuildRecordAccessors(int processorMaxCount, int memoryMaxCount, int diskDriveMaxCount, int lanAdapterMaxCount, int wlanAdapterMaxCount, int certificateMaxCount, int batteriesMaxCount, IEnumerable deviceDetailCustomKeys, IEnumerable assignedUserDetailCustomKeys) { const string DateFormat = "yyyy-MM-dd"; const string DateTimeFormat = DateFormat + " HH:mm:ss"; @@ -330,6 +347,16 @@ namespace Disco.Services.Devices.Exporting metadata.Add(nameof(DeviceExportOptions.AssignedUserGivenName), new List() { new DeviceExportFieldMetadata(nameof(DeviceExportOptions.AssignedUserGivenName), typeof(string), r => r.AssignedUser?.GivenName, csvStringEncoded) }); metadata.Add(nameof(DeviceExportOptions.AssignedUserPhoneNumber), new List() { new DeviceExportFieldMetadata(nameof(DeviceExportOptions.AssignedUserPhoneNumber), typeof(string), r => r.AssignedUser?.PhoneNumber, csvStringEncoded) }); metadata.Add(nameof(DeviceExportOptions.AssignedUserEmailAddress), new List() { new DeviceExportFieldMetadata(nameof(DeviceExportOptions.AssignedUserEmailAddress), typeof(string), r => r.AssignedUser?.EmailAddress, csvStringEncoded) }); + if (assignedUserDetailCustomKeys != null) + { + var assignedUserDetailCustomFields = new List(); + foreach (var detailKey in assignedUserDetailCustomKeys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase)) + { + var key = detailKey; + assignedUserDetailCustomFields.Add(new DeviceExportFieldMetadata(detailKey, detailKey, typeof(string), r => r.AssignedUserCustomDetails != null && r.AssignedUserCustomDetails.TryGetValue(key, out var value) ? value : null, csvStringEncoded)); + } + metadata.Add(nameof(DeviceExportOptions.AssignedUserDetailCustom), assignedUserDetailCustomFields); + } // Jobs metadata.Add(nameof(DeviceExportOptions.JobsTotalCount), new List() { new DeviceExportFieldMetadata(nameof(DeviceExportOptions.JobsTotalCount), typeof(int), r => r.JobsTotalCount, csvToStringEncoded) }); @@ -508,6 +535,16 @@ namespace Disco.Services.Devices.Exporting } metadata.Add(nameof(DeviceExportOptions.DetailBatteries), batteriesFields); metadata.Add(nameof(DeviceExportOptions.DetailKeyboard), new List() { new DeviceExportFieldMetadata(nameof(DeviceExportOptions.DetailKeyboard), typeof(string), r => r.DeviceDetails.Where(dd => dd.Key == DeviceDetail.HardwareKeyKeyboard).Select(dd => dd.Value).FirstOrDefault(), csvStringEncoded) }); + if (deviceDetailCustomKeys != null) + { + var deviceDetailCustomFields = new List(); + foreach (var detailKey in deviceDetailCustomKeys.OrderBy(k => k, StringComparer.OrdinalIgnoreCase)) + { + var key = detailKey; + deviceDetailCustomFields.Add(new DeviceExportFieldMetadata(detailKey, detailKey, typeof(string), r => r.DeviceDetailCustom != null && r.DeviceDetailCustom.TryGetValue(key, out var value) ? value : null, csvStringEncoded)); + } + metadata.Add(nameof(DeviceExportOptions.DetailCustom), deviceDetailCustomFields); + } return metadata; }