From 19aa824d97446943d85409c9056f1abd11c60cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20=20MENAGER?= <herve.menager@pasteur.fr> Date: Thu, 6 Dec 2018 14:22:50 +0100 Subject: [PATCH] refactor annotations to compound queryset to the model level this is mainly to be used in compounds list, but will ultimately be useful everywhere Former-commit-id: 720068133a0e7c7f5d9bd562d77467ab70d0c477 --- ippisite/ippidb/models.py | 40 +++++++++---------- ippisite/ippidb/templates/compound_list.html | 26 ++++++------ .../ippidb/templates/compound_t_list.html | 8 ++-- ippisite/ippidb/views.py | 36 ++++------------- 4 files changed, 43 insertions(+), 67 deletions(-) diff --git a/ippisite/ippidb/models.py b/ippisite/ippidb/models.py index 885e34ea..7ec524f0 100644 --- a/ippisite/ippidb/models.py +++ b/ippisite/ippidb/models.py @@ -6,8 +6,11 @@ from __future__ import unicode_literals import operator from django.db import models -from django.db.models import Max from django.conf import settings +from django.db.models import Max, Min, Count, F, Q +from django.db.models.functions import Cast +from django.db.models import FloatField + from .utils import FingerPrinter, smi2inchi, smi2inchikey from .ws import get_pubmed_info, get_google_patent_info, get_uniprot_info, get_taxonomy_info, get_go_info, get_pfam_info @@ -268,7 +271,22 @@ class PpiComplex(models.Model): return 'PPI {}, Complex {} ({})'.format(self.ppi, self.complex, self.cc_nb) +class CompoundManager(models.Manager): + + def get_queryset(self): + qs = super().get_queryset() + # with number of publications + qs = qs.annotate(pubs=Count('refcompoundbiblio', distinct=True)) + # with best activity + qs = qs.annotate(best_activity=Max('compoundactivityresult__activity')) + # with LE + qs = qs.annotate(le=Cast(1.37 * Max('compoundactivityresult__activity') / F('nb_atom_non_h'),FloatField())) + # with LLE + qs = qs.annotate(lle=Cast(Max('compoundactivityresult__activity') - F('a_log_p'),FloatField())) + return qs + class Compound(AutoFillableModel): + objects = CompoundManager() canonical_smile = models.TextField( 'Canonical Smile', unique=True) is_macrocycle = models.BooleanField('Contains one or more macrocycles') @@ -454,26 +472,6 @@ class Compound(AutoFillableModel): return None return self.compoundactivityresult_set.filter(activity=best_pXC50_activity)[0] - @property - def le(self): - """ - LE: Ligand Efficiency - """ - best_pXC50_activity = self.best_pXC50_activity - if best_pXC50_activity is None: - return None - return (1.37 * float(best_pXC50_activity))/self.nb_atom_non_h - - @property - def lle(self): - """ - LLE: Lipophilic Efficiency - """ - best_pXC50_activity = self.best_pXC50_activity - if best_pXC50_activity is None: - return None - return float(best_pXC50_activity - self.a_log_p) - @property def best_pXC50_activity_ppi_name(self): """ diff --git a/ippisite/ippidb/templates/compound_list.html b/ippisite/ippidb/templates/compound_list.html index 3bc7b4f4..2310b9ec 100644 --- a/ippisite/ippidb/templates/compound_list.html +++ b/ippisite/ippidb/templates/compound_list.html @@ -63,9 +63,9 @@ Activity and Efficiencies </button> <div class="dropdown-menu" aria-labelledby="aeMenuButton"> - {% include "modal_open_button.html" with label="Best Activity" param_name="qs_ba" %} - {% include "modal_open_button.html" with label="LE" param_name="qs_le" %} - {% include "modal_open_button.html" with label="LLE" param_name="qs_lle" %} + {% include "modal_open_button.html" with label="Best Activity" param_name="best_activity" %} + {% include "modal_open_button.html" with label="LE" param_name="le" %} + {% include "modal_open_button.html" with label="LLE" param_name="lle" %} </div> </div> <div class="dropdown btn-group" style="display: inline-flex;"> @@ -125,7 +125,7 @@ <div class="m-2 d-flex justify-content-between"> <span>{{ paginator.count }} compounds</span> - {% if selected_family or selected_ppi or selected_disease or selected_taxonomy or selected_boundcomplex or molecular_weight or a_log_p or nb_donor_h or nb_acceptor_h or tpsa or nb_rotatable_bonds or nb_aromatic_sssr or nb_chiral_centers or fsp3 or pubs or similar_to or qs_ba or qs_le or qs_lle %} + {% if selected_family or selected_ppi or selected_disease or selected_taxonomy or selected_boundcomplex or molecular_weight or a_log_p or nb_donor_h or nb_acceptor_h or tpsa or nb_rotatable_bonds or nb_aromatic_sssr or nb_chiral_centers or fsp3 or pubs or similar_to or best_activity or le or lle %} <span> - filters: {% if selected_family %} @@ -183,14 +183,14 @@ {% if similar_to %} {% include "similarity_badge.html" with param_name="similar_to" param_value=similar_to_query param_label="Similar to " %} {% endif %} - {% if qs_ba %} - {% include "slider_badge.html" with param_name="qs_ba" param_value_min=qs_ba_value_min param_value_max=qs_ba_value_max param_label="Best Act." %} + {% if best_activity %} + {% include "slider_badge.html" with param_name="best_activity" param_value_min=best_activity_value_min param_value_max=best_activity_value_max param_label="Best Act." %} {% endif %} - {% if qs_le %} - {% include "slider_badge.html" with param_name="qs_le" param_value_min=qs_le_value_min param_value_max=qs_le_value_max param_label="LE" %} + {% if le %} + {% include "slider_badge.html" with param_name="le" param_value_min=le_value_min param_value_max=le_value_max param_label="LE" %} {% endif %} - {% if qs_lle %} - {% include "slider_badge.html" with param_name="qs_lle" param_value_min=qs_lle_value_min param_value_max=qs_lle_value_max param_label="LLE" %} + {% if lle %} + {% include "slider_badge.html" with param_name="lle" param_value_min=lle_value_min param_value_max=lle_value_max param_label="LLE" %} {% endif %} </span> {% endif %} @@ -261,8 +261,8 @@ {% include "slider_modal.html" with label="FSP3" param_name="fsp3" param_min=fsp3_min param_max=fsp3_max param_value=fsp3 param_value_min=fsp3_value_min param_value_max=fsp3_value_max step='0.1' param_label='Select a cutoff value for the FSP3 of the compounds to be selected'%} {% include "slider_modal.html" with label="Publications" param_name="pubs" param_min=pubs_min param_max=pubs_max param_value=pubs param_value_min=pubs_value_min param_value_max=pubs_value_max step='1' param_label='Select a cutoff value for the number of publications mentioning the compounds to be selected'%} {% include "marvinjs_modal.html" with smiles=similar_to_query fingerprint=similar_to_fingerprint %} - {% include "slider_modal.html" with label="Best Activiry" param_name="qs_ba" param_min=qs_ba_min param_max=qs_ba_max param_value=qs_ba param_value_min=qs_ba_value_min param_value_max=qs_ba_value_max step='1' param_label='Select a cutoff value for the best activity of the compounds to be selected'%} - {% include "slider_modal.html" with label="LE" param_name="qs_le" param_min=qs_le_min param_max=qs_le_max param_value=qs_le param_value_min=qs_le_value_min param_value_max=qs_le_value_max step='0.1' param_label='Select a cutoff value for the LE of the compounds to be selected'%} - {% include "slider_modal.html" with label="LLE" param_name="qs_lle" param_min=qs_lle_min param_max=qs_lle_max param_value=qs_lle param_value_min=qs_lle_value_min param_value_max=qs_lle_value_max step='1' param_label='Select a cutoff value for the LLE of the compounds to be selected'%} + {% include "slider_modal.html" with label="Best Activiry" param_name="best_activity" param_min=best_activity_min param_max=best_activity_max param_value=best_activity param_value_min=best_activity_value_min param_value_max=best_activity_value_max step='1' param_label='Select a cutoff value for the best activity of the compounds to be selected'%} + {% include "slider_modal.html" with label="LE" param_name="le" param_min=le_min param_max=le_max param_value=le param_value_min=le_value_min param_value_max=le_value_max step='0.1' param_label='Select a cutoff value for the LE of the compounds to be selected'%} + {% include "slider_modal.html" with label="LLE" param_name="lle" param_min=lle_min param_max=lle_max param_value=lle param_value_min=lle_value_min param_value_max=lle_value_max step='1' param_label='Select a cutoff value for the LLE of the compounds to be selected'%} {% endblock %} diff --git a/ippisite/ippidb/templates/compound_t_list.html b/ippisite/ippidb/templates/compound_t_list.html index 2aa8d62b..efdabffd 100644 --- a/ippisite/ippidb/templates/compound_t_list.html +++ b/ippisite/ippidb/templates/compound_t_list.html @@ -20,7 +20,7 @@ {% include "compound_t_colhead.html" with col_id="nb_chiral_centers" col_name="R/S" col_title="Number of chiral centers" %} {% include "compound_t_colhead.html" with col_id="le" col_name="LE" col_title="Ligand Efficiency" %} {% include "compound_t_colhead.html" with col_id="lle" col_name="LLE" col_title="Lipophilic Efficiency" %} - {% include "compound_t_colhead.html" with col_id="qs_ba" col_name="Best Activity" col_title="Best pXC50 Activity" %} + {% include "compound_t_colhead.html" with col_id="best_activity" col_name="Best Activity" col_title="Best pXC50 Activity" %} <button type="button" class="btn btn-primary float-right" style="width: inherit!important" data-toggle="modal" data-target="#selectColumns"> <i class="fa fa-columns" title="customize columns"></i> </button> @@ -84,8 +84,8 @@ {% if "lle" in fields %} <td scope="col">{{ compound.lle|floatformat:2 }}</td> {% endif %} - {% if "qs_ba" in fields %} - <td scope="col">{{ compound.qs_ba|floatformat:2 }}</td> + {% if "best_activity" in fields %} + <td scope="col">{{ compound.best_activity|floatformat:2 }}</td> {% endif %} </tr> {% endfor %} @@ -155,7 +155,7 @@ <label><input type="checkbox" name="field" value="lle" class="mr-1" {% if 'lle' in fields %}checked="checked"{% endif %} />Lipophilic Efficiency</label> </div> <div class="row"> - <label><input type="checkbox" name="field" value="qs_ba" class="mr-1" {% if 'qs_ba' in fields %}checked="checked"{% endif %} />Best pXC50 Activity</label> + <label><input type="checkbox" name="field" value="best_activity" class="mr-1" {% if 'best_activity' in fields %}checked="checked"{% endif %} />Best pXC50 Activity</label> </div> </div> <div class="modal-footer"> diff --git a/ippisite/ippidb/views.py b/ippisite/ippidb/views.py index fa9078c2..0cdee4bf 100644 --- a/ippisite/ippidb/views.py +++ b/ippisite/ippidb/views.py @@ -279,19 +279,6 @@ class CompoundRangeFilterHandler(object): queryset = queryset.filter(**filter_dict) # max and min value are the max and min for qs = Compound.objects - # annotate - # with number of publications - if self.parameter_name == 'pubs': - qs = qs.annotate(pubs=Count('refcompoundbiblio', distinct=True)) - # with best activity - elif self.parameter_name == 'qs_ba': - qs = qs.annotate(qs_ba=Max('compoundactivityresult__activity')) - # with LE - elif self.parameter_name == 'qs_le': - qs = qs.annotate(qs_le=Cast(1.37 * Max('compoundactivityresult__activity') / F('nb_atom_non_h'),FloatField())) - # with LLE - elif self.parameter_name == 'qs_lle': - qs = qs.annotate(qs_lle=Cast(Max('compoundactivityresult__activity') - F('a_log_p'),FloatField())) self.filter_context[self.parameter_name+'_max'] = self.get_max(qs) self.filter_context[self.parameter_name+'_min'] = self.get_min(qs) return queryset @@ -344,7 +331,7 @@ class CompoundListView(ListView): table_view_default_fields = ['id', 'canonical_smiles', 'common_name', 'molecular_weight', 'a_log_p', 'compound_action_ligand_ids', 'pubs'] - sort_by_option_ids = ['id', 'molecular_weight', 'a_log_p', 'nb_aromatic_sssr', 'nb_chiral_centers', 'pubs', 'qs_le', 'qs_lle', 'qs_ba'] + sort_by_option_ids = ['id', 'molecular_weight', 'a_log_p', 'nb_aromatic_sssr', 'nb_chiral_centers', 'pubs', 'le', 'lle', 'best_activity'] def get_ordering(self): # sort by options @@ -353,11 +340,11 @@ class CompoundListView(ListView): for sort_by_option_id in self.sort_by_option_ids: if sort_by_option_id == 'pubs': name = 'Number of publications' - elif sort_by_option_id == 'qs_le': + elif sort_by_option_id == 'le': name = 'Ligand Efficiency' - elif sort_by_option_id == 'qs_lle': + elif sort_by_option_id == 'lle': name = 'Lipophilic Efficiency' - elif sort_by_option_id == 'qs_ba': + elif sort_by_option_id == 'best_activity': name = 'Best Activity' else: name = compound_fields.get(sort_by_option_id) @@ -397,15 +384,6 @@ class CompoundListView(ListView): self.filter_context = {} # get queryset qs = super().get_queryset() - # annotate - # with number of publications - qs = qs.annotate(pubs=Count('refcompoundbiblio', distinct=True)) - # with best activity - qs = qs.annotate(qs_ba=Max('compoundactivityresult__activity')) - # with LE - qs = qs.annotate(qs_le=Cast(1.37 * Max('compoundactivityresult__activity') / F('nb_atom_non_h'),FloatField())) - # with LLE - qs = qs.annotate(qs_lle=Cast(Max('compoundactivityresult__activity') - F('a_log_p'),FloatField())) # add filters cfhs = [\ CompoundListFilterHandler(PpiFamily, 'compoundaction__ppi__family', 'ppi__compoundaction__compound', 'family', self.filter_context, self.request.GET), @@ -423,9 +401,9 @@ class CompoundListView(ListView): CompoundRangeFilterHandler('nb_chiral_centers', self.filter_context, self.request.GET), CompoundRangeFilterHandler('fsp3', self.filter_context, self.request.GET, 0.1), CompoundRangeFilterHandler('pubs', self.filter_context, self.request.GET), - CompoundRangeFilterHandler('qs_ba', self.filter_context, self.request.GET), - CompoundRangeFilterHandler('qs_le', self.filter_context, self.request.GET, 0.1), - CompoundRangeFilterHandler('qs_lle', self.filter_context, self.request.GET), + CompoundRangeFilterHandler('best_activity', self.filter_context, self.request.GET), + CompoundRangeFilterHandler('le', self.filter_context, self.request.GET, 0.1), + CompoundRangeFilterHandler('lle', self.filter_context, self.request.GET), ExistsFilterHandler('pubchem_id', self.filter_context, self.request.GET), ExistsFilterHandler('chemspider_id', self.filter_context, self.request.GET), ExistsFilterHandler('chembl_id', self.filter_context, self.request.GET), -- GitLab