Skip to content

Commit d7b3c4e

Browse files
msevestreclaude
andauthored
Replace MiKTeX/LaTeX reporting with Markdown + QuestPDF (#325)
* Replace MiKTeX/LaTeX reporting with Markdown + QuestPDF Removes the dependency on OSPSuite.TeXReporting and MiKTeX, replacing it with a simpler, pure .NET solution: - Markdown reports with embedded SVG charts (viewable in VS Code, GitHub) - PDF reports via QuestPDF (no external tools required) - Custom SVG chart generator for 2D line charts with log/linear scale - Report format switchable via ReportFormat enum New files: - Reporting/Charts/SvgChartGenerator.cs - SVG line chart generation - Reporting/Markdown/* - Markdown builder pattern implementation - Reporting/Pdf/PdfReportDocument.cs - QuestPDF document - Services/MarkdownReportingTask.cs, PdfReportingTask.cs Removed: - All *TeXBuilder.cs files - *Reporter.cs files - OSPSuite.TeXReporting dependency from all projects Closes #183 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Restore fixProductNameForWindows11 method for tests The method was removed during cross-platform refactoring but is needed by OperatingSystemInfoSpecs tests that use reflection to call it. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Revert OperatingSystemInfo to original Windows-specific implementation Keep the original Windows-specific OperatingSystemInfo that uses Registry, WMI, and SystemInformation APIs. Update Core and Tests projects to target net8.0-windows. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Trigger update * Address PR comments * Refactor test to use real MarkdownBuilderRepository Replace TestMarkdownBuilderRepository with the real MarkdownBuilderRepository and all production builders. This improves test coverage by exercising the actual builder implementations rather than duplicating their logic. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add MissingFileComparisonResult handling in Markdown and PDF reports - Create MissingFileComparisonResultMarkdownBuilder to properly report missing file details including which folder contains the file and which folder is missing it - Add ComposeMissingFileResult in PdfReportDocument to display the missing file validation message in PDF reports - Register the new builder in ValidatorRegister - Add builder to test setup Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix PR review findings: table escaping, null checks, and test refactoring - Escape pipe characters in MarkdownReportContext.AppendTable to prevent broken markdown tables when cell values contain '|' - Add null validation for installationValidationResult and RunSummary in both MarkdownReportingTask and PdfReportingTask before accessing StartTime - Refactor MarkdownReportingSpecs to use ContextForIntegration, resolving IMarkdownReportingTask from the DI container instead of manually constructing all builders Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Refactor ValidationReportingTask to use ReportOptions parameter Replace DefaultFormat property with ReportOptions parameter that includes: - ReportFormat as flags enum (None, Markdown, Pdf, All) - OpenReport boolean - Convenience properties ExportToMarkdown and ExportToPdf This allows generating both Markdown and PDF reports in a single call by using ReportFormat.All. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Bump to version 13. ALso use correct builder * Fix Markdown bold formatting by removing trailing space from ValidationResult caption The trailing space in the caption prevented proper bold formatting in Markdown output (**Result of the validation: ** instead of **Result of the validation:**). * Only generate charts for invalid outputs in Markdown and PDF reports Valid outputs with matching curves don't need charts. Removing the valid-outputs-with-data loop prevents generating thousands of unnecessary pages when a file has many outputs but only a few are invalid. * Ensures that we export the expected format * Add all valid curves back * Use report-specific filenames for PDF output Pass reportName parameter to reportOutputPath so PDF filenames reflect the actual report type (FolderComparison vs InstallationValidation). * Extract shared helpers, disable start without report format, add tests - Extract ChartDataFactory.CreateFor() to eliminate duplicate createChartData methods in OutputComparisonResultMarkdownBuilder and PdfReportDocument - Extract ColorExtensions.ToHexString() to replace colorToHex in SvgChartGenerator, toQuestColor in PdfReportDocument, and inline hex formatting in MarkdownReportContext - Fix PDF run summary labels: start/end time now have their own labels instead of reusing BatchRunDuration for the start time - Disable start button in MainView and SimulationComparisonView when neither PDF nor Markdown report format is selected - Replace Enumerable.Select allocation with Enumerable.Repeat in MarkdownReportContext.AppendTable separator row - Add MarkdownReportContextSpecs unit tests covering headings, tables, colored status, bold formatting, and content accumulation - Add observations to MarkdownReportingSpecs for SVG chart, validation state, and deviation section * Replace hardcoded strings with resources and add null guards - Add localized resource keys to Assets.Reporting: StartTime, EndTime, Yes, No, OSLabel, FileWasContainedInFolder, ButWasMissingInFolder, ValidationPerformedIn - Replace all hardcoded English strings in PdfReportDocument with Assets.Reporting resources for consistent localization - Add constructor null guards in PdfReportDocument for validationResult, comparisonResult, and svgChartGenerator - Add null guard for comparisonResult in both PdfReportingTask and MarkdownReportingTask CreateReport overloads * Ensure enable state is reset --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: msevestre <msevestre@users.noreply.github.com>
1 parent ab2e4c5 commit d7b3c4e

62 files changed

Lines changed: 2465 additions & 817 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,5 @@ ModelManifest.xml
246246
# FAKE - F# Make
247247
.fake/
248248
*.ncrunchsolution
249+
250+
.claude/settings.local.json

src/InstallationValidator.Core/Assets/Captions.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public static string ValidationDescription
5252
public static readonly string IgnoreRemovedCurves= "Ignore removed curves";
5353
public static readonly string ReallyCancelFolderComparison = "Really cancel folder comparison?";
5454
public static readonly string ExclusionList = "Exclusion List";
55+
public static readonly string ExportToPdf = "Export to PDF";
56+
public static readonly string ExportToMarkdown = "Export to Markdown";
5557
}
5658

5759
public static class Logs
@@ -187,8 +189,13 @@ public static class Reporting
187189
public static readonly string OverallValidationResult = "Overall Validation Result";
188190
public static readonly string FailedValidations = "Failed Validations";
189191
public static readonly string InputConfigurationFolder = "Input Configuration Folder";
192+
public static readonly string StartTime = "Start time";
193+
public static readonly string EndTime = "End time";
190194
public static readonly string BatchRunDuration = "Run Duration";
191-
public static readonly string ValidationResult = "Result of the validation: ";
195+
public static readonly string Yes = "Yes";
196+
public static readonly string No = "No";
197+
public static readonly string OSLabel = "OS";
198+
public static readonly string ValidationResult = "Result of the validation:";
192199
public static readonly string Simulation = "Simulation";
193200
public static readonly string Deviation = "Deviation";
194201
public static readonly string OutputPath = "Output Path";
@@ -213,6 +220,10 @@ public static string InstallationValidationPerformedIn(string startTime, string
213220

214221
public static string ComparisonFolder(string folderName) => $"{folderName} Folder";
215222

223+
public static string FileWasContainedInFolder(string fileName) => $"{fileName} was contained in folder:";
224+
public static readonly string ButWasMissingInFolder = "but was missing in folder:";
225+
public static string ValidationPerformedIn(string duration) => $"Validation performed in {duration}";
226+
216227
public static string MissingFileValidationMessage(string fileName, string folderContainingFile, string folderWithoutFile) =>
217228
$"{fileName} was contained in folder:{Environment.NewLine}{folderContainingFile}{Environment.NewLine}but was missing in folder:{Environment.NewLine}{folderWithoutFile}";
218229
}

src/InstallationValidator.Core/Domain/OperatingSystemInfo.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ namespace InstallationValidator.Core.Domain
1010
public class OperatingSystemInfo
1111
{
1212
private const string WINDOWS_REG_KEY = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
13-
1413
public string ComputerName { get; } = Environment.MachineName;
1514
public string Architecture => Environment.Is64BitOperatingSystem ? "x64" : "x32";
1615

@@ -103,4 +102,4 @@ public bool IsRunningOnVirtualMachine
103102

104103
public bool IsRunningOnTerminalSession => SystemInformation.TerminalServerSession;
105104
}
106-
}
105+
}
Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net472</TargetFramework>
4+
<TargetFramework>net8.0-windows</TargetFramework>
5+
<UseWindowsForms>true</UseWindowsForms>
6+
<EnableWindowsTargeting>true</EnableWindowsTargeting>
57
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
68
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
79
<PackageTags>open-systems-pharmacology, ospsuite-components</PackageTags>
@@ -10,7 +12,7 @@
1012
<OutputPath>bin\$(Configuration)</OutputPath>
1113
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
1214
<NoWarn>1591</NoWarn>
13-
<Version Condition="'$(Version)' == ''">12.3.0</Version>
15+
<Version Condition="'$(Version)' == ''">13.0.0</Version>
1416
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
1517
</PropertyGroup>
1618

@@ -24,15 +26,19 @@
2426

2527
<ItemGroup>
2628
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
27-
<PackageReference Include="OSPSuite.Assets.Images" Version="12.3.1" />
28-
<PackageReference Include="OSPSuite.Infrastructure" Version="12.3.1" />
29-
<PackageReference Include="OSPSuite.Infrastructure.Castle" Version="12.3.1" />
30-
<PackageReference Include="OSPSuite.Infrastructure.Reporting" Version="12.3.1" />
31-
<PackageReference Include="OSPSuite.Presentation" Version="12.3.1" />
32-
<PackageReference Include="OSPSuite.TeXReporting" Version="3.0.1.1" />
33-
<PackageReference Include="OSPSuite.Utility" Version="4.1.1.1" />
34-
<PackageReference Include="OSPSuite.Core" Version="12.3.1" />
35-
<PackageReference Include="OSPSuite.Assets" Version="12.3.1" />
29+
<PackageReference Include="OSPSuite.Assets.Images" Version="13.0.87" />
30+
<PackageReference Include="OSPSuite.DevExpress" Version="21.2.15.1" />
31+
<PackageReference Include="OSPSuite.FuncParser" Version="4.0.0.76" />
32+
<PackageReference Include="OSPSuite.Infrastructure" Version="13.0.87" />
33+
<PackageReference Include="OSPSuite.Infrastructure.Castle" Version="13.0.87" />
34+
<PackageReference Include="OSPSuite.Presentation" Version="13.0.87" />
35+
<PackageReference Include="OSPSuite.SimModel" Version="4.0.0.79" />
36+
<PackageReference Include="OSPSuite.Utility" Version="4.1.1.5" />
37+
<PackageReference Include="OSPSuite.Core" Version="13.0.87" />
38+
<PackageReference Include="OSPSuite.Assets" Version="13.0.87" />
39+
<PackageReference Include="QuestPDF" Version="2024.3.0" />
40+
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
41+
<PackageReference Include="System.Management" Version="8.0.0" />
3642
</ItemGroup>
3743

3844
<ItemGroup>
@@ -42,9 +48,5 @@
4248
</None>
4349
</ItemGroup>
4450

45-
<ItemGroup>
46-
<Reference Include="System.Management" />
47-
<Reference Include="System.Windows.Forms" />
48-
</ItemGroup>
4951

5052
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace InstallationValidator.Core.Presentation.DTO
2+
{
3+
public class ReportOptionsDTO
4+
{
5+
public bool ExportToPdf { get; set; } = true;
6+
public bool ExportToMarkdown { get; set; }
7+
}
8+
}

src/InstallationValidator.Core/Presentation/MainPresenter.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class MainPresenter : AbstractDisposablePresenter<IMainView, IMainPresent
2828
private readonly IInstallationValidatorConfiguration _configuration;
2929
private readonly IValidationReportingTask _validationReportingTask;
3030
private readonly FolderDTO _outputFolderDTO = new FolderDTO(folderMustExist: false);
31+
private readonly ReportOptionsDTO _reportOptionsDTO = new ReportOptionsDTO();
3132
private CancellationTokenSource _cancellationTokenSource;
3233
private bool _validationRunning;
3334

@@ -40,6 +41,7 @@ public MainPresenter(IMainView view, IDialogCreator dialogCreator, IBatchStarter
4041
_validationReportingTask = validationReportingTask;
4142
_outputFolderDTO.FolderPath = configuration.DefaultOutputPath;
4243
view.BindTo(_outputFolderDTO);
44+
view.BindToReportOptions(_reportOptionsDTO);
4345
}
4446

4547
public void SelectOutputFolder()
@@ -86,7 +88,7 @@ public async Task<InstallationValidationResult> StartInstallationValidation()
8688
validationResult.RunSummary = runSummary;
8789

8890
this.LogLine(Logs.StartingReport);
89-
await _validationReportingTask.CreateReport(validationResult, _outputFolderDTO.FolderPath, openReport: true);
91+
await _validationReportingTask.CreateReport(validationResult, _outputFolderDTO.FolderPath, new ReportOptions(reportFormatFromDTO(), openReport: true));
9092
this.LogLine();
9193

9294
this.LogLine(Logs.ValidationCompleted);
@@ -109,6 +111,14 @@ public async Task<InstallationValidationResult> StartInstallationValidation()
109111
}
110112
}
111113

114+
private ReportFormat reportFormatFromDTO()
115+
{
116+
var format = ReportFormat.None;
117+
if (_reportOptionsDTO.ExportToPdf) format |= ReportFormat.Pdf;
118+
if (_reportOptionsDTO.ExportToMarkdown) format |= ReportFormat.Markdown;
119+
return format;
120+
}
121+
112122
private void updateValidationRunningState(bool running)
113123
{
114124
_validationRunning = running;

src/InstallationValidator.Core/Presentation/SimulationComparisonPresenter.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class SimulationComparisonPresenter : AbstractDisposablePresenter<ISimula
3535
private readonly IBatchComparisonTask _batchComparisonTask;
3636
private readonly IValidationReportingTask _validationReportingTask;
3737
private readonly FolderComparisonDTO _folderComparisonDTO;
38+
private readonly ReportOptionsDTO _reportOptionsDTO = new ReportOptionsDTO();
3839

3940
public SimulationComparisonPresenter(ISimulationComparisonView view, IInstallationValidatorConfiguration configuration, IDialogCreator dialogCreator,
4041
IBatchComparisonTask batchComparisonTask, IValidationReportingTask validationReportingTask) : base(view)
@@ -45,6 +46,7 @@ public SimulationComparisonPresenter(ISimulationComparisonView view, IInstallati
4546
_validationReportingTask = validationReportingTask;
4647
_folderComparisonDTO = new FolderComparisonDTO();
4748
view.BindTo(_folderComparisonDTO);
49+
view.BindToReportOptions(_reportOptionsDTO);
4850
}
4951

5052
public void Handle(AppendTextToLogEvent eventToHandle)
@@ -70,7 +72,7 @@ public async Task StartComparison()
7072
this.LogLine();
7173

7274
this.LogLine(Logs.StartingReport);
73-
await _validationReportingTask.CreateReport(comparisonResult, _folderComparisonDTO.FirstFolder.FolderPath, _folderComparisonDTO.SecondFolder.FolderPath, openReport: true);
75+
await _validationReportingTask.CreateReport(comparisonResult, _folderComparisonDTO.FirstFolder.FolderPath, _folderComparisonDTO.SecondFolder.FolderPath, new ReportOptions(reportFormatFromDTO(), openReport: true));
7476
this.LogLine();
7577

7678
this.LogLine(Logs.ComparisonCompleted);
@@ -102,6 +104,14 @@ private ComparisonSettings comparisonSettingsFromDTO()
102104
};
103105
}
104106

107+
private ReportFormat reportFormatFromDTO()
108+
{
109+
var format = ReportFormat.None;
110+
if (_reportOptionsDTO.ExportToPdf) format |= ReportFormat.Pdf;
111+
if (_reportOptionsDTO.ExportToMarkdown) format |= ReportFormat.Markdown;
112+
return format;
113+
}
114+
105115
private void updateComparisonRunningState(bool running)
106116
{
107117
_comparisonRunning = running;

src/InstallationValidator.Core/Presentation/Views/IMainView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace InstallationValidator.Core.Presentation.Views
66
public interface IMainView : ILoggerView, IView<IMainPresenter>
77
{
88
void BindTo(FolderDTO outputFolderDTO);
9+
void BindToReportOptions(ReportOptionsDTO reportOptionsDTO);
910
void ValidationIsRunning(bool validationRunning);
1011
}
1112
}

src/InstallationValidator.Core/Presentation/Views/ISimulationComparisonView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public interface ISimulationComparisonView : ILoggerView, IView<ISimulationCompa
77
{
88
void ComparisonIsRunning(bool comparisonRunning);
99
void BindTo(FolderComparisonDTO folderComparisonDTO);
10+
void BindToReportOptions(ReportOptionsDTO reportOptionsDTO);
1011
}
1112
}

src/InstallationValidator.Core/Reporting/BatchComparisonResultReporter.cs

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)