Commit 2bd6a9b1 authored by Hervé  MENAGER's avatar Hervé MENAGER
Browse files

Merge branch 'master' into 'release'

Prepare for v1.0.3

See merge request !20
parents 24b5d5bf e231ff32
Pipeline #42116 canceled with stages
...@@ -54,7 +54,8 @@ extensions = [ ...@@ -54,7 +54,8 @@ extensions = [
'sphinx.ext.intersphinx', 'sphinx.ext.intersphinx',
'sphinx.ext.coverage', 'sphinx.ext.coverage',
'sphinx.ext.viewcode', 'sphinx.ext.viewcode',
'sphinx.ext.graphviz' 'sphinx.ext.graphviz',
'sphinxcontrib.bibtex'
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
......
...@@ -18,6 +18,7 @@ These pages contain the documentation for technical maintenance and development ...@@ -18,6 +18,7 @@ These pages contain the documentation for technical maintenance and development
install install
configuration configuration
database database
webservices
modules modules
......
This diff is collapsed.
...@@ -8,6 +8,7 @@ import operator ...@@ -8,6 +8,7 @@ import operator
import re import re
import sys import sys
from boltons.iterutils import flatten, unique_iter
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
...@@ -985,6 +986,20 @@ class Compound(AutoFillableModel): ...@@ -985,6 +986,20 @@ class Compound(AutoFillableModel):
.count() .count()
) )
@property
def pk_tests_count(self):
"""
Return the number of associated pharmacokinetic tests
"""
return self.compoundpkresult_set.all().count()
@property
def cytotoxicity_tests_count(self):
"""
Return the number of associated cytotoxicity tests
"""
return self.compoundcytotoxicityresult_set.all().count()
@property @property
def sorted_similar_drugbank_compounds(self): def sorted_similar_drugbank_compounds(self):
""" """
...@@ -1183,6 +1198,60 @@ class Compound(AutoFillableModel): ...@@ -1183,6 +1198,60 @@ class Compound(AutoFillableModel):
CompoundTanimoto.objects.filter(compound=self).delete() CompoundTanimoto.objects.filter(compound=self).delete()
DrugbankCompoundTanimoto.objects.filter(compound=self).delete() DrugbankCompoundTanimoto.objects.filter(compound=self).delete()
def get_target_activities_table(self):
"""
Return test activity result data as a list of
items containing for each PPI family the compound
was tested against, the best activity, the linked diseases
and the modulation types
"""
ppi_families = [
item["test_activity_description__ppi__family"]
for item in self.compoundactivityresult_set.filter(
~Q(activity_type="KdRat")
)
.order_by()
.values("test_activity_description__ppi__family")
.distinct()
]
results = []
for ppi_family in ppi_families:
family_compound_activity_results = self.compoundactivityresult_set.filter(
~Q(activity_type="KdRat")
).filter(test_activity_description__ppi__family__id=ppi_family)
best_activity = (
family_compound_activity_results.order_by("-activity")
.values("activity")
.first()["activity"]
)
diseases = list(
unique_iter(
flatten(
[
list(fcar.test_activity_description.ppi.diseases.all())
for fcar in family_compound_activity_results
]
)
)
)
modulation_types = list(
set(
[
fcar.get_modulation_type_display()
for fcar in family_compound_activity_results
]
)
)
results.append(
{
"family": PpiFamily.objects.get(id=ppi_family),
"best_activity": best_activity,
"diseases": diseases,
"modulation_types": modulation_types,
}
)
return results
class CompoundTanimoto(models.Model): class CompoundTanimoto(models.Model):
canonical_smiles = models.TextField("Canonical Smile") canonical_smiles = models.TextField("Canonical Smile")
...@@ -1867,9 +1936,9 @@ def update_compound_cached_properties(compounds_queryset=None): ...@@ -1867,9 +1936,9 @@ def update_compound_cached_properties(compounds_queryset=None):
) )
.values("_bindtest_av")[:1] .values("_bindtest_av")[:1]
), ),
pktest_av=Exists(CompoundPKResult.objects.filter(id=OuterRef("id"))), pktest_av=Exists(CompoundPKResult.objects.filter(compound__id=OuterRef("id"))),
cytoxtest_av=Exists( cytoxtest_av=Exists(
CompoundCytotoxicityResult.objects.filter(id=OuterRef("id")) CompoundCytotoxicityResult.objects.filter(compound__id=OuterRef("id"))
), ),
insilico_av=Subquery( insilico_av=Subquery(
compounds_queryset.filter(id=OuterRef("id")) compounds_queryset.filter(id=OuterRef("id"))
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
<div class="row" style="display: inline-block;height:auto; margin-right: 15px; margin-left: 15px;"> <div class="row" style="display: inline-block;height:auto; margin-right: 15px; margin-left: 15px;">
<div class="tab-content" id="tabContent"> <div class="tab-content" id="tabContent">
<div class="tab-pane fade show active" id="compound" role="tabpanel" aria-labelledby="compound-tab"> <div class="tab-pane fade show active" id="compound" role="tabpanel" aria-labelledby="compound-tab">
<div class="card"> <div class="card inner-wrap">
<div class="card-body"> <div class="card-body">
{% include "compound_l_item.html" with compound=compound show_detail=True %} {% include "compound_l_item.html" with compound=compound show_detail=True %}
</div> </div>
...@@ -382,20 +382,7 @@ ...@@ -382,20 +382,7 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-sm-12 col-md-9" style="margin: 10px;"> <div class="col-sm-12 col-md-9" style="margin: 10px;">
<h5 class="card_title">Summary</h5> <h5 class="card_title">Summary</h5>
<div class="card_border row" style="text-align: center;"> {% include "compound_test_counts.html" with compound=compound show_bibrefs=True %}
<div class="col-md-4">
<h6 class="card_title">Bibliographic ressources</h6>
<p>{{ compound.biblio_refs.count }}</p>
</div>
<div class="col-md-4">
<h6 class="card_title">Biochemical tests</h6>
<p>{{ compound.bioch_tests_count }}</p>
</div>
<div class="col-md-4">
<h6 class="card_title">Cellular tests</h6>
<p>{{ compound.cell_tests_count }}</p>
</div>
</div>
</div> </div>
</div> </div>
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
......
{%if compound.pubchem_id or compound.chembl_id or compound.chemspider_id or compound.ligand_id %} {%if compound.pubchem_id or compound.chembl_id or compound.chemspider_id or compound.ligand_id %}
<table class="table mb-0"> <table class="table m-1 card_border">
<tbody> <tbody>
<tr class="row"> <tr class="table-row">
<td scope="col" class="col-2 text-center"> <td scope="col" class="col-2 text-center">
{% if compound.pubchem_id %} {% if compound.pubchem_id %}
<a href="{{ compound.pubchem_url }}" target="_blank"><img <a href="{{ compound.pubchem_url }}" target="_blank"><img
......
<table class="table mb-0"> <table class="table m-1 card_border">
<tbody> <tbody>
<tr class="row"> <tr class="table-row">
<td scope="col" class="col-2 text-center"> <td scope="col" class="col-2 text-center">
<a href="https://www.ncbi.nlm.nih.gov/pccompound?term={{ compound.inchikey }}" target="_blank"><img src="/static/images/Other/Pubchemlogo.png" style="height: 1.5em;" title="search INCHIKEY on PubChem" /></a> <a href="https://www.ncbi.nlm.nih.gov/pccompound?term={{ compound.inchikey }}" target="_blank"><img src="/static/images/Other/Pubchemlogo.png" style="height: 1.5em;" title="search INCHIKEY on PubChem" /></a>
</td> </td>
......
{% load customtags %}
<div class="row m-2 border border-info bg-light border_card" style="box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.2);"> <div class="row m-2 border border-info bg-light border_card" style="box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.2);">
{%if show_detail != True %} {%if show_detail != True %}
<div class="col-sm-3 border-info d-flex justify-content-center align-content-center {% if compound.replaced_with or compound.is_validated is False %}bg_warning{% endif %}"> <div class="col-sm-3 border-info justify-content-center align-content-center {% if compound.replaced_with or compound.is_validated is False %}bg_warning{% endif %}">
<a href="/compounds/{{ compound.id }}">{% include "compound_smiles_draw.html" with id=compound.id smile=compound.canonical_smile %}</a> <div class="row">
<h2 class="position-absolute" style="top:0.3em; left:0.3em;"><span class="badge badge-light"><a href="/compounds/{{ compound.id }}">{{ compound.id }}</a></span>{% if compound.replaced_with is not None %} <a style="overflow: hidden;" href="/compounds/{{ compound.id }}">{% include "compound_smiles_draw.html" with id=compound.id smile=compound.canonical_smile %}</a>
<span style="font-size: 26px; color:#fa8005; font-size: 75%;">replaced with <a href="/compounds/{{ compound.replaced_with.id }}">{{ compound.replaced_with.id }}</a></span>{% elif compound.is_validated is False %}<span <h2 class="position-absolute" style="top:0.3em; left:0.3em;"><span class="badge badge-light"><a href="/compounds/{{ compound.id }}">{{ compound.id }}</a></span>{% if compound.replaced_with is not None %}
style="font-size: 26px; color:#fa8005;">not validated</span>{% endif %}</h2> <span style="font-size: 26px; color:#fa8005; font-size: 75%;">replaced with <a href="/compounds/{{ compound.replaced_with.id }}">{{ compound.replaced_with.id }}</a></span>{% elif compound.is_validated is False %}<span
{% if compound.tanimoto != None %} style="font-size: 26px; color:#fa8005;">not validated</span>{% endif %}</h2>
<h2 class="position-absolute" style="top:0.3em; right:0.3em;" title="tanimoto similarity value"><span class="badge badge-light text-warning">{{ compound.tanimoto }}</span></h2> {% if compound.tanimoto != None %}
{% endif %} <h2 class="position-absolute" style="top:0.3em; right:0.3em;" title="tanimoto similarity value"><span class="badge badge-light text-warning">{{ compound.tanimoto }}</span></h2>
{% endif %}
</div>
<h4 class="pt-2 compound_list_title">Chemistry rules</h4>
<div class="row m-1">
<table class="table table-sm col-sm-12 col-md-6 card_border m-1" style="text-align: center;">
<thead>
<tr>
<th scope="col" class="card_title col_width">Lipinski's RO5</th>
<th scope="col" class="card_title col_width">Veber</th>
<th scope="col" class="card_title col_width">Pfizer's 3/75</th>
</tr>
</thead>
<tbody>
<tr>
<td>{% rule_status_icon compound.lipinsky "Lipinski's RO5" %}</td>
<td>{% rule_status_icon compound.veber "Veber" %}</td>
<td>{% rule_status_icon compound.pfizer "Pfizer" %}</td>
</tr>
</tbody>
</table>
</div>
</div> </div>
{% endif %} {% endif %}
<div class="{%if show_detail != True %}col-sm-9{% else %}col-sm-12{% endif %}"> <div class="{%if show_detail != True %}col-sm-9{% else %}col-sm-12{% endif %}">
...@@ -17,16 +40,16 @@ ...@@ -17,16 +40,16 @@
<li class="list-group-item">Common name: {{ compound.common_name }}</li> <li class="list-group-item">Common name: {{ compound.common_name }}</li>
{% endif %} {% endif %}
{% if compound.canonical_smile %} {% if compound.canonical_smile %}
<li class="list-group-item">Canonical SMILES: {{ compound.canonical_smile }}</li> <li class="list-group-item">Canonical SMILES: <pre style="overflow: scroll;">{{ compound.canonical_smile }}</pre></li>
{% endif %} {% endif %}
{% if compound.iupac_name %} {% if compound.iupac_name %}
<li class="list-group-item">IUPAC name: {{ compound.iupac_name }}</li> <li class="list-group-item">IUPAC name: <pre style="overflow: scroll;">{{ compound.iupac_name }}</pre></li>
{% endif %} {% endif %}
{% if compound.inchi %} {% if compound.inchi %}
<li class="list-group-item">InChi: {{ compound.inchi }}</li> <li class="list-group-item">InChi: <pre style="overflow: scroll;">{{ compound.inchi }}</pre></li>
{% endif %} {% endif %}
{% if compound.inchikey %} {% if compound.inchikey %}
<li class="list-group-item">InChiKey: {{ compound.inchikey }}</li> <li class="list-group-item">InChiKey: <pre style="overflow: scroll;">{{ compound.inchikey }}</pre></li>
{% endif %} {% endif %}
</ul> </ul>
{%if compound.pubchem_id or compound.chembl_id or compound.chemspider_id or compound.ligand_id %} {%if compound.pubchem_id or compound.chembl_id or compound.chemspider_id or compound.ligand_id %}
...@@ -38,24 +61,56 @@ ...@@ -38,24 +61,56 @@
{% include "compound_dbsearch_links.html" %} {% include "compound_dbsearch_links.html" %}
{% endif %} {% endif %}
{% if compound.biblio_refs %} {% if compound.biblio_refs %}
<h4 class="pt-2 compound_list_title">Bibliography</h4> <h4 class="pt-2 compound_list_title">Bibliography ({{compound.biblio_refs.count}})</h4>
<table class="table"> <table class="table m-1 card_border">
<thead> <thead>
<tr> <tr class="table-row">
<th scope="col">Publication</th> <th scope="col">Publication</th>
<th scope="col" title="Compound name in publication">Name</th> <th scope="col" title="Compound name in publication">Name</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for biblio_ref in compound.biblio_refs %} {% for biblio_ref in compound.biblio_refs %}
<tr> <tr class="table-row">
<td scope="col">{% include "biblio_citation.html" with bibliography=biblio_ref.bibliography%}</td> <td scope="col">{% include "biblio_citation.html" with bibliography=biblio_ref.bibliography%}</td>
<td scope="col">{{ biblio_ref.compound_name }}</td> <td scope="col">{{ biblio_ref.compound_name }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</ul> <h4 class="pt-2 compound_list_title">Pharmacological data</h4>
{% include "compound_test_counts.html" with compound=compound show_bibrefs=False %}
<h4 class="pt-2 compound_list_title">Targets</h4>
<table class="table m-1 card_border">
<thead>
<tr class="table-row">
<th scope="col">PPI family</th>
<th scope="col">Best activity</th>
<th scope="col">Diseases</th>
<th scope="col" title="Molecular Mechanism of Action">MMoA</th>
</tr>
</thead>
<tbody>
{% for row in compound.get_target_activities_table %}
<tr class="table-row">
<td scope="col">{{ row.family.name }}</td>
<td scope="col">{{ row.best_activity|floatformat:2 }}</td>
<td scope="col">
{% for disease in row.diseases %}
{{ disease.name }}
{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
<td scope="col">
{% for modulation_type in row.modulation_types %}
{{ modulation_type }}
{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %} {% endif %}
</div> </div>
</div> </div>
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
</div> </div>
<div class="inner-wrap container-fluid "> <div class="inner-wrap container-fluid ">
<div class="row flex-xl-nowrap"> <div class="row">
<main class="col-12" role="main"> <main class="col-12" role="main">
<h1 class="page-title">Query compounds</h1> <h1 class="page-title">Query compounds</h1>
......
<table class="table m-1 card_border">
<thead>
<tr class="table-row">
{% if show_bibrefs %}
<th scope="col">Bibliographic ressources</th>
{% endif %}
<th scope="col" title="Biochemical tests">Biochemical tests</th>
<th scope="col" title="Cellular tests">Cellular tests</th>
<th scope="col" title="Pharmacokinetic tests">PK tests</th>
<th scope="col" title="Cytotoxicity tests">Cytotoxicity tests</th>
</tr>
</thead>
<tbody>
<tr class="table-row">
{% if show_bibrefs %}
<td scope="col">{{ compound.biblio_refs.count }}</td>
{% endif %}
<th scope="col" title="Biochemical tests">{{ compound.bioch_tests_count }}</th>
<th scope="col" title="Cellular tests">{{ compound.cell_tests_count }}</th>
<th scope="col" title="Pharmacokinetic tests">{{ compound.pk_tests_count }}</th>
<th scope="col" title="Cytotoxicity tests">{{ compound.cytotoxicity_tests_count }}</th>
</tr>
</tbody>
</table>
\ No newline at end of file
...@@ -29,6 +29,22 @@ def status_class(value): ...@@ -29,6 +29,22 @@ def status_class(value):
return "table-secondary" return "table-secondary"
@register.simple_tag
def rule_status_icon(value, title):
if value is True:
class_suffix = "success"
icon_suffix = "check"
elif value is False:
class_suffix = "danger"
icon_suffix = "times"
else:
class_suffix = "secondary"
class_suffix = "question"
return mark_safe(
f'<i class="fas fa-{icon_suffix} text-{class_suffix}" title="{title}: failed"></i>'
)
@register.filter @register.filter
def bootstrap(object): def bootstrap(object):
return mark_safe("".join(bootstrap_core(object))) return mark_safe("".join(bootstrap_core(object)))
...@@ -88,7 +104,11 @@ def bootstrap_core(object): ...@@ -88,7 +104,11 @@ def bootstrap_core(object):
css_classes.add("formset-item-delete") css_classes.add("formset-item-delete")
# HACK: if the css_class is a bootstrap column layout, transfer it to container element # HACK: if the css_class is a bootstrap column layout, transfer it to container element
for css_class in css_classes: for css_class in css_classes:
if css_class.startswith("col") or css_class.startswith("order-") or css_class.startswith("mb-"): if (
css_class.startswith("col")
or css_class.startswith("order-")
or css_class.startswith("mb-")
):
wrapping_classes += f" {css_class}" wrapping_classes += f" {css_class}"
css_classes = set( css_classes = set(
[ [
......
...@@ -29,3 +29,4 @@ git+https://gitlab.pasteur.fr/hmenager/django-diu.git#egg=django_diu ...@@ -29,3 +29,4 @@ git+https://gitlab.pasteur.fr/hmenager/django-diu.git#egg=django_diu
django-crispy-forms django-crispy-forms
celery==4.4.7 celery==4.4.7
django-polymorphic django-polymorphic
boltons
...@@ -6,6 +6,7 @@ flake8-black ...@@ -6,6 +6,7 @@ flake8-black
# documentation generation # documentation generation
sphinx sphinx
sphinx_rtd_theme sphinx_rtd_theme
sphinxcontrib-bibtex
# coverage # coverage
coverage coverage
# tests # tests
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment