Skip to content

Commit f2dc243

Browse files
authored
Merge pull request #63 from VariantEffect/mappedVariant
Mapped variants
2 parents fdd9cf3 + 1455c25 commit f2dc243

File tree

7 files changed

+148
-0
lines changed

7 files changed

+148
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Add mapped_variants table
2+
3+
Revision ID: 33e99d4b90cc
4+
Revises: 988ca84c701b
5+
Create Date: 2023-05-15 17:24:07.847206
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '33e99d4b90cc'
14+
down_revision = 'da9ba478647d'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table('mapped_variants',
22+
sa.Column('id', sa.Integer(), nullable=False),
23+
sa.Column('pre_mapped', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
24+
sa.Column('post_mapped', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
25+
sa.Column('variant_id', sa.Integer(), nullable=False),
26+
sa.ForeignKeyConstraint(['variant_id'], ['variants.id'], ),
27+
sa.PrimaryKeyConstraint('id')
28+
)
29+
op.create_index(op.f('ix_mapped_variants_id'), 'mapped_variants', ['id'], unique=False)
30+
# ### end Alembic commands ###
31+
32+
33+
def downgrade():
34+
# ### commands auto generated by Alembic - please adjust! ###
35+
op.drop_index(op.f('ix_mapped_variants_id'), table_name='mapped_variants')
36+
op.drop_table('mapped_variants')
37+
# ### end Alembic commands ###

src/mavedb/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"genome_identifier",
99
"keyword",
1010
"license",
11+
"mapped_variant",
1112
"publication_identifier",
1213
"raw_read_identifier",
1314
"reference_genome",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from sqlalchemy import Column, ForeignKey, Integer
2+
from sqlalchemy.orm import relationship, backref
3+
4+
from mavedb.db.base import Base
5+
from mavedb.deps import JSONB
6+
7+
class MappedVariant(Base):
8+
__tablename__ = "mapped_variants"
9+
10+
id = Column(Integer, primary_key=True, index=True)
11+
12+
pre_mapped = Column(JSONB, nullable=False)
13+
post_mapped = Column(JSONB, nullable=False)
14+
15+
variant_id = Column(Integer, ForeignKey("variants.id"), nullable=False)
16+
variant = relationship("Variant", backref=backref("mapped_variants", cascade="all,delete-orphan"))
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from typing import Any, List, Optional
2+
3+
from fastapi import APIRouter, Depends
4+
from fastapi.exceptions import HTTPException
5+
from sqlalchemy.orm import Session
6+
from sqlalchemy.exc import MultipleResultsFound
7+
8+
from mavedb import deps
9+
10+
from mavedb.models.mapped_variant import MappedVariant
11+
from mavedb.models.variant import Variant
12+
from mavedb.view_models import mapped_variant
13+
14+
async def fetch_mapped_variant_by_variant_urn(db, urn: str) -> Optional[MappedVariant]:
15+
"""
16+
We may combine this function back to show_mapped_variant if none of any new function call it.
17+
Fetch one mapped variant by variant URN.
18+
19+
:param db: An active database session.
20+
:param urn: The variant URN.
21+
:return: The mapped variant, or None if the URL was not found or refers to a private score set not owned by the specified
22+
user.
23+
"""
24+
try:
25+
item = db.query(MappedVariant).filter(Variant.urn == urn).filter(MappedVariant.variant_id == Variant.id).one_or_none()
26+
except MultipleResultsFound:
27+
raise HTTPException(status_code=500, detail=f"Multiple variants with URN {urn} were found.")
28+
if not item:
29+
raise HTTPException(status_code=404, detail=f"Mapped variant with URN {urn} not found")
30+
return item
31+
32+
router = APIRouter(prefix="/api/v1/mapped-variants", tags=["mapped variants"], responses={404: {"description": "Not found"}})
33+
34+
@router.get("/{urn}", status_code=200, response_model=mapped_variant.MappedVariant, responses={404: {}, 500: {}})
35+
async def show_mapped_variant(
36+
*, urn: str, db: Session = Depends(deps.get_db)
37+
) -> Any:
38+
"""
39+
Fetch a mapped variant by URN.
40+
"""
41+
42+
return await fetch_mapped_variant_by_variant_urn(db, urn)

src/mavedb/routers/score_sets.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
from mavedb.models.enums.processing_state import ProcessingState
3636
from mavedb.models.experiment import Experiment
3737
from mavedb.models.license import License
38+
from mavedb.models.mapped_variant import MappedVariant
3839
from mavedb.models.reference_map import ReferenceMap
3940
from mavedb.models.score_set import ScoreSet
4041
from mavedb.models.target_gene import TargetGene
4142
from mavedb.models.user import User
4243
from mavedb.models.variant import Variant
4344
from mavedb.models.wild_type_sequence import WildTypeSequence
45+
from mavedb.view_models import mapped_variant
4446
from mavedb.view_models import score_set
4547
from mavedb.view_models.search import ScoreSetsSearch
4648

@@ -179,6 +181,25 @@ async def get_score_set_counts_csv(
179181
return StreamingResponse(iter([stream.getvalue()]), media_type="text/csv")
180182

181183

184+
@router.get(
185+
"/score-sets/{urn}/mapped-variants", status_code=200, response_model=list[mapped_variant.MappedVariant])
186+
def get_score_set_mapped_variants(
187+
*,
188+
urn: str,
189+
db: Session = Depends(deps.get_db),
190+
) -> Any:
191+
"""
192+
Return mapped variants from a score set, identified by URN.
193+
"""
194+
mapped_variants = (
195+
db.query(MappedVariant).filter(ScoreSet.urn == urn).filter(ScoreSet.id == Variant.score_set_id).filter(Variant.id == MappedVariant.variant_id).all()
196+
)
197+
198+
if not mapped_variants:
199+
raise HTTPException(status_code=404, detail=f"No mapped variant associated with score set URN {urn} was found")
200+
201+
return mapped_variants
202+
182203
class HGVSColumns:
183204
NUCLEOTIDE: str = "hgvs_nt" # dataset.constants.hgvs_nt_column
184205
TRANSCRIPT: str = "hgvs_splice" # dataset.constants.hgvs_splice_column

src/mavedb/server_main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
experiment_sets,
2525
experiments,
2626
licenses,
27+
mapped_variant,
2728
publication_identifiers,
2829
target_gene_identifiers,
2930
raw_read_identifiers,
@@ -57,6 +58,7 @@
5758
app.include_router(experiment_sets.router)
5859
app.include_router(experiments.router)
5960
app.include_router(licenses.router)
61+
app.include_router(mapped_variant.router)
6062
app.include_router(publication_identifiers.router)
6163
app.include_router(raw_read_identifiers.router)
6264
app.include_router(reference_genomes.router)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import Any
2+
3+
from .base.base import BaseModel
4+
5+
6+
class MappedVariantBase(BaseModel):
7+
pre_mapped: Any
8+
post_mapped: Any
9+
variant_id: int
10+
11+
12+
class MappedVariantCreate(MappedVariantBase):
13+
pass
14+
15+
16+
class MappedVariantUpdate(MappedVariantBase):
17+
pass
18+
19+
# Properties shared by models stored in DB
20+
class SavedMappedVariant(MappedVariantBase):
21+
id: int
22+
23+
class Config:
24+
orm_mode = True
25+
26+
27+
# Properties to return to non-admin clients
28+
class MappedVariant(SavedMappedVariant):
29+
pass

0 commit comments

Comments
 (0)