Skip to content

Commit 5cfe052

Browse files
committed
Refactor library handling and update command line args
Updated `CommandLineOptions.cs` to use `CqlVersionedLibraryIdentifier` for library parsing and validation. Modified `LoadValueSets` in `ResourceHelper.cs` to accept value set IDs for validation. Enhanced `LibraryRunner.cs` to utilize the new library identifier and added a method to retrieve unique ValueSet IDs. Introduced `EnumerableExtensions` with a new `SelectWhere` method for collection filtering. Updated `launchSettings.json` to remove "Library-" prefix from command line arguments.
1 parent 9fb05cb commit 5cfe052

File tree

4 files changed

+85
-16
lines changed

4 files changed

+85
-16
lines changed

Demo/CLI/Helpers/CommandLineOptions.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88

99
using CommandLine;
1010
using Dumpify;
11+
using Hl7.Cql.Runtime;
12+
using Hl7.Cql.Runtime.Parsing;
1113

1214
namespace CLI.Helpers
1315
{
1416
internal class CommandLineOptions
1517
{
1618
[Option('l', "library", Required = true, HelpText = "The name of a measure Library resource, which contains name and version.")]
1719
public string Library { get; set; } = "";
18-
public string LibraryName => Library.Split('-')[1];
19-
public string LibraryVersion => Library.Split('-')[2];
20+
21+
public CqlVersionedLibraryIdentifier LibraryIdentifier => CqlVersionedLibraryIdentifier.Parse(Library);
2022

2123
[Option('d', "data", HelpText = "The folder for test data.")]
2224
public string DataDirectory { get; set; } = "";
@@ -45,10 +47,10 @@ internal class CommandLineOptions
4547
public static void EnsureValidOptions(CommandLineOptions options)
4648
{
4749
// Validate the library name format
48-
if (options.Library.Count(c => c == '-') != 2
49-
&& !options.Library.StartsWith("Library-", StringComparison.OrdinalIgnoreCase))
50+
CqlParseError? cqlParseError = null;
51+
if (!CqlVersionedLibraryIdentifier.TryParse(options.Library, out _, err => cqlParseError = err))
5052
{
51-
throw new InvalidOperationException($"The name of the Library resource '{options.Library}' should be written as 'Library-<name>-<version>'.");
53+
throw new InvalidOperationException($"The name of the Library resource '{options.Library}' did not parse as a valid identifier ({cqlParseError}).");
5254
}
5355

5456
// Check if the data directory is set

Demo/CLI/Helpers/ResourceHelper.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,13 @@ public static Bundle LoadBundle(string bundleFile)
4242
return bundle;
4343
}
4444

45-
public static IValueSetDictionary LoadValueSets(DirectoryInfo directory)
45+
public static IValueSetDictionary LoadValueSets(
46+
DirectoryInfo directory,
47+
IEnumerable<string> valueSetIds)
4648
{
49+
// TODO: Should this be used to validate the valuesets?
50+
var valueSetIdSet = valueSetIds.ToHashSet();
51+
4752
var valueSets = new List<ValueSet>();
4853
foreach (var file in directory.GetFiles("*.json", SearchOption.AllDirectories))
4954
{

Demo/CLI/LibraryRunner.cs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88

99
using CLI.Helpers;
1010
using Dumpify;
11+
using Hl7.Cql.Abstractions;
1112
using Hl7.Cql.CodeGeneration.NET;
1213
using Hl7.Cql.Fhir;
1314
using Hl7.Cql.Fhir.Serialization.Extensions;
1415
using Hl7.Cql.Invocation.Toolkit;
1516
using Hl7.Cql.Invocation.Toolkit.Extensions;
17+
using Hl7.Cql.Primitives;
1618
using Hl7.Cql.Runtime;
1719
using Hl7.Cql.ValueSets;
1820
using Hl7.Fhir.Model;
@@ -50,25 +52,66 @@ public void RunWithResources()
5052
{
5153
//run using Library Resource files - production scenario, no debugging inline with measures project
5254
Console.WriteLine($"Loading resources for Library: {_opts.Library}");
53-
var librarySetName = CqlVersionedLibraryIdentifier.ParseFromIdentifierAndVersion(_opts.LibraryName, _opts.LibraryVersion);
55+
var libraryIdentifier = _opts.LibraryIdentifier;
56+
57+
// TODO: Not efficient to load all resources in the directory,
58+
// still have to add an extension to InvocationToolkit to load only the library resource
59+
// and its dependencies
5460
using var librarySetInvoker = new InvocationToolkit()
5561
.AddAssemblyBinariesInFhirLibrariesFromDirectory(new (_opts.ResourcesDirectory))
56-
.CreateLibrarySetInvoker(librarySetName);
62+
.CreateLibrarySetInvoker(libraryIdentifier);
5763
RunShared(_opts, librarySetInvoker);
5864
}
5965

6066
private void RunShared(CommandLineOptions opt, LibrarySetInvoker librarySetInvoker)
6167
{
6268
Console.WriteLine("Loading value sets");
63-
IValueSetDictionary valueSets = ResourceHelper.LoadValueSets(new DirectoryInfo(opt.ValueSetsDirectory));
69+
var valueSetIds = GetValueSetIds(librarySetInvoker, opt.Library);
70+
IValueSetDictionary valueSets = ResourceHelper.LoadValueSets(
71+
new DirectoryInfo(opt.ValueSetsDirectory),
72+
valueSetIds);
6473

6574
Console.WriteLine("Loading test case files");
66-
var testDataDir = Path.Join(opt.DataDirectory, opt.LibraryName);
75+
var testDataDir = Path.Join(opt.DataDirectory, (string)opt.LibraryIdentifier.Identifier);
6776
var patientList = ProcessTestPatients(testDataDir, librarySetInvoker, valueSets);
6877
//optionally use patientList Dictionary
6978
}
7079

71-
80+
/// <summary>
81+
/// Retrieves a set of unique ValueSet IDs from the specified library and its dependencies.
82+
/// </summary>
83+
/// <param name="librarySetInvoker">
84+
/// An instance of <see cref="LibrarySetInvoker"/> used to access library invokers and their dependencies.
85+
/// </param>
86+
/// <param name="libraryName">
87+
/// The name of the library from which to retrieve ValueSet IDs. This should be a valid CQL library identifier.
88+
/// </param>
89+
/// <returns>
90+
/// A <see cref="HashSet{T}"/> containing the unique ValueSet IDs defined in the specified library and its dependencies.
91+
/// </returns>
92+
/// <exception cref="KeyNotFoundException">
93+
/// Thrown if the specified library name does not exist in the <paramref name="librarySetInvoker"/>.
94+
/// </exception>
95+
/// <exception cref="ArgumentException">
96+
/// Thrown if the <paramref name="libraryName"/> is not a valid CQL library identifier.
97+
/// </exception>
98+
private static List<string> GetValueSetIds(LibrarySetInvoker librarySetInvoker, string libraryName)
99+
{
100+
var libraryIdentifier = CqlVersionedLibraryIdentifier.Parse(libraryName);
101+
var libraryInvoker = librarySetInvoker.LibraryInvokers[libraryIdentifier];
102+
var libraryInvokerAndDependencies = libraryInvoker
103+
.SelectDependencyLibraries(includeSelf: true, recursive: true)
104+
.ToArray();
105+
var valueSetIds = libraryInvokerAndDependencies
106+
.SelectMany(li => li.Definitions.Values)
107+
.SelectWhere(d =>
108+
d.CqlDefinitionAttribute is CqlValueSetDefinitionAttribute vsda
109+
? (true, vsda)
110+
: default)
111+
.Select(vsda => vsda.ValueSetId)
112+
.ToList();
113+
return valueSetIds;
114+
}
72115

73116
#region Processing Patients
74117
private Dictionary<string, Dictionary<string, object>> ProcessTestPatients(
@@ -160,7 +203,7 @@ public Dictionary<string, object> ProcessBundle(LibrarySetInvoker librarySetInvo
160203

161204
// Invoke all expressions and collect results
162205
var results = expressions.SelectResults(setup, new SelectResultsOptions(
163-
InvocationExceptionCallback: (definition, exception, _) =>
206+
InvocationExceptionCallback: (definition, exception, _) =>
164207
{
165208
var errorKey = $"{definition.LibraryInvoker.LibraryIdentifier.Identifier}-{definition.DefinitionSignature.Name}";
166209
errors.TryAdd(errorKey, exception);
@@ -173,7 +216,7 @@ public Dictionary<string, object> ProcessBundle(LibrarySetInvoker librarySetInvo
173216
// Use library identifier and definition name to create unique key
174217
var declName = $"{definitionInvoker.LibraryInvoker.LibraryIdentifier.Identifier}-{definitionInvoker.DefinitionSignature.Name}";
175218
Console.WriteLine($"Running definition: {definitionInvoker.DefinitionSignature.Name}");
176-
219+
177220
// Use TryAdd to avoid duplicate key exceptions
178221
if (!values.TryAdd(declName, invocationResult!))
179222
{
@@ -243,3 +286,22 @@ private void WritePatientResults(Dictionary<string, object> patientResults)
243286
#endregion Writing Output
244287
}
245288
}
289+
290+
file static class EnumerableExtensions
291+
{
292+
/// <summary>
293+
/// Projects each element of a collection into a new form based on a selector function, and filters out elements based on a condition.
294+
/// </summary>
295+
/// <typeparam name="T">The type of elements in the source collection.</typeparam>
296+
/// <typeparam name="TR">The type of elements in the resulting collection.</typeparam>
297+
/// <param name="source">The source collection.</param>
298+
/// <param name="selector">A function to test each element for a condition and project the element into a new form.</param>
299+
/// <returns>An enumerable collection that contains the transformed elements that satisfy the condition.</returns>
300+
public static IEnumerable<TR> SelectWhere<T, TR>(this IEnumerable<T> source, Func<T, (bool include, TR resultOrDefault)> selector)
301+
{
302+
foreach (var item in source)
303+
if (selector(item) is (include: true, { } resultOrDefault))
304+
yield return resultOrDefault;
305+
}
306+
307+
}

Demo/Measures.Demo/Properties/launchSettings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
"profiles": {
33
"BCSEHEDISMY2022-1.0.0": {
44
"commandName": "Project",
5-
"commandLineArgs": "--l Library-BCSEHEDISMY2022-1.0.0"
5+
"commandLineArgs": "--l BCSEHEDISMY2022-1.0.0"
66
},
77
"AdultOutpatientEncountersFHIR4-2.2.000": {
88
"commandName": "Project",
9-
"commandLineArgs": "--l Library-AdultOutpatientEncountersFHIR4-2.2.000"
9+
"commandLineArgs": "--l AdultOutpatientEncountersFHIR4-2.2.000"
1010
},
1111
"ColorectalCancerScreeningsFHIR-0.0.003": {
1212
"commandName": "Project",
13-
"commandLineArgs": "--l Library-ColorectalCancerScreeningsFHIR-0.0.003"
13+
"commandLineArgs": "--l ColorectalCancerScreeningsFHIR-0.0.003"
1414
}
1515
}
1616
}

0 commit comments

Comments
 (0)