Skip to content

Commit

Permalink
initial draft of civic panel endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
acoffman committed Jun 7, 2017
1 parent b6a44aa commit f6741b8
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 0 deletions.
16 changes: 16 additions & 0 deletions app/controllers/panels_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class PanelsController < ApplicationController
actions_without_auth :index, :show
def index
render json: PipelineType.pluck(:name)
end

def show
panel = CivicPanel.new(params[:minimum_score] || 30, params[:pipeline_tech])
@panel = panel.presenter_classname.new(panel.variants)
respond_to do |format|
format.tsv do
headers['Content-Disposition'] = "attachment; filename=\"civic_panel_export_#{panel.pipeline_type}_#{panel.score_cutoff}.tsv\""
end
end
end
end
28 changes: 28 additions & 0 deletions app/models/civic_panel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class CivicPanel
attr_reader :score_cutoff, :pipeline_type

def initialize(score_cutoff = 30, pipeline_type = '')
@score_cutoff = score_cutoff.to_i
@pipeline_type = pipeline_type.downcase
end

def variants
variants = PipelineType.includes(variant_types: {variants: [:evidence_items, :gene]})
.find_by('lower(pipeline_types.name) = ?', pipeline_type)
.variant_types
.flat_map(&:variants)
.reject { |v| v.chromosome.blank? || v.start.blank? }
.uniq

variants.reject do |v|
Actions::CalculateCivicScore.new(v).perform <= score_cutoff
end
end

def presenter_classname
{
'captureseq' => Panels::CaptureSeq,
'nanostring' => Panels::NanoString
}[pipeline_type]
end
end
91 changes: 91 additions & 0 deletions app/presenters/panels/capture_seq.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
module Panels
class CaptureSeq
attr_reader :variants, :already_tiled_genes, :genes_to_tile

def initialize(variants)
@variants = variants
@genes_to_tile = Set.new
@already_tiled_genes = Set.new
end

def partitioned_variants
variants.each { |v| genes_to_tile << v.gene if should_tile_gene?(v) }
rows = []
variants.each do |v|
tsv_variant(v).each do |row|
rows << row
end
end
rows.uniq
end

def headers
[
'gene',
'chr',
'start',
'stop',
'chr2',
'start2',
'stop2',
'gene2',
'variant_name',
'variant_types',
'tiling_whole_gene',
]
end

private
def tsv_variant(v)
if genes_to_tile.include?(v.gene) && !already_tiled_genes.include?(v.gene) && !v.chromosome2.present?
already_tiled_genes << v.gene
tiled_gene(v)
elsif !genes_to_tile.include?(v.gene)
[untiled_variant(v)]
else
[]
end
end

def untiled_variant(v)
[
v.gene.name,
v.chromosome,
v.start,
v.stop,
v.chromosome2 || '',
v.start2 || '',
v.stop2 || '',
v.representative_transcript2.present? ? Scrapers::EnsemblRest.get_gene_name_for_transcript(v.representative_transcript2) : '',
v.name,
v.variant_types.map(&:name).join(','),
false
]
end

def tiled_gene(v)
ensembl_gene_id = Scrapers::EnsemblRest.get_gene_id_for_transcript(v.representative_transcript)
exon_coordinates = Scrapers::EnsemblRest.get_exon_coordinates_for_gene_id(ensembl_gene_id)
variants_on_gene = variants.select { |x| x.gene_id == v.gene_id }
exon_coordinates.map do |coordinates|
[
v.gene.name,
v.chromosome,
coordinates['start'],
coordinates['end'],
'',
'',
'',
'',
variants_on_gene.map(&:name).join(','),
variants_on_gene.flat_map(&:variant_types).map(&:name).uniq.join(','),
true
]
end
end

def should_tile_gene?(v)
v.stop.to_i - v.start.to_i >= 1000 && v.chromosome2.blank?
end
end
end
91 changes: 91 additions & 0 deletions app/presenters/panels/nano_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
module Panels
class NanoString
attr_reader :variants, :already_tiled_genes, :genes_to_tile

def initialize(variants)
@variants = variants
@genes_to_tile = Set.new
@already_tiled_genes = Set.new
end

def partitioned_variants
variants.each { |v| genes_to_tile << v.gene if should_tile_gene?(v) }
rows = []
variants.each do |v|
tsv_variant(v).each do |row|
rows << row
end
end
rows.uniq
end

