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>
                 &nbsp;-&nbsp;filters:&nbsp;
                 {% 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