Package Loading via directory support.
Comparison includes extensions.
Comparison includes source items that do not have a matching target.
This commit is contained in:
Gino Canessa 2024-07-17 16:41:27 -05:00
Родитель 516fb0fd84
Коммит 200e8cb10a
15 изменённых файлов: 640 добавлений и 114 удалений

Просмотреть файл

@ -190,4 +190,5 @@ public record class PackageComparison
public required Dictionary<string, List<StructureComparison>> ComplexTypes { get; init; }
public required Dictionary<string, List<StructureComparison>> Resources { get; init; }
//public required Dictionary<string, ComparisonRecord<StructureInfoRec, ElementInfoRec, ElementTypeInfoRec>> LogicalModels { get; init; }
public required Dictionary<string, List<StructureComparison>> Extensions { get; init; }
}

Просмотреть файл

@ -111,6 +111,15 @@ public class PackageComparer
//}
}
/// <summary>
/// Compares this optional bool object to another to determine their relative ordering.
/// </summary>
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
/// <param name="compareExtensions">(Optional) The bool to compare to this object.</param>
/// <returns>
/// Negative if 'compareExtensions' is less than '', 0 if they are equal, or positive if it is
/// greater.
/// </returns>
public PackageComparison Compare()
{
Console.WriteLine(
@ -136,7 +145,7 @@ public class PackageComparer
}
string outputDir = string.IsNullOrEmpty(_config.CrossVersionMapDestinationPath)
? Path.Combine(_config.OutputDirectory, $"{_sourceRLiteral}_{_targetRLiteral}")
? _config.OutputDirectory
: Path.Combine(_config.CrossVersionMapDestinationPath, $"{_sourceRLiteral}_{_targetRLiteral}");
if (!Directory.Exists(outputDir))
@ -314,6 +323,33 @@ public class PackageComparer
}
}
Dictionary<string, List<StructureComparison>> extensions = CompareStructures(_source.ExtensionsByUrl, _target.ExtensionsByUrl);
if (mdWriter != null)
{
WriteComparisonOverview(mdWriter, "Extensions", extensions);
string mdSubDir = Path.Combine(pageDir, "Extensions");
if (!Directory.Exists(mdSubDir))
{
Directory.CreateDirectory(mdSubDir);
}
foreach (List<StructureComparison> vcs in extensions.Values)
{
foreach (StructureComparison c in vcs)
{
//string name = GetName(c.Left, c.Right);
//string filename = Path.Combine(subDir, $"{name}.md");
string filename = Path.Combine(mdSubDir, $"{c.CompositeName}.md");
using ExportStreamWriter writer = CreateMarkdownWriter(filename);
{
WriteComparisonFile(writer, string.Empty, c);
}
}
}
}
// TODO(ginoc): Logical models are tracked by URL in collections, but structure mapping is done by name.
//Dictionary<string, ComparisonRecord<StructureInfoRec, ElementInfoRec, ElementTypeInfoRec>> logical = Compare(FhirArtifactClassEnum.LogicalModel, _left.LogicalModelsByUrl, _right.LogicalModelsByUrl);
//if (mdWriter != null)
@ -349,6 +385,7 @@ public class PackageComparer
ComplexTypes = complexTypeComparisons,
Resources = resources,
//LogicalModels = logical,
Extensions = extensions,
};
if (mdWriter != null)
@ -1163,6 +1200,27 @@ public class PackageComparer
comparisons.Add(directComparison);
}
}
// check for something that has no counterpart
if (comparisons.Count == 0)
{
comparisons.Add(new()
{
Relationship = null,
Message = $"{sourceVs.Url} does not exist in target and has no mapping",
Source = new()
{
Url = sourceVs.Url,
Name = sourceVs.Name,
NamePascal = sourceVs.Name.ToPascalCase(),
Title = sourceVs.Title,
Description = sourceVs.Description,
},
Target = null,
CompositeName = $"{_sourceRLiteral}-{sourceVs.Name}",
ConceptComparisons = [],
});
}
}
return results;
@ -1628,6 +1686,29 @@ public class PackageComparer
comparisons.Add(directComparison);
}
}
// check for something that has no counterpart
if (comparisons.Count == 0)
{
comparisons.Add(new()
{
Relationship = null,
Message = $"{sourceSdName} does not exist in target and has no mapping",
Source = new()
{
Name = sourceSd.Name,
Url = sourceSd.Url,
Title = sourceSd.Title,
Description = sourceSd.Description,
Purpose = sourceSd.Purpose,
SnapshotCount = sourceSd.Snapshot.Element.Count,
DifferentialCount = sourceSd.Differential.Element.Count,
},
Target = null,
CompositeName = $"{_sourceRLiteral}-{sourceSd.Name}",
ElementComparisons = [],
});
}
}
return results;

