Skip to content

Commit

Permalink
First version of samples reports generated in the backend
Browse files Browse the repository at this point in the history
First version of samples reports generated in the backend

First version of samples reports generated in the backend

First version of samples reports generated in the backend
  • Loading branch information
leandroradusky committed Aug 2, 2023
1 parent 4c45652 commit d0ff551
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 244 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ gem 'jbuilder', '~> 2.5'
gem 'view_components', git: 'https://github.com/manastech/rails-view_components.git', branch: 'master'
gem 'prawn'
gem 'prawn-svg'
gem 'prawn-table'

# Authentication
# gem 'bcrypt-ruby', '~> 3.1.2'
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@ GEM
css_parser (~> 1.6)
prawn (>= 0.11.1, < 3)
rexml (~> 3.2)
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
premailer (1.12.1)
addressable
css_parser (>= 1.6.0)
Expand Down Expand Up @@ -662,6 +664,7 @@ DEPENDENCIES
paranoia (< 2.5.0)
prawn
prawn-svg
prawn-table
premailer-rails (< 1.10)
pry-byebug (< 3.10.0)
pry-rescue
Expand Down
Binary file added app/assets/fonts/OpenSans/OpenSans-Bold.ttf
Binary file not shown.
Binary file added app/assets/fonts/OpenSans/OpenSans-Regular.ttf
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ var SamplesReportsBarChart = React.createClass({
x={x(d.label)+x.rangeBand()/2-1}
y={y(d[barVariable]) - error}
width={2}
height={error*2} />
height={error*2+1} />
<rect
x={x(d.label)}
y={y(d[barVariable]) - error - 1}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/_charts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
}

.errorbar{
fill: $gray2;
fill: #BBBBBB;
opacity: 1;
}

Expand Down
50 changes: 37 additions & 13 deletions app/controllers/samples_reports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class SamplesReportsController < ApplicationController
include SamplesReportsHelper

skip_before_action :verify_authenticity_token

helper_method :boxes_data
helper_method :available_institutions
helper_method :confusion_matrix
Expand Down Expand Up @@ -66,26 +68,48 @@ def create
end
end

def print
# convert json param to hash
json_params = JSON.parse(params[:json])
samples_report = SamplesReport.find(json_params["id"])

return unless authorize_resource(samples_report, READ_SAMPLES_REPORT)

purpose = samples_report.samples[0].box.purpose

options = {
:samples_report => samples_report,
:purpose => purpose
}

if purpose == "Challenge"
options[:threshold] = json_params["threshold"].to_f
options[:auc] = json_params["auc"].to_f
options[:threshold_tpr] = json_params["threshold_tpr"].to_f
options[:threshold_fpr] = json_params["threshold_fpr"].to_f
else
options[:threshold] = 0.0
end

options[:confusion_matrix] = confusion_matrix(samples_report.samples, options[:threshold])

options[:measured_signal_svg] = json_params["measured_signal_svg"]
options[:specific_svg] = json_params["specific_svg"]

send_data NihReport.new(options).render,
filename: "#{samples_report.name}.pdf",
type: "application/pdf",
disposition: "inline"
end

def show
@samples_report = SamplesReport.find(params[:id])
return unless authorize_resource(@samples_report, READ_SAMPLES_REPORT)
@reports_data = measured_signal_data(@samples_report)
@samples_without_results_count = @samples_report.samples.without_results.count
@purpose = @samples_report.samples[0].box.purpose

if params[:display] == "pdf"
gon.samples_report_id = @samples_report.id
gon.samples_report_name = @samples_report.name
gon.purpose = @purpose
gon.threshold = params[:threshold]
gon.min_threshold = params[:minthreshold]
gon.max_threshold = params[:maxthreshold]
render "_pdf_report", layout: false
else
@max_signal = @reports_data.reduce(0) { |a, e| e[:max] > a ? e[:max] : a }
@can_delete = has_access?(@samples_report, DELETE_SAMPLES_REPORT)
end
@max_signal = @reports_data.reduce(0) { |a, e| e[:max] > a ? e[:max] : a }
@can_delete = has_access?(@samples_report, DELETE_SAMPLES_REPORT)
end

def delete
Expand Down
189 changes: 189 additions & 0 deletions app/documents/nih_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
class NihReport < BasePdf
include SamplesReportsHelper

def initialize(options = {})
@samples_report = options[:samples_report]
@purpose = options[:purpose]
@confusion_matrix = options[:confusion_matrix]
@options = options
end

def document
@document ||= Prawn::Document.new(
top_margin: 30,
left_margin: 20,
right_margin: 20,
bottom_margin: 30,
page_size: [842, 595], # A4
print_scaling: :none
)
end

def setup
font_families.update("OpenSans" => {
normal: assets_path.join("fonts/OpenSans/OpenSans-Regular.ttf"),
bold: assets_path.join("fonts/OpenSans/OpenSans-Bold.ttf"),
})
font "OpenSans"
font_size 5.3
default_leading 0
end

def template
render_header
render_report_details
render_confussion_matrix
start_new_page
render_header
render_svg_plot(@options[:measured_signal_svg])
start_new_page
render_header
render_svg_plot(@options[:specific_svg])
end

def render
setup
template
document.render
end

protected

def render_header
bounding_box [bounds.left, bounds.top], width: bounds.right do
text "Report: #{@samples_report.name}", character_spacing: -0.07, size: 10, position: :right, color: "999999", indent_paragraphs: 600
move_down 3
text "Created at #{@samples_report.created_at.strftime(I18n.t('date.input_format.pattern'))}", character_spacing: -0.07, size: 10, position: :right, color: "999999", indent_paragraphs: 600
move_up 25
image assets_path.join("images/cdx-logo-bw.png"), width: 50, position: 540
move_down 30
end
end

def render_report_details

data = [
["<b>Purpose</b>", @purpose],
["<b>Samples</b>", "#{@samples_report.samples_report_samples.length} samples" + (@samples_report.samples.without_results.count > 0 ? "\n(#{@samples_report.samples.without_results.count} without results)" : "")],
]

if @purpose == "LOD"
data << ["<b>LOB</b>", @samples_report.lob&.round(3)]
data << ["<b>LOD</b>", @samples_report.lod&.round(3)]
elsif @purpose == "Challenge"
data << ["<b>Threshold</b>", @options[:threshold]&.round(3)]
data << ["<b>Computed ROC AUC</b>", @options[:auc]&.round(3)]
data << ["<b>Threshold's TPR</b>", @options[:threshold_tpr]&.round(3)]
data << ["<b>Threshold's FPR</b>", @options[:threshold_fpr]&.round(3)]
end

# transpose data
data = data.transpose

text "Summary", size: 15, style: :bold, indent_paragraphs: 60
move_down 10

table data, position: :center, width: 650 do
cells.style do |cell|
cell.border_width = 10
cell.border_color = "FFFFFF"
cell.padding = 5
cell.size = 10
cell.inline_format = true
cell.align = :center
end
end

move_down 30
end

def render_confussion_matrix
move_down 10
data = [
["", "PREDICTED\nNEGATIVE", "PREDICTED\n POSITIVE", "TOTAL"],
[
"ACTUAL\nNEGATIVE",
"<font size='18'>#{@confusion_matrix[:true_negative]}</font>\nTrue Negative",
"<font size='18'>#{@confusion_matrix[:false_positive]}</font>\nFalse Positive",
"<font size='18'>#{@confusion_matrix[:true_negative] + @confusion_matrix[:false_positive]}</font>\nActual Negative"
],
[
"ACTUAL\nPOSITIVE",
"<font size='18'>#{@confusion_matrix[:false_negative]}</font>\nFalse Negative",
"<font size='18'>#{@confusion_matrix[:true_positive]}</font>\nTrue Positive",
"<font size='18'>#{@confusion_matrix[:false_negative] + @confusion_matrix[:true_positive]}</font>\nActual Positive"
],
[
"TOTAL",
"<font size='18'>#{@confusion_matrix[:true_negative] + @confusion_matrix[:false_negative]}</font>\nPredicted Negative",
"<font size='18'>#{@confusion_matrix[:false_positive] + @confusion_matrix[:true_positive]}</font>\nPredicted Positive",
"<font size='18'>#{@confusion_matrix[:true_negative] + @confusion_matrix[:false_positive] + @confusion_matrix[:false_negative] + @confusion_matrix[:true_positive]}</font>\nTotal"
],
]

inside_colors = [
"FFFFFF", "FFFFFF", "FFFFFF", "FFFFFF",
"FFFFFF", "F0F0F0", "F0F0F0", "FFFFFF",
"FFFFFF", "F0F0F0", "F0F0F0", "FFFFFF",
"FFFFFF", "FFFFFF", "FFFFFF", "FFFFFF",
]
text_colors = [
"666666", "666666", "666666", "666666",
"666666", "000000", "000000", "666666",
"666666", "000000", "000000", "666666",
"666666", "666666", "666666", "666666",
]
text_rotate = [
0, 0, 0, 0,
90, 0, 0, 0,
90, 0, 0, 0,
90, 0, 0, 0,
]
cell_widths = [
70, 200, 200, 100,
70, 200, 200, 100,
70, 200, 200, 100,
70, 200, 200, 100,
]
cell_heights = [
40, 40, 40, 40,
70, 70, 70, 70,
70, 70, 70, 70,
50, 50, 50, 50,
]

text "Confusion Matrix", size: 15, style: :bold, indent_paragraphs: 60
move_down 30

i=0
table data, position: :center do
cells.style do |cell|
cell.border_width = 10
cell.border_color = "FFFFFF"
cell.background_color = inside_colors[i]
cell.padding_left = 5
cell.padding_right = 5
cell.padding_top = 2
cell.padding_bottom = 2
cell.size = 10
cell.align = :center
cell.valign = :center
cell.text_color = text_colors[i]
cell.rotate = text_rotate[i]
cell.rotate_around = :center
cell.height = cell_heights[i]
cell.width = cell_widths[i]
cell.inline_format = true
i+=1
end
end
@current_cursor = cursor

rounded_rectangle [0, @current_cursor + 50], 50, @current_cursor - cursor + 10, 10
end

def render_svg_plot(svg)
svg URI.unescape(svg), vposition: :center
end

end
Loading

0 comments on commit d0ff551

Please sign in to comment.