from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser
from django.contrib.postgres.fields import JSONField
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
import uuid
import random
import structlog
from pangea.core.mixins import AutoCreatedUpdatedMixin
from .mixins import (
MoneraMixin,
BiotaMixin,
MicrobeMixin,
)
from .constants import MD2_COLUMN_NAMES
[docs]class TaxonName(AutoCreatedUpdatedMixin):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
taxon_id = models.TextField(editable=False, db_index=True)
name = models.TextField(blank=False, db_index=True)
name_type = models.TextField(blank=False, db_index=True)
@property
def tree_node(self):
return TreeNode.objects.get(taxon_id=self.taxon_id)
def __str__(self):
return f'<TreeOfLife::TaxonName name="{self.name}" name_type="{self.name_type}" taxon_id="{self.taxon_id}" uuid="{self.uuid}">'
[docs]class TreeNode(AutoCreatedUpdatedMixin):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
taxon_id = models.TextField(editable=False, db_index=True)
parent = models.ForeignKey(
'TreeNode', on_delete=models.CASCADE, null=True,
related_name='children'
)
rank = models.TextField(blank=False, db_index=True)
def __str__(self):
parent_id = self.taxon_id # root
if self.taxon_id != '1':
parent_id = self.parent.taxon_id
return f'<TreeOfLife::TreeNode taxon_id="{self.taxon_id}" parent_id="{parent_id}" uuid="{self.uuid}"'
@property
def is_root(self):
return self.taxon_id == '1'
@property
def canon_name(self):
return TaxonName.objects.get(taxon_id=self.taxon_id, name_type='scientific name')
@property
def all_names(self):
return TaxonName.objects.filter(taxon_id=self.taxon_id)
@property
def annotation(self):
try:
for attr in ['bacteria_annotation_set', 'archaea_annotation_set',
'fungi_annotation_set', 'virus_annotation_set']:
if hasattr(self, attr):
return getattr(self, attr).all()[0].as_dict()
except IndexError: # No annotation present
return {}
[docs] def ancestors(self, reducer=lambda x: x):
"""Return a list of TreeNodes that are ancestors of this node.
Start with this node."""
out = []
ancestor = self
while ancestor:
out.append(reducer(ancestor))
ancestor = ancestor.parent
return out
[docs] @classmethod
def byname(cls, name):
def get_tid(myname):
names = TaxonName.objects.filter(name=myname) # rarely returns more than one name
if len(names) > 1:
snames = names.filter(name_type='scientific name')
if len(snames) > 0:
names = snames
tid = names[0].taxon_id
return tid
try:
return cls.objects.get(taxon_id=name)
except ObjectDoesNotExist:
try:
tid = get_tid(name)
return cls.objects.get(taxon_id=tid)
except (ObjectDoesNotExist, MultipleObjectsReturned):
tid = get_tid(name.lower())
return cls.objects.get(taxon_id=tid)
[docs]class MicrobeDirectoryEntry(AutoCreatedUpdatedMixin):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
taxon_id = models.TextField(editable=False, db_index=True)
[docs] class Meta:
abstract = True
[docs] def as_dict(self):
"""Return a dict of annotations."""
out = {}
for attr in MD2_COLUMN_NAMES:
if hasattr(self, attr):
out[attr] = getattr(self, attr)
return out
[docs]class Bacteria(MicrobeDirectoryEntry, MoneraMixin,):
tree_node = models.ForeignKey(
TreeNode,
on_delete=models.CASCADE,
related_name='bacteria_annotation_set',
unique=True
)
[docs]class Archaea(MicrobeDirectoryEntry, MoneraMixin,):
tree_node = models.ForeignKey(
TreeNode,
on_delete=models.CASCADE,
related_name='archaea_annotation_set',
unique=True
)
[docs]class Fungi(MicrobeDirectoryEntry, BiotaMixin,):
tree_node = models.ForeignKey(
TreeNode,
on_delete=models.CASCADE,
related_name='fungi_annotation_set',
unique=True
)
[docs]class Virus(MicrobeDirectoryEntry, MicrobeMixin,):
tree_node = models.ForeignKey(
TreeNode,
on_delete=models.CASCADE,
related_name='virus_annotation_set',
unique=True
)
virus_name = models.TextField()
virus_lineage = models.TextField()
kegg_genome = models.TextField()
kegg_disease = models.TextField()
disease = models.TextField()
host_name = models.TextField()
host_lineage = models.TextField()