diff --git a/README.md b/README.md index fedfc48..c5d559a 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ you will need to download the OASis database (22GB uncompressed). If you have [Docker](https://www.docker.com/products/docker-desktop), you can run a simplified BioPhi server using: ```bash -docker run TBD +docker run [TO DO] ``` ### 2b. Run simplified server using Conda @@ -49,8 +49,9 @@ conda install biophi biophi web ``` -**Note:** This is simplified usage for local use only. See Deploying your own BioPhi server *(TODO LINK)* to learn about -deploying BioPhi properly on a server. +**Note:** This is simplified usage for local use only. +See [Deploying your own BioPhi server](#deploying-your-own-biophi-server) section below +to learn about deploying BioPhi properly on a server. @@ -62,7 +63,26 @@ BioPhi also provides a command-line interface that enables bulk processing. See more ```bash -TBD +# Get humanized FASTA +# Expected input: Both chains of each antibody should have the same ID +# with an optional _VL/_VH or _HC/_LC suffix +biophi sapiens mabs.fa --fasta-only --output humanized.fa + +# Run full humanization & humanness evaluation pipeline +biophi sapiens mabs.fa \ + --oasis-db path/to/downloaded/OASis_9mers_v1.db \ + --output humanized/ + +# Get the Sapiens probability matrix (score of each residue at each position) +biophi sapiens mabs.fa --scores-only --output scores.csv + +# Get mean Sapiens score (one score for each sequence) +biophi sapiens mabs.fa --mean-score-only --output scores.csv + +# Get OASis humanness evaluation +biophi oasis mabs.fa \ + --oasis-db path/to/downloaded/OASis_9mers_v1.db \ + --output oasis.xlsx ``` diff --git a/biophi/common/web/static/bio.css b/biophi/common/web/static/bio.css index 6d1615f..feda534 100644 --- a/biophi/common/web/static/bio.css +++ b/biophi/common/web/static/bio.css @@ -460,7 +460,7 @@ td.vernier { z-index: 10; display: inline-block; position: absolute; - bottom: -0.80rem; + bottom: -0.95rem; left: 0px; } diff --git a/biophi/common/web/static/main.css b/biophi/common/web/static/main.css index 25f37bc..1384bb6 100644 --- a/biophi/common/web/static/main.css +++ b/biophi/common/web/static/main.css @@ -131,6 +131,12 @@ p.subheading { width: 280px; } +.tooltip-peptides .tooltip-inner { + max-width: 320px; + min-width: 280px; + font-size: 90%; +} + tr[data-href] { cursor: pointer; } diff --git a/biophi/common/web/templates/numbering_component.html b/biophi/common/web/templates/numbering_component.html index 577ad04..8c2fde9 100644 --- a/biophi/common/web/templates/numbering_component.html +++ b/biophi/common/web/templates/numbering_component.html @@ -14,10 +14,14 @@
- + {% if humanizing %} + + {% endif %} diff --git a/biophi/common/web/views.py b/biophi/common/web/views.py index 9838b07..c3686d0 100644 --- a/biophi/common/web/views.py +++ b/biophi/common/web/views.py @@ -122,8 +122,10 @@ def icon(name, s=16): @app.template_global() -def info_icon(text): - return Markup(f'{icon("info-circle-fill")}') +def info_icon(text, filled=False, secondary=True, delay=100, s=16): + name = 'info-circle-fill' if filled else 'info-circle' + return Markup(f'{icon(name, s=s)}') @app.template_global() diff --git a/biophi/humanization/cli/oasis.py b/biophi/humanization/cli/oasis.py index d56b01e..292e8e6 100644 --- a/biophi/humanization/cli/oasis.py +++ b/biophi/humanization/cli/oasis.py @@ -11,11 +11,11 @@ @click.command() @click.argument('inputs', required=True, nargs=-1) @click.option('--output', required=False, help='Output XLSX report file path') -@click.option('--oas-db', required=True, help='OAS peptide database connection string') +@click.option('--oasis-db', required=True, help='OAS peptide database connection string') @click.option('--scheme', default='kabat', help='Numbering scheme (kabat, chothia, imgt, aho)') @click.option('--cdr-definition', default='kabat', help='Numbering scheme (kabat, chothia, imgt, north)') @click.option('--min-percent-subjects', default=10, type=float, help='Minimum percent of OAS subjects to consider peptide human') -def oasis(inputs, output, oas_db, scheme, cdr_definition, min_percent_subjects): +def oasis(inputs, output, oasis_db, scheme, cdr_definition, min_percent_subjects): """OASis: Antibody humanness evaluation using 9-mer peptide search. OASis evaluates antibody humanness by searching all overlapping 9-mers @@ -25,8 +25,8 @@ def oasis(inputs, output, oas_db, scheme, cdr_definition, min_percent_subjects): \b # Evaluate humanness from FASTA file(s), save OASis humanness report to directory - biophi oasis input.fa --output ./report/ \\ - --oas-db sqlite:////Absolute/path/to/oas_human_subject_9mers_2019_11.db + biophi oasis input.fa --output ./report.xlsx \\ + --oasis-db sqlite:////Absolute/path/to/oas_human_subject_9mers_2019_11.db INPUTS: Input FASTA file path(s) """ @@ -35,12 +35,15 @@ def oasis(inputs, output, oas_db, scheme, cdr_definition, min_percent_subjects): | | | |/ _ \\\\___ \| / __| | |_| / ___ \___| | \__ \\ \___/_/ \_\___/|_|___/ - {}'''.format(f'version 1.0'.rjust(20))) + ''') assert 1 <= min_percent_subjects <= 90, '--min-percent-subjects should be between 1 and 90' + if not output.endswith('.xlsx'): + raise ValueError(f'The --output is a spreadsheet and should have an .xlsx extension') + click.echo(f'Settings:', err=True) - click.echo(f'- OAS database: {oas_db}', err=True) + click.echo(f'- OASis database: {oasis_db}', err=True) click.echo('', err=True) click.echo(f'Loading chains: {" ".join(inputs)}', err=True) @@ -66,7 +69,7 @@ def oasis(inputs, output, oas_db, scheme, cdr_definition, min_percent_subjects): show_unpaired_warning(antibody_inputs) oasis_params = OASisParams( - oasis_db_path=oas_db, + oasis_db_path=oasis_db, min_fraction_subjects=min_percent_subjects/100 ) pool = Pool() diff --git a/biophi/humanization/cli/sapiens.py b/biophi/humanization/cli/sapiens.py index 1509e45..94461da 100644 --- a/biophi/humanization/cli/sapiens.py +++ b/biophi/humanization/cli/sapiens.py @@ -23,14 +23,14 @@ @click.option('--fasta-only', is_flag=True, default=False, type=bool, help='Output only a FASTA file with humanized sequences (speeds up processing)') @click.option('--scores-only', is_flag=True, default=False, type=bool, help='Output only a CSV file with Sapiens position*residue scores') @click.option('--mean-score-only', is_flag=True, default=False, type=bool, help='Output only a CSV file with one Sapiens score per sequence') -@click.option('--oas-db', required=False, help='OAS peptide database connection string (required to run OASis)') +@click.option('--oasis-db', required=False, help='OAS peptide database connection string (required to run OASis)') @click.option('--version', default='latest', help='Sapiens trained model name') @click.option('--iterations', type=int, default=1, help='Run Sapiens given number of times to discover more humanizing mutations') @click.option('--scheme', default=HumanizationParams.cdr_definition, help=f'Numbering scheme: one of {", ".join(SUPPORTED_SCHEMES)}') @click.option('--cdr-definition', default=HumanizationParams.cdr_definition, help=f'CDR definition: one of {", ".join(SUPPORTED_CDR_DEFINITIONS)}') @click.option('--humanize-cdrs', is_flag=True, default=False, type=bool, help='Allow humanizing mutations in CDRs') @click.option('--limit', required=False, metavar='N', type=int, help='Process only first N records') -def sapiens(inputs, output, fasta_only, scores_only, mean_score_only, version, iterations, scheme, cdr_definition, humanize_cdrs, limit, oas_db): +def sapiens(inputs, output, fasta_only, scores_only, mean_score_only, version, iterations, scheme, cdr_definition, humanize_cdrs, limit, oasis_db): """Sapiens: Antibody humanization using deep learning. Sapiens is trained on 20 million natural antibody sequences @@ -50,7 +50,7 @@ def sapiens(inputs, output, fasta_only, scores_only, mean_score_only, version, i \b # Humanize FASTA file(s), save to directory along with OASis humanness report biophi sapiens input.fa --output ./report/ \\ - --oas-db sqlite:////Absolute/path/to/oas_human_subject_9mers_2019_11.db + --oasis-db sqlite:////Absolute/path/to/oas_human_subject_9mers_2019_11.db INPUTS: Input FASTA file path(s). If not provided, creates an interactive session. """ @@ -60,7 +60,7 @@ def sapiens(inputs, output, fasta_only, scores_only, mean_score_only, version, i \___ \ / _` | '_ \| |/ _ \ '_ \/ __| ___| | |_| | |_| | | __/ | | \__ \\ |____/ \__,_| __/|_|\___|_| |_|___/ - |_| ''') + |_| ''') click.echo(f'Settings:', err=True) click.echo(f'- Predicting using Sapiens model: {version}', err=True) @@ -84,9 +84,9 @@ def sapiens(inputs, output, fasta_only, scores_only, mean_score_only, version, i iterations=iterations ) oasis_params = OASisParams( - oasis_db_path=oas_db, + oasis_db_path=oasis_db, min_fraction_subjects=0.10 - ) if oas_db else None + ) if oasis_db else None if inputs: if scores_only or mean_score_only: @@ -210,7 +210,10 @@ def sapiens_fasta_only(inputs, output_fasta, humanization_params, limit=None): def sapiens_full(inputs, output_dir, humanization_params, oasis_params, limit=None): if oasis_params is None: - raise ValueError('OASis params need to be provided for full output, or consider using --fasta-only') + raise ValueError('Use --oasis-db PATH_TO_OASIS.db to get full output, or consider using --fasta-only') + if output_dir is None: + raise ValueError('Use --output mydir/ to specify the output directory') + if not os.path.exists(output_dir) or not os.path.isdir(output_dir): os.mkdir(output_dir) if len(os.listdir(output_dir)): diff --git a/biophi/humanization/methods/humanization.py b/biophi/humanization/methods/humanization.py index 314efe0..ff9f106 100644 --- a/biophi/humanization/methods/humanization.py +++ b/biophi/humanization/methods/humanization.py @@ -26,6 +26,7 @@ class SapiensHumanizationParams(HumanizationParams): method = 'sapiens' model_version: str = 'latest' humanize_cdrs: bool = False + backmutate_vernier: bool = False iterations: int = 1 def get_export_name(self): @@ -75,9 +76,14 @@ class HumanizedResidueAnnot: @dataclass class ChainHumanization: + # Chain object containing the parental sequence parental_chain: Chain + # Chain object containing the humanized sequence humanized_chain: Chain + # Scores predicted from last parental sequence, can be used to explain the prediction scores: Dict[Position, Dict[str, float]] + # Scores predicted from last humanized sequence, can be used to propose next mutations + next_scores: Dict[Position, Dict[str, float]] @cached_property def alignment(self) -> Alignment: @@ -90,11 +96,12 @@ def get_alignment_string(self): chain_label = 'VH' if self.parental_chain.is_heavy_chain() else 'VL' return f'{self.parental_chain.name} {chain_label}\n{self.alignment}' - def get_top_scores(self, n): + def get_top_scores(self, n, next=False): + scores = self.next_scores if next else self.scores top_scores = [] for i in range(n): top_n_scores = [] - for pos, aa_scores in self.scores.items(): + for pos, aa_scores in scores.items(): aa, score = sorted(aa_scores.items(), key=lambda d: -d[1])[i] top_n_scores.append((pos, aa, score)) top_scores.append(top_n_scores) @@ -181,7 +188,10 @@ def cdr_grafting_humanize_chain(parental_chain: Chain, params: CDRGraftingHumani # Compute Sapiens scores (used for Designer and final Sapiens pass if enabled) sapiens_humanization = sapiens_humanize_chain( humanized_chain, - params=SapiensHumanizationParams(iterations=params.sapiens_iterations) + params=SapiensHumanizationParams( + iterations=params.sapiens_iterations, + backmutate_vernier=params.backmutate_vernier + ) ) if params.sapiens_iterations: humanized_chain = sapiens_humanization.humanized_chain @@ -189,17 +199,20 @@ def cdr_grafting_humanize_chain(parental_chain: Chain, params: CDRGraftingHumani return ChainHumanization( parental_chain=parental_chain, humanized_chain=humanized_chain, - scores=sapiens_humanization.scores + scores=sapiens_humanization.scores, + next_scores=sapiens_humanization.next_scores ) def sapiens_humanize_chain(parental_chain: Chain, params: SapiensHumanizationParams) -> ChainHumanization: # Repeat Sapiens multiple times if requested, we start with the parental chain humanized_chain = parental_chain.clone() - # Get Sapiens scores as a positions (rows) by amino acids (columns) matrix - pred = sapiens_predict_chain(humanized_chain, model_version=params.model_version) + pred = None for iteration in range(params.iterations): + # Get Sapiens scores as a positions (rows) by amino acids (columns) matrix + pred = sapiens_predict_chain(humanized_chain, model_version=params.model_version) + # Create humanized sequence by taking the amino acid with highest score at each position humanized_seq = ''.join(pred.idxmax(axis=1).values) @@ -210,13 +223,24 @@ def sapiens_humanize_chain(parental_chain: Chain, params: SapiensHumanizationPar # Graft parental CDRs into the humanized sequence, unless humanizing CDRs as well if not params.humanize_cdrs: - humanized_chain = parental_chain.graft_cdrs_onto(humanized_chain) + humanized_chain = parental_chain.graft_cdrs_onto( + humanized_chain, + backmutate_vernier=params.backmutate_vernier + ) + else: + if params.backmutate_vernier: + raise ValueError('Cannot backmutate Vernier regions when humanizing CDRs') - # Get Sapiens scores as a positions (rows) by amino acids (columns) matrix - pred = sapiens_predict_chain(humanized_chain, model_version=params.model_version) + if pred is None: + # Support case with 0 iterations, still return probabilities + pred = sapiens_predict_chain(parental_chain, model_version=params.model_version) + + # Predict scores of potential next mutation from the final humanized sequence + pred_next = sapiens_predict_chain(humanized_chain, model_version=params.model_version) return ChainHumanization( parental_chain=parental_chain, humanized_chain=humanized_chain, - scores={pos: row.to_dict() for pos, (i, row) in zip(humanized_chain.positions, pred.iterrows())} + scores={pos: row.to_dict() for pos, (i, row) in zip(humanized_chain.positions, pred.iterrows())}, + next_scores={pos: row.to_dict() for pos, (i, row) in zip(humanized_chain.positions, pred_next.iterrows())}, ) \ No newline at end of file diff --git a/biophi/humanization/methods/humanness.py b/biophi/humanization/methods/humanness.py index 4cd6516..cc7e65b 100644 --- a/biophi/humanization/methods/humanness.py +++ b/biophi/humanization/methods/humanness.py @@ -65,6 +65,7 @@ class ChainHumanness: v_germline_names: List[str] j_germline_names: List[str] v_germline_family: str + v_germline_suffix: str germline_family_residue_frequency: Dict[Position, Dict[str, float]] chain_type_residue_frequency: Dict[Position, Dict[str, float]] @@ -128,18 +129,17 @@ def get_peptide_length(self) -> int: assert len(set(lengths)) == 1, f'Peptides should have same lengths, got: {set(lengths)}' return lengths[0] - def get_positional_humanness(self, min_fraction_subjects) -> List[Tuple[Position, str, int]]: + def get_positional_humanness(self, min_fraction_subjects) -> List[Tuple[Position, str, List[PeptideHumanness]]]: chain_positions = list(self.chain.positions) - chain_len = len(self.chain) peptide_len = self.get_peptide_length() assert peptide_len % 2 == 1, 'Peptide needs to have odd length' - half = int((peptide_len - 1) / 2) annots = [] for raw_pos, (pos, aa) in enumerate(self.chain): - window = [chain_positions[i] for i in range(max(0, raw_pos-half), min(chain_len, raw_pos+half+1))] - num_non_human = sum(not self.peptides[peptide_pos].is_human(min_fraction_subjects) - for peptide_pos in window if peptide_pos in self.peptides) - annots.append((pos, aa, num_non_human)) + window = [chain_positions[i] for i in range(max(0, raw_pos-peptide_len+1), raw_pos+1)] + peptides = [self.peptides[peptide_pos] + for peptide_pos in window if peptide_pos in self.peptides] + non_human_peptides = [p for p in peptides if not p.is_human(min_fraction_subjects)] + annots.append((pos, aa, non_human_peptides)) return annots def to_peptide_dataframe(self) -> pd.DataFrame: @@ -250,7 +250,7 @@ def get_germline_content(self): if self.vl: num_germline_residues += self.vl.num_germline_residues num_total_residues += len(self.vl.chain) - if num_total_residues is 0: + if num_total_residues == 0: return None return num_germline_residues / num_total_residues @@ -266,9 +266,8 @@ def chop_seq_peptides(seq: Union[SeqRecord, Chain], peptide_length): seq = ''.join(seq.positions.values()) else: raise ValueError(f'Unsupported sequence type: {type(seq)}') - left = int(peptide_length/2) - right = int(np.ceil(peptide_length/2)) - return [(positions[center], seq[center - left:center + right]) for center in range(left, len(seq) - right + 1)] + + return [(pos, seq[i:i + peptide_length]) for i, pos in enumerate(positions[:-peptide_length+1])] def get_antibody_humanness(vh: Optional[Chain], vl: Optional[Chain], params: OASisParams) -> AntibodyHumanness: @@ -289,6 +288,12 @@ def get_chain_oasis_peptides(chain, params: OASisParams): num_oas_occurrences=None ) for pos, peptide in pos_peptides} + if params.oasis_db_path.endswith('.gz'): + raise ValueError('The OASis DB file needs to be unzipped (use "gunzip DB_PATH.db.gz")') + + if not os.path.exists(params.oasis_db_path): + raise FileNotFoundError(f'The OASis DB path does not exist: {params.oasis_db_path}') + oas_engine = create_engine('sqlite:///' + os.path.abspath(params.oasis_db_path), echo=False) oas_filter_chain = "Heavy" if chain.is_heavy_chain() else "Light" @@ -323,6 +328,7 @@ def get_chain_humanness(chain: Chain, params: OASisParams) -> ChainHumanness: for pos, aa in imgt_chain) v_germline_family = top_v.name.split('-')[0].split('/')[0] + v_germline_suffix = top_v.name.replace(v_germline_family, '') return ChainHumanness( chain=chain, imgt_chain=imgt_chain, @@ -331,6 +337,7 @@ def get_chain_humanness(chain: Chain, params: OASisParams) -> ChainHumanness: j_germline_names=[chain.name for chain in j_germline_chains], peptides=peptides, v_germline_family=v_germline_family, + v_germline_suffix=v_germline_suffix, germline_family_residue_frequency=get_germline_family_residue_frequency(chain, imgt_chain, v_germline_family), chain_type_residue_frequency=get_chain_type_residue_frequency(chain, imgt_chain) ) diff --git a/biophi/humanization/web/static/humanization.css b/biophi/humanization/web/static/humanization.css index ca8e909..9e99215 100644 --- a/biophi/humanization/web/static/humanization.css +++ b/biophi/humanization/web/static/humanization.css @@ -88,11 +88,19 @@ position: relative; } +.designer-line a:not([href]) { + cursor: default; +} + +.designer-line a:not([href]) { + cursor: default; +} + .designer-line:not(.designer-parental-line) a:not(:hover):not(.aa-match) { background: white !important; } -.designer-line a:hover, .designer-line.designer-result-line span:hover, .designer-line .active { +.designer-line a[href]:hover, .designer-line.designer-result-line span:hover, .designer-line .active { margin: -2px -2px; font-size: 16px; line-height: 24px; diff --git a/biophi/humanization/web/templates/humanization/designer_detail.html b/biophi/humanization/web/templates/humanization/designer_detail.html index bdcf81c..b4cebd2 100644 --- a/biophi/humanization/web/templates/humanization/designer_detail.html +++ b/biophi/humanization/web/templates/humanization/designer_detail.html @@ -49,84 +49,112 @@

Antibody Designer {{ result.input.name

-
-
-
- - {{ '{:.0%}'.format(result.parental_humanness.get_oasis_percentile(MIN_SUBJ)) }} -    - {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(MIN_SUBJ)) }} - - Humanness percentile -
-
-
-
- - {{ '{:.0%}'.format(result.parental_humanness.get_oasis_identity(MIN_SUBJ)) - }} → {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ)) }} - - Human identity -
+ +
+
+
+
+ {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ)) }} + OASis identity + + ({{ '+{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ) - result.parental_humanness.get_oasis_identity(MIN_SUBJ)) }}) +  {{ info_icon('{}/{} peptides are considered human (are found in at least {:.0%} of human subjects)'.format(result.humanized_humanness.get_num_human_peptides(MIN_SUBJ), result.humanized_humanness.get_num_peptides(), MIN_SUBJ), s=22) }} + + +
+

+ This corresponds to the + + {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(MIN_SUBJ)) }} + + percentile {{ info_icon('Percentile of OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }} +

+
-
-
- - {{ '{:.0%}'.format(result.parental_humanness.get_germline_content()) - }} → {{ '{:.0%}'.format(result.humanized_humanness.get_germline_content()) }} - - Germline content -
+
+
+
+ {{ '{:.0%}'.format(result.humanized_humanness.get_germline_content()) }} + germline content + + ({{ '+{:.0%}'.format(result.humanized_humanness.get_germline_content() - result.parental_humanness.get_germline_content()) }}) + {{ info_icon('Sequence identity with nearest heavy and light human germline sequences', s=22) }} + +
+

+ {{ result.humanized_humanness.vh.v_germline_family }}{{ result.humanized_humanness.vh.v_germline_suffix }} + {% if result.humanized_humanness.vh and result.humanized_humanness.vl %}+{% endif %} + {{ result.humanized_humanness.vl.v_germline_family }}{{ result.humanized_humanness.vl.v_germline_suffix }} +

+
{% if result.humanized_humanness.vh %} -

Heavy chain

+

Heavy chain

{{ - chain_designer(result.parental_humanness.vh, result.humanized_humanness.vh, result.humanization.vh, 'vh') + chain_designer(result, result.parental_humanness.vh, result.humanized_humanness.vh, result.humanization.vh, 'vh') }} {% endif %} {% if result.humanized_humanness.vl %} -

Light chain

+

Light chain

{{ - chain_designer(result.parental_humanness.vl, result.humanized_humanness.vl, result.humanization.vl, 'vl') + chain_designer(result, result.parental_humanness.vl, result.humanized_humanness.vl, result.humanization.vl, 'vl') }} {% endif %} {% endblock %} -{% macro chain_designer(parental_humanness, humanized_humanness, humanization, id) %} +{% macro pos_tooltip(pos) %} +{{ pos.scheme.title() }} {{ pos }} +{% if pos.is_in_cdr() %} + ({{ pos.get_region() }}) +{% elif pos.cdr_definition == 'kabat' and pos.is_in_vernier() %} + (Vernier zone) +{% endif %} +{% endmacro %} + +{% macro mutation_tooltip(pos, current_aa, new_aa, backmutate=False) %} +{{ pos_tooltip(pos) }} +{% if current_aa != new_aa and new_aa != '-' %} + {% if backmutate %} +
{{ aa_name(new_aa) }} → {{ aa_name(current_aa) }} +
Click to revert mutation + {% else %} +
Click to mutate +
{{ aa_name(current_aa) }} → {{ aa_name(new_aa) }} + {% endif %} +{% else %} +
{{ aa_name(new_aa) }} +{% endif %} +{% endmacro %} + +{% macro chain_designer(result, parental_humanness, humanized_humanness, humanization, id) %}
- {% for pos, aa, num_non_human in parental_humanness.get_positional_humanness(MIN_SUBJ) - %}{% set is_vernier = pos.cdr_definition == 'kabat' and pos.is_in_vernier() + {% for pos, aa, non_human_peptides in parental_humanness.get_positional_humanness(MIN_SUBJ) + %}{% set num_non_human = (non_human_peptides | length) %}{% set peptide = parental_humanness.get_peptide(pos, edges=True) %}{% set freqs = parental_humanness.germline_family_residue_frequency.get(pos) - %}{{ aa }}{% endfor %} Parental
- {% for pos, aa, num_non_human in humanized_humanness.get_positional_humanness(MIN_SUBJ) - %}{% set is_vernier = pos.cdr_definition == 'kabat' and pos.is_in_vernier() + {% for pos, aa, non_human_peptides in humanized_humanness.get_positional_humanness(MIN_SUBJ) + %}{% set num_non_human = (non_human_peptides | length) %}{% set peptide = humanized_humanness.get_peptide(pos, edges=True) %}{% set freqs = humanized_humanness.germline_family_residue_frequency.get(pos) %}{{ aa }}{% endfor %} Result @@ -134,29 +162,38 @@

Light chain

- {% for scores in humanization.get_top_scores(5) %} + {% for scores in humanization.get_top_scores(5, next=True) %}
{% for pos, aa, score in scores %}{% if score > MIN_SAPIENS_SCORE %}{{aa}}{% else %}{% endif %}{% endfor %} @@ -171,13 +208,12 @@

Light chain

{% for pos, aa, freq in freqs %}{% if freq > MIN_FAMILY_FREQ %}{{ aa }}{% else %}{% endif %}{% + {% if humanized_humanness.chain[pos] == aa %}class="aa-match"{% endif %} + style="background: rgb({{255-freq**0.5*255}},{{255-freq**0.5*100}},{{255-freq**0.5*100}});">{{ aa }}{% else %}{% endif %}{% endfor %} #{{ loop.index }} at germline position
@@ -192,11 +228,10 @@

Light chain

{% for (imgt_pos, seq_aa), (pos, _) in zip(humanized_humanness.imgt_chain, humanized_humanness.chain) %}{% set germline_aa = v_germlines[i][imgt_pos] or j_germlines[i][imgt_pos] or '-' %}{{ germline_aa }}{% + style="background: #b3f7b3; {% if pos.is_in_cdr() %}opacity: 0.6;{% endif %}">{{ germline_aa }}{% endfor %} {{ v_germlines[i].name }}, {{ j_germlines[i].name }}
@@ -226,7 +261,7 @@

Light chain

elem.classList.remove('active') }) })) - var designerLinks = document.querySelectorAll('.designer-line a'); + var designerLinks = document.querySelectorAll('.designer-line a[href]'); designerLinks.forEach(target => target.addEventListener('click', function (event) { if(target.hasAttribute('href')) { designerLinks.forEach(function(elem) { diff --git a/biophi/humanization/web/templates/humanization/humanize_alignment_component.html b/biophi/humanization/web/templates/humanization/humanize_alignment_component.html index b78cfcb..391921c 100644 --- a/biophi/humanization/web/templates/humanization/humanize_alignment_component.html +++ b/biophi/humanization/web/templates/humanization/humanize_alignment_component.html @@ -17,41 +17,79 @@ {% macro format_percent_subjects(freq) %}{% if freq is none %}?{% -elif freq >= 0.01 or freq == 0 %}{{ '{:.0%}'.format(freq | round(2, 'floor')) }}{% +elif freq == 0 %}0{% +elif freq >= 0.01 %}{{ '{:.0%}'.format(freq | round(2, 'floor')) }}{% else %}{{ '{:.1%}'.format(freq | round(3, 'floor')) }}{% endif %}{% endmacro %} + +{% macro chain_humanness_header(chain_humanness, min_subj) %} + + {{ '{:.0%}'.format(chain_humanness.get_oasis_identity(min_subj)) }} OASis identity + + | + {{ '{:.0%}'.format(chain_humanness.get_oasis_percentile(min_subj)) }} + OASis percentile + | {{ '{:.0%}'.format(chain_humanness.get_germline_content()) }} germline content + +{% endmacro %} + +{% macro format_sequence_tooltip(v_germline_family, pos, aa, freqs, non_human_peptides, peptide) %} + + {% if freqs %} + {% if freqs.get(aa, 0) < RARE_FAMILY_FREQUENCY %} + {{ aa_name(aa) }} is a rare residue at {{ pos.scheme.title() }} {{ pos }}
+ ({{ format_aa_frequency(freqs.get(aa, 0)) }} frequency in repertoires of {{ v_germline_family }})

+ {% endif %} + {% else %} + Frequency for {{ v_germline_family }} not available

+ {% endif %} + + {% if non_human_peptides %} + Non-human peptides: + + {% for peptide in non_human_peptides %} +
{{ peptide.seq }} ({% + if peptide.fraction_oas_subjects is none %}?{% + elif peptide.fraction_oas_subjects == 0 %}NOT PRESENT{% + elif peptide.fraction_oas_subjects >= 0.01 %}{{ '{:.0%} of human subjects'.format(peptide.fraction_oas_subjects | round(2, 'floor')) }}{% + else %}{{ '{:.1%} of human subjects'.format(peptide.fraction_oas_subjects | round(3, 'floor')) }}{% + endif %}) + {% endfor %} +
+ {% endif %} + + {{ pos.scheme.title() }} {{ pos }} {% if pos.is_in_cdr() %}({{ pos.get_region() }}){% elif is_vernier %}(Vernier zone){% endif %} + +{% endmacro %} + {% macro annot_num(num, suffix_many, suffix_one) %}{{big_number_format(num, precision=0)}}{% if suffix_one and num == 1 %} {{suffix_one}}{% elif suffix_many %} {{suffix_many}}{% endif %}{% endmacro %} {% macro oasis_sequence_pair(parental_humanness, humanized_humanness, min_subj, url='', num_germlines=5) %}
- {% for pos, aa, num_non_human in parental_humanness.get_positional_humanness(min_subj) + {% for pos, aa, non_human_peptides in parental_humanness.get_positional_humanness(min_subj) %}{% set peptide = parental_humanness.get_peptide(pos, edges=True) + %}{% set num_non_human = (non_human_peptides | length) %}{% set freqs = parental_humanness.germline_family_residue_frequency.get(pos) %}{{ aa }}{% endfor %} Parental
- {% for pos, aa, num_non_human in humanized_humanness.get_positional_humanness(min_subj) + {% for pos, aa, non_human_peptides in humanized_humanness.get_positional_humanness(min_subj) %}{% set peptide = humanized_humanness.get_peptide(pos, edges=True) + %}{% set num_non_human = (non_human_peptides | length) %}{% set freqs = humanized_humanness.germline_family_residue_frequency.get(pos) %}{{ aa }}{% endfor %} Humanized diff --git a/biophi/humanization/web/templates/humanization/humanize_detail.html b/biophi/humanization/web/templates/humanization/humanize_detail.html index 95b5f19..61301cf 100644 --- a/biophi/humanization/web/templates/humanization/humanize_detail.html +++ b/biophi/humanization/web/templates/humanization/humanize_detail.html @@ -6,7 +6,7 @@ {% from 'humanization/humanize_alignment_component.html' - import oasis_sequence_pair, format_percent_subjects, format_aa_frequency_tooltip, format_aa_frequency, annot_num with context + import chain_humanness_header, oasis_sequence_pair, format_percent_subjects, format_aa_frequency_tooltip, format_aa_frequency, annot_num with context %} {% block main %} @@ -58,57 +58,64 @@

Humanization {{ result.input.name }}

-
+ +
-
- - {{ '{:.0%}'.format(result.parental_humanness.get_oasis_percentile(MIN_SUBJ)) }} -    - {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(MIN_SUBJ)) }} +
+ {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ)) }} + OASis identity + + ({{ '+{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ) - result.parental_humanness.get_oasis_identity(MIN_SUBJ)) }}) - OASis percentile
-

Percentile of "OASis identity" among therapeutic antibodies

-
-
-
-
-
- - {{ '{:.0%}'.format(result.parental_humanness.get_oasis_identity(MIN_SUBJ)) - }} → {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_identity(MIN_SUBJ)) }} +

+ {{ result.humanized_humanness.get_num_human_peptides(MIN_SUBJ) }}/{{ result.humanized_humanness.get_num_peptides() }} peptides are considered human +
(are found in at least {{ '{:.0%}'.format(MIN_SUBJ) }} of human subjects) +

+

+ This corresponds to the + + {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(MIN_SUBJ)) }} - OASis identity -

-

Fraction of 9-mer peptides found in at least {{ '{:.0%}'.format(MIN_SUBJ) }} of human subjects

+ percentile {{ info_icon('Percentile of OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }} +

-
- - {{ '{:.0%}'.format(result.parental_humanness.get_germline_content()) - }} → {{ '{:.0%}'.format(result.humanized_humanness.get_germline_content()) }} +
+ {{ '{:.0%}'.format(result.humanized_humanness.get_germline_content()) }} + germline content + + ({{ '+{:.0%}'.format(result.humanized_humanness.get_germline_content() - result.parental_humanness.get_germline_content()) }}) - Germline content
-

Sequence identity with nearest heavy and light human germline sequences.

+

Sequence identity with nearest heavy and light human germline sequences.

+

+ {{ result.humanized_humanness.vh.v_germline_family }}{{ result.humanized_humanness.vh.v_germline_suffix }} + {% if result.humanized_humanness.vh and result.humanized_humanness.vl %}+{% endif %} + {{ result.humanized_humanness.vl.v_germline_family }}{{ result.humanized_humanness.vl.v_germline_suffix }} +

{% if result.humanized_humanness.vh %} -

Heavy chain

+

+ Heavy chain + {{ chain_humanness_header(result.humanized_humanness.vh, MIN_SUBJ) }} +

{{ oasis_sequence_pair(result.parental_humanness.vh, result.humanized_humanness.vh, MIN_SUBJ) }} {% endif %} {% if result.humanized_humanness.vl %} -

Light chain

+

+ Light chain + {{ chain_humanness_header(result.humanized_humanness.vl, MIN_SUBJ) }} +

{{ oasis_sequence_pair(result.parental_humanness.vl, result.humanized_humanness.vl, MIN_SUBJ) }} @@ -126,24 +133,20 @@

Detailed view

{% if result.humanization.vh %} -

Heavy chain

+

Heavy chain table

{{ humanization_table(result.humanization.vh, result.parental_humanness.vh, result.humanized_humanness.vh) }} {% endif %} {% if result.humanization.vl %} -

Light chain

+

Light chain table

{{ humanization_table(result.humanization.vl, result.parental_humanness.vl, result.humanized_humanness.vl) }} {% endif %} {% endblock %} - - -{% macro format_peptide_aa(peptide, aa, loop_index) %}{% if loop_index == (((peptide | length) / 2) | round(0, 'ceil') | int) %}{{ aa }}{% else %}{{ aa }}{% endif %}{% endmacro %} - {% macro format_peptide(peptide, other_peptide) %} -{% for a, b in zip(peptide, other_peptide) %}{% if a != b %}{{ format_peptide_aa(peptide, a, loop.index) }}{% else %}{{format_peptide_aa(peptide, a, loop.index) }}{% endif %}{% endfor %} +{% for a, b in zip(peptide, other_peptide) %}{% if a != b %}{{ a }}{% else %}{{ a }}{% endif %}{% endfor %} {% endmacro %} @@ -197,31 +200,31 @@

Light chain

- + - + - + {% if chain_humanization.scores %} - + {% endif %} - + - - + + {% for position, (a, b) in chain_humanization.alignment %} {% set scores = chain_humanization.scores.get(position) %} - {% set freqs = parental_humanness.germline_family_residue_frequency.get(position) %} + {% set freqs = humanized_humanness.germline_family_residue_frequency.get(position) %} {% endif %} {% endif %} diff --git a/biophi/humanization/web/templates/humanization/humanize_input.html b/biophi/humanization/web/templates/humanization/humanize_input.html index 78c20bb..da8c09f 100644 --- a/biophi/humanization/web/templates/humanization/humanize_input.html +++ b/biophi/humanization/web/templates/humanization/humanize_input.html @@ -20,7 +20,9 @@

1Inp

2Humanization settings

- {% include 'numbering_component.html' %} + {% with humanizing=1 %} + {% include 'numbering_component.html' %} + {% endwith %} {% include 'humanization/humanize_settings_component.html' %}
diff --git a/biophi/humanization/web/templates/humanization/humanize_params_summary.html b/biophi/humanization/web/templates/humanization/humanize_params_summary.html index 2b7aa2c..bb20282 100644 --- a/biophi/humanization/web/templates/humanization/humanize_params_summary.html +++ b/biophi/humanization/web/templates/humanization/humanize_params_summary.html @@ -6,18 +6,7 @@ {% if result.humanization_params.humanize_cdrs %} | Humanizing CDRs ({{ result.humanization_params.cdr_definition }}) {% else %} - | Keeping parental CDRs ({{ result.humanization_params.cdr_definition }}) - {% endif %} - {% if not results %} - | - {% if result.humanized_humanness.vh %} - VH Germline: auto - ({{ result.humanized_humanness.vh.v_germline_names[0] }}){% if result.humanized_humanness.vl %},{% endif %} - {% endif %} - {% if result.humanized_humanness.vl %} - VL Germline: auto - ({{ result.humanized_humanness.vl.v_germline_names[0] }}) - {% endif %} + | Keeping parental CDRs ({{ result.humanization_params.cdr_definition }}{% if result.humanization_params.backmutate_vernier %} + vernier{% endif %}) {% endif %} {% elif result.humanization_params.method == 'cdr_grafting' %} {% if result.humanization_params.backmutate_vernier %} @@ -30,16 +19,10 @@ {% endif %} | {% if result.humanized_humanness.vh %} - VH Germline: {{ result.humanization_params.heavy_v_germline }}{% - if not results %} - ({{ result.humanized_humanness.vh.v_germline_names[0] }}){% - endif %}{% if result.humanized_humanness.vl %},{% endif %} + VH Germline: {{ result.humanization_params.heavy_v_germline }}{% if result.humanized_humanness.vl %},{% endif %} {% endif %} {% if result.humanized_humanness.vl %} VL Germline: {{ result.humanization_params.light_v_germline }} - {% if not results %} - ({{ result.humanized_humanness.vl.v_germline_names[0] }}) - {% endif %} {% endif %} {% endif %} diff --git a/biophi/humanization/web/templates/humanization/humanize_results.html b/biophi/humanization/web/templates/humanization/humanize_results.html index ff59be0..571c948 100644 --- a/biophi/humanization/web/templates/humanization/humanize_results.html +++ b/biophi/humanization/web/templates/humanization/humanize_results.html @@ -94,11 +94,11 @@
Light chain

- - - - - + + + + + @@ -127,10 +127,10 @@
Light chain
{% if isinstance(result, Exception) %} {% else %} - - + + {% if result.humanized_humanness.vh %} diff --git a/biophi/humanization/web/templates/humanization/humanize_settings_component.html b/biophi/humanization/web/templates/humanization/humanize_settings_component.html index 07ef02b..0db47a0 100644 --- a/biophi/humanization/web/templates/humanization/humanize_settings_component.html +++ b/biophi/humanization/web/templates/humanization/humanize_settings_component.html @@ -18,12 +18,7 @@

Sapiens is a high-throughput humanization method based on deep learning on antibody repertoires of more than 500 human subjects from the Observed Antibody Space database.

- - Advanced settings {{ icon('sliders') }} - - -
-
+
-

Graft the CDR regions into the nearest human V and J gene sequences based on % sequence identity.

- - Advanced settings {{ icon('sliders') }} - - -
-
-
- - -
-
- - -
+
+
+ +
-
-
- - -
+
+ +
-
-
- - -
+
+
+
+ +
diff --git a/biophi/humanization/web/templates/humanization/humanness_report_detail.html b/biophi/humanization/web/templates/humanization/humanness_report_detail.html index 03465ae..6571bbb 100644 --- a/biophi/humanization/web/templates/humanization/humanness_report_detail.html +++ b/biophi/humanization/web/templates/humanization/humanness_report_detail.html @@ -6,7 +6,7 @@ {% from 'humanization/humanize_alignment_component.html' - import format_percent_subjects, format_aa_frequency_tooltip, format_aa_frequency, annot_num with context + import chain_humanness_header, format_sequence_tooltip, format_percent_subjects, format_aa_frequency_tooltip, format_aa_frequency, annot_num with context %} {% block main %} @@ -49,48 +49,46 @@

Humanness report {{ result.input.name }}

-
+ +
-
- - {{ '{:.0%}'.format(result.humanness.get_oasis_percentile(MIN_SUBJ)) }} - - OASis percentile +
+ {{ '{:.0%}'.format(result.humanness.get_oasis_identity(MIN_SUBJ)) }} OASis identity
-

Percentile of "OASis identity" among therapeutic antibodies

-
-
-
-
-
- - {{ '{:.0%}'.format(result.humanness.get_oasis_identity(MIN_SUBJ)) }} +

+ {{ result.humanness.get_num_human_peptides(MIN_SUBJ) }}/{{ result.humanness.get_num_peptides() }} peptides are considered human +
(are found in at least {{ '{:.0%}'.format(MIN_SUBJ) }} of human subjects) +

+

+ This corresponds to the + + {{ '{:.0%}'.format(result.humanness.get_oasis_percentile(MIN_SUBJ)) }} - OASis identity -

-

Fraction of 9-mer peptides found in at least {{ '{:.0%}'.format(MIN_SUBJ) }} of human subjects

+ percentile {{ info_icon('Percentile of OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }} +

-
- - {{ '{:.0%}'.format(result.humanness.get_germline_content()) }} - - Germline content +
+ {{ '{:.0%}'.format(result.humanness.get_germline_content()) }} + germline content
-

Sequence identity with nearest heavy and light human germline sequences.

+

Sequence identity with nearest heavy and light human germline sequences.

+

+ {{ result.humanness.vh.v_germline_family }}{{ result.humanness.vh.v_germline_suffix }} + {% if result.humanness.vh and result.humanness.vl %}+{% endif %} + {{ result.humanness.vl.v_germline_family }}{{ result.humanness.vl.v_germline_suffix }} +

{% if result.humanness.vh %} -

+

Heavy chain - - {{ '{:.0%}'.format(result.humanness.vh.get_oasis_percentile(MIN_SUBJ)) }} percentile - + {{ chain_humanness_header(result.humanness.vh, MIN_SUBJ) }}

{{ oasis_sequence(result.humanness.vh) @@ -98,11 +96,9 @@

{% endif %} {% if result.humanness.vl %} -

+

Light chain - - {{ '{:.0%}'.format(result.humanness.vl.get_oasis_percentile(MIN_SUBJ)) }} percentile - + {{ chain_humanness_header(result.humanness.vl, MIN_SUBJ) }}

{{ oasis_sequence(result.humanness.vl) @@ -110,12 +106,12 @@

{% endif %} {% if result.humanness.vh %} -

Heavy chain

+

Heavy chain table

{{ oasis_table(result.humanness.vh) }} {% endif %} {% if result.humanness.vl %} -

Light chain

+

Light chain table

{{ oasis_table(result.humanness.vl) }} {% endif %} @@ -125,15 +121,13 @@

Light chain

{% macro oasis_sequence(chain_humanness) %}
- {% for pos, aa, num_non_human in chain_humanness.get_positional_humanness(MIN_SUBJ) + {% for pos, aa, non_human_peptides in chain_humanness.get_positional_humanness(MIN_SUBJ) %}{% set peptide = chain_humanness.get_peptide(pos, edges=True) + %}{% set num_non_human = (non_human_peptides | length) %}{% set freqs = chain_humanness.germline_family_residue_frequency.get(pos) %}{{ aa }}{% endfor %} @@ -155,11 +149,11 @@

Light chain

{% endmacro %} {% macro oasis_table(chain_humanness) %} -
Sequence Confidence9-mersPeptides Observed Antibody Space
RegionRegion {{ chain_humanization.parental_chain.scheme.title() }} Orig MutScoreSapiens
Score {{ info_icon('Confidence score predicted by Sapiens deep learning method. Corresponds to probability of seeing given residue in the context of the given sequence in human repertoires from the Observed Antibody Space database.') }}
FrequencyResidue
Freq {{ info_icon('Residue frequency in {} germline family at given {} position in human repertoires from the Observed Antibody Space database.'.format(humanized_humanness.v_germline_family, humanized_humanness.chain.scheme.title())) }}
Parental HumanizedSubjects9-mer hitsOAS
Subjects {{ info_icon('Percentage of human subjects from the Observed Antibody Space database containing this peptide in their repertoire.') }}
OAS
Sequences {{ info_icon('Number of total antibody sequences containing this peptide across across all human subjects in the Observed Antibody Space database.') }}
{{ position.get_region() | upper }}{% if position.cdr_definition == 'kabat' and position.is_in_vernier() %} Vernier{% endif %} @@ -235,7 +238,7 @@

Light chain

- {{ '{:.0f}'.format(scores[b]*100) if scores and b in scores else '?' }} + {{ '{:.0%}'.format(scores[b]) if scores and b in scores else '?' }} Light chain {% if chain_humanization.scores %} {{ - '{:.0f}'.format(scores[a]*100) if scores and a in scores else '?' + '{:.0%}'.format(scores[a]) if scores and a in scores else '?' }} → {{ - '{:.0f}'.format(scores[b]*100) if scores and b in scores else '?' + '{:.0%}'.format(scores[b]) if scores and b in scores else '?' }}
OASis PercentileOASis IdentityGermline identityGermlinesHumanizing mutationsOASis identity {{ info_icon('Fraction of 9-mer peptides considered human based on given prevalence threshold') }}OASis percentile {{ info_icon('Percentile of OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }}Germline content {{ info_icon('Sequence identity with nearest heavy and light human germline sequences') }}Germlines {{ info_icon('Nearest human germline genes based on sequence similarity.') }}Humanizing mutations {{ info_icon('Number of humanizing mutations made to the parental sequence.') }}
Failed: {{ result }}{{ '{:.0%}'.format(result.parental_humanness.get_oasis_percentile(min_subj)) }}{{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(min_subj)) }} {{ '{:.0%}'.format(result.parental_humanness.get_oasis_identity(min_subj)) }} {{ '{:.0%}'.format(result.humanized_humanness.get_oasis_identity(min_subj)) }}{{ '{:.0%}'.format(result.parental_humanness.get_oasis_percentile(min_subj)) }}{{ '{:.0%}'.format(result.humanized_humanness.get_oasis_percentile(min_subj)) }} {{ '{:.0%}'.format(result.parental_humanness.get_germline_content()) }} {{ '{:.0%}'.format(result.humanized_humanness.get_germline_content()) }}
+
- + @@ -169,11 +163,11 @@

Light chain

- - - - - + + + + + @@ -198,15 +192,15 @@

Light chain

- + - - - - - - - - - - - + + + + + + + + + + + @@ -72,8 +72,8 @@

Humanness report results

{% else %} {% set min_subj = result.oasis_params.min_fraction_subjects %} - + {{ chain_row(result.humanness.vh, min_subj) }} @@ -110,10 +110,10 @@

Humanness report results

{% macro chain_row(chain_humanness, min_subj) %} {% if chain_humanness %} + - {% else %} diff --git a/biophi/humanization/web/templates/humanization/humanness_settings_component.html b/biophi/humanization/web/templates/humanization/humanness_settings_component.html index 90e3a43..6af1658 100644 --- a/biophi/humanization/web/templates/humanization/humanness_settings_component.html +++ b/biophi/humanization/web/templates/humanization/humanness_settings_component.html @@ -13,7 +13,7 @@ {% endfor %} -
+
diff --git a/biophi/humanization/web/views.py b/biophi/humanization/web/views.py index bd43467..68e55a4 100644 --- a/biophi/humanization/web/views.py +++ b/biophi/humanization/web/views.py @@ -101,7 +101,13 @@ def humanize_post(): return antibody_inputs scheme = request.form['scheme'] - cdr_definition = request.form['cdr_definition'] + humanizing_cdr_definition = request.form['cdr_definition'] + if humanizing_cdr_definition == 'kabat_vernier': + cdr_definition = 'kabat' + backmutate_vernier = True + else: + cdr_definition = humanizing_cdr_definition + backmutate_vernier = False method = request.form.get('method', 'manual') if method == 'manual': @@ -112,6 +118,7 @@ def humanize_post(): humanization_params = SapiensHumanizationParams( scheme=scheme, cdr_definition=cdr_definition, + backmutate_vernier=backmutate_vernier, humanize_cdrs='humanize_cdrs' in request.form, model_version='latest', iterations=iterations @@ -120,9 +127,9 @@ def humanize_post(): humanization_params = CDRGraftingHumanizationParams( scheme=scheme, cdr_definition=cdr_definition, + backmutate_vernier=backmutate_vernier, heavy_v_germline=request.form['heavy_v_germline'], light_v_germline=request.form['light_v_germline'], - backmutate_vernier=('cdr_grafting_backmutate_vernier' in request.form), sapiens_iterations=(1 if 'sapiens_final_pass' in request.form else 0) ) else:
PositionAmino acidAmino acid OASis 9-mer peptide search
Region {{ list(chain_humanness.peptides)[0].scheme.title() }} AAChain
Freq
Family
Freq
PeptideOAS SubjectsOAS SeqsChain
Freq {{ info_icon('Residue frequency in {} chain at given {} position'.format(chain_humanness.chain.chain_type, chain_humanness.chain.scheme.title())) }}
Family
Freq {{ info_icon('Residue frequency in {} germline family at given {} position'.format(chain_humanness.v_germline_family, chain_humanness.chain.scheme.title())) }}
Peptide {{ info_icon('9-mer peptide starting at given position.') }}OAS
Subjects {{ info_icon('Percentage of OAS human subjects containing this peptide in their repertoire.') }}
OAS
Sequences {{ info_icon('Number of total antibody sequences containing this peptide across across all human subjects in OAS.') }}
{{ position.get_region() }} {{ position }}{{ aa }}{{ aa }} {{ format_aa_frequency(chain_freqs.get(aa, 0) if chain_freqs else None) }} {{ format_aa_frequency(family_freqs.get(aa, 0) if family_freqs else None) }} diff --git a/biophi/humanization/web/templates/humanization/humanness_report_table.html b/biophi/humanization/web/templates/humanization/humanness_report_table.html index 9120f2d..3a5b50c 100644 --- a/biophi/humanization/web/templates/humanization/humanness_report_table.html +++ b/biophi/humanization/web/templates/humanization/humanness_report_table.html @@ -50,17 +50,17 @@

Humanness report results

NameOASis
Percentile
OASis
Identity
Germline
Content
OASis
Percentile
OASis
Identity
Germline
Gene
Germline
Content
OASis
Percentile
OASis
Identity
Germline
Gene
Germline
Content
OASis
Identity {{ info_icon('Fraction of 9-mer peptides considered human based on given prevalence threshold') }}
OASis
Percentile {{ info_icon('Percentile of OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }}
Germline
Content {{ info_icon('Sequence identity with nearest heavy and light human germline sequences') }}
OASis
Identity {{ info_icon('Fraction of heavy chain 9-mer peptides considered human based on given prevalence threshold') }}
OASis
Percentile {{ info_icon('Percentile of heavy chain OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }}
Germline
Gene {{ info_icon('Nearest human heavy germline gene based on sequence similarity.') }}
Germline
Content {{ info_icon('Sequence identity with nearest human heavy germline sequence') }}
OASis
Identity {{ info_icon('Fraction of light chain 9-mer peptides considered human based on given prevalence threshold') }}
OASis
Percentile {{ info_icon('Percentile of light chain OASis identity among therapeutic mAbs at the current prevalence threshold. Zero percentile corresponds to the least human and 100% percentile corresponds to the most human mAb in the clinic, including all clinical stage human, humanized and murine therapeutics.') }}
Germline
Gene {{ info_icon('Nearest human light germline gene based on sequence similarity.') }}
Germline
Content {{ info_icon('Sequence identity with nearest human light germline sequence') }}
Actions
Failed: {{ result }}{{ '{:.0%}'.format(result.humanness.get_oasis_percentile(min_subj)) }} {{ '{:.0%}'.format(result.humanness.get_oasis_identity(min_subj)) }}{{ '{:.0%}'.format(result.humanness.get_oasis_percentile(min_subj)) }} {{ '{:.0%}'.format(result.humanness.get_germline_content()) }}{{ '{:.0%}'.format(chain_humanness.get_oasis_identity(min_subj)) }} {{ '{:.0%}'.format(chain_humanness.get_oasis_percentile(min_subj)) }} {{ '{:.0%}'.format(chain_humanness.get_oasis_identity(min_subj)) }} {{ chain_humanness.v_germline_names[0] }} {{ '{:.0%}'.format(chain_humanness.get_germline_content()) }}