Просмотреть файл

@ -222,6 +222,24 @@ public class ConfigRoot : ICodeGenConfig
},
};
[ConfigOption(
ArgName = "--fhir-version",
EnvName = "Fhir_Version",
Description = "FHIR version to use.")]
public string FhirVersion { get; set; } = string.Empty;
private static ConfigurationOption FhirVersionParameter { get; } = new()
{
Name = "FhirVersion",
EnvVarName = "Fhir_Version",
DefaultValue = string.Empty,
CliOption = new System.CommandLine.Option<string>("--fhir-version", "FHIR version to use.")
{
Arity = System.CommandLine.ArgumentArity.ZeroOrOne,
IsRequired = false,
},
};
/// <summary>
/// Gets all the configuration options.
/// </summary>
@ -237,6 +255,7 @@ public class ConfigRoot : ICodeGenConfig
AutoLoadExpansionsParameter,
ResolvePackageDependenciesParameter,
OfflineModeParameter,
FhirVersionParameter,
];
/// <summary>
@ -498,6 +517,9 @@ public class ConfigRoot : ICodeGenConfig
case "ResolvePackageDependencies":
ResolvePackageDependencies = GetOpt(parseResult, opt.CliOption, ResolvePackageDependencies);
break;
case "FhirVersion":
FhirVersion = GetOpt(parseResult, opt.CliOption, FhirVersion);
break;
}
}
}

Просмотреть файл

@ -50,6 +50,9 @@ public record class LoaderOptions
/// </summary>
public bool OfflineMode { get; init; } = false;
/// <summary>Gets or initializes the FHIR version.</summary>
public string FhirVersion { get; init; } = string.Empty;
/// <summary>
/// Gets or sets the JSON model.
/// </summary>

Просмотреть файл

@ -66,6 +66,8 @@ public class PackageLoader : IDisposable
/// <summary>True to load dependencies when loading packages.</summary>
private bool _loadDependencies;
private FhirReleases.FhirSequenceCodes _defaultFhirVersion;
/// <summary>The JSON model.</summary>
private LoaderOptions.JsonDeserializationModel _jsonModel;
@ -118,6 +120,8 @@ public class PackageLoader : IDisposable
}
}
_defaultFhirVersion = FhirReleases.FhirVersionToSequence(opts.FhirVersion);
_jsonOptions = opts.FhirJsonOptions;
_jsonParser = new(opts.FhirJsonSettings);
@ -420,14 +424,146 @@ public class PackageLoader : IDisposable
return false;
}
private void CreateConverterIfRequired(FhirReleases.FhirSequenceCodes fhirSequence)
{
// create the converter we need
switch (fhirSequence)
{
case FhirReleases.FhirSequenceCodes.DSTU2:
{
if (_converter_20_50 == null)
{
lock (_convertLockObject)
{
_converter_20_50 ??= new();
}
}
}
break;
case FhirReleases.FhirSequenceCodes.STU3:
{
if (_converter_30_50 == null)
{
lock (_convertLockObject)
{
_converter_30_50 ??= new();
}
}
}
break;
case FhirReleases.FhirSequenceCodes.R4:
case FhirReleases.FhirSequenceCodes.R4B:
{
if (_converter_43_50 == null)
{
lock (_convertLockObject)
{
_converter_43_50 ??= new();
}
}
}
break;
default:
case FhirReleases.FhirSequenceCodes.R5:
{
}
break;
}
}
private bool LoadFromDirectory(ref DefinitionCollection? definitions, string sourcePath, string? fhirVersion)
{
FhirReleases.FhirSequenceCodes fhirSequence = string.IsNullOrEmpty(fhirVersion)
? _defaultFhirVersion
: FhirReleases.FhirVersionToSequence(fhirVersion!);
if (fhirSequence == FhirReleases.FhirSequenceCodes.Unknown)
{
throw new Exception("Cannot load from a directory with an unknown FHIR version");
}
CreateConverterIfRequired(fhirSequence);
string? name = Path.GetFileName(sourcePath);
if (name == null)
{
throw new Exception($"Failed to get directory name from '{sourcePath}'");
}
string canonical = $"file://{sourcePath}";
definitions ??= new()
{
Name = name,
FhirSequence = fhirSequence,
FhirVersionLiteral = fhirSequence.ToLiteral(),
MainPackageId = name,
MainPackageVersion = "dev",
MainPackageCanonical = canonical,
};
// get files in the directory
string[] files = Directory.GetFiles(sourcePath, "*.json", SearchOption.TopDirectoryOnly);
foreach (string path in files)
{
string fileExtension = Path.GetExtension(path);
object? r;
switch (fhirSequence)
{
case FhirReleases.FhirSequenceCodes.DSTU2:
{
r = ParseContents20(fileExtension, path: path);
}
break;
case FhirReleases.FhirSequenceCodes.STU3:
{
r = ParseContents30(fileExtension, path: path);
}
break;
case FhirReleases.FhirSequenceCodes.R4:
case FhirReleases.FhirSequenceCodes.R4B:
{
r = ParseContents43(fileExtension, path: path);
}
break;
default:
case FhirReleases.FhirSequenceCodes.R5:
{
r = ParseContentsPoco(fileExtension, path);
}
break;
}
if (r == null)
{
throw new Exception($"Failed to parse '{path}'");
}
definitions.AddResource(r, _defaultFhirVersion, name, "dev", canonical);
}
return true;
}
/// <summary>Loads a package.</summary>
/// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
/// <param name="name"> The name.</param>
/// <param name="packages">The cached package.</param>
/// <param name="packages"> The cached package.</param>
/// <param name="definitions">(Optional) The definitions.</param>
/// <param name="fhirVersion">(Optional) The FHIR version.</param>
/// <returns>An asynchronous result that yields the package.</returns>
public async Task<DefinitionCollection?> LoadPackages(
IEnumerable<string> packages,
DefinitionCollection? definitions = null)
DefinitionCollection? definitions = null,
string? fhirVersion = null)
{
if (!packages.Any())
{
@ -436,6 +572,26 @@ public class PackageLoader : IDisposable
foreach (string inputDirective in packages)
{
if (string.IsNullOrEmpty(inputDirective))
{
continue;
}
// check to see if this is actually a directory
if ((inputDirective.IndexOfAny(Path.GetInvalidPathChars()) == -1) &&
Directory.Exists(inputDirective))
{
Console.WriteLine($"Processing directory {inputDirective}...");
if (!LoadFromDirectory(ref definitions, inputDirective, fhirVersion))
{
throw new Exception($"Failed to load package from directory: {inputDirective}");
}
// if we loaded from the directory, just continue
continue;
}
// TODO(ginoc): PR in to Parse FHIR-style directives, remove when added.
string directive = inputDirective.Contains('@')
? inputDirective
@ -546,52 +702,7 @@ public class PackageLoader : IDisposable
? definitions.FhirSequence
: FhirReleases.FhirVersionToSequence(packageFhirVersionLiteral ?? string.Empty);
// create the converter we need
switch (definitions.FhirSequence)
{
case FhirReleases.FhirSequenceCodes.DSTU2:
{
if (_converter_20_50 == null)
{
lock (_convertLockObject)
{
_converter_20_50 ??= new();
}
}
}
break;
case FhirReleases.FhirSequenceCodes.STU3:
{
if (_converter_30_50 == null)
{
lock (_convertLockObject)
{
_converter_30_50 ??= new();
}
}
}
break;
case FhirReleases.FhirSequenceCodes.R4:
case FhirReleases.FhirSequenceCodes.R4B:
{
if (_converter_43_50 == null)
{
lock (_convertLockObject)
{
_converter_43_50 ??= new();
}
}
}
break;
default:
case FhirReleases.FhirSequenceCodes.R5:
{
}
break;
}
CreateConverterIfRequired(definitions.FhirSequence);
// if we are resolving dependencies, check them now
if (_loadDependencies && (manifest.Dependencies?.Any() ?? false))

Просмотреть файл

@ -0,0 +1,58 @@
// <copyright file="FileSystemUtils.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// </copyright>
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.Health.Fhir.CodeGen.Utils;
public abstract class FileSystemUtils
{
/// <summary>Searches for the FHIR specification directory.</summary>
/// <exception cref="DirectoryNotFoundException">Thrown when the requested directory is not
/// present.</exception>
/// <param name="startDir"> The start directory.</param>
/// <param name="dirName"> The name of the directory we are searching for.</param>
/// <param name="throwIfNotFound">(Optional) True to throw if not found.</param>
/// <returns>The found FHIR directory.</returns>
public static string FindRelativeDir(
string startDir,
string dirName,
bool throwIfNotFound = true)
{
string currentDir = startDir switch
{
null => Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty,
"" => Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty,
"." => Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty,
"./" => Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty,
"~" => Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)) ?? string.Empty,
"~/" => Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)) ?? string.Empty,
_ => startDir,
};
string testDir = Path.Combine(currentDir, dirName);
while (!Directory.Exists(testDir))
{
currentDir = Path.GetFullPath(Path.Combine(currentDir, ".."));
if (currentDir == Path.GetPathRoot(currentDir))
{
if (throwIfNotFound)
{
throw new DirectoryNotFoundException($"Could not find directory {dirName}!");
}
return string.Empty;
}
testDir = Path.Combine(currentDir, dirName);
}
return testDir;
}
}

