Skip to content

Commit

Permalink
Merge pull request #369 from Clinical-Genomics/exons_on_gene_overview
Browse files Browse the repository at this point in the history
Add exons stats to gene overview
  • Loading branch information
northwestwitch authored Oct 10, 2024
2 parents 786f508 + 84dd2f5 commit 6bbec18
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 43 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- A MANE coverage report, showing coverage and coverage completeness only on MANE transcripts for the provided list of genes
- Link out from MANE overview to gene overview
- Save ensembl_transcript_id and exon rank info on exons database records
- Display MANE badges on gene overview report
- `Create PDF` button on MANE overview and gene overview pages
### Changed
- Do not use stored cases/samples any more and run stats exclusively on d4 files paths provided by the user in real time
- How parameters are passed to starlette.templating since it was raising a deprecation warning.
Expand All @@ -18,6 +20,7 @@
- Computing coverage completeness stats using d4tools `perc_cov` stat function (much quicker reports)
- Moved functions computing the coverage stats to a separate `meta/handle_coverage_stats.py` module
- Refactored code collecting stats shown on gene overview report
- Gene report to contain both transcripts and exons stats
### Fixed
- Updated dependencies including `certifi` to address dependabot alert
- Update pytest to v.7.4.4 to address a `ReDoS` vulnerability
Expand Down
11 changes: 1 addition & 10 deletions src/chanjo2/endpoints/overview.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,7 @@ async def gene_overview(
)

return templates.TemplateResponse(
request=request,
name="gene-overview.html",
context={
"interval_type": gene_overview_content["interval_type"],
"gene": gene_overview_content.get("gene"),
"interval_coverage_stats": gene_overview_content.get(
"samples_coverage_stats_by_interval"
),
"levels": gene_overview_content["levels"],
},
request=request, name="gene-overview.html", context=gene_overview_content
)


Expand Down
40 changes: 37 additions & 3 deletions src/chanjo2/meta/handle_report_contents.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def get_gene_overview_coverage_stats(form_data: GeneReportForm, session: Session
threshold_levels=form_data.completeness_thresholds
),
"interval_type": form_data.interval_type.value,
"samples_coverage_stats_by_interval": {},
"transcript_coverage_stats": {},
}

set_samples_coverage_files(session=session, samples=form_data.samples)
Expand All @@ -217,20 +217,54 @@ def get_gene_overview_coverage_stats(form_data: GeneReportForm, session: Session
build=form_data.build, hgnc_id=form_data.hgnc_gene_id, db=session
)
if gene is None:
gene_stats["gene"] = {"hgnc_id": form_data.hgnc_gene_id}
return gene_stats