def headers
[
'gene',
'chr',
'start',
'stop',
'chr2',
'start2',
'stop2',
'gene2',
'variant_name',
'variant_types',
'tiling_whole_gene',
]
end

private
def tsv_variant(v)
if genes_to_tile.include?(v.gene) && !already_tiled_genes.include?(v.gene) && !v.chromosome2.present?
already_tiled_genes << v.gene
tiled_gene(v)
elsif !genes_to_tile.include?(v.gene)
[untiled_variant(v)]
else
[]
end
end

def untiled_variant(v)
[
v.gene.name,
v.chromosome,
v.start,
v.stop,
v.chromosome2 || '',
v.start2 || '',
v.stop2 || '',
v.representative_transcript2.present? ? Scrapers::EnsemblRest.get_gene_name_for_transcript(v.representative_transcript2) : '',
v.name,
v.variant_types.map(&:name).join(','),
false
]
end

def tiled_gene(v)
ensembl_gene_id = Scrapers::EnsemblRest.get_gene_id_for_transcript(v.representative_transcript)
exon_coordinates = Scrapers::EnsemblRest.get_exon_coordinates_for_gene_id(ensembl_gene_id)
variants_on_gene = variants.select { |x| x.gene_id == v.gene_id }
exon_coordinates.map do |coordinates|
[
v.gene.name,
v.chromosome,
coordinates['start'],
coordinates['end'],
'',
'',
'',
'',
variants_on_gene.map(&:name).join(','),
variants_on_gene.flat_map(&:variant_types).map(&:name).uniq.join(','),
true,
]
end
end

def should_tile_gene?(v)
v.stop.to_i - v.start.to_i >= 1000 && v.chromosome2.blank?
end
end
end
4 changes: 4 additions & 0 deletions app/views/panels/show.tsv.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<%= @panel.headers.join("\t") %>
<% @panel.partitioned_variants.each do |variant| %>
<%= variant.join("\t").html_safe %>
<% end %>
24 changes: 24 additions & 0 deletions lib/importer/panel_bucket_adaptor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Importer
class PanelBucketAdaptor
def self.valid_row?(row)
true
end

def self.create_entities_for_row(row)
variant_type = VariantType.find_by(soid: row['soid'])
if variant_type
existing_pipelines = variant_type.pipeline_types
pipeline_types.each do |type|
if row[type].present? && row[type].downcase == 'yes'
existing_pipelines << PipelineType.where(name: type).first_or_create
end
end
variant_type.pipeline_types = existing_pipelines.uniq
end
end

def self.pipeline_types
["CaptureSeq", "NanoString"]
end
end
end
7 changes: 7 additions & 0 deletions lib/importer/panel_buckets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Importer
class PanelBuckets
def self.import!(file = 'capture_buckets.tsv')
Importer::TsvReader.new(file, Importer::PanelBucketAdaptor).import!
end
end
end
57 changes: 57 additions & 0 deletions lib/scrapers/ensembl_rest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module Scrapers
class EnsemblRest
def self.get_gene_name_for_transcript(transcript)
resp = Util.make_get_request(url_from_transcript(transcript))
extract_gene_symbol_from_response(resp)
rescue
''
end

def self.get_gene_id_for_transcript(transcript)
resp = Util.make_get_request(url_from_transcript(transcript))
extract_gene_id_from_response(resp)
rescue
''
end

def self.get_exon_coordinates_for_gene_id(gene_id)
resp = Util.make_get_request(exon_url_from_gene_id(gene_id))
extract_exon_coordinates_from_response(resp)
end

def self.url_from_transcript(transcript)
"http://grch37.rest.ensembl.org/overlap/id/#{transcript}?feature=gene;content-type=application/json"
end

def self.exon_url_from_gene_id(gene_id)
"http://grch37.rest.ensembl.org/overlap/id/#{gene_id}.json?feature=exon"
end

def self.extract_exon_coordinates_from_response(text)
values = JSON.parse(text)
if values.any?
values.uniq { |v| v['exon_id'] }
else
[]
end
end

def self.extract_gene_symbol_from_response(text)
values = JSON.parse(text)
if values.any?
values.select { |x| x['feature_type'] == 'gene' }.first['external_name']
else
''
end
end

def self.extract_gene_id_from_response(text)
values = JSON.parse(text)
if values.any?
values.select { |x| x['feature_type'] == 'gene' }.first['id']
else
''
end
end
end
end

0 comments on commit f6741b8

Please sign in to comment.