Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.2.0 #38

Merged
merged 41 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a84fe11
Add preferred transcripts parameter
rernst Jul 21, 2020
d691767
Add metadata.
rernst Sep 2, 2020
80f4151
Add sample_type to run().
rernst Sep 10, 2020
dabe313
Allow partial sample name to search samples
rernst Oct 2, 2020
88c3227
Fix sample upload.
rernst Jan 6, 2021
0461477
Add Minimal % 15x edit to panel forms
rernst Jan 6, 2021
dfd50ef
Add panel edit page (metadata) and update panel verion update page.
rernst Jan 6, 2021
68d871c
Show cov req when creating new panel.
rernst Jan 7, 2021
1fb0ddc
Add event logger for panel + panel_version models
rernst Feb 9, 2021
5426353
Fix eventlogger.
rernst Feb 9, 2021
0d837f7
Update sample_projects type
rernst Feb 9, 2021
9485995
Update clinical contacts field
rernst Feb 9, 2021
9ae1630
Create random sample set, sovles #22
rernst Feb 9, 2021
a6f64ec
Replace json with txt as column type
rernst Feb 9, 2021
31d04e1
Add core gene model and edit pages.
rernst Feb 19, 2021
e84b52b
Update QC colors, use panel cov req and core gene
rernst Feb 19, 2021
51e55e7
Don't cleanup transcripts or panel
rernst Feb 19, 2021
72e1c9a
Join core genes
rernst Feb 19, 2021
339969a
Merge multiple model imports.
rernst Feb 22, 2021
03ff344
Remove trailing space
rernst Feb 22, 2021
548a10c
Explain disabled LoadDesign function.
rernst Feb 22, 2021
a3cc2b3
Update based on review comments
rernst Feb 22, 2021
ea267b8
Merge pull request #36 from UMCUGenetics/metadata
rernst Feb 22, 2021
6426a80
Add min input length, Fix #28
rernst Feb 22, 2021
a83254c
Add Panel/Gene search to sampleset view, solve #24
rernst Feb 24, 2021
acd71bd
Update form label
rernst Feb 25, 2021
7430160
Update sampleset cli to skip merge samples.
rernst Mar 2, 2021
e913052
Open samplesets to all users.
rernst Mar 2, 2021
81da31a
Disable validate if panel_version is validated.
rernst Mar 5, 2021
1e4619a
Add supress_none jinja2 filter.
rernst Mar 5, 2021
7686877
Add info about setting up a new(dev) db.
rernst Mar 5, 2021
001d134
Fix validate update
rernst Mar 5, 2021
afff545
Fix typo.
rernst Mar 11, 2021
e571a2a
Consistent naming of PanelNewVersionForm
rernst Mar 11, 2021
bc9afdb
Consitent tabs
rernst Mar 11, 2021
4533429
Remove unused macro
rernst Mar 11, 2021
0abab20
Add new line at end of file
rernst Mar 11, 2021
5484c29
Update gene list description.
rernst Mar 11, 2021
e1097bd
Update Sample set label
rernst Mar 11, 2021
619f59e
Update code layout
rernst Mar 12, 2021
532a605
Remove duplicate space.
rernst Mar 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions ExonCov.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
manager.add_command('search_sample', cli.SearchSample())
manager.add_command('remove_sample', cli.RemoveSample())
manager.add_command('check_samples', cli.CheckSamples())
manager.add_command('create_sample_set', cli.CreateSampleSet())

db_manager.add_command('migrate', MigrateCommand)
db_manager.add_command('stats', cli.PrintStats())
db_manager.add_command('panel_genes', cli.PrintPanelGenesTable())
db_manager.add_command('transcript_gene', cli.PrintTranscripts())
db_manager.add_command('gene_transcripts', cli.PrintTranscripts())
db_manager.add_command('import_alias_table', cli.ImportAliasTable())
db_manager.add_command('export_panel_bed', cli.PrintPanelBed())
#db_manager.add_command('load_design', cli.LoadDesign())
# db_manager.add_command('load_design', cli.LoadDesign()) # Disabled, can be used to setup a new database from scratch

if __name__ == "__main__":
manager.run()
27 changes: 26 additions & 1 deletion ExonCov/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from flask_security import Security, SQLAlchemyUserDatastore
from flask_debugtoolbar import DebugToolbarExtension

from utils import url_for_other_page

from utils import url_for_other_page, event_logger