gene_stats["gene"] = gene
sql_intervals = set_sql_intervals(
transcripts_intervals = set_sql_intervals(
db=session,
interval_type=SQLTranscript,
genes=[gene],
transcript_tags=[],
)
gene_stats["samples_coverage_stats_by_interval"] = get_gene_overview_stats(
exons_intervals = set_sql_intervals(db=session, interval_type=SQLExon, genes=[gene])
sql_intervals = transcripts_intervals + exons_intervals

samples_coverage_by_interval = get_gene_overview_stats(
sql_intervals=sql_intervals,
samples=form_data.samples,
completeness_thresholds=form_data.completeness_thresholds,
)

for sql_interval in sql_intervals:
interval_length: int = abs(sql_interval.stop - sql_interval.start)
coords: str = (
f"{sql_interval.chromosome}:{sql_interval.start}-{sql_interval.stop}"
)

if type(sql_interval) == SQLTranscript:
gene_stats["transcript_coverage_stats"][sql_interval.ensembl_id] = {
"interval_type": "transcript",
"mane_select": sql_interval.refseq_mane_select,
"mane_plus_clinical": sql_interval.refseq_mane_plus_clinical,
"mrna": sql_interval.refseq_mrna,
"stats": samples_coverage_by_interval[sql_interval.ensembl_id],
"length": interval_length,
"coordinates": coords,
"exons": {},
}
continue

gene_stats["transcript_coverage_stats"][sql_interval.ensembl_transcript_id][
"exons"
][sql_interval.ensembl_id] = {
"interval_type": "exon",
"transcript_rank": int(sql_interval.rank_in_transcript),
"stats": samples_coverage_by_interval[sql_interval.ensembl_id],
"length": interval_length,
"coordinates": coords,
}

return gene_stats


Expand Down
130 changes: 100 additions & 30 deletions src/chanjo2/templates/gene-overview.html
Original file line number Diff line number Diff line change
@@ -1,41 +1,111 @@
{% extends "base-layout.html" %}

{% block css %}
{{ super() }}
<style>
.badge {
color: white;
padding: 4px 8px;
text-align: center;
border-radius: 5px;
float: right;
}

.exon_blue {
color: #2874a6;
}
</style>
{% endblock %}

{% block pdf_export %}
<div class="collapse navbar-collapse justify-content-end" id="bs-example-navbar-collapse-1">
<button class="navbar-brand" onclick="window.focus(); window.print();">Create PDF</button>
</div><!-- /.navbar-collapse -->
{% endblock %}


{% macro one_interval_table(interval_id, interval_features) %}
<tr>
<td class="row">
<div class="panel-default">
<div class="panel-heading {% if interval_features.interval_type == 'exon' %} exon_blue {% endif %}" >
{{ interval_features.interval_type|capitalize }} <strong>{{ interval_id }}</strong>
<span> - Coordinates: {{interval_features.coordinates}}</span>
<span> - size: {{interval_features.length}}</span>
{% if interval_features.mane_select %}
<span class="badge" style="background-color: black;">MANE Select: {{interval_features.mane_select}}</span>
{% endif %}
{% if interval_features.mane_plus_clinical %}
<span class="badge" style="background-color: black;">MANE Plus Clinical: {{interval_features.mane_plus_clinical}}</span>
{% endif %}
{% if interval_features.mrna %}
<span class="badge" style="background-color: gray;">{{interval_features.mrna}}</span>
{% endif %}
{% if interval_features.transcript_rank %}
<span class="badge" style="background-color: #2874a6">rank: {{interval_features.transcript_rank}}</span>
{% endif %}
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered {% if interval_features.interval_type == 'exon' %} exon_blue {% endif %}">
<thead>
<th>Sample</th>
<th>Mean coverage</th>
{% for level, _ in levels.items() %}
<th>Completeness {{ level }}x [%]</th>
{% endfor %}
</thead>
<tbody>
{% for sample_stats in interval_features.stats %}
<td>{{ sample_stats[0] }}</td>
<td>{{ sample_stats[1]|round(2) }}</td>
{% for level, _ in levels.items() %}
<td>{{ (sample_stats[2][level] * 100)|round(2) }}</td>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
</td>
</tr>
{% endmacro %}


{% macro gene_stats_macro() %}
<h2>Gene: {{ gene.hgnc_symbol or gene.hgnc_id }}</h2>
<br>
{% for interval_id, samples_stats in interval_coverage_stats.items() %}
<tr>
<td class="row">
<div class="panel-default">
<div class="panel-heading">
{{ interval_type }}: <strong>{{ interval_id }}</strong>
{% for transcript_id, transcript_features in transcript_coverage_stats.items() %}

{{ one_interval_table(transcript_id, transcript_features) }}

<!-- Show eventual exons stats -->
<div class="accordion" id="exons-accordion">
<div class="accordion-item">
<h2 class="accordion-header" id="flush-heading_{{transcript_id}}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapse_{{transcript_id}}" aria-expanded="false" aria-controls="flush-collapse_{{transcript_id}}">
Exons
</button>
</h2>
<div id="flush-collapse_{{transcript_id}}" class="accordion-collapse collapse" aria-labelledby="flush-heading_{{transcript_id}}">
<div class="accordion-body">
{% if transcript_features.exons %}
{% for exon_id, exon_stats in transcript_features.exons.items()|sort(attribute='1.transcript_rank') %}
{{ one_interval_table(exon_id, exon_stats) }}
{% endfor %}
{% else %}
No exons stats available for this transcript
{% endif %}

</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered">
<caption>Coverage overview over gene {{ gene.hgnc_symbol or gene.hgnc_id }} - {{interval_id}}</caption>
<thead>
<th>Sample</th>
<th>Mean coverage</th>
{% for level, _ in levels.items() %}
<th>Completeness {{ level }}x [%]</th>
{% endfor %}
</thead>
<tbody>
{% for sample_stats in samples_stats %}
<td>{{ sample_stats[0] }}</td>
<td>{{ sample_stats[1]|round(2) }}</td>
{% for level, _ in levels.items() %}
<td>{{ (sample_stats[2][level] * 100)|round(2) }}</td>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
</td>
</tr>
</div> <!--end of accordion item -->
</div> <!-- end of accordion div -->
<br>
<br>
{% else %}
No intervals found in database for gene {{gene.hgnc_symbol or gene.hgnc_id}}.
{% endfor %}

{% endmacro %}

{% block title %}
Expand Down
6 changes: 6 additions & 0 deletions src/chanjo2/templates/mane-overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
</style>
{% endblock %}

{% block pdf_export %}
<div class="collapse navbar-collapse justify-content-end" id="bs-example-navbar-collapse-1">
<button class="navbar-brand" onclick="window.focus(); window.print();">Create PDF</button>
</div><!-- /.navbar-collapse -->
{% endblock %}

{% macro report_filters() %}
<div class="accordion" id="filter-accordion">
<div class="accordion-item">
Expand Down

0 comments on commit 6bbec18

Please sign in to comment.