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 = [
'sphinx.ext.intersphinx',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx.ext.graphviz'
'sphinx.ext.graphviz',
'sphinxcontrib.bibtex'
]
# 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
install
configuration
database
webservices
modules
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -8,6 +8,7 @@ import operator
import re
import sys
from boltons.iterutils import flatten, unique_iter
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
......@@ -985,6 +986,20 @@ class Compound(AutoFillableModel):
.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
def sorted_similar_drugbank_compounds(self):
"""
......@@ -1183,6 +1198,60 @@ class Compound(AutoFillableModel):
CompoundTanimoto.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):
canonical_smiles = models.TextField("Canonical Smile")
......@@ -1867,9 +1936,9 @@ def update_compound_cached_properties(compounds_queryset=None):
)
.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(
CompoundCytotoxicityResult.objects.filter(id=OuterRef("id"))
CompoundCytotoxicityResult.objects.filter(compound__id=OuterRef("id"))
),
insilico_av=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
......
......@@ -55,7 +55,7 @@
<div class="row" style="display: inline-block;height:auto; margin-right: 15px; margin-left: 15px;">
<div class="tab-content" id="tabContent">
<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">
{% include "compound_l_item.html" with compound=compound show_detail=True %}
</div>
......@@ -382,20 +382,7 @@
<div class="row d-flex justify-content-center">
<div class="col-sm-12 col-md-9" style="margin: 10px;">
<h5 class="card_title">Summary</h5>
<div class="card_border row" style="text-align: center;">
<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>
{% include "compound_test_counts.html" with compound=compound show_bibrefs=True %}
</div>
</div>
<div class="row d-flex justify-content-center">
......
{%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>
<tr class="row">
<tr class="table-row">
<td scope="col" class="col-2 text-center">
{% if compound.pubchem_id %}
<a href="{{ compound.pubchem_url }}" target="_blank"><img
......
<table class="table mb-0">
<table class="table m-1 card_border">
<tbody>
<tr class="row">
<tr class="table-row">
<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>
</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);">
{%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 %}">
<a href="/compounds/{{ compound.id }}">{% include "compound_smiles_draw.html" with id=compound.id smile=compound.canonical_smile %}</a>
<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 %}
<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
style="font-size: 26px; color:#fa8005;">not validated</span>{% endif %}</h2>
{% if compound.tanimoto != None %}
<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 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 %}">
<div class="row">
<a style="overflow: hidden;" href="/compounds/{{ compound.id }}">{% include "compound_smiles_draw.html" with id=compound.id smile=compound.canonical_smile %}</a>
<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 %}
<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
style="font-size: 26px; color:#fa8005;">not validated</span>{% endif %}</h2>
{% if compound.tanimoto != None %}
<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>
{% endif %}
<div class="{%if show_detail != True %}col-sm-9{% else %}col-sm-12{% endif %}">
......@@ -17,16 +40,16 @@
<li class="list-group-item">Common name: {{ compound.common_name }}</li>
{% endif %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
</ul>
{%if compound.pubchem_id or compound.chembl_id or compound.chemspider_id or compound.ligand_id %}
......@@ -38,24 +61,56 @@
{% include "compound_dbsearch_links.html" %}
{% endif %}
{% if compound.biblio_refs %}
<h4 class="pt-2 compound_list_title">Bibliography</h4>
<table class="table">
<h4 class="pt-2 compound_list_title">Bibliography ({{compound.biblio_refs.count}})</h4>
<table class="table m-1 card_border">
<thead>
<tr>
<tr class="table-row">
<th scope="col">Publication</th>
<th scope="col" title="Compound name in publication">Name</th>
</tr>
</thead>
<tbody>
{% 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">{{ biblio_ref.compound_name }}</td>
</tr>
{% endfor %}
</tbody>
</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 %}
</div>
</div>
......@@ -29,7 +29,7 @@
</div>
<div class="inner-wrap container-fluid ">
<div class="row flex-xl-nowrap">
<div class="row">
<main class="col-12" role="main">
<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):
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
def bootstrap(object):
return mark_safe("".join(bootstrap_core(object)))
......@@ -88,7 +104,11 @@ def bootstrap_core(object):
css_classes.add("formset-item-delete")
# HACK: if the css_class is a bootstrap column layout, transfer it to container element
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}"
css_classes = set(
[
......
......@@ -29,3 +29,4 @@ git+https://gitlab.pasteur.fr/hmenager/django-diu.git#egg=django_diu
django-crispy-forms
celery==4.4.7
django-polymorphic
boltons
......@@ -6,6 +6,7 @@ flake8-black
# documentation generation
sphinx
sphinx_rtd_theme
sphinxcontrib-bibtex
# coverage
coverage
# 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