Skip to content

Lesson plan: generate PDFs from activity markdowns, redesign cover page with HTML template, and open via blob in new tab#36

Merged
milljoniaer merged 4 commits intomainfrom
copilot/update-learn-plans-pdf
Mar 21, 2026
Merged

Lesson plan: generate PDFs from activity markdowns, redesign cover page with HTML template, and open via blob in new tab#36
milljoniaer merged 4 commits intomainfrom
copilot/update-learn-plans-pdf

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 20, 2026

Lesson plan PDFs were concatenating the raw uploaded SOURCE_PDF documents instead of the LLM-generated markdown content, the cover/TOC page was built with bare iText API calls producing a plain unstyled layout, and the download forced a file save instead of opening in the browser.

Cover page — HTML template

Replaces the programmatic iText summary page with a new lesson-plan-cover.html template rendered via HtmlConverter, matching the existing activity PDF design language (LEARN-Hub logo, #2f70b3 blue headings, styled tables with alternating rows).

Placeholders populated at render time: {{logoDataUri}}, {{downloadDate}}, {{totalDuration}}, {{activityCount}}, {{activitiesTableRows}}, {{searchCriteriaSection}}, {{breaksSection}}.

Activity PDFs — request-map markdowns first, with DB and SOURCE_PDF fallback

The frontend already sends full activity objects (including markdowns[].content and markdowns[].landscape) in the lesson plan request body. PDFService.getActivityPdfs() now uses these request-map markdowns as the primary source, rendering each section (Deckblatt → Artikulationsschema → Hintergrundwissen) via MarkdownToPdfService. Falls back to DB markdowns if the request map is empty, then to SOURCE_PDF for older activities.

// 1. Use markdown content from the request map (fastest – already in memory)
List<byte[]> markdownPdfs = generatePdfsFromRequestMarkdowns(activityMap, activityName);

// 2. If not in request, look them up in the DB
if (markdownPdfs.isEmpty()) { ... markdownPdfs = generatePdfsFromMarkdowns(dbActivity); }

// 3. Fallback: SOURCE_PDF (from request documents array or DB)

getLessonPlanInfo — checks request markdowns first

The availability check now inspects inline markdown content in the request map first, before performing DB lookups, and returns canGenerateLessonPlan = true for any activity that has markdowns (in request or DB) or a SOURCE_PDF.

Open in new tab via blob URL

LessonPlanModal.handleDownload() now uses window.URL.createObjectURL(blob) + window.open(url, "_blank"), matching the openBlobInNewTab pattern from ActivityDetails.tsx. The blob URL is revoked after 10 seconds to release memory. The lesson plan controller response disposition is changed from attachment to inline.

Other

  • MarkdownToHtmlService.getLogoDataUri() getter added so the cover page renderer can embed the logo without duplicating the loading logic.
  • All user-supplied strings are HTML-escaped before injection into the cover page template.

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

- Add lesson-plan-cover.html template with LEARN-Hub branding
- PDFService: inject MarkdownToHtmlService & MarkdownToPdfService
- PDFService: replace iText programmatic cover page with HTML template
- PDFService: generate activity PDFs from markdowns (deckblatt,
  artikulationsschema, hintergrundwissen); fallback to SOURCE_PDF
- PDFService: getLessonPlanInfo checks for markdowns first
- MarkdownToHtmlService: add getLogoDataUri() getter
- PDFServiceTest: add mocks and 2 new tests for updated info logic

Co-authored-by: milljoniaer <78978542+milljoniaer@users.noreply.github.com>
Copilot AI changed the title [WIP] Update learn plans PDF to concatenate activities and improve content table Lesson plan: generate PDFs from activity markdowns and redesign cover page with HTML template Mar 20, 2026
Copilot AI requested a review from milljoniaer March 20, 2026 12:03
@milljoniaer
Copy link
Copy Markdown
Collaborator

@copilot , so right now only the cover page of the lesson plan gets rendered, the concateneted merged pdfs are missing. also please dont download directly, but do it via this blob we also have in the activity download section

milljoniaer and others added 2 commits March 20, 2026 13:23
Backend:
- getActivityPdfs(): use request-map markdowns as primary source
  (frontend sends full activity objects incl. content + landscape),
  fall back to DB markdowns, then SOURCE_PDF
- hasMarkdownsOrSourcePdf(): check request-map markdowns first
- Lesson plan controller: disposition "attachment" → "inline"
- New test: requestMapHasMarkdowns path

Frontend:
- LessonPlanModal: replace <a download> with window.open blob URL
  to match ActivityDetails.tsx blob-in-new-tab pattern; revoke URL
  after 10 s to avoid memory leaks

Co-authored-by: milljoniaer <78978542+milljoniaer@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 20, 2026

@copilot , so right now only the cover page of the lesson plan gets rendered, the concateneted merged pdfs are missing. also please dont download directly, but do it via this blob we...

Fixed in commit 971b862.

Missing activity PDFs (root cause): getActivityPdfs() was only looking up markdowns from the DB. The frontend already sends the full activity objects (including markdowns[].content and markdowns[].landscape) in the request body. The new logic uses those request-map markdowns as the primary source, falls back to DB markdowns if the map is empty, then falls back to the SOURCE_PDF as a last resort. getLessonPlanInfo() now also checks request-map markdowns first.

Blob / open in new tab: LessonPlanModal.handleDownload() now uses window.URL.createObjectURL(blob) + window.open(url, "_blank"), matching the openBlobInNewTab pattern from ActivityDetails.tsx. The URL is revoked after 10 s to avoid memory leaks.

Disposition: Lesson plan controller changed from "attachment" to "inline" so the PDF opens directly in the browser.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • jfrog-prod-use1-shared-virginia-main.s3.amazonaws.com
    • Triggering command: /usr/lib/jvm/temurin-21-jdk-amd64/bin/java /usr/lib/jvm/temurin-21-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /home/REDACTED/.m2/wrapper/dists/apache-maven-3.9.11/a2d47e15/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/home/REDACTED/.m2/wrapper/dists/apache-maven-3.9.11/a2d47e15/bin/m2.conf -Dmaven.home=/home/REDACTED/.m2/wrapper/dists/apache-maven-3.9.11/a2d47e15 -Dlibrary.jansi.path=/home/REDACTED/.m2/wrapper/dists/apache-maven-3.9.11/a2d47e15/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/LEARN-Hub/LEARN-Hub/server org.codehaus.plexus.classworlds.launcher.Launcher clean compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java -Dfile.encoding=UTF-8 -Xms256M -Xss8M -Djdk.util.zip.disableZip64ExtraFieldValidation=true -Dsun.misc.unsafe.memory.access=allow --add-opens java.base/sun.reflect.annotation=ALL-UNNAMED -Xmx14580m -classpath /opt/hostedtoolcache/CodeQL/2.24.3/x64/codeql/java/tools/semmle-extractor-java.jar:/opt/hostedtoolcache/CodeQL/2.24.3/x64/codeql/java/tools/lombok-javac-extend.jar com.semmle.extractor.java.JavaExtractor --jdk-version 17 --javac-args @@@/tmp/codeql-scratch-21fe057196e5a270/dbs/java/log/ext/javac.args (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Lesson plan: generate PDFs from activity markdowns and redesign cover page with HTML template Lesson plan: generate PDFs from activity markdowns, redesign cover page with HTML template, and open via blob in new tab Mar 20, 2026
@milljoniaer milljoniaer marked this pull request as ready for review March 21, 2026 17:34
@milljoniaer milljoniaer merged commit 346a9db into main Mar 21, 2026
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