Skip to content

VPR-157 feat(a11y): accessible Word, PDF, and Excel exports#165

Open
rlorenzo wants to merge 16 commits intomainfrom
VPR-157-accessible-word-pdf-export
Open

VPR-157 feat(a11y): accessible Word, PDF, and Excel exports#165
rlorenzo wants to merge 16 commits intomainfrom
VPR-157-accessible-word-pdf-export

Conversation

@rlorenzo
Copy link
Copy Markdown
Contributor

@rlorenzo rlorenzo commented May 1, 2026

Summary

Adds WCAG 2.1 / PDF/UA-1 tagging and Excel a11y to every export pipeline currently shipping in VIPER:

New helpers (web/Classes/Utilities/)

  • WordAccessibilityHelper — heading styles, image alt text, table-header rows, document language, core file properties.
  • PdfAccessibilityHelperWithAccessibility(...) extension wrapping DocumentMetadata + DocumentSettings.PDFUA_Conformance = PDFUA_1.
  • ExcelAccessibilityHelper — workbook core properties, PromoteToAccessibleTable for structured Excel Tables.
  • QuestPDF Community license assignment moved to Program.cs so it's set once at startup.

Word export tagged

  • Photo Gallery (PhotoExportService): heading styles, alt text on every photo, document language, core properties.

PDF exports tagged (institution/date row → SemanticIgnore, title → SemanticHeader1, dept/sub-headers → SemanticHeader2/SemanticHeader3, content tables → SemanticTable, footers → artifact via shared AddPdfPageNumberFooter helper, WithAccessibility for metadata + PDF/UA conformance):

  • Photo Gallery (Students)
  • Emergency Contact Overview + Report (Students)
  • Clinical Effort (Effort)
  • Clinical Schedule (Effort)
  • School Summary (Effort)
  • Department Summary (Effort)
  • Teaching Activity — Grouped + Individual (Effort)
  • Merit Summary (Effort)
  • Merit Detail + Merit Average (Effort)
  • Evaluation Summary + Evaluation Detail (Effort)
  • Multi-Year Merit (Effort)