# Setup APP
app = Flask(__name__)
Expand Down Expand Up @@ -37,3 +38,27 @@ def security_context_processor():
h=flask_admin.helpers,
get_url=url_for
)

# DB event listeners
@db.event.listens_for(models.Panel, "after_insert")
@db.event.listens_for(models.PanelVersion, "after_insert")
def after_update(mapper, connection, target):
event_data = dict(target.__dict__)
event_logger(connection, models.EventLog, target.__class__.__name__, 'insert', event_data)


@db.event.listens_for(models.Panel, "after_update")
@db.event.listens_for(models.PanelVersion, "after_update")
def after_insert(mapper, connection, target):
event_data = dict(target.__dict__)
event_logger(connection, models.EventLog, target.__class__.__name__, 'update', event_data)


# Custom filters
@app.template_filter('supress_none')
def supress_none_filter(value):
"""Jinja2 filter to supress none/empty values."""
if not value:
return '-'
else:
return value
rernst marked this conversation as resolved.
Show resolved Hide resolved
76 changes: 52 additions & 24 deletions ExonCov/admin_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from flask_security import current_user

from . import db, admin
from .models import Exon, Transcript, Gene, Panel, PanelVersion, CustomPanel, Sample, SampleProject, SampleSet, SequencingRun, User, Role
import models


class CustomModelView(ModelView):
Expand Down Expand Up @@ -33,21 +33,31 @@ def _handle_view(self, name, **kwargs):

class PanelAdminView(CustomModelView):
"""Panel admin view."""
column_list = ['name', 'versions']
column_list = ['name', 'versions', 'disease_description_eng', 'clinic_contact', 'staff_member', 'comments']
column_searchable_list = ['name']

form_columns = ['name']
form_columns = [
'name', 'disease_description_nl', 'disease_description_eng', 'comments', 'patientfolder_alissa',
'clinic_contact', 'staff_member'
]


class PanelVersionAdminView(CustomModelView):
"""Panel version admin view."""
column_searchable_list = ['panel_name']

form_columns = ['panel', 'version_year', 'version_revision', 'comments', 'active', 'validated', 'transcripts']
form_columns = [
'panel', 'version_year', 'version_revision', 'comments', 'coverage_requirement_15', 'active', 'validated',
'transcripts', 'core_genes'
]
form_ajax_refs = {
'transcripts': {
'fields': ['name', 'gene_id'],
'page_size': 10
},
'core_genes': {
'fields': ['id'],
'page_size': 10
}
}

Expand All @@ -57,7 +67,10 @@ class CustomPanelAdminView(CustomModelView):
column_list = ['id', 'created_by', 'date', 'research_number', 'comments', 'validated', 'validated_by', 'validated_date']
column_searchable_list = ['id', 'comments', 'research_number']

form_columns = ['created_by', 'date', 'research_number', 'comments', 'validated', 'validated_by', 'validated_date', 'samples', 'transcripts']
form_columns = [
'created_by', 'date', 'research_number', 'comments', 'validated', 'validated_by', 'validated_date',
'samples', 'transcripts'
]

form_ajax_refs = {
'transcripts': {
Expand Down Expand Up @@ -121,11 +134,14 @@ class ExonAdminView(CustomModelView):

class SampleAdminView(CustomModelView):
"""Sample admin view."""
column_list = ['name', 'project', 'sequencing_runs', 'import_date']
column_list = ['name', 'type', 'project', 'sequencing_runs', 'import_date']
column_sortable_list = ['name', 'import_date']
column_searchable_list = ['name']

form_columns = ['name', 'project', 'sequencing_runs', 'import_date', 'file_name', 'import_command', 'exon_measurement_file']
form_columns = [
'name', 'type', 'project', 'sequencing_runs', 'import_date', 'file_name', 'import_command',
'exon_measurement_file'
]
form_ajax_refs = {
'project': {
'fields': ['name'],
Expand All @@ -140,10 +156,10 @@ class SampleAdminView(CustomModelView):

class SampleProjectAdminView(CustomModelView):
"""SequencingRun admin view."""
column_list = ['name']
column_searchable_list = ['name']
column_list = ['name', 'type']
column_searchable_list = ['name', 'type']

form_columns = ['name']
form_columns = ['name', 'type']


class SampleSetAdminView(CustomModelView):
Expand All @@ -167,29 +183,41 @@ class SequencingRunAdminView(CustomModelView):
form_columns = ['name', 'platform_unit']


class UserAdmin(CustomModelView):
class UserAdminView(CustomModelView):
"""User admin model."""

can_create = False

column_list = ('first_name', 'last_name', 'email', 'roles', 'active')
column_searchable_list = ['first_name', 'last_name', 'email']

form_columns = ('first_name', 'last_name', 'email', 'roles', 'active', 'password')


# Link view classes and models
admin.add_view(PanelAdminView(Panel, db.session))
admin.add_view(PanelVersionAdminView(PanelVersion, db.session))
admin.add_view(CustomPanelAdminView(CustomPanel, db.session))
class EventLogAdminView(CustomModelView):
"""EventLog admin model."""
column_filters = ['table', 'action', 'modified_on']
column_searchable_list = ['data']

admin.add_view(SampleAdminView(Sample, db.session))
admin.add_view(SampleProjectAdminView(SampleProject, db.session))
admin.add_view(SampleSetAdminView(SampleSet, db.session))
admin.add_view(SequencingRunAdminView(SequencingRun, db.session))
can_create = False
can_edit = False
can_view_details = True

admin.add_view(GeneAdminView(Gene, db.session))
admin.add_view(TranscriptAdminView(Transcript, db.session))
admin.add_view(ExonAdminView(Exon, db.session))

admin.add_view(UserAdmin(User, db.session))
admin.add_view(CustomModelView(Role, db.session))
# Link view classes and models
admin.add_view(PanelAdminView(models.Panel, db.session))
admin.add_view(PanelVersionAdminView(models.PanelVersion, db.session))
admin.add_view(CustomPanelAdminView(models.CustomPanel, db.session))

admin.add_view(SampleAdminView(models.Sample, db.session))
admin.add_view(SampleProjectAdminView(models.SampleProject, db.session))
admin.add_view(SampleSetAdminView(models.SampleSet, db.session))
admin.add_view(SequencingRunAdminView(models.SequencingRun, db.session))

admin.add_view(GeneAdminView(models.Gene, db.session))
admin.add_view(TranscriptAdminView(models.Transcript, db.session))
admin.add_view(ExonAdminView(models.Exon, db.session))

admin.add_view(UserAdminView(models.User, db.session))
admin.add_view(CustomModelView(models.Role, db.session))
admin.add_view(EventLogAdminView(models.EventLog, db.session))
109 changes: 96 additions & 13 deletions ExonCov/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@
import os
import shlex
import urllib
import datetime

from flask_script import Command, Option
from sqlalchemy import func
from sqlalchemy.orm import joinedload
from sqlalchemy.exc import IntegrityError
from sqlalchemy.sql.expression import func
import tempfile
import shutil
import pysam

from . import app, db, utils
from .models import Gene, GeneAlias, Transcript, Exon, SequencingRun, Sample, SampleProject, TranscriptMeasurement, Panel, PanelVersion, CustomPanel
from .models import (
Gene, GeneAlias, Transcript, Exon, SequencingRun, Sample, SampleProject, TranscriptMeasurement, Panel,
PanelVersion, CustomPanel, SampleSet
)
from .utils import weighted_average


Expand Down Expand Up @@ -65,25 +70,31 @@ def run(self):


class PrintTranscripts(Command):
"""Print tab delimited transcript / gene table"""
"""Print tab delimited gene / transcript table"""

def run(self):
print('{transcript}\t{gene}'.format(transcript='Transcript', gene='Gene'))
option_list = (
Option('-p', '--preferred_transcripts', dest='preferred_transcripts', default=False, action='store_true', help="Print preferred transcripts only"),
)

transcripts = Transcript.query.options(joinedload('gene'))
def run(self, preferred_transcripts):
print('{gene}\t{transcript}'.format(gene='Gene', transcript='Transcript'))

for transcript in transcripts:
print('{transcript}\t{gene}'.format(
transcript=transcript.name,
gene=transcript.gene
))
genes = Gene.query.options(joinedload('transcripts'))

for gene in genes:
if preferred_transcripts:
print('{gene}\t{transcript}'.format(gene=gene.id, transcript=gene.default_transcript.name))
else:
for transcript in gene.transcripts:
print('{gene}\t{transcript}'.format(gene=gene.id, transcript=transcript.name))


class ImportBam(Command):
"""Import sample from bam file."""

option_list = (
Option('project_name'),
Option('sample_type', choices=['WES', 'WGS', 'RNA']),
Option('bam'),
Option('-b', '--exon_bed', dest='exon_bed_file', default=app.config['EXON_BED_FILE']),
Option('-t', '--threads', dest='threads', default=1),
Expand All @@ -92,7 +103,7 @@ class ImportBam(Command):
Option('--temp', dest='temp_path', default=None),
)

def run(self, bam, project_name, exon_bed_file, threads, overwrite, print_output, temp_path):
def run(self, project_name, sample_type, bam, exon_bed_file, threads, overwrite, print_output, temp_path):
try:
bam_file = pysam.AlignmentFile(bam, "rb")
except IOError as e:
Expand Down Expand Up @@ -128,7 +139,8 @@ def run(self, bam, project_name, exon_bed_file, threads, overwrite, print_output
sample_project, sample_project_exists = utils.get_one_or_create(
db.session,
SampleProject,
name=project_name
name=project_name,
type=''
) # returns object and exists bool
FiniDG marked this conversation as resolved.
Show resolved Hide resolved

# Look for sample in database
Expand All @@ -153,6 +165,7 @@ def run(self, bam, project_name, exon_bed_file, threads, overwrite, print_output
sample = Sample(
name=sample_name,
project=sample_project,
type=sample_type,
file_name=bam,
import_command=sambamba_command,
sequencing_runs=sequencing_runs.values(),
Expand Down Expand Up @@ -307,7 +320,7 @@ class SearchSample(Command):
)

def run(self, sample_name):
samples = Sample.query.filter_by(name=sample_name).all()
samples = Sample.query.filter(Sample.name.like('%{0}%'.format(sample_name))).all()

print("Sample ID\tSample Name\tProject\tSequencing Runs\tCustom Panels")
for sample in samples:
Expand Down Expand Up @@ -357,6 +370,76 @@ def run(self):
print("No errors found.")


class CreateSampleSet(Command):
"""Create (random) sample set."""

option_list = (
Option('name'),
Option('-d', '--max_days', dest='max_days', type=int, default=180),
Option('-s', '--sample_filter', dest='sample_filter', default=''),
Option('-t', '--sample_type', dest='sample_type', default='WES'),
Option('-n', '--sample_number', dest='sample_number', type=int, default=100),
)

def run(self, name, max_days, sample_filter, sample_type, sample_number):
description = '{0} random {1} samples. Maximum age: {2} days. Sample name filter: {3}'.format(
sample_number, sample_type, max_days, sample_filter
)
filter_date = datetime.date.today() - datetime.timedelta(days=max_days)

samples = (
Sample.query
.filter(Sample.name.like('%{0}%'.format(sample_filter)))
.filter_by(type=sample_type)
.order_by(func.rand())
)
sample_count = 0

sample_set = SampleSet(
name=name,
description=description,
)

for sample in samples:
# Filter sampels: import date, 'special' project type (validation etc), Merge samples,
if (
sample.import_date > filter_date
and not sample.project.type
and len(sample.sequencing_runs) == 1
and sample.sequencing_runs[0].platform_unit in sample.project.name
):
sample_set.samples.append(sample)
sample_count += 1

if sample_count >= sample_number:
break

if len(sample_set.samples) != sample_number:
print 'Not enough samples found to create sample set, found {0} samples.'.format(len(sample_set.samples))
else:
print 'Creating new random sample set:'
print '\tName: {0}'.format(sample_set.name)
print '\tDescription: {0}'.format(sample_set.description)
print '\tSamples:'
for sample in sample_set.samples:
print '\t\t{0}\t{1}\t{2}\t{3}\t{4}'.format(
sample.name,
sample.type,
sample.project,
sample.sequencing_runs,
sample.import_date
)
FiniDG marked this conversation as resolved.
Show resolved Hide resolved

confirmation = ''
while confirmation not in ['y', 'n']:
confirmation = raw_input('Please check samples and press [Y/y] to continue or [N/n] to abort. ').lower()

if confirmation == 'y':
db.session.add(sample_set)
db.session.commit()
print 'Sample set created, make sure to activate it via the admin page.'


class ImportAliasTable(Command):
"""Import gene aliases from HGNC (ftp://ftp.ebi.ac.uk/pub/databases/genenames/new/tsv/hgnc_complete_set.txt)"""

Expand Down
Loading