Просмотреть файл

@ -10,8 +10,8 @@
<Application.Styles>
<!--<FluentTheme />-->
<themes:MaterialTheme BaseTheme="Dark" PrimaryColor="Indigo" SecondaryColor="Green" />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
<StyleInclude Source="avares://fhir-codegen/Icons.axaml" />
<!--<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>-->
<!--<StyleInclude Source="avares://fhir-codegen/Icons.axaml" />-->
<materialIcons:MaterialIconStyles />
<Style Selector="AutoCompleteBox">
<Setter Property="Margin" Value="8"/>
@ -20,12 +20,20 @@
<Style Selector="Button">
<Setter Property="Margin" Value="8"/>
</Style>
<Style Selector="Border.cgIconBorder32">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Background" Value="#5665be"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style Selector="Button.cgIconButton">
<Setter Property="Margin" Value="8"/>
<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="Indigo"/>
<!--<Setter Property="Background" Value="Indigo"/>-->
</Style>
<Style Selector="ComboBox">
<Setter Property="Margin" Value="8"/>
@ -36,6 +44,25 @@
</Style>
<Style Selector="Label">
<Setter Property="Margin" Value="8"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style Selector="materialIcons|MaterialIcon.cgIcon24">
<Setter Property="Margin" Value="8"/>
<!--<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>-->
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="24"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style Selector="Panel.cgIconPanel">
<Setter Property="Margin" Value="8"/>
<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Background" Value="#5665be"/>
</Style>
<!--<Style Selector="Grid">
<Style Selector="^:Grid.RowDefinition">
@ -44,7 +71,9 @@
</Style>-->
<Style Selector="TextBox">
<Setter Property="Margin" Value="8"/>
<Setter Property="Width" Value="200"/>
<Setter Property="MinWidth" Value="400"/>
<!--<Setter Property="HorizontalAlignment" Value="Stretch"/>-->
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Application.Styles>

Просмотреть файл

@ -550,6 +550,7 @@ public class Program
JsonModel = LoaderOptions.JsonDeserializationModel.SystemTextJson,
AutoLoadExpansions = rootConfig.AutoLoadExpansions,
ResolvePackageDependencies = rootConfig.ResolvePackageDependencies,
FhirVersion = rootConfig.FhirVersion,
});
DefinitionCollection? loaded = await loader.LoadPackages(rootConfig.Packages)
@ -655,6 +656,7 @@ public class Program
JsonModel = LoaderOptions.JsonDeserializationModel.SystemTextJson,
AutoLoadExpansions = config.AutoLoadExpansions,
ResolvePackageDependencies = config.ResolvePackageDependencies,
FhirVersion = config.FhirVersion,
});
@ -695,6 +697,7 @@ public class Program
JsonModel = LoaderOptions.JsonDeserializationModel.SystemTextJson,
AutoLoadExpansions = config.AutoLoadExpansions,
ResolvePackageDependencies = config.ResolvePackageDependencies,
FhirVersion = config.FhirVersion,
});
DefinitionCollection? loadedRight = await loaderLeft.LoadPackages(config.ComparePackages)
@ -999,37 +1002,37 @@ public class Program
}
/// <summary>Searches for the FHIR specification directory.</summary>
/// <exception cref="DirectoryNotFoundException">Thrown when the requested directory is not
/// present.</exception>
/// <param name="dirName"> The name of the directory we are searching for.</param>
/// <param name="throwIfNotFound">(Optional) True to throw if not found.</param>
/// <returns>The found FHIR directory.</returns>
public static string FindRelativeDir(
string startDir,
string dirName,
bool throwIfNotFound = true)
{
string currentDir = string.IsNullOrEmpty(startDir) ? Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty : startDir;
string testDir = Path.Combine(currentDir, dirName);
///// <summary>Searches for the FHIR specification directory.</summary>
///// <exception cref="DirectoryNotFoundException">Thrown when the requested directory is not
///// present.</exception>
///// <param name="dirName"> The name of the directory we are searching for.</param>
///// <param name="throwIfNotFound">(Optional) True to throw if not found.</param>
///// <returns>The found FHIR directory.</returns>
//public static string FindRelativeDir(
// string startDir,
// string dirName,
// bool throwIfNotFound = true)
//{
// string currentDir = string.IsNullOrEmpty(startDir) ? Path.GetDirectoryName(AppContext.BaseDirectory) ?? string.Empty : startDir;
// string testDir = Path.Combine(currentDir, dirName);
while (!Directory.Exists(testDir))
{
currentDir = Path.GetFullPath(Path.Combine(currentDir, ".."));
// while (!Directory.Exists(testDir))
// {
// currentDir = Path.GetFullPath(Path.Combine(currentDir, ".."));
if (currentDir == Path.GetPathRoot(currentDir))
{
if (throwIfNotFound)
{
throw new DirectoryNotFoundException($"Could not find directory {dirName}!");
}
// if (currentDir == Path.GetPathRoot(currentDir))
// {
// if (throwIfNotFound)
// {
// throw new DirectoryNotFoundException($"Could not find directory {dirName}!");
// }
return string.Empty;
}
// return string.Empty;
// }
testDir = Path.Combine(currentDir, dirName);
}
// testDir = Path.Combine(currentDir, dirName);
// }
return testDir;
}
// return testDir;
//}
}

