Commit d2ec2513 authored by Rachel's avatar Rachel
Browse files

Merge branch 'master' of gitlab.pasteur.fr:ippidb/ippidb-web

parents 3b109654 e231ff32
Pipeline #42105 failed with stages
in 12 minutes and 6 seconds
......@@ -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 diff is collapsed.
......@@ -172,20 +172,6 @@ class IdForm(forms.Form):
.first()
)
# def get_or_create(self):
# b = models.Bibliography.objects \
# .filter(id_source=self.cleaned_data["id_source"]).filter(source=self.cleaned_data["source"]) \
# .annotate(used=Count("refcompoundbiblio")) \
# .filter(used=0) \
# .first()
# if b is not None:
# return b, False
# else:
# return models.Bibliography.objects.create(
# source=self.cleaned_data["source"],
# id_source=self.cleaned_data["id_source"],
# ), True
""" Step 2 : BibliographyForm """
......@@ -446,7 +432,6 @@ class ComplexCompositionBaseFormSet(forms.BaseFormSet):
form_kwargs["nb_copies_in_complex"] = nb_copies_in_complex
self.has_partner = has_partner
self.has_bound = has_bound
print("kwargs", form_kwargs)
super().__init__(form_kwargs=form_kwargs, *args, **kwargs)
......@@ -479,7 +464,7 @@ class PpiModelForm(ModelForm):
"data-olsontology": "mondo",
"data-selectpath": "https://www.ebi.ac.uk/ols/",
"olstype": "",
"class": ""
"class": "",
}
),
)
......@@ -489,12 +474,6 @@ class PpiModelForm(ModelForm):
required=False,
)
# other_diseases = forms.CharField(
# label=_("other_diseases_label"),
# help_text=_("other_diseases_help_text"),
# required=False,
# )
class Meta:
model = models.Ppi
fields = (
......@@ -506,7 +485,6 @@ class PpiModelForm(ModelForm):
"pockets_nb", # pockets_nb is Ppi.pockets_nb in the xlsx file of #33
"ols_diseases",
"selected_diseases",
# 'other_diseases',
)
widgets = {
"id": forms.HiddenInput(),
......@@ -533,17 +511,6 @@ class PpiModelForm(ModelForm):
self.fields["symmetry"].initial = symmetry.first()
self.fields["symmetry"].widget = forms.HiddenInput()
pdb_id = initial.get("pdb_id", None)
if pdb_id is not None:
# FIXME move PFAM choice to relevant form ;)
pfams = ws.get_pdb_pfam_mapping(pdb_id) # noqa: F841
# choices = self.fields["family_name"].choices
# for key, values in pfams.items():
# text = "{} ({})".format(values["name"], key)
# choices.append((text, text))
# self.fields["family_name"].choices = choices
# self.fields["family_name"].initial = choices[-1][0]
def full_clean(self):
super().full_clean()
if hasattr(self, "cleaned_data") and "family_name" in self.cleaned_data:
......@@ -571,13 +538,6 @@ class PpiModelForm(ModelForm):
name=name, identifier=identifier
)
self.instance.diseases.add(disease)
# for new_disease in self.cleaned_data['other_diseases'].split(','):
# new_disease = new_disease.strip()
# if len(new_disease) == 0:
# continue
# print(new_disease)
# disease, created = Disease.objects.get_or_create(name=new_disease)
# self.instance.diseases.add(disease)
self.instance.family = models.PpiFamily.objects.get(
id=self.cleaned_data["family"]
)
......@@ -878,6 +838,11 @@ class CompoundActivityResultForm(ModelForm):
"activity": widgets.HiddenInput(),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for f in self.fields.values():
f.widget.attrs.update({"class": "col-3"})
def has_changed(self):
"""
Test if the form has changed, we consider that it has not changed if it is not linked to an actual instance and
......@@ -1049,7 +1014,7 @@ CompoundActivityResultInlineFormset = inlineformset_factory(
class TestActivityDescriptionForm(forms.ModelForm):
test_name = CharFieldDataList(
data_class=models.TestActivityDescription, data_attr="test_name"
data_class=models.TestActivityDescription, data_attr="test_name",
)
cell_line_name = CharFieldDataList(
......@@ -1074,6 +1039,11 @@ class TestActivityDescriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["test_type"].widget.attrs["onchange"] = "test_type_changed(this);"
self.fields["test_type"].widget.attrs.update({"class": "col-2"})
self.fields["test_name"].widget.attrs.update({"class": "col-10"})
self.fields["test_modulation_type"].widget.attrs.update({"class": "col-2"})
self.fields["nb_active_compounds"].widget.attrs.update({"class": "col-3"})
self.fields["cell_line_name"].widget.attrs.update({"class": "col-7"})
def has_changed(self):
"""
......@@ -1201,6 +1171,11 @@ class CompoundCytotoxicityResultForm(ModelForm):
fields = ("compound_name", "toxicity")
widgets = {"toxicity": forms.Select(choices=((True, "True"), (False, "False")))}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for f in self.fields.values():
f.widget.attrs.update({"class": "col-3"})
def has_changed(self):
"""
Test if the form has changed, we consider that it has not changed if it is not linked to an actual instance and
......@@ -1315,6 +1290,9 @@ class TestCytotoxDescriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["compound_concentration"].required = True
self.fields["cell_line_name"].widget.attrs.update({"class": "col"})
self.fields["compound_concentration"].widget.attrs.update({"class": "col"})
self.fields["test_name"].widget.attrs.update({"class": "col"})
def has_changed(self):
"""
......@@ -1413,6 +1391,13 @@ class CompoundPKResultForm(ModelForm):
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for f in self.fields.values():
f.widget.attrs.update({"class": "col-3"})
if isinstance(f, forms.BooleanField) and not isinstance(f.widget, forms.Select):
f.widget.attrs.update({"class": "col-3 mb-4"})
def has_changed(self):
"""
Test if the form has changed, we consider that it has not changed if it is not linked to an actual instance and
......@@ -1524,6 +1509,8 @@ class TestPKDescriptionForm(forms.ModelForm):
super().__init__(*args, **kwargs)
for f in ["administration_mode", "dose"]:
self.fields[f].required = True
for f in self.fields.values():
f.widget.attrs.update({"class": "col-3"})
def has_changed(self):
"""
......
......@@ -3,13 +3,13 @@
from django.db import migrations, models
import django.db.models.deletion
from ippidb.models import Compound, DrugBankCompound
def run_generate_drugbank_inchikey(apps, schema_editor):
DrugBankCompound = apps.get_model("ippidb", "DrugBankCompound")
for drugbank_compound in DrugBankCompound.objects.all():
drugbank_compound.save(autofill=True)
def set_compound_drugbank_links(apps, schema_editor):
Compound = apps.get_model("ippidb", "Compound")
for compound in Compound.objects.all():
compound.set_drugbank_link()
compound.save()
......
# Generated by Django 2.2.1 on 2020-09-14 18:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('ippidb', '0064_proteindomaincomplex_polymorphic_ctype'),
]
operations = [
migrations.AddField(
model_name='compound',
name='replaced_with',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ippidb.Compound', verbose_name='Replacement ID'),
),
]
......@@ -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
......@@ -570,11 +571,13 @@ class CompoundsManager(models.Manager):
qs = self.get_queryset()
if current_user.is_anonymous:
qs = qs.exclude(compoundaction__ppi__contribution__validated=False)
qs = qs.filter(replaced_with__isnull=True)
elif not current_user.is_superuser:
qs = qs.exclude(
Q(compoundaction__ppi__contribution__validated=False),
~Q(compoundaction__ppi__contribution__contributor=current_user),
)
qs = qs.filter(replaced_with__isnull=True)
return qs
def validated(self):
......@@ -865,6 +868,14 @@ class Compound(AutoFillableModel):
verbose_name="Number of tests available", null=True, blank=True
)
replaced_with = models.ForeignKey(
"self",
verbose_name="Replacement ID",
null=True,
blank=True,
on_delete=models.PROTECT,
)
class Meta:
ordering = ["id"]
indexes = [
......@@ -975,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):
"""
......@@ -1072,6 +1097,59 @@ class Compound(AutoFillableModel):
"""
return reverse("compound_card", kwargs={"pk": self.pk})
@property
def pubchem_url(self):
"""
Get absolute URL to the corresponding PubChem entry
"""
return (
f"https://pubchem.ncbi.nlm.nih.gov/compound/{ self.pubchem_id }"
if self.pubchem_id
else None
)
@property
def chembl_url(self):
"""
Get absolute URL to the corresponding Chembl entry
"""
return (
f"https://www.ebi.ac.uk/chembldb/compound/inspect/{ self.chembl_id }"
if self.chembl_id
else None
)
@property
def chemspider_url(self):
"""
Get absolute URL to the corresponding ChemSpider entry
"""
return (
f"http://www.chemspider.com/Chemical-Structure.{ self.chemspider_id}.html"
if self.chemspider_id
else None
)
@property
def pdbligand_url(self):
"""
Get absolute URL to the corresponding PDB ligand entry
"""
return (
f"https://www.rcsb.org/ligand/{ self.ligand_id}" if self.ligand_id else None
)
@property
def drugbank_url(self):
"""
Get absolute URL to the corresponding Drugbank entry
"""
return (
f"https://www.drugbank.ca/drugs/{ self.drugbank_id}"
if self.drugbank_id
else None
)
def clean(self):
"""
Perform additional checks:
......@@ -1097,7 +1175,9 @@ class Compound(AutoFillableModel):
Replace the references to a given compound in the data with
references to another new compound. used to deal with
duplicates in the database
also delete this object
"""
self.replaced_with = replacing_compound
for ref in RefCompoundBiblio.objects.filter(compound=self):
ref.compound = replacing_compound
ref.save()
......@@ -1118,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")
......@@ -1495,21 +1629,14 @@ class ContributionManager(models.Manager):
if current_user.is_anonymous:
qs = qs.exclude(validated=False)
elif not current_user.is_superuser:
qs = qs.exclude(
Q(validated=False),
~Q(contributor=current_user),
)
qs = qs.exclude(Q(validated=False), ~Q(contributor=current_user),)
return qs
def validated(self):
"""
Get validated contributions
"""
return (
super()
.get_queryset()
.exclude(validated=False)
)
return super().get_queryset().exclude(validated=False)
class Contribution(models.Model):
......@@ -1551,11 +1678,6 @@ def update_compound_cached_properties(compounds_queryset=None):
)
c.save()
compounds_queryset.update(
pubs=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(_pubs=Count("refcompoundbiblio", distinct=True))
.values("_pubs")[:1]
),
best_activity=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(
......@@ -1566,23 +1688,23 @@ def update_compound_cached_properties(compounds_queryset=None):
)
.values("_best_activity")[:1]
),
)
compounds_queryset.update(
pubs=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(_pubs=Count("refcompoundbiblio", distinct=True))
.values("_pubs")[:1]
),
le=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(
_le=Cast(
1.37 * Max("compoundactivityresult__activity") / F("nb_atom_non_h"),
FloatField(),
)
_le=Cast(1.37 * F("best_activity") / F("nb_atom_non_h"), FloatField(),)
)
.values("_le")[:1]
),
lle=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(
_lle=Cast(
Max("compoundactivityresult__activity") - F("a_log_p"), FloatField()
)
)
.annotate(_lle=Cast(F("best_activity") - F("a_log_p"), FloatField()))
.values("_lle")[:1]
),
lipinsky_mw=Subquery(
......@@ -1814,11 +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"))
......
......@@ -225,7 +225,7 @@ Description: IPPI-DB Theme
.input_field {
width: 100%;
margin-right: 2px;
margin-right: 0px;
font-family: "BrandonGrotesqueReg";
text-align: left;
margin-top: 0px;
......@@ -268,6 +268,10 @@ Description: IPPI-DB Theme
padding-bottom: 1%;
}
.form-group.form-check{
padding-left: 2.25rem;
}
.form-group.form-check, .compound .input_field input {
text-align: left;
}
......@@ -356,6 +360,9 @@ Description: IPPI-DB Theme
.formset-item .formset-item-delete-host {
font-size: initial;
text-align: right;
position: absolute;
right: 3em;
margin-top: -4em;
}
.formset-item-delete-host input {
......@@ -1352,6 +1359,7 @@ Description: IPPI-DB Theme
.test_title {
text-align: left;
margin-top: 15px;
margin-bottom: 1.5em;
}
.tests {
......
function test_type_changed(source){
var cell_line_input = document.getElementById(source.id.replace("test_type","cell_line_name")).parentNode;
if (source.value=="CELL"){
cell_line_input.style.display = '';
cell_line_input.style.visibility = '';
}else{
cell_line_input.style.display = 'none';
cell_line_input.style.visibility = 'hidden';
}
}
\ No newline at end of file
......@@ -23,7 +23,11 @@ function add_nested_form_to_nested_formset(source, prefix, parent_prefix){
.find(".formset-nested-item")[0]
.outerHTML;
if($(source).closest(".formset-item").children("input").length>0){
current_item_name = $(source).closest(".formset-item").children("input")[0].name;
}else{
current_item_name = $(source).closest(".formset-item").children("div").children("input")[0].name;
}
current_item_name = current_item_name.substring(parent_prefix.length+1,current_item_name.indexOf('-',parent_prefix.length+1));
empty_form_as_str = empty_form_as_str.replace(
new RegExp(parent_prefix+"-__prefix__","g"),
......
......@@ -625,7 +625,7 @@ def launch_validate_contributions(contribution_ids: List[int]):
run_update_compound_cached_properties.s(),
run_compute_drugbank_similarity.s(),
run_validate.s(),
)
),
)
contribution_jobs.append(compounds_properties_computation_group)
# compounds_properties_computation_group.delay()
......
......@@ -6,7 +6,8 @@
<script src="/static/js/ippidb-charts.js" type="text/javascript"></script>
{% endblock %}
{% block title %}compound {{ compound.id }} detail{% if compound.common_name %} ({{compound.common_name}}){% endif %}{% endblock %}
{% block title %}compound {{ compound.id }} detail{% if compound.common_name %}
({{compound.common_name}}){% endif %}{% endblock %}
{% block content %}
<div id="mainnav">
......@@ -36,7 +37,9 @@
{% include "compound_smiles_draw.html" with id=compound.id smile=compound.canonical_smile width=250 height=250%}
<div style="display: flex;
align-items: center;">
{% if compound.is_validated is False %}
{% if compound.replaced_with is not None %}
<h1 class="page-title-warning" style="margin: 0;">Compound {{ compound.id }} - This compound is replaced with <a href="/compounds/{{ compound.replaced_with.id }}">compound {{ compound.replaced_with.id }}</a> </h1>
{% elif compound.is_validated is False %}
<h1 class="page-title-warning" style="margin: 0;">Compound {{ compound.id }} - This compound has not been
validated by a curator yet, data might be incomplete or
inaccurate</h1>
......@@ -52,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>
......@@ -61,7 +64,8 @@
<div class="tab-pane fade" id="physicochemistry" role="tabpanel" aria-labelledby="physicochemistry-tab">
<div class="row d-flex justify-content-center">
<div class="col-sm-12 col-md-9" style="margin: 10px;">
<h5 class="card_title">Physicochemical filters <span class="info_point" onclick="showLegend('show_legend_physico')"><i class="fas fa-info-circle"></i></span>
<h5 class="card_title">Physicochemical filters <span class="info_point"
onclick="showLegend('show_legend_physico')"><i class="fas fa-info-circle"></i></span>
</h5>
<div id="show_legend_physico" style="display: none; padding:8px;">
<div class="row d-flex justify-content-center" style=" box-shadow: 0 3px 11px 0 rgba(0, 0, 0, 0.2);
......@@ -224,8 +228,10 @@
</tbody>
</table>
</div>
<div class="col" style="margin: 10px;">
<h5 class="card_title">Radar chart<span class="info_point" onclick="showLegend('show_legend_radar')"> <i class="fas fa-info-circle"></i></span>
</div>
<div class="col-sm-12 col-md-9" style="margin: 10px;">
<h5 class="card_title">Radar chart<span class="info_point" onclick="showLegend('show_legend_radar')"> <i
class="fas fa-info-circle"></i></span>
</h5>
<div id="show_legend_radar" style="display: none; padding:8px;">