diff --git a/api-spec.yaml b/api-spec.yaml index b10cfe2..ba15f85 100644 --- a/api-spec.yaml +++ b/api-spec.yaml @@ -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: @@ -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" diff --git a/api.py b/api.py index d93fc8d..c09d737 100644 --- a/api.py +++ b/api.py @@ -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 diff --git a/pyard/ard.py b/pyard/ard.py index 95ccac0..d83cd3f 100644 --- a/pyard/ard.py +++ b/pyard/ard.py @@ -26,18 +26,11 @@ 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, @@ -45,6 +38,13 @@ 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, @@ -837,3 +837,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 diff --git a/requirements-deploy.txt b/requirements-deploy.txt index 0e1ec03..0095330 100644 --- a/requirements-deploy.txt +++ b/requirements-deploy.txt @@ -1,2 +1,2 @@ -connexion[swagger-ui]==2.13.0 +connexion[swagger-ui, flask]==2.14.2 gunicorn==20.1.0 diff --git a/scripts/pyard b/scripts/pyard index 8f182d6..630d212 100755 --- a/scripts/pyard +++ b/scripts/pyard @@ -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) @@ -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) @@ -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) @@ -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: