Skip to content

Commit

Permalink
Merge pull request #625 from DFE-Digital/1980-spike-generate-pdf-cert…
Browse files Browse the repository at this point in the history
…ificates-in-aytq

[Spike] Generate PDF Certificates
  • Loading branch information
richardpattinson authored Jan 16, 2025
2 parents 58a9f6c + 216ba81 commit 5500e44
Show file tree
Hide file tree
Showing 33 changed files with 1,117 additions and 80 deletions.
13 changes: 12 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ RUN DATABASE_PASSWORD=required-to-run-but-not-used \
bundle exec rails assets:precompile

# Cleanup to save space in the production image
RUN rm -rf node_modules log/* tmp/* /tmp && \
RUN rm -rf log/* tmp/* /tmp && \
rm -rf /usr/local/bundle/cache && \
rm -rf .env && \
find /usr/local/bundle/gems -name "*.c" -delete && \
Expand Down Expand Up @@ -85,6 +85,17 @@ COPY --from=builder /usr/local/bundle/ /usr/local/bundle/
ARG COMMIT_SHA
ENV GIT_SHA=$COMMIT_SHA
ENV SHA=$GIT_SHA
# Set up puppeteer (for PDF generation)
# https://pptr.dev/troubleshooting#running-on-alpine
RUN apk add --no-cache \
chromium \
nss \
freetype \
harfbuzz \
ca-certificates \
ttf-freefont \
nodejs
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

CMD bundle exec rails db:migrate:ignore_concurrent_migration_exceptions && \

Check warning on line 100 in Dockerfile

View workflow job for this annotation

GitHub Actions / Image build and push

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/

Check warning on line 100 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-no-cache

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/

Check warning on line 100 in Dockerfile

View workflow job for this annotation

GitHub Actions / build-no-cache

JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals

JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals More info: https://docs.docker.com/go/dockerfile/rule/json-args-recommended/
bundle exec rails data:migrate:ignore_concurrent_migration_exceptions && \
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ gem "govuk_feature_flags",
git: "https://github.com/DFE-Digital/govuk_feature_flags.git",
tag: "v1.0.1"
gem "govuk_markdown"
gem "grover"
gem "hashie"
gem "jsbundling-rails"
gem "jwt"
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ GEM
cgi (0.4.1)
childprocess (5.0.0)
coderay (1.1.3)
combine_pdf (1.0.26)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.3.4)
connection_pool (2.4.1)
console1984 (0.2.1)
Expand Down Expand Up @@ -251,6 +254,9 @@ GEM
govuk_markdown (2.0.2)
activesupport
redcarpet
grover (1.1.7)
combine_pdf (~> 1.0)
nokogiri (~> 1.0)
haml (6.1.1)
temple (>= 0.8.2)
thor
Expand Down Expand Up @@ -533,6 +539,7 @@ GEM
rubocop-rspec (3.0.1)
rubocop (~> 1.61)
ruby-progressbar (1.13.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
securerandom (0.3.1)
semantic_logger (4.16.0)
Expand Down Expand Up @@ -674,6 +681,7 @@ DEPENDENCIES
govuk_design_system_formbuilder (~> 5.6)
govuk_feature_flags!
govuk_markdown
grover
hashie
jsbundling-rails
jwt
Expand Down
65 changes: 65 additions & 0 deletions app/assets/builds/certificates.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
body {
margin: 0;
padding: 0;
}

.container {
position: absolute;
top: 0px;
left: 0px;
margin: 0;
padding: 0;
width: 100%;
}

.content {
position: absolute;
top: 350px;
left: 100px;
width: 100%;
}

.header-image {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
}

.heading {
font-size: 82px;
font-family: Helvetica;
color: #063961;
}

.text {
font-size: 18px;
font-family: Helvetica;
color: #231f20;
}

.content-npq {
position: absolute;
top: 350px;
left: 85px;
width: 100%;
}

.text-npq {
font-size: 32px;
font-family: Arial;
color: #231f20;
}

.heading-npq {
font-size: 48px;
font-family: Arial;
color: #231f20;
}

.name-heading-npq {
max-width: 70%;
text-align: left;
font-size: clamp(16px, 4vw, 40px);
white-space: nowrap;
}
56 changes: 56 additions & 0 deletions app/assets/stylesheets/certificates.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
body {
margin: 0;
padding: 0;
}
.container {
position: absolute;
top: 0px;
left: 0px;
margin: 0;
padding: 0;
width: 100%;
}
.content {
position: absolute;
top: 350px;
left: 100px;
width: 100%;
}
.header-image {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
}
.heading {
font-size: 82px;
font-family: Helvetica;
color: #063961;
}
.text {
font-size: 18px;
font-family: Helvetica;
color: #231f20;
}
.content-npq {
position: absolute;
top: 350px;
left: 85px;
width: 100%;
}
.text-npq {
font-size: 32px;
font-family: Arial;
color: #231f20;
}
.heading-npq {
font-size: 48px;
font-family: Arial;
color: #231f20;
}
.name-heading-npq {
max-width: 70%;
text-align: left;
font-size: clamp(16px, 4vw, 40px);
white-space: nowrap;
}
9 changes: 3 additions & 6 deletions app/components/induction_summary_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class InductionSummaryComponent < ViewComponent::Base

attr_accessor :qualification

delegate :awarded_at, :certificate_type, :details, :name, to: :qualification
delegate :awarded_at, :type, :details, :name, to: :qualification

def detail_classes
"app__induction-details"
Expand Down Expand Up @@ -71,7 +71,7 @@ def rows
}
]

if details.respond_to?(:certificate_url) && details.certificate_url.present?
if details.status == "Pass"
@rows << {
key: {
text: "Certificate"
Expand All @@ -80,10 +80,7 @@ def rows
text:
link_to(
"Download Induction certificate",
qualifications_certificate_path(
certificate_type,
certificate_url: details.certificate_url
),
qualifications_certificate_path(:induction, format: :pdf),
class: "govuk-link"
)
}
Expand Down
5 changes: 1 addition & 4 deletions app/components/qualification_summary_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ def rows
text:
link_to(
"Download #{type.to_s.upcase} certificate",
qualifications_certificate_path(
certificate_type,
certificate_url: qualification.certificate_url
),
qualifications_certificate_path(type, format: :pdf),
class: "govuk-link"
)
}
Expand Down
61 changes: 47 additions & 14 deletions app/controllers/qualifications/certificates_controller.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
module Qualifications
class CertificatesController < QualificationsInterfaceController
layout "certificate"

def show
client =
QualificationsApi::Client.new(token: session[:identity_user_token])
certificate =
client.certificate(
name: current_user.name,
type: certificate_type,
url: params[:certificate_url]
)

if certificate
send_data certificate.file_data,
filename: certificate.file_name,
content_type: "application/pdf"
else
unless render_certificate?
redirect_to qualifications_dashboard_path,
alert: "Certificate not found"
return
end
Rails.logger.debug @qualification.certificate_type.downcase
html = render_to_string(template: "qualifications/certificates/_#{@qualification.certificate_type.downcase}",
formats: [:html],
locals: { teacher: @teacher, qualification: @qualification },
layout: "layouts/certificate")
grover = Grover.new(html, format: 'A4', display_url: ENV["HOSTING_DOMAIN"])
pdf = grover.to_pdf
send_data pdf, filename: "#{@teacher.name}_#{@qualification.type.downcase}_certificate.pdf",
type: 'application/pdf', disposition: 'attachment'
end

private

def client
token = if FeatureFlags::FeatureFlag.active?(:one_login)
:onelogin_user_token
else
:identity_user_token
end
@client ||= QualificationsApi::Client.new(token: session[token])
end

def teacher
@teacher ||= client.teacher
end

def qualification
@qualification ||= teacher.qualifications.find { |q| q.type == certificate_type.to_sym }
end

def render_certificate?
return if qualification.blank?

case qualification.type.to_sym
when :induction
teacher.passed_induction?
when :qts
teacher.qts_awarded?
when :eyts
teacher.eyts_awarded?
when :NPQEL,:NPQLTD,:NPQLT,:NPQH,:NPQML,:NPQLL,:NPQEYL,:NPQSL,:NPQLBC
teacher.npq_awarded?
else
qualification.awarded_at.present?
end
end

def certificate_type
if params[:id].to_sym.in? QualificationsApi::Certificate::VALID_TYPES
params[:id]
Expand Down
3 changes: 2 additions & 1 deletion app/lib/qualifications_api/certificate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module QualificationsApi
class Certificate
include ActiveModel::Model

VALID_TYPES = %i[eyts induction itt mq npq qts].freeze
VALID_TYPES = %i[eyts induction itt NPQEL NPQLTD NPQLT NPQH NPQML NPQLL NPQEYL NPQSL NPQLBC qts].freeze

attr_accessor :name, :type, :file_data

def file_name
Expand Down
4 changes: 4 additions & 0 deletions app/lib/qualifications_api/teacher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ def eyts_awarded?
api_data.eyts&.awarded.present?
end

def npq_awarded?
api_data.npq_qualifications.first&.awarded.present?
end

def eyps_awarded?
api_data.eyps&.awarded.present?
end
Expand Down
19 changes: 18 additions & 1 deletion app/models/qualification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ class Qualification
attr_accessor :awarded_at, :certificate_url, :name, :status_description, :type
attr_writer :details

FORMATTED_QUALIFICATION_TEXT = {
NPQEL: "National Professional <br/> Qualification for <br/> Executive Leadership",
NPQLTD: "National Professional <br/> Qualification for Leading <br/> Teacher Development",
NPQLT: "National Professional <br/> Qualification for Leading <br/> Teaching",
NPQH: "National Professional <br/> Qualification for <br/> Headship",
NPQML:"National Professional <br/> Qualification for Middle <br/> Leadership",
NPQLL: "National Professional <br/> Qualification for Leading <br/> Literacy",
NPQEYL: "National Professional <br/> Qualification for Early <br/> Years Leadership",
NPQSL: "National Professional <br/> Qualification for Senior <br/> Leadership",
NPQLBC: "National Professional <br/> Qualification for Leading <br/> Behaviour and Culture"
}.freeze

def certificate_type
return :npq if type&.downcase&.starts_with?("npq")

Expand Down Expand Up @@ -33,7 +45,7 @@ def mq?
end

def npq?
type&.downcase&.starts_with?("npq")
type&.starts_with?("NPQ")
end

def qts?
Expand All @@ -43,4 +55,9 @@ def qts?
def eyts?
type == :eyts
end

def formatted_qualification_name
FORMATTED_QUALIFICATION_TEXT[type].html_safe
end

end
17 changes: 17 additions & 0 deletions app/views/layouts/certificate.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Certificate</title>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

<%= stylesheet_link_tag :certificates %>

</head>
<body>
<div class="container">
<%# TODO: Certificate image per type %>
<%= yield %>
</div>
</body>
</html>
Loading

0 comments on commit 5500e44

Please sign in to comment.