Excel exports tagged (workbook core properties on all; structured PromoteToAccessibleTable where data rows are uniform):

  • Emergency Contact Overview + Report (structured tables)
  • Clinical Effort — per job-group structured tables
  • Clinical Schedule — structured table
  • Department Summary — instructor rows promoted; summary rows stay outside
  • Teaching Activity Individual — course rows promoted
  • School Summary, Teaching Activity Grouped, Merit Summary, Merit Detail/Average, Eval Summary/Detail, Multi-Year — core properties only (interleaved totals/summary rows aren't safe to wrap as a single table).

Tests — 32 unit tests across WordAccessibilityHelperTests, PdfAccessibilityHelperTests, ExcelAccessibilityHelperTests. Includes regression guards for the review issues addressed in the helpers commit (Title style not marked default, Title vs H1 font sizes differ, alt text leaves Title null when caller doesn't pass one).

Summary by CodeRabbit

  • New Features
    • PDF reports now include page numbers in footers for easier navigation.
    • Enhanced accessibility compliance across PDF, Excel, and Word exports for improved assistive technology compatibility.
    • Excel exports now feature accessible table structures with improved metadata properties.
    • Word exports include enhanced accessibility formatting and consistent document metadata.

rlorenzo added 14 commits April 30, 2026 20:52
Word: register Title and Heading 2 styles, set core file properties
(Title/Subject/Creator/Language), and write image alt text following
the displayed-name rule from PLAN-Word-PDF-WCAG.md.

PDF: enable PDFUA_1 conformance with metadata, and tag titles
(SemanticHeader1), group headers (SemanticHeader2), photos
(SemanticImage with alt text), and the page-number footer
(SemanticIgnore artifact). Photo-grid tables stay untagged because
they're layout, not data.
Adds AddPdfPageNumberFooter to BaseReportService — a SemanticIgnore-
wrapped footer (filter line + page numbers) so the repeated content is
treated as a page artifact instead of being announced on every page.

ClinicalEffortService is converted as the reference implementation: it
chains WithAccessibility for PDF/UA and metadata, marks the institution
+ date row as SemanticIgnore, the report title as SemanticHeader1, and
each job-group section as SemanticHeader2. The other 8 effort services
(ClinicalSchedule, DeptSummary, EvaluationReport, MeritMultiYear,
MeritReport, MeritSummary, SchoolSummary, TeachingActivity) need the
same treatment in a follow-up.
Excel exports also need workbook core properties and structured tables
so the Office Accessibility Checker passes and screen readers announce
column headers correctly. Companion to PdfAccessibilityHelper /
WordAccessibilityHelper, applied across Effort report Excel exports
in subsequent commits.
…lity

PDF: header marked as semantic H1, institution/date row and footer
flagged as repetition artifacts, content table tagged. PDF/UA metadata
applied via WithAccessibility.

Excel: workbook core properties set; data range promoted to a structured
Excel Table so screen readers announce row data in column-header terms.
PDF: institution/date row marked as artifact, title as H1, content table
tagged, footer routed through the shared AddPdfPageNumberFooter helper,
PDF/UA metadata applied via WithAccessibility.

Excel: workbook core properties set. Skipped structured-table promotion
because each department block emits totals + faculty-count + averages
rows of differing shapes — promoting them as one table would mislead
screen readers about row uniformity.
…ility

PDF: institution/date row hidden as artifact, title as H1, dept name as
H2, content table tagged, footer routed through AddPdfPageNumberFooter,
WithAccessibility metadata applied.

Excel: workbook core properties set; the instructor rows (uniform shape)
promoted to a structured Excel Table so AT announces effort columns by
header. Department-totals and faculty-count rows intentionally remain
outside the table.
…lity

Covers both Grouped (per-department) and Individual (per-instructor)
PDFs: institution/date row hidden as artifact, title as H1, dept (and
instructor on Individual) as H2, content table tagged, footer routed
through AddPdfPageNumberFooter. WithAccessibility metadata applied.

Excel: workbook core properties set on both variants. The Individual
sheets get their course rows promoted to a structured Excel Table; the
Grouped variant has interleaved instructor totals so a single table
would misrepresent row uniformity — core properties only there.
PDF: institution/date row marked as artifact, title as H1, dept as H2,
job group as H3, content table tagged. Footer routed through the shared
helper. WithAccessibility metadata applied.

Excel: workbook core properties set. Skipped table promotion — every
sheet is just totals + faculty-count + averages rows; no uniform body
data to wrap as a table.
Covers MeritDetail and MeritAverage reports. Both PDFs: institution/date
row marked artifact, title as H1, dept as H2, job-group as H3 (Average
report), content tables tagged, footers routed through AddPdfPageNumberFooter,
WithAccessibility metadata applied.

Both Excels: workbook core properties set. Skipped table promotion —
detail rows are interleaved with instructor-totals/dept-totals rows of
differing shape.
…ility

Covers EvalSummary and EvalDetail PDFs and Excels: institution/date row
hidden as artifact, title as H1, dept as H2, content tables tagged,
footers routed through AddPdfPageNumberFooter. WithAccessibility metadata
applied. Excel workbook core properties set on both variants. Skipped
table promotion — both variants interleave instructor-average and
department-average summary rows with course detail rows.
Both pages of the PDF (Merit Activity, Evaluations) get institution/date
artifacts hidden, the report title as H1 (Merit page) / H2 (Eval page),
content tables tagged, and footers marked as artifacts. WithAccessibility
metadata applied at document level.

Excel: workbook core properties set. Skipped table promotion — the merit
and eval sections share one worksheet with section headers + per-year
header rows + interleaved totals; no uniform body data to wrap.
…xports

Both services already had PDF accessibility tagging — this catches the
matching Excel exports up to the same standard:

- Workbook core properties set (Title / Subject / Author / Created)
- Data ranges promoted to structured Excel Tables so screen readers can
  announce columns by header. Per-job-group tables for Clinical Effort,
  one each for Emergency Contact Overview and Report.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@rlorenzo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 59 minutes and 3 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3a6f01be-db4d-4bdc-bd2d-2bfc55e3f928

📥 Commits

Reviewing files that changed from the base of the PR and between 37e151c and e9c3a3a.

📒 Files selected for processing (15)
  • web/Areas/Effort/Services/BaseReportService.cs
  • web/Areas/Effort/Services/ClinicalScheduleService.cs
  • web/Areas/Effort/Services/EvaluationReportService.cs
  • web/Areas/Effort/Services/IClinicalEffortService.cs
  • web/Areas/Effort/Services/IClinicalScheduleService.cs
  • web/Areas/Effort/Services/IDeptSummaryService.cs
  • web/Areas/Effort/Services/IEvaluationReportService.cs
  • web/Areas/Effort/Services/IMeritMultiYearService.cs
  • web/Areas/Effort/Services/IMeritReportService.cs
  • web/Areas/Effort/Services/IMeritSummaryService.cs
  • web/Areas/Effort/Services/ISchoolSummaryService.cs
  • web/Areas/Effort/Services/ITeachingActivityService.cs
  • web/Areas/Students/Services/EmergencyContactExportService.cs
  • web/Areas/Students/Services/PhotoExportService.cs
  • web/Classes/Utilities/WordAccessibilityHelper.cs
📝 Walkthrough

Walkthrough

This pull request introduces comprehensive accessibility support for PDF, Excel, and Word document exports across multiple reporting services. Three new utility helpers are added to standardize accessibility metadata and semantic markup, along with extensive test coverage validating their functionality. Report generation services are systematically updated to use these helpers while centralizing QuestPDF license configuration in application startup.

Changes

Cohort / File(s) Summary
Accessibility Helper Test Suites
test/Classes/Utilities/ExcelAccessibilityHelperTests.cs, test/Classes/Utilities/PdfAccessibilityHelperTests.cs, test/Classes/Utilities/WordAccessibilityHelperTests.cs
Comprehensive test coverage for new accessibility utilities, verifying core property assignment (title, subject, author, language), default value fallbacks, table/style promotion, and metadata/conformance settings.
Core Accessibility Utilities
web/Classes/Utilities/ExcelAccessibilityHelper.cs, web/Classes/Utilities/PdfAccessibilityHelper.cs, web/Classes/Utilities/WordAccessibilityHelper.cs
New static helper classes providing reusable methods to apply accessibility metadata and semantic markup to workbooks, PDF documents, and Word documents respectively. Includes table name sanitization, style registration, and core property configuration.
Report Services (Effort Area)
web/Areas/Effort/Services/BaseReportService.cs, web/Areas/Effort/Services/ClinicalEffortService.cs, web/Areas/Effort/Services/ClinicalScheduleService.cs, web/Areas/Effort/Services/DeptSummaryService.cs, web/Areas/Effort/Services/EvaluationReportService.cs, web/Areas/Effort/Services/MeritMultiYearService.cs, web/Areas/Effort/Services/MeritReportService.cs, web/Areas/Effort/Services/MeritSummaryService.cs, web/Areas/Effort/Services/SchoolSummaryService.cs, web/Areas/Effort/Services/TeachingActivityService.cs
Updated PDF and Excel generation to use accessibility helpers for metadata, apply QuestPDF semantic markup (SemanticIgnore, SemanticHeader*, SemanticTable), replace manual footer logic with centralized helper, and promote Excel ranges to accessible tables. Removes duplicate license configuration.
Report Services (Students Area)
web/Areas/Students/Services/EmergencyContactExportService.cs, web/Areas/Students/Services/PhotoExportService.cs
Enhanced Excel and PDF exports with accessibility metadata via helpers, semantic markup for headers/footers, and—for PhotoExportService—integrated WordAccessibilityHelper for Word output including alt text on images.
Application Startup
web/Program.cs
Centralizes QuestPDF license configuration (LicenseType.Community) at application initialization, removing duplicate assignments from individual services.

Sequence Diagram(s)

sequenceDiagram
    participant Service as Report Service
    participant Helper as Accessibility Helper
    participant Doc as Document<br/>(PDF/Excel/Word)
    
    Service->>Service: Generate document content<br/>(headers, tables, data)
    Service->>Helper: Call SetCoreProperties/<br/>WithAccessibility
    Helper->>Doc: Apply metadata<br/>(title, subject, author, language)
    Helper->>Doc: Apply semantic markup<br/>(PDFUA, styles, conformance)
    Helper->>Doc: Configure structure<br/>(headers, tables, table names)
    Helper-->>Service: Return modified document
    Service->>Doc: Return to user
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With tables and headers, made clean and accessible,
Our helpers ensure all documents are resectable,
From Excel to PDF, and Word docs so fine,
Metadata, semantics—all perfectly aligned!
Screen readers rejoice, as we hop toward compliance, 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly and specifically summarizes the main change: introducing accessibility features (WCAG 2.1 / PDF/UA-1 compliance) across Word, PDF, and Excel export pipelines.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch VPR-157-accessible-word-pdf-export

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 59 minutes and 3 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 1, 2026

Codecov Report

❌ Patch coverage is 37.88546% with 282 lines in your changes missing coverage. Please review.
✅ Project coverage is 43.52%. Comparing base (5cb764f) to head (e9c3a3a).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
web/Areas/Students/Services/PhotoExportService.cs 0.00% 71 Missing ⚠️
...b/Areas/Effort/Services/EvaluationReportService.cs 0.00% 33 Missing ⚠️
...Students/Services/EmergencyContactExportService.cs 0.00% 28 Missing ⚠️
web/Areas/Effort/Services/MeritReportService.cs 0.00% 26 Missing ⚠️
web/Areas/Effort/Services/DeptSummaryService.cs 0.00% 20 Missing ⚠️
...b/Areas/Effort/Services/TeachingActivityService.cs 45.45% 18 Missing ⚠️
...b/Areas/Effort/Services/ClinicalScheduleService.cs 0.00% 17 Missing ⚠️
web/Areas/Effort/Services/BaseReportService.cs 0.00% 16 Missing ⚠️
web/Areas/Effort/Services/MeritMultiYearService.cs 17.64% 14 Missing ⚠️
web/Areas/Effort/Services/MeritSummaryService.cs 0.00% 12 Missing ⚠️
... and 4 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #165      +/-   ##
==========================================
+ Coverage   43.24%   43.52%   +0.28%     
==========================================
  Files         862      866       +4     
  Lines       50405    50646     +241     
  Branches     4706     4742      +36     
==========================================
+ Hits        21796    22043     +247     
+ Misses      28086    28078       -8     
- Partials      523      525       +2     
Flag Coverage Δ
backend 43.61% <37.88%> (+0.29%) ⬆️
frontend 41.69% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/Areas/Effort/Services/ClinicalScheduleService.cs (1)

422-435: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the trailing blank Excel column in single-term exports.

When showTotal is false, col is already one past the last populated header. Using it as totalCols makes PromoteToAccessibleTable capture an empty extra column.

Suggested fix
-        int totalCols = col;
+        int totalCols = showTotal ? col : col - 1;

Also applies to: 464-468

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/Areas/Effort/Services/ClinicalScheduleService.cs` around lines 422 - 435,
In ClinicalScheduleService, the header logic uses `col` (which is one past the
last filled column when `showTotal` is false) as `totalCols`, causing
PromoteToAccessibleTable to include a trailing empty column; change the
`totalCols` computation to reflect the last populated column (e.g., use
`totalCols = showTotal ? col : col - 1` or `totalCols = col - (showTotal ? 0 :
1)`) and apply the same fix to the other occurrence around the block using
`row`/`col` at lines 464-468 so PromoteToAccessibleTable receives the correct
column range.
🧹 Nitpick comments (2)
web/Areas/Effort/Services/EvaluationReportService.cs (1)

681-725: ⚡ Quick win

Promote the summary grid to an accessible table before the department total.

This sheet now gets core properties, but the instructor rows are still plain cells. That means screen readers still won't get header associations for the main data block even though the rows are uniform up to Department Average.

Suggested fix
         // Column headers
+        int headerRow = row;
         ws.Cell(row, 1).Value = "Instructor";
         ws.Cell(row, 2).Value = "Average";
         ws.Range($"{row}:{row}").Style.Font.Bold = true;
         ws.SheetView.FreezeRows(row);
         row++;
@@
         foreach (var instructor in dept.Instructors)
         {
             ws.Cell(row, 1).Value = ExcelHelper.SanitizeStringCell(instructor.Instructor);
             ws.Cell(row, 2).Value = instructor.WeightedAverage;
             ws.Cell(row, 2).Style.NumberFormat.Format = "0.00";
             row++;
         }
+
+        var lastDataRow = row - 1;
+        if (lastDataRow > headerRow)
+        {
+            ExcelAccessibilityHelper.PromoteToAccessibleTable(
+                ws.Range(headerRow, 1, lastDataRow, 2),
+                $"EvalSummary_{dept.Department}");
+        }

         // Department average
         ws.Cell(row, 1).Value = "Department Average";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/Areas/Effort/Services/EvaluationReportService.cs` around lines 681 - 725,
The instructor list is written as plain cells in GenerateEvalSummaryExcel so
screen readers can't associate headers; convert the header + instructor rows
into an actual Excel table before writing the "Department Average" row: after
you write the column headers (the code that sets ws.Cell(row,1) "Instructor" /
ws.Cell(row,2) "Average") and after the loop that writes dept.Instructors, call
ws.Tables.Add(...) (or the ClosedXML equivalent) using the range from the header
row to the last instructor row (use the local row variable to compute the end
row), give the table a unique name (e.g., based on dept.Department), ensure the
header row is marked as headers and autofilter is enabled, and then continue to
write the Department Average and call ShadeExcelRow; keep existing helpers
AddExcelHeader/AddExcelFilterLine, ExcelHelper.SanitizeStringCell, and
ShadeExcelRow unchanged.
web/Areas/Students/Services/PhotoExportService.cs (1)

186-251: ⚖️ Poor tradeoff

Consider extracting common image-drawing logic.

The inline drawing construction (wpDocProps, picNonVisualProps, SetImageAltText, inline assembly) is repeated in both the large-layout chunked path and the standard-layout path (lines 304-370). A private helper like CreatePhotoDrawing(MainDocumentPart, byte[], uint imageId, long width, long height, string altText) could reduce duplication.

However, given the OpenXML fluent construction style and the pragma already suppressing S3220, this is optional and can be deferred.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/Areas/Students/Services/PhotoExportService.cs` around lines 186 - 251,
The repeated OpenXML image-drawing construction in PhotoExportService should be
extracted into a private helper to remove duplication: add a method like
CreatePhotoDrawing(MainDocumentPart mainPart, byte[] photoBytes, ref uint
imageId, long width, long height, string altText) that (1) adds an ImagePart and
feeds photoBytes, (2) increments imageId and creates DocProperties and
NonVisualDrawingProperties, (3) calls WordAccessibilityHelper.SetImageAltText,
(4) constructs and returns a DocumentFormat.OpenXml.Wordprocessing.Drawing (or
the Inline to wrap into a Drawing). Replace the inline construction blocks in
the standard-layout and large-layout paths with calls to this helper and append
the returned drawing into the photoRun; ensure imageId is shared (pass by ref)
and altText uses student.GroupExportName.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/Areas/Effort/Services/BaseReportService.cs`:
- Around line 226-243: The footer currently calls footer.SemanticIgnore() which
hides the entire footer (including filter text added via AddPdfFilterLine) from
accessibility tools; change AddPdfPageNumberFooter so that
AddPdfFilterLine(col.Item(), filters) is rendered outside the SemanticIgnore
scope and only the page counter column (the col.Item().AlignCenter().Text block
that calls CurrentPageNumber/TotalPages) is wrapped with SemanticIgnore(),
ensuring filter labels/values remain in the semantic tree while the page counter
remains ignored by assistive tech.

In `@web/Program.cs`:
- Around line 46-48: The test PdfAccessibilityHelperTests fails because
Document.Create() is called without initializing QuestPDF.Settings.License; move
the license initialization into a shared helper (e.g., a static method like
EnsureQuestPdfLicense()) that sets QuestPDF.Settings.License =
LicenseType.Community and call that helper from Program.cs startup and from the
PdfAccessibilityHelperTests (or add a one-line call to the helper in the test
class setup) so any code path (including tests) that creates QuestPDF documents
runs after the license is set.

---

Outside diff comments:
In `@web/Areas/Effort/Services/ClinicalScheduleService.cs`:
- Around line 422-435: In ClinicalScheduleService, the header logic uses `col`
(which is one past the last filled column when `showTotal` is false) as
`totalCols`, causing PromoteToAccessibleTable to include a trailing empty
column; change the `totalCols` computation to reflect the last populated column
(e.g., use `totalCols = showTotal ? col : col - 1` or `totalCols = col -
(showTotal ? 0 : 1)`) and apply the same fix to the other occurrence around the
block using `row`/`col` at lines 464-468 so PromoteToAccessibleTable receives
the correct column range.

---

Nitpick comments:
In `@web/Areas/Effort/Services/EvaluationReportService.cs`:
- Around line 681-725: The instructor list is written as plain cells in
GenerateEvalSummaryExcel so screen readers can't associate headers; convert the
header + instructor rows into an actual Excel table before writing the
"Department Average" row: after you write the column headers (the code that sets
ws.Cell(row,1) "Instructor" / ws.Cell(row,2) "Average") and after the loop that
writes dept.Instructors, call ws.Tables.Add(...) (or the ClosedXML equivalent)
using the range from the header row to the last instructor row (use the local
row variable to compute the end row), give the table a unique name (e.g., based
on dept.Department), ensure the header row is marked as headers and autofilter
is enabled, and then continue to write the Department Average and call
ShadeExcelRow; keep existing helpers AddExcelHeader/AddExcelFilterLine,
ExcelHelper.SanitizeStringCell, and ShadeExcelRow unchanged.

In `@web/Areas/Students/Services/PhotoExportService.cs`:
- Around line 186-251: The repeated OpenXML image-drawing construction in
PhotoExportService should be extracted into a private helper to remove
duplication: add a method like CreatePhotoDrawing(MainDocumentPart mainPart,
byte[] photoBytes, ref uint imageId, long width, long height, string altText)
that (1) adds an ImagePart and feeds photoBytes, (2) increments imageId and
creates DocProperties and NonVisualDrawingProperties, (3) calls
WordAccessibilityHelper.SetImageAltText, (4) constructs and returns a
DocumentFormat.OpenXml.Wordprocessing.Drawing (or the Inline to wrap into a
Drawing). Replace the inline construction blocks in the standard-layout and
large-layout paths with calls to this helper and append the returned drawing
into the photoRun; ensure imageId is shared (pass by ref) and altText uses
student.GroupExportName.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d36d72a0-5f85-4fcb-9a8f-5ae8c231687f

📥 Commits

Reviewing files that changed from the base of the PR and between 5cb764f and 37e151c.

📒 Files selected for processing (19)
  • test/Classes/Utilities/ExcelAccessibilityHelperTests.cs
  • test/Classes/Utilities/PdfAccessibilityHelperTests.cs
  • test/Classes/Utilities/WordAccessibilityHelperTests.cs
  • web/Areas/Effort/Services/BaseReportService.cs
  • web/Areas/Effort/Services/ClinicalEffortService.cs
  • web/Areas/Effort/Services/ClinicalScheduleService.cs
  • web/Areas/Effort/Services/DeptSummaryService.cs
  • web/Areas/Effort/Services/EvaluationReportService.cs
  • web/Areas/Effort/Services/MeritMultiYearService.cs
  • web/Areas/Effort/Services/MeritReportService.cs
  • web/Areas/Effort/Services/MeritSummaryService.cs
  • web/Areas/Effort/Services/SchoolSummaryService.cs
  • web/Areas/Effort/Services/TeachingActivityService.cs
  • web/Areas/Students/Services/EmergencyContactExportService.cs
  • web/Areas/Students/Services/PhotoExportService.cs
  • web/Classes/Utilities/ExcelAccessibilityHelper.cs
  • web/Classes/Utilities/PdfAccessibilityHelper.cs
  • web/Classes/Utilities/WordAccessibilityHelper.cs
  • web/Program.cs

Comment thread web/Areas/Effort/Services/BaseReportService.cs
Comment thread web/Program.cs
- AddPdfPageNumberFooter: only the "Page N of M" counter is wrapped in
  SemanticIgnore. The filter summary was also being hidden, which lost
  context for reports (e.g. evaluation reports) where filters appear
  nowhere outside the footer.
- ClinicalScheduleService Excel: totalCols was off-by-one when showTotal
  is false, causing PromoteToAccessibleTable to capture an empty
  trailing column.
- EvaluationReportService GenerateEvalSummaryExcel: instructor rows are
  now promoted to a structured Excel Table; matches the same treatment
  the eval-detail and other uniform-row exports already get.
@codecov-commenter
Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 2.92kB (0.14%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
viper-frontend-esm 2.14MB 2.92kB (0.14%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: viper-frontend-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/GenericError-*.css 434 bytes 203.39kB 0.21%
assets/TermManagement-*.js 449 bytes 53.58kB 0.85%
assets/schedule-*.js (New) 51.46kB 51.46kB 100.0% 🚀
assets/CourseDetail-*.js 193 bytes 40.63kB 0.48%
assets/PhotoGallery-*.js 75 bytes 35.62kB 0.21%
assets/InstructorEdit-*.js 245 bytes 32.23kB 0.77%
assets/InstructorList-*.js 31 bytes 26.09kB 0.12%
assets/StaffDashboard-*.js -265 bytes 25.6kB -1.02%
assets/CrossListedCoursesSection-*.js 111 bytes 23.7kB 0.47%
assets/GenericError-*.js -537 bytes 21.11kB -2.48%
assets/ClinicianScheduleView-*.js -1.38kB 18.9kB -6.81%
assets/MultiYearReport-*.js 74 bytes 18.64kB 0.4%
assets/EmergencyContactForm-*.js 17 bytes 17.57kB 0.1%
assets/AuditList-*.js 38 bytes 17.39kB 0.22%
assets/RotationScheduleView-*.js 120 bytes 17.01kB 0.71%
assets/CourseList-*.js 109 bytes 15.99kB 0.69%
assets/EffortTypeList-*.js 71 bytes 15.4kB 0.46%
assets/CourseImportDialog-*.js 43 bytes 10.88kB 0.4%
assets/CourseLinkDialog-*.js 225 bytes 9.31kB 2.48%
assets/ManageBundleCompetencies-*.js 85 bytes 8.9kB 0.96%
assets/StudentClassYear-*.js -1 bytes 8.44kB -0.01%
assets/EmergencyContactView-*.js 36 bytes 8.03kB 0.45%
assets/schedule-*.css (New) 7.07kB 7.07kB 100.0% 🚀
assets/ManageSessionCompetencies-*.js 96 bytes 6.86kB 1.42%
assets/ManageCompetencies-*.js 70 bytes 6.65kB 1.06%
assets/TermSelection-*.js -57 bytes 6.45kB -0.88%
assets/UnitList-*.js 57 bytes 6.34kB 0.91%
assets/CompetenciesBundleReport-*.js 122 bytes 6.1kB 2.04%
assets/AssessmentEpa-*.js 39 bytes 5.73kB 0.69%
assets/MyAssessments-*.js 75 bytes 5.67kB 1.34%
assets/EffortRecordEditDialog-*.js 77 bytes 5.42kB 1.44%
assets/SchoolSummary-*.js -114 bytes 4.32kB -2.57%
assets/ManageCourseCompetencies-*.js 52 bytes 3.7kB 1.43%
assets/LevelSelect-*.js -16 bytes 2.65kB -0.6%
assets/SchoolSummary-*.css -49 bytes 1.75kB -2.73%
assets/TermManagement-*.css 54 bytes 1.46kB 3.85%
assets/StaffDashboard-*.css -66 bytes 1.45kB -4.34%
assets/CompetenciesBundleReport-*.css 594 bytes 1.28kB 86.97% ⚠️
assets/colors-*.js (New) 891 bytes 891 bytes 100.0% 🚀
assets/StatusBadge-*.js (New) 458 bytes 458 bytes 100.0% 🚀
assets/format-*.js 4 bytes 161 bytes 2.55%
assets/RecentSelections-*.js (Deleted) -51.59kB 0 bytes -100.0% 🗑️
assets/RecentSelections-*.css (Deleted) -6.48kB 0 bytes -100.0% 🗑️

…aces

- Fix EnsureAccessibilityStyles_IsIdempotent CI failure: OpenXML 3.1.1
  rejects re-binding a Styles tree that's already attached. Read the
  existing tree once and only assign stylesPart.Styles when fresh.
- Add interface XML docs on the 11 export contracts touched by this PR,
  describing layout and where structured Excel Tables are used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants