Skip to content

Commit

Permalink
Merge pull request #289 from pbashyal-nmdp/similar-endpoint
Browse files Browse the repository at this point in the history
Create `similar` endpoint
  • Loading branch information
mmaiers-nmdp authored Nov 20, 2023
2 parents b1a995d + 9da850c commit d43a035
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 51 deletions.
74 changes: 63 additions & 11 deletions api-spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ info:
servers:
- url: 'http://localhost:8080'
tags:
- name: Database
description: IPD-IMGT/HLA DB Information
- name: ARD Reduction
description: Reduce GL String to ARD
- name: CWD Reduction
description: Reduce GL String to CWD (version 2)
- name: MAC
description: Expand/Collapse MAC to/from alleles
- name: DRBX Blender
description: Blend DRBX based on DRB1 and DRB3/4/5
- name: Validation
description: Validate a GL String or Allele
- name: DRBX Blender
description: Blend DRBX based on DRB1 and DRB3/4/5
- name: Broad Splits
description: Broad Split Mappings
- name: Database
description: IPD-IMGT/HLA DB Information
paths:
/version:
get:
Expand Down Expand Up @@ -462,10 +466,58 @@ paths:
Broad/Split mapping not found for the given allele
content:
application/json:
schema:
type: object
properties:
message:
description: Mapping not found
type: string
example: "Broad/Split not found"
schema:
type: object
properties:
message:
description: Mapping not found
type: string
example: "Broad/Split not found"
/similar/{allele_prefix}:
get:
tags:
- Database
operationId: api.similar_controller
summary: Search For Similar Alleles and MACs
description: |
Given a prefix of an allele or MAC, find all the alleles
beginning with the prefix.
parameters:
- name: allele_prefix
in: path
description: |
Prefix of an Allele/MAC with a minimum of locus and a first field
required: true
schema:
type: string
example: A*01:9
responses:
200:
description: List of alleles with the given prefix
content:
application/json:
schema:
type: array
items:
example:
- A*01:91
- A*01:92
- A*01:93
- A*01:94
- A*01:95
- A*01:96
- A*01:97
- A*01:98
- A*01:99
404:
description: |
No matching alleles or MACs found
content:
application/json:
schema:
type: object
properties:
message:
description: No similar alleles
type: string
example: "No similar alleles"
9 changes: 9 additions & 0 deletions api.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,12 @@ def cwd_redux_controller():

# if no data is sent
return {"message": "No input data provided"}, 404


def similar_controller(allele_prefix: str):
if allele_prefix:
alleles = ard.similar_alleles(allele_prefix)
if alleles:
return alleles, 200
return {"message": "No similar alleles found."}, 400
return {"message": "No input data provided"}, 404
60 changes: 52 additions & 8 deletions pyard/ard.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,25 @@
import sqlite3
import sys
from collections import Counter
from typing import Iterable, List
from typing import Iterable, List, Union

from . import broad_splits, smart_sort
from . import data_repository as dr
from . import db
from .exceptions import InvalidAlleleError, InvalidMACError, InvalidTypingError
from .misc import (
get_n_field_allele,
get_2field_allele,
is_2_field_allele,
validate_reduction_type,
)
from .constants import (
HLA_regex,
VALID_REDUCTION_TYPES,
expression_chars,
DEFAULT_CACHE_SIZE,
G_GROUP_LOCI,
)
from .exceptions import InvalidAlleleError, InvalidMACError, InvalidTypingError
from .misc import (
get_n_field_allele,
get_2field_allele,
is_2_field_allele,
validate_reduction_type,
)

default_config = {
"reduce_serology": True,
Expand Down Expand Up @@ -851,3 +851,47 @@ def get_db_version(self) -> str:
@return:
"""
return dr.get_db_version(self.db_connection)

def similar_alleles(self, prefix: str) -> Union[List, None]:
"""
Given a prefix, find similar alleles or MACs starting with the prefix.
The minimum prefix needs to specify is the locus with a `*`,
and a first field of the allele/MAC.
@param prefix: The prefix for allele or MAC
@return: List of alleles/MACs that start with the prefix
"""

if "*" not in prefix: # Only for those that have locus
return None

locus, fields = prefix.split("*")
# if at least a field is specified after *
if fields:
# Will check only for and after 2 fields
if len(fields.split(":")) == 2:
first_field, mac_prefix = fields.split(":")
if mac_prefix.isalpha(): # Check for MACs
similar_mac_names = db.similar_mac(self.db_connection, mac_prefix)
if similar_mac_names:
locus_prefix = f"{locus}*{first_field}"
# Build all the mac codes with the prefix
mac_codes = [
f"{locus_prefix}:{code}" for code in similar_mac_names
]
# show only the valid macs
real_mac_codes = sorted(
filter(lambda mac: self.is_mac(mac), mac_codes)
)
return real_mac_codes

# find similar alleles
similar_allele_names = db.similar_alleles(self.db_connection, prefix)
if similar_allele_names:
alleles = sorted(
similar_allele_names,
key=functools.cmp_to_key(smart_sort.smart_sort_comparator),
)
return alleles

return None
2 changes: 1 addition & 1 deletion requirements-deploy.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
connexion[swagger-ui]==2.13.0
connexion[swagger-ui, flask]==2.14.2
gunicorn==20.1.0
38 changes: 7 additions & 31 deletions scripts/pyard
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,18 @@ from pyard.exceptions import InvalidAlleleError, InvalidTypingError, InvalidMACE
from pyard.misc import get_data_dir, get_imgt_version


def find_similar_alleles(ard, prefix):
if "*" in prefix: # Only for those that have locus
locus, fields = prefix.split("*")
if fields: # Only if at least a field is specified after *
if len(fields.split(":")) == 2: # Check for MACs
first_field, mac_prefix = fields.split(":")
if mac_prefix.isalpha():
similar_mac_names = similar_mac(ard.db_connection, mac_prefix)
if similar_mac_names:
locus_prefix = f"{locus}*{first_field}"
# TODO: validate all the mac codes with the prefix
# show only the valid macs
for code in sorted(similar_mac_names):
print(f"{locus_prefix}:{code}")
else:
# Nothing after *
sys.exit(2)
def find_similar_alleles(prefix):
alleles = ard.similar_alleles(prefix)
if alleles:
for allele in alleles:
print(allele)
sys.exit(0)
else:
# No *
sys.exit(1)

# find similar alleles
similar_allele_names = similar_alleles(ard.db_connection, prefix)
if similar_allele_names:
for allele in sorted(
similar_allele_names,
key=functools.cmp_to_key(smart_sort.smart_sort_comparator),
):
print(allele)
sys.exit(0)


def lookup_mac_codes():
global e
try:
mac = ard.lookup_mac(args.lookup_mac)
print(mac)
Expand All @@ -76,7 +54,6 @@ def lookup_mac_codes():


def expand_mac_code():
global allele_list, e
try:
allele_list = ard.expand_mac(args.expand_mac)
print(allele_list)
Expand All @@ -100,7 +77,6 @@ def show_version():


def perform_cwd_redux():
global cwd_redux
if args.validate:
ard.validate(args.cwd)
cwd_redux = ard.cwd_redux(args.cwd)
Expand Down Expand Up @@ -202,7 +178,7 @@ if __name__ == "__main__":

# Handle --similar option
if args.similar_allele:
find_similar_alleles(ard, args.similar_allele)
find_similar_alleles(args.similar_allele)

try:
if args.cwd:
Expand Down

0 comments on commit d43a035

Please sign in to comment.