Просмотреть файл

@ -18,13 +18,17 @@ using CommunityToolkit.Mvvm.Input;
using Hl7.Fhir.Utility;
using Material.Icons;
using Microsoft.Health.Fhir.CodeGen._ForPackages;
using Microsoft.Health.Fhir.CodeGen.CompareTool;
using Microsoft.Health.Fhir.CodeGen.Configuration;
using Microsoft.Health.Fhir.CodeGen.Loader;
using Microsoft.Health.Fhir.CodeGen.Models;
using Microsoft.Health.Fhir.CodeGenCommon.Packaging;
namespace fhir_codegen.ViewModels;
public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewModel
{
public static string Label => "Compare FHIR Releases";
public static string Label => "Compare FHIR Definitions";
public static MaterialIconKind IconKind => MaterialIconKind.Compare;
//public static StreamGeometry? IconGeometry => (Application.Current?.TryGetResource("book_question_mark_regular", out object? icon) ?? false) && icon is StreamGeometry sg
// ? sg
@ -51,10 +55,43 @@ public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewMode
private string _crossVersionDirectory = "git/fhir-cross-version";
[ObservableProperty]
private int _sourceIndex = 0;
private int _sourceReleaseIndex = 0;
[ObservableProperty]
private int _targetIndex = 0;
private int _targetReleaseIndex = 0;
[ObservableProperty]
private string _sourceDirectory = "C:\\git\\version-modeling\\20191231\\hl7.fhir.r5.core#4.2.0"; // string.Empty;
[ObservableProperty]
private int _sourceDirectoryFhirVersionIndex = 2;
[ObservableProperty]
private string _targetDirectory = "C:\\git\\version-modeling\\20181227\\hl7.fhir.r4.core#4.0.1"; // string.Empty;
[ObservableProperty]
private int _targetDirectoryFhirVersionIndex = 2;
[ObservableProperty]
private string[] _fhirVersions = [ "DSTU2", "STU3", "R4", "R4B", "R5", "R6" ];
[ObservableProperty]
private string _saveDirectory = "C:\\git\\version-modeling\\20191231\\_prev_02_down";
[ObservableProperty]
private List<ValueSetComparison> _valueSetComparisons = [];
[ObservableProperty]
private List<PrimitiveTypeComparison> _primitiveComparisons = [];
[ObservableProperty]
private List<StructureComparison> _complexTypeComparisons = [];
[ObservableProperty]
private List<StructureComparison> _resourceComparisons = [];
[ObservableProperty]
private List<StructureComparison> _extensionComparisons = [];
[ObservableProperty]
private List<string> _corePackages = [];
@ -65,7 +102,8 @@ public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewMode
private static readonly Regex _corePackageRegex = new Regex("^hl7\\.fhir\\.r\\d+[A-Za-z]?\\.(core)$", RegexOptions.Compiled);
private static readonly HashSet<string> _releaseVersions = [ "1.0.2", "3.0.2", "4.0.1", "4.3.0", "5.0.0", ];
public CoreComparisonViewModel()
public CoreComparisonViewModel(object? args = null)
: base()
{
// get the current configuration
ConfigGui? config = Gui.RunningConfiguration;
@ -105,7 +143,7 @@ public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewMode
}
[RelayCommand]
private void RunComparison()
private void RunReleaseComparison()
{
if (Processing == true)
{
@ -115,7 +153,7 @@ public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewMode
Processing = true;
ErrorMessage = null;
if (SourceIndex == TargetIndex)
if (SourceReleaseIndex == TargetReleaseIndex)
{
ErrorMessage = "Source and target cannot be the same";
Processing = false;
@ -124,12 +162,84 @@ public partial class CoreComparisonViewModel : ViewModelBase, INavigableViewMode
ConfigCompare compareOptions = new()
{
Packages = [ CorePackages[SourceIndex] ],
ComparePackages = [ CorePackages[TargetIndex] ],
Packages = [ CorePackages[SourceReleaseIndex] ],
ComparePackages = [ CorePackages[TargetReleaseIndex] ],
CrossVersionMapSourcePath = CrossVersionDirectory,
MapSaveStyle = ConfigCompare.ComparisonMapSaveStyle.None,
NoOutput = true,
};
}
[RelayCommand]
private void RunDirectoryComparison()
{
if (Processing == true)
{
return;
}
Task.Run(DoDirectoryComparison);
}
private async void DoDirectoryComparison()
{
Processing = true;
ErrorMessage = null;
ValueSetComparisons = [];
PrimitiveComparisons = [];
ComplexTypeComparisons = [];
ResourceComparisons = [];
ExtensionComparisons = [];
if (string.IsNullOrEmpty(SourceDirectory) || string.IsNullOrEmpty(TargetDirectory) || (SourceDirectory == TargetDirectory))
{
ErrorMessage = "Source and target are required and cannot be the same";
Processing = false;
return;
}
PackageLoader loader = new(new());
DefinitionCollection? source = await loader.LoadPackages([SourceDirectory], fhirVersion: FhirVersions[SourceDirectoryFhirVersionIndex]);
if (source == null)
{
ErrorMessage = "Failed to load source definitions";
Processing = false;
return;
}
DefinitionCollection? target = await loader.LoadPackages([TargetDirectory], fhirVersion: FhirVersions[TargetDirectoryFhirVersionIndex]);
if (target == null)
{
ErrorMessage = "Failed to load target definitions";
Processing = false;
return;
}
ConfigCompare compareOptions = string.IsNullOrEmpty(SaveDirectory)
? new()
{
MapSaveStyle = ConfigCompare.ComparisonMapSaveStyle.None,
NoOutput = true,
}
: new()
{
OutputDirectory = SaveDirectory,
//CrossVersionMapDestinationPath = SaveDirectory,
MapSaveStyle = ConfigCompare.ComparisonMapSaveStyle.Source,
NoOutput = false,
};
PackageComparer comparer = new(compareOptions, source, target);
PackageComparison results = comparer.Compare();
ValueSetComparisons = results.ValueSets.Values.SelectMany(l => l.Select(v => v)).ToList();
PrimitiveComparisons = results.PrimitiveTypes.Values.SelectMany(l => l.Select(v => v)).ToList();
ComplexTypeComparisons = results.ComplexTypes.Values.SelectMany(l => l.Select(v => v)).ToList();
ResourceComparisons = results.Resources.Values.SelectMany(l => l.Select(v => v)).ToList();
ExtensionComparisons = results.Extensions.Values.SelectMany(l => l.Select(v => v)).ToList();
Processing = false;
ErrorMessage = null;
}
}

Просмотреть файл

@ -9,17 +9,18 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using fhir_codegen.Views;
using Material.Icons;
using Material.Styles.Themes;
namespace fhir_codegen.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
[ObservableProperty]
private bool _isPaneOpen;
@ -29,6 +30,14 @@ public partial class MainWindowViewModel : ViewModelBase
[ObservableProperty]
private NavigationItemTemplate? _selectedNavigationItem;
//[ObservableProperty]
//private MaterialTheme? _theme = Avalonia.Application.Current?.LocateMaterialTheme<MaterialTheme>();
public MainWindowViewModel(object? args = null)
:base()
{
}
partial void OnSelectedNavigationItemChanged(NavigationItemTemplate? value)
{
if (value == null)
@ -36,8 +45,12 @@ public partial class MainWindowViewModel : ViewModelBase
return;
}
ViewModelBase? target = (ViewModelBase?)Activator.CreateInstance(value.Target);
NavigateTo(value.Target);
}
public void NavigateTo(Type targetViewModel, object? args = null)
{
ViewModelBase? target = (ViewModelBase?)Activator.CreateInstance(targetViewModel, args);
if (target == null)
{
return;

Просмотреть файл

@ -93,7 +93,8 @@ public partial class WelcomePageViewModel : ViewModelBase, INavigableViewModel
FilteredInstalledPackages = new(packages.Where(p => p.Moniker.Contains(filterValue, StringComparison.OrdinalIgnoreCase)));
}
public WelcomePageViewModel()
public WelcomePageViewModel(object? args = null)
:base()
{
// get the current configuration
ConfigGui? config = Gui.RunningConfiguration;

Просмотреть файл

@ -32,7 +32,7 @@
</StackPanel>
<TabControl Grid.Row="1">
<TabItem Header="Configuration">
<TabItem Header="FHIR Releases">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
@ -40,8 +40,8 @@
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="64" /> <!-- ReleasesOnly -->
<RowDefinition Height="64" /> <!-- SourcePackage -->
<RowDefinition Height="64" /> <!-- RowTargetPacakge -->
<RowDefinition Height="64" /> <!-- SourceReleaseIndex -->
<RowDefinition Height="64" /> <!-- TargetReleaseIndex -->
<RowDefinition Height="64" /> <!-- RowRunComparison -->
<RowDefinition Height="64" /> <!-- RowErrorMessage -->
<RowDefinition Height="64" /> <!-- RowSourceMaps -->
@ -49,26 +49,117 @@
<CheckBox Grid.Row="0" Grid.Column="1" IsChecked="{Binding OnlyReleaseVersions}">Only Show Official Releases</CheckBox>
<Label Grid.Row="1" Grid.Column="0" Content="Source (left) Package:" VerticalAlignment="Center"/>
<ComboBox Grid.Row="1" Grid.Column="1" SelectedIndex="{Binding SourceIndex}" ItemsSource="{Binding CorePackages}"/>
<Label Grid.Row="1" Grid.Column="0" Content="Source (left) Package:" HorizontalAlignment="Right"/>
<ComboBox Grid.Row="1" Grid.Column="1" SelectedIndex="{Binding SourceReleaseIndex}" ItemsSource="{Binding CorePackages}" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="0" Content="Target (right) Package:" VerticalAlignment="Center"/>
<ComboBox Grid.Row="2" Grid.Column="1" SelectedIndex="{Binding TargetIndex}" ItemsSource="{Binding CorePackages}"/>
<Label Grid.Row="2" Grid.Column="0" Content="Target (right) Package:" HorizontalAlignment="Right"/>
<ComboBox Grid.Row="2" Grid.Column="1" SelectedIndex="{Binding TargetReleaseIndex}" ItemsSource="{Binding CorePackages}" HorizontalAlignment="Left"/>
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal">
<Button Content="Run Comparison" Command="{Binding RunComparisonCommand}" />
<!--<ProgressBar Classes="Circle"/>-->
<StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Left">
<Button Content="Run Comparison" Command="{Binding RunReleaseComparisonCommand}" />
<ProgressBar Classes="Circle" IsVisible="{Binding Processing}"/>
</StackPanel>
<Label Grid.Row="4" Grid.Column="1" Content="{Binding ErrorMessage}" Foreground="Red" />
<Label Grid.Row="4" Grid.Column="1" Content="{Binding ErrorMessage}" Foreground="Red" HorizontalAlignment="Left"/>
<Label Grid.Row="5" Grid.Column="0" Content="Map source path"/>
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding CrossVersionDirectory}" />
<Label Grid.Row="5" Grid.Column="0" Content="Map source path" HorizontalAlignment="Right"/>
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding CrossVersionDirectory}" HorizontalAlignment="Left"/>
</Grid>
</TabItem>
<TabItem Header="Directories">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="64" /> <!-- 0 SourceDirectory -->
<RowDefinition Height="64" /> <!-- 1 SourceDirectoryFhirVersion -->
<RowDefinition Height="64" /> <!-- 2 TargetDirectory -->
<RowDefinition Height="64" /> <!-- 3 TargetDirectoryFhirVersion -->
<RowDefinition Height="64" /> <!-- 4 SaveDirectory -->
<RowDefinition Height="64" /> <!-- 5 RowRunComparison -->
<RowDefinition Height="64" /> <!-- 6 RowErrorMessage -->
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Source (left) Package:" HorizontalAlignment="Right"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding SourceDirectory}" HorizontalAlignment="Left"/>
<Label Grid.Row="1" Grid.Column="0" Content="Source (left) FHIR Version:" HorizontalAlignment="Right"/>
<ComboBox Grid.Row="1" Grid.Column="1" SelectedIndex="{Binding SourceDirectoryFhirVersionIndex}" ItemsSource="{Binding FhirVersions}" HorizontalAlignment="Left"/>
<Label Grid.Row="2" Grid.Column="0" Content="Target (right) Package:" HorizontalAlignment="Right"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding TargetDirectory}" HorizontalAlignment="Left"/>
<Label Grid.Row="3" Grid.Column="0" Content="Target (right) FHIR Version:" HorizontalAlignment="Right"/>
<ComboBox Grid.Row="3" Grid.Column="1" SelectedIndex="{Binding TargetDirectoryFhirVersionIndex}" ItemsSource="{Binding FhirVersions}" HorizontalAlignment="Left"/>
<Label Grid.Row="4" Grid.Column="0" Content="Save Directory:" HorizontalAlignment="Right"/>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding SaveDirectory}" HorizontalAlignment="Left"/>
<StackPanel Grid.Row="5" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Left">
<Button Content="Run Comparison" Command="{Binding RunDirectoryComparisonCommand}" />
<ProgressBar Classes="Circle" IsVisible="{Binding Processing}"/>
</StackPanel>
<Label Grid.Row="5" Grid.Column="1" Content="{Binding ErrorMessage}" Foreground="Red" HorizontalAlignment="Left"/>
</Grid>
</TabItem>
<TabItem Header="Results">
<TabControl>
<TabItem Header="Value Sets">
<DataGrid ItemsSource="{Binding ValueSetComparisons}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Resource" Binding="{Binding Path=Source.Name}" Width="*"/>
<DataGridTextColumn Header="Title" Binding="{Binding Path=Source.Title}" Width="*"/>
<DataGridTextColumn Header="Relationship" Binding="{Binding Path=Relationship}" Width="*"/>
<DataGridTextColumn Header="Message" Binding="{Binding Path=Message}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Primitives">
<DataGrid ItemsSource="{Binding PrimitiveComparisons}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Resource" Binding="{Binding Path=Source.Name}" Width="*"/>
<DataGridTextColumn Header="Title" Binding="{Binding Path=Source.Title}" Width="*"/>
<DataGridTextColumn Header="Relationship" Binding="{Binding Path=Relationship}" Width="*"/>
<DataGridTextColumn Header="Message" Binding="{Binding Path=Message}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Complex Types">
<DataGrid ItemsSource="{Binding ComplexTypeComparisons}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Resource" Binding="{Binding Path=Source.Name}" Width="*"/>
<DataGridTextColumn Header="Title" Binding="{Binding Path=Source.Title}" Width="*"/>
<DataGridTextColumn Header="Relationship" Binding="{Binding Path=Relationship}" Width="*"/>
<DataGridTextColumn Header="Message" Binding="{Binding Path=Message}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Resources">
<DataGrid ItemsSource="{Binding ResourceComparisons}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Resource" Binding="{Binding Path=Source.Name}" Width="*"/>
<DataGridTextColumn Header="Title" Binding="{Binding Path=Source.Title}" Width="*"/>
<DataGridTextColumn Header="Relationship" Binding="{Binding Path=Relationship}" Width="*"/>
<DataGridTextColumn Header="Message" Binding="{Binding Path=Message}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Extensions">
<DataGrid ItemsSource="{Binding ExtensionComparisons}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Resource" Binding="{Binding Path=Source.Name}" Width="*"/>
<DataGridTextColumn Header="Title" Binding="{Binding Path=Source.Title}" Width="*"/>
<DataGridTextColumn Header="Relationship" Binding="{Binding Path=Relationship}" Width="*"/>
<DataGridTextColumn Header="Message" Binding="{Binding Path=Message}" Width="3*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
</TabItem>
</TabControl>

