Feature: Document Template Packages

Document Templates can be grouped into a package and generated on-demand
in the same was as individual document templates. Packages can be
generated in bulk.
This commit is contained in:
Gary Sharp
2016-11-14 01:21:23 +11:00
parent ef8df08e29
commit aca037ecf8
75 changed files with 8008 additions and 2078 deletions
@@ -2,6 +2,7 @@
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Services.Authorization;
using Disco.Services.Documents;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Users;
@@ -54,6 +55,10 @@ namespace Disco.Services
return ats.Where(at => at.FilterExpressionMatches(d, Database, User, TimeStamp, DocumentState.DefaultState())).ToList();
}
public static List<DocumentTemplatePackage> AvailableDocumentTemplatePackages(this Device d, DiscoDataContext Database, User TechnicianUser)
{
return DocumentTemplatePackages.AvailablePackages(d, Database, TechnicianUser);
}
public static bool UpdateLastNetworkLogonDate(this Device Device)
{
+2
View File
@@ -281,9 +281,11 @@
<Compile Include="Documents\AttachmentImport\ImportPage.cs" />
<Compile Include="Documents\AttachmentImport\ThumbnailUpdateTask.cs" />
<Compile Include="Documents\DocumentsLog.cs" />
<Compile Include="Documents\DocumentTemplatePackageExtensions.cs" />
<Compile Include="Documents\DocumentTemplateExtensions.cs" />
<Compile Include="Documents\DocumentTemplateDataStoreExtensions.cs" />
<Compile Include="Documents\DocumentTemplateExpressionExtensions.cs" />
<Compile Include="Documents\DocumentTemplatePackages.cs" />
<Compile Include="Documents\DocumentUniqueIdentifier.cs" />
<Compile Include="Documents\DocumentUniqueIdentifierExtensions.cs" />
<Compile Include="Documents\ManagedGroups\DocumentTemplateDevicesManagedGroup.cs" />
@@ -4,7 +4,6 @@ using Disco.Models.Services.Documents;
using Disco.Services.Documents;
using Disco.Services.Expressions;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Disco.Services
@@ -27,11 +26,11 @@ namespace Disco.Services
{
if (!string.IsNullOrEmpty(dt.FilterExpression))
{
Expression compiledExpression = dt.FilterExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
var compiledExpression = dt.FilterExpressionFromCache();
var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
try
{
object er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
var er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (er is bool)
{
return (bool)er;
@@ -60,25 +59,18 @@ namespace Disco.Services
ExpressionCache.InvalidateKey("DocumentTemplate_OnImportExpression", dt.Id);
}
public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, List<DocumentUniqueIdentifier> PageIdentifiers)
public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, IAttachment Data, DiscoDataContext Database, User User, DateTime TimeStamp, List<DocumentUniqueIdentifier> PageIdentifiers)
{
if (!string.IsNullOrEmpty(dt.OnImportAttachmentExpression))
{
Expression compiledExpression = dt.OnImportAttachmentExpressionFromCache();
IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null);
var compiledExpression = dt.OnImportAttachmentExpressionFromCache();
var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null);
evaluatorVariables.Add("PageIdentifiers", PageIdentifiers);
try
{
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (result == null)
return null;
else
return result.ToString();
}
catch
{
throw;
}
var result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (result == null)
return null;
else
return result.ToString();
}
return null;
}
@@ -97,17 +89,11 @@ namespace Disco.Services
{
if (!string.IsNullOrEmpty(dt.OnGenerateExpression))
{
Expression compiledExpression = dt.OnGenerateExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
try
{
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
return result.ToString();
}
catch
{
throw;
}
var compiledExpression = dt.OnGenerateExpressionFromCache();
var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
var result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
return result.ToString();
}
return null;
}
@@ -1,16 +1,13 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services
{
public static class DocumentTemplateActionExtensions
public static class DocumentTemplateExtensions
{
public static Bitmap GenerateTemplatePreview(this DocumentTemplate DocumentTemplate, DiscoDataContext Database, int Width, int PageGapHeight, bool DrawPageBorder)
@@ -0,0 +1,143 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Services.Expressions;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services
{
public static class DocumentTemplatePackageExtensions
{
public static List<JobSubType> GetJobSubTypes(this DocumentTemplatePackage package, IEnumerable<JobSubType> JobSubTypes)
{
var result = new List<JobSubType>();
if (package.JobSubTypes != null && package.JobSubTypes.Count > 0)
{
foreach (var jobSubTypeRefId in package.JobSubTypes)
{
var jobTypeId = jobSubTypeRefId.Substring(0, jobSubTypeRefId.IndexOf('_'));
var jobSubTypeId = jobSubTypeRefId.Substring(jobTypeId.Length + 1);
result.Add(JobSubTypes.First(jst => jst.JobTypeId == jobTypeId && jst.Id == jobSubTypeId));
}
}
return result;
}
public static List<DocumentTemplate> GetDocumentTemplates(this DocumentTemplatePackage package, DiscoDataContext Database)
{
var result = new List<DocumentTemplate>();
if (package.DocumentTemplateIds != null && package.DocumentTemplateIds.Count > 0)
{
var dbScope = package.Scope.ToString();
var dbTemplates = Database.DocumentTemplates
.Where(dt => package.DocumentTemplateIds.Contains(dt.Id) && dt.Scope == dbScope)
.ToList();
foreach (var id in package.DocumentTemplateIds)
{
var template = dbTemplates.FirstOrDefault(t => t.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
if (template != null)
{
result.Add(template);
}
}
}
return result;
}
public static List<DocumentTemplate> GetDocumentTemplates(this DocumentTemplatePackage package, IEnumerable<DocumentTemplate> DocumentTemplates)
{
var result = new List<DocumentTemplate>();
if (package.DocumentTemplateIds != null && package.DocumentTemplateIds.Count > 0)
{
var dbScope = package.Scope.ToString();
foreach (var id in package.DocumentTemplateIds)
{
var template = DocumentTemplates.FirstOrDefault(t => t.Id == id && t.Scope == dbScope);
if (template != null)
{
result.Add(template);
}
}
}
return result;
}
public static Expression FilterExpressionFromCache(this DocumentTemplatePackage package)
{
return ExpressionCache.GetValue("DocumentTemplatePackage_FilterExpression", package.Id, () => { return Expression.TokenizeSingleDynamic(null, package.FilterExpression, 0); });
}
public static void FilterExpressionInvalidateCache(this DocumentTemplatePackage package)
{
ExpressionCache.InvalidateKey("DocumentTemplatePackage_FilterExpression", package.Id);
}
public static bool FilterExpressionMatches(this DocumentTemplatePackage package, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State)
{
if (!string.IsNullOrEmpty(package.FilterExpression))
{
var compiledExpression = package.FilterExpressionFromCache();
var evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State);
evaluatorVariables.Add("Package", package);
try
{
object er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
if (er is bool)
{
return (bool)er;
}
bool erBool;
if (bool.TryParse(er.ToString(), out erBool))
{
return erBool;
}
}
catch
{
return false;
}
}
return true;
}
public static Expression OnGenerateExpressionFromCache(this DocumentTemplatePackage package)
{
return ExpressionCache.GetValue("DocumentTemplatePackage_OnGenerateExpression", package.Id, () => { return Expression.TokenizeSingleDynamic(null, package.OnGenerateExpression, 0); });
}
public static void OnGenerateExpressionInvalidateCache(this DocumentTemplatePackage package)
{
ExpressionCache.InvalidateKey("DocumentTemplatePackage_OnGenerateExpression", package.Id);
}
public static string EvaluateOnGenerateExpression(this DocumentTemplatePackage package, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State)
{
if (!string.IsNullOrEmpty(package.OnGenerateExpression))
{
Expression compiledExpression = package.OnGenerateExpressionFromCache();
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State);
evaluatorVariables.Add("Package", package);
try
{
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
return result.ToString();
}
catch
{
throw;
}
}
return null;
}
}
}
@@ -0,0 +1,187 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace Disco.Services.Documents
{
public static class DocumentTemplatePackages
{
private static ConcurrentDictionary<string, DocumentTemplatePackage> cache;
static DocumentTemplatePackages()
{
using (var database = new DiscoDataContext())
{
var packages = database.DiscoConfiguration.Documents.Packages;
if (packages == null)
{
cache = new ConcurrentDictionary<string, DocumentTemplatePackage>(StringComparer.OrdinalIgnoreCase);
}
else
{
cache = new ConcurrentDictionary<string, DocumentTemplatePackage>(
packages.Select(p => new KeyValuePair<string, DocumentTemplatePackage>(p.Id, p)),
StringComparer.OrdinalIgnoreCase);
}
}
}
public static List<DocumentTemplatePackage> GetPackages()
=> cache.Values.ToList();
public static DocumentTemplatePackage GetPackage(string Id)
{
DocumentTemplatePackage package;
if (cache.TryGetValue(Id, out package))
return package;
else
return null;
}
public static IEnumerable<DocumentTemplatePackage> AvailablePackages(DiscoDataContext Database, AttachmentTypes Scope)
{
var packages = cache.Values.Where(p => p.Scope == Scope).ToList();
if (packages.Count > 0)
{
var dbScope = Scope.ToString();
var validTemplateIds = Database.DocumentTemplates
.Where(dt => dt.Scope == dbScope)
.Select(dt => dt.Id).ToList();
return packages.Where(p =>
!p.IsHidden &&
p.DocumentTemplateIds != null && p.DocumentTemplateIds.Count > 0 &&
p.DocumentTemplateIds.Count(id => validTemplateIds.Contains(id)) > 0);
}
return Enumerable.Empty<DocumentTemplatePackage>();
}
public static List<DocumentTemplatePackage> AvailablePackages(this Device device, DiscoDataContext Database, User TechnicianUser)
{
var packages = new List<DocumentTemplatePackage>();
foreach (var package in AvailablePackages(Database, AttachmentTypes.Device))
{
if (package.FilterExpressionMatches(device, Database, TechnicianUser, DateTime.Now, DocumentState.DefaultState()))
{
packages.Add(package);
}
}
return packages;
}
public static List<DocumentTemplatePackage> AvailablePackages(this Job job, DiscoDataContext Database, User TechnicianUser)
{
var packages = new List<DocumentTemplatePackage>();
foreach (var package in AvailablePackages(Database, AttachmentTypes.Job))
{
bool subTypeMatch = true; // default match
if (package.JobSubTypes != null && package.JobSubTypes.Count > 0)
{
subTypeMatch = false; // enforce match
foreach (var subType in job.JobSubTypes)
{
if (package.JobSubTypes.Contains($"{subType.JobTypeId}_{subType.Id}", StringComparer.OrdinalIgnoreCase))
{
subTypeMatch = true;
break;
}
}
}
if (subTypeMatch)
{
if (package.FilterExpressionMatches(job, Database, TechnicianUser, DateTime.Now, DocumentState.DefaultState()))
{
packages.Add(package);
}
}
}
return packages;
}
public static List<DocumentTemplatePackage> AvailablePackages(this User user, DiscoDataContext Database, User TechnicianUser)
{
var packages = new List<DocumentTemplatePackage>();
foreach (var package in AvailablePackages(Database, AttachmentTypes.User))
{
if (package.FilterExpressionMatches(user, Database, TechnicianUser, DateTime.Now, DocumentState.DefaultState()))
{
packages.Add(package);
}
}
return packages;
}
public static DocumentTemplatePackage CreatePackage(DocumentTemplatePackage Package)
{
if (string.IsNullOrWhiteSpace(Package.Id))
throw new ArgumentNullException(nameof(Package), "The Package Id is required");
if (cache.ContainsKey(Package.Id)) // Name Unique
throw new ArgumentException("Another Package already exists with that Id", nameof(Package));
if (string.IsNullOrWhiteSpace(Package.Description))
throw new ArgumentNullException(nameof(Package), "The Package Description is required");
if (cache.TryAdd(Package.Id, Package))
{
PersistCache();
return Package;
}
else
throw new Exception("Unable to add the Package to the Cache, check the Package Id and try again");
}
public static DocumentTemplatePackage UpdatePackage(DocumentTemplatePackage Package)
{
DocumentTemplatePackage existingPackage;
if (string.IsNullOrWhiteSpace(Package.Id))
throw new ArgumentNullException(nameof(Package), "The Package Id is required");
if (!cache.TryGetValue(Package.Id, out existingPackage)) // Name Unique
throw new ArgumentException("The Package Id does not exist", nameof(Package));
if (string.IsNullOrWhiteSpace(Package.Description))
throw new ArgumentNullException(nameof(Package), "The Package Description is required");
if (cache.TryUpdate(Package.Id, Package, existingPackage))
{
PersistCache();
return Package;
}
else
throw new Exception("Unable to update the Package in the Cache, there were concurrent updates to the same package");
}
public static void RemovePackage(string Id)
{
DocumentTemplatePackage existingPackage;
if (cache.TryRemove(Id, out existingPackage))
{
PersistCache();
}
}
private static void PersistCache()
{
var packages = cache.Values.ToList();
if (packages.Count == 0)
packages = null;
using (var database = new DiscoDataContext())
{
database.DiscoConfiguration.Documents.Packages = packages;
database.SaveChanges();
}
}
}
}
+48 -75
View File
@@ -1,4 +1,5 @@
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Services.Logging;
using Disco.Services.Logging.Models;
@@ -23,7 +24,9 @@ namespace Disco.Services.Documents
ImportPageError = 120,
ImportPageUndetectedStored = 150,
DocumentGenerated = 500,
DocumentGeneratedWithExpression
DocumentGeneratedWithExpression,
DocumentPackageGenerated = 600,
DocumentPackageGeneratedWithExpression,
}
private const int _ModuleId = 40;
@@ -175,93 +178,41 @@ namespace Disco.Services.Documents
PageNumber
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
Device.SerialNumber,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
Job.Id,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
User.UserId,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, IAttachmentTarget Data, User Author, string ExpressionResult)
{
if (Data is Job)
LogDocumentGenerated(Template, (Job)Data, Author, ExpressionResult);
else if (Data is User)
LogDocumentGenerated(Template, (User)Data, Author, ExpressionResult);
else if (Data is Device)
LogDocumentGenerated(Template, (Device)Data, Author, ExpressionResult);
else
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
Log(EventTypeIds.DocumentGeneratedWithExpression, new object[]
{
Template.Id,
"UNKNOWN",
Data.AttachmentReferenceId,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author)
public static void LogDocumentPackageGenerated(DocumentTemplatePackage Package, IAttachmentTarget Data, User Author, string ExpressionResult)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
Device.SerialNumber,
Author.UserId
});
Log(EventTypeIds.DocumentPackageGeneratedWithExpression, new object[]
{
Package.Id,
Data.AttachmentReferenceId,
Author.UserId,
ExpressionResult
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author)
public static void LogDocumentGenerated(DocumentTemplate Template, IAttachmentTarget Data, User Author)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
Job.Id,
Author.UserId
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author)
{
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
User.UserId,
Author.UserId
});
}
public static void LogDocumentGenerated(DocumentTemplate Template, object Data, User Author)
{
if (Data is Job)
LogDocumentGenerated(Template, (Job)Data, Author);
else if (Data is User)
LogDocumentGenerated(Template, (User)Data, Author);
else if (Data is Device)
LogDocumentGenerated(Template, (Device)Data, Author);
else
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
Log(EventTypeIds.DocumentGenerated, new object[]
{
Template.Id,
"UNKNOWN",
Data.AttachmentReferenceId,
Author.UserId
});
}
public static void LogDocumentPackageGenerated(DocumentTemplatePackage Package, IAttachmentTarget Data, User Author)
{
Log(EventTypeIds.DocumentPackageGenerated, new object[]
{
Package.Id,
Data.AttachmentReferenceId,
Author.UserId
});
}
@@ -433,6 +384,28 @@ namespace Disco.Services.Documents
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.DocumentPackageGenerated,
ModuleId = _ModuleId,
Name = "Document Package Generated",
Format = "A '{0}' document package was generated for '{1}' by '{2}'",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
},
new LogEventType
{
Id = (int)EventTypeIds.DocumentPackageGeneratedWithExpression,
ModuleId = _ModuleId,
Name = "Document Package Generated with Expression",
Format = "A '{0}' document package was generated for '{1}' by '{2}'. The expression returned: {3}",
Severity = (int)LogEventType.Severities.Information,
UseLive = true,
UsePersist = true,
UseDisplay = true
}
};
}
+5
View File
@@ -3,6 +3,7 @@ using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Models.Services.Jobs.JobLists;
using Disco.Services.Authorization;
using Disco.Services.Documents;
using Disco.Services.Expressions;
using Disco.Services.Interop.ActiveDirectory;
using Disco.Services.Plugins;
@@ -230,6 +231,10 @@ namespace Disco.Services
return dts;
}
public static List<DocumentTemplatePackage> AvailableDocumentTemplatePackages(this Job j, DiscoDataContext Database, User TechnicianUser)
{
return DocumentTemplatePackages.AvailablePackages(j, Database, TechnicianUser);
}
public static DateTime ValidateDateAfterOpened(this Job j, DateTime d)
{
+6
View File
@@ -1,6 +1,7 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Disco.Services.Documents;
using Disco.Services.Interop.ActiveDirectory;
using System;
using System.Collections.Generic;
@@ -35,6 +36,11 @@ namespace Disco.Services
return dts;
}
public static List<DocumentTemplatePackage> AvailableDocumentTemplatePackages(this User u, DiscoDataContext Database, User TechnicianUser)
{
return DocumentTemplatePackages.AvailablePackages(u, Database, TechnicianUser);
}
public static List<DeviceUserAssignment> CurrentDeviceUserAssignments(this User u)
{
return u.DeviceUserAssignments.Where(dua => !dua.UnassignedDate.HasValue).ToList();