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 = [ ...@@ -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.
...@@ -172,20 +172,6 @@ class IdForm(forms.Form): ...@@ -172,20 +172,6 @@ class IdForm(forms.Form):
.first() .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 """ """ Step 2 : BibliographyForm """
...@@ -446,7 +432,6 @@ class ComplexCompositionBaseFormSet(forms.BaseFormSet): ...@@ -446,7 +432,6 @@ class ComplexCompositionBaseFormSet(forms.BaseFormSet):
form_kwargs["nb_copies_in_complex"] = nb_copies_in_complex form_kwargs["nb_copies_in_complex"] = nb_copies_in_complex
self.has_partner = has_partner self.has_partner = has_partner
self.has_bound = has_bound self.has_bound = has_bound
print("kwargs", form_kwargs)
super().__init__(form_kwargs=form_kwargs, *args, **kwargs) super().__init__(form_kwargs=form_kwargs, *args, **kwargs)
...@@ -479,7 +464,7 @@ class PpiModelForm(ModelForm): ...@@ -479,7 +464,7 @@ class PpiModelForm(ModelForm):
"data-olsontology": "mondo", "data-olsontology": "mondo",
"data-selectpath": "https://www.ebi.ac.uk/ols/", "data-selectpath": "https://www.ebi.ac.uk/ols/",
"olstype": "", "olstype": "",
"class": "" "class": "",
} }
), ),
) )
...@@ -489,12 +474,6 @@ class PpiModelForm(ModelForm): ...@@ -489,12 +474,6 @@ class PpiModelForm(ModelForm):
required=False, required=False,
) )
# other_diseases = forms.CharField(
# label=_("other_diseases_label"),
# help_text=_("other_diseases_help_text"),
# required=False,
# )
class Meta: class Meta:
model = models.Ppi model = models.Ppi
fields = ( fields = (
...@@ -506,7 +485,6 @@ class PpiModelForm(ModelForm): ...@@ -506,7 +485,6 @@ class PpiModelForm(ModelForm):
"pockets_nb", # pockets_nb is Ppi.pockets_nb in the xlsx file of #33 "pockets_nb", # pockets_nb is Ppi.pockets_nb in the xlsx file of #33
"ols_diseases", "ols_diseases",
"selected_diseases", "selected_diseases",
# 'other_diseases',
) )
widgets = { widgets = {
"id": forms.HiddenInput(), "id": forms.HiddenInput(),
...@@ -533,17 +511,6 @@ class PpiModelForm(ModelForm): ...@@ -533,17 +511,6 @@ class PpiModelForm(ModelForm):
self.fields["symmetry"].initial = symmetry.first() self.fields["symmetry"].initial = symmetry.first()
self.fields["symmetry"].widget = forms.HiddenInput() 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): def full_clean(self):
super().full_clean() super().full_clean()
if hasattr(self, "cleaned_data") and "family_name" in self.cleaned_data: if hasattr(self, "cleaned_data") and "family_name" in self.cleaned_data:
...@@ -571,13 +538,6 @@ class PpiModelForm(ModelForm): ...@@ -571,13 +538,6 @@ class PpiModelForm(ModelForm):
name=name, identifier=identifier name=name, identifier=identifier
) )
self.instance.diseases.add(disease) 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( self.instance.family = models.PpiFamily.objects.get(
id=self.cleaned_data["family"] id=self.cleaned_data["family"]
) )
...@@ -878,6 +838,11 @@ class CompoundActivityResultForm(ModelForm): ...@@ -878,6 +838,11 @@ class CompoundActivityResultForm(ModelForm):
"activity": widgets.HiddenInput(), "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): 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 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( ...@@ -1049,7 +1014,7 @@ CompoundActivityResultInlineFormset = inlineformset_factory(
class TestActivityDescriptionForm(forms.ModelForm): class TestActivityDescriptionForm(forms.ModelForm):
test_name = CharFieldDataList( test_name = CharFieldDataList(
data_class=models.TestActivityDescription, data_attr="test_name" data_class=models.TestActivityDescription, data_attr="test_name",
) )
cell_line_name = CharFieldDataList( cell_line_name = CharFieldDataList(
...@@ -1074,6 +1039,11 @@ class TestActivityDescriptionForm(forms.ModelForm): ...@@ -1074,6 +1039,11 @@ class TestActivityDescriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["test_type"].widget.attrs["onchange"] = "test_type_changed(this);" 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): def has_changed(self):
""" """
...@@ -1201,6 +1171,11 @@ class CompoundCytotoxicityResultForm(ModelForm): ...@@ -1201,6 +1171,11 @@ class CompoundCytotoxicityResultForm(ModelForm):
fields = ("compound_name", "toxicity") fields = ("compound_name", "toxicity")
widgets = {"toxicity": forms.Select(choices=((True, "True"), (False, "False")))} 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): 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 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): ...@@ -1315,6 +1290,9 @@ class TestCytotoxDescriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["compound_concentration"].required = True 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): def has_changed(self):
""" """
...@@ -1413,6 +1391,13 @@ class CompoundPKResultForm(ModelForm): ...@@ -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): 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 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): ...@@ -1524,6 +1509,8 @@ class TestPKDescriptionForm(forms.ModelForm):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for f in ["administration_mode", "dose"]: for f in ["administration_mode", "dose"]:
self.fields[f].required = True self.fields[f].required = True
for f in self.fields.values():
f.widget.attrs.update({"class": "col-3"})
def has_changed(self): def has_changed(self):
""" """
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from ippidb.models import Compound, DrugBankCompound
def run_generate_drugbank_inchikey(apps, schema_editor): def run_generate_drugbank_inchikey(apps, schema_editor):
DrugBankCompound = apps.get_model("ippidb", "DrugBankCompound")
for drugbank_compound in DrugBankCompound.objects.all(): for drugbank_compound in DrugBankCompound.objects.all():
drugbank_compound.save(autofill=True) drugbank_compound.save(autofill=True)
def set_compound_drugbank_links(apps, schema_editor): def set_compound_drugbank_links(apps, schema_editor):
Compound = apps.get_model("ippidb", "Compound")
for compound in Compound.objects.all(): for compound in Compound.objects.all():
compound.set_drugbank_link() compound.set_drugbank_link()
compound.save() 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 ...@@ -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
...@@ -570,11 +571,13 @@ class CompoundsManager(models.Manager): ...@@ -570,11 +571,13 @@ class CompoundsManager(models.Manager):
qs = self.get_queryset() qs = self.get_queryset()
if current_user.is_anonymous: if current_user.is_anonymous:
qs = qs.exclude(compoundaction__ppi__contribution__validated=False) qs = qs.exclude(compoundaction__ppi__contribution__validated=False)
qs = qs.filter(replaced_with__isnull=True)
elif not current_user.is_superuser: elif not current_user.is_superuser:
qs = qs.exclude( qs = qs.exclude(
Q(compoundaction__ppi__contribution__validated=False), Q(compoundaction__ppi__contribution__validated=False),
~Q(compoundaction__ppi__contribution__contributor=current_user), ~Q(compoundaction__ppi__contribution__contributor=current_user),
) )
qs = qs.filter(replaced_with__isnull=True)
return qs return qs
def validated(self): def validated(self):
...@@ -865,6 +868,14 @@ class Compound(AutoFillableModel): ...@@ -865,6 +868,14 @@ class Compound(AutoFillableModel):
verbose_name="Number of tests available", null=True, blank=True 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: class Meta:
ordering = ["id"] ordering = ["id"]
indexes = [ indexes = [
...@@ -975,6 +986,20 @@ class Compound(AutoFillableModel): ...@@ -975,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):
""" """
...@@ -1072,6 +1097,59 @@ class Compound(AutoFillableModel): ...@@ -1072,6 +1097,59 @@ class Compound(AutoFillableModel):
""" """
return reverse("compound_card", kwargs={"pk": self.pk}) 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): def clean(self):
""" """
Perform additional checks: Perform additional checks:
...@@ -1097,7 +1175,9 @@ class Compound(AutoFillableModel): ...@@ -1097,7 +1175,9 @@ class Compound(AutoFillableModel):
Replace the references to a given compound in the data with Replace the references to a given compound in the data with
references to another new compound. used to deal with references to another new compound. used to deal with
duplicates in the database duplicates in the database
also delete this object
""" """
self.replaced_with = replacing_compound
for ref in RefCompoundBiblio.objects.filter(compound=self): for ref in RefCompoundBiblio.objects.filter(compound=self):
ref.compound = replacing_compound ref.compound = replacing_compound
ref.save() ref.save()
...@@ -1118,6 +1198,60 @@ class Compound(AutoFillableModel): ...@@ -1118,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")
...@@ -1495,21 +1629,14 @@ class ContributionManager(models.Manager): ...@@ -1495,21 +1629,14 @@ class ContributionManager(models.Manager):
if current_user.is_anonymous: if current_user.is_anonymous:
qs = qs.exclude(validated=False) qs = qs.exclude(validated=False)
elif not current_user.is_superuser: elif not current_user.is_superuser:
qs = qs.exclude( qs = qs.exclude(Q(validated=False), ~Q(contributor=current_user),)
Q(validated=False),
~Q(contributor=current_user),
)
return qs return qs
def validated(self): def validated(self):
""" """
Get validated contributions Get validated contributions
""" """
return ( return super().get_queryset().exclude(validated=False)
super()
.get_queryset()
.exclude(validated=False)
)
class Contribution(models.Model): class Contribution(models.Model):
...@@ -1551,11 +1678,6 @@ def update_compound_cached_properties(compounds_queryset=None): ...@@ -1551,11 +1678,6 @@ def update_compound_cached_properties(compounds_queryset=None):
) )
c.save() c.save()
compounds_queryset.update( compounds_queryset.update(
pubs=Subquery(
compounds_queryset.filter(id=OuterRef("id"))
.annotate(_pubs=Count("refcompoundbiblio", distinct=True))
.values("_pubs")[:1]
),
best_activity=Subquery( best_activity=Subquery(
compounds_queryset.filter(id=OuterRef("id")) compounds_queryset.filter(id=OuterRef("id"))
.annotate( .annotate(
...@@ -1566,23 +1688,23 @@ def update_compound_cached_properties(compounds_queryset=None): ...@@ -1566,23 +1688,23 @@ def update_compound_cached_properties(compounds_queryset=None):
) )
.values("_best_activity")[:1] .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( le=Subquery(
compounds_queryset.filter(id=OuterRef("id")) compounds_queryset.filter(id=OuterRef("id"))
.annotate( .annotate(
_le=Cast( _le=Cast(1.37 * F("best_activity") / F("nb_atom_non_h"), FloatField(),)
1.37 * Max("compoundactivityresult__activity") / F("nb_atom_non_h"),
FloatField(),
)
) )
.values("_le")[:1] .values("_le")[:1]
), ),
lle=Subquery( lle=Subquery(
compounds_queryset.filter(id=OuterRef("id")) compounds_queryset.filter(id=OuterRef("id"