Просмотреть файл

@ -34,12 +34,15 @@
</Button>
<ListBox ItemsSource="{Binding NavigationItems}"
SelectedItem="{Binding SelectedNavigationItem}">
SelectedItem="{Binding SelectedNavigationItem}"
Margin="0">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:NavigationItemTemplate}">
<StackPanel Spacing="12" Margin="4 4" Orientation="Horizontal">
<materialIcons:MaterialIcon Kind="{Binding IconKind}" Classes="cgInlineIcon" />
<TextBlock Text="{Binding Label}" />
<StackPanel Spacing="12" Margin="0" Orientation="Horizontal">
<Border Classes="cgIconBorder32">
<materialIcons:MaterialIcon Kind="{Binding IconKind}" Classes="cgIcon24"/>
</Border>
<TextBlock Text="{Binding Label}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>

Просмотреть файл

@ -1,7 +1,10 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using fhir_codegen;
using fhir_codegen.ViewModels;
namespace fhir_codegen.Views;

Просмотреть файл

@ -40,9 +40,6 @@
<DataGrid
Grid.Row="1"
ItemsSource="{Binding FilteredInstalledPackages}"
GridLinesVisibility="All"
BorderBrush="Gray"
BorderThickness="1"
IsReadOnly="True"
CanUserSortColumns="True"
VerticalAlignment="Stretch"