<filename>proso_tasks/models.py<gh_stars>1-10
from jsonfield import JSONField
from django.db import models
from proso.django.models import ModelDiffMixin
from proso_models.models import Item, ItemRelation, Answer
from django.db.models.signals import pre_save, post_save, pre_delete
from django.dispatch import receiver
from proso.django.models import disable_for_loaddata
import logging


LOGGER = logging.getLogger('django.request')


class TaskManager(models.Manager):

    def prepare_related(self):
        return self


class Task(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_tasks")
    lang = models.CharField(max_length=2)
    content = JSONField()
    active = models.BooleanField(default=True)

    objects = TaskManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_task",
            "lang": self.lang,
            "content": self.content,
        }

        return json

    def __str__(self):
        return "Task {0.pk} ({0.lang})".format(self)


class ContextManager(models.Manager):

    def prepare_related(self):
        return self


class Context(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_contexts")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    content = JSONField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = ContextManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False, with_content=True):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_context",
            "lang": self.lang,
            "name": self.name,
        }
        if with_content:
            json['content'] = self.content
        return json

    def __str__(self):
        return "{0.name} ({0.lang})".format(self)


class TaskInstanceManager(models.Manager):

    def prepare_related(self):
        return self.select_related('context', 'task')


class TaskInstance(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_instances")
    lang = models.CharField(max_length=2)
    task = models.ForeignKey(Task, related_name="instances")
    context = models.ForeignKey(Context, related_name="instances")
    description = JSONField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = TaskInstanceManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False, contexts=True):
        data = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_instance",
            "active": self.active,
            "lang": self.lang,
            "task": self.task.to_json(nested=True),
            "description": self.description
        }
        if not nested and contexts:
            data["context"] = self.context.to_json(nested=True)
        else:
            data["context_id"] = self.context_id
        return data

    def __str__(self):
        return "{0.task} - {0.context}".format(self)


class SkillManager(models.Manager):

    def prepare_related(self):
        return self


class Skill(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_skills")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    active = models.BooleanField(default=True)

    objects = SkillManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_skill",
            "lang": self.lang,
            "name": self.name,
        }
        return json

    def __str__(self):
        return "{0.name} ({0.lang})".format(self)


class TaskAnswerManager(models.Manager):

    def prepare_related(self):
        return self.select_related('context', 'metainfo')

    def from_json(self, json_object, practice_context, practice_set, user_id):
        json_object = dict(json_object)
        task_instance = TaskInstance.objects.get(pk=json_object['task_instance_id'])
        json_object['item_id'] = task_instance.item_id
        json_object['item_asked_id'] = task_instance.item_id
        json_object['item_answered_id'] = task_instance.item_id if json_object['correct'] else None
        json_object['lang'] = task_instance.lang
        answer = Answer.objects.from_json(json_object, practice_context, practice_set, user_id, object_class=TaskAnswer)
        if 'question' in json_object:
            answer.question = json_object['question']
        if 'answer' in json_object:
            answer.answer = json_object['answer']
        answer.save()
        return answer


class TaskAnswer(Answer):
    question = models.CharField(max_length=255, null=True, blank=True)
    answer = models.CharField(max_length=255, null=True, blank=True)

    objects = TaskAnswerManager()

    def to_json(self, nested=False):
        json = Answer.to_json(self, nested=nested)
        json['object_type'] = "task_answer"
        return json


@receiver(pre_save, sender=Task)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=TaskInstance)
@receiver(pre_save, sender=Skill)
@disable_for_loaddata
def create_items(sender, instance, **kwargs):
    """
    When one of the defined objects is created, initialize also its item.
    """
    if instance.item_id is None and instance.item is None:
        item = Item()
        if hasattr(instance, 'active'):
            item.active = getattr(instance, 'active')
        item.save()
        instance.item = item


@receiver(pre_save, sender=Task)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=TaskInstance)
@receiver(pre_save, sender=Skill)
@disable_for_loaddata
def change_activity(sender, instance, **kwargs):
    if 'active' in instance.diff:
        instance.item.active = instance.active
        instance.item.save()


@receiver(post_save, sender=TaskInstance)
@disable_for_loaddata
def add_parent(sender, instance, **kwargs):
    """
    When a task instance is created, create also an item relation.
    """
    if not kwargs['created']:
        return
    for att in ['task', 'context']:
        parent = getattr(instance, att).item_id
        child = instance.item_id
        ItemRelation.objects.get_or_create(
            parent_id=parent,
            child_id=child,
            visible=True,
        )


@receiver(pre_save, sender=TaskInstance)
@disable_for_loaddata
def change_parent(sender, instance, **kwargs):
    """
    When the given task instance has changed. Look at task and context and change
    the corresponding item relation.
    """
    if instance.id is None:
        return
    if len({'task', 'task_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['task'][0] if 'task' in diff else diff['task_id'][0]
        parent_id = parent.item_id if isinstance(parent, Task) else Task.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.task.item_id, child_id=child_id, visible=True)
    if len({'context', 'context_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['context'][0] if 'context' in diff else diff['context_id'][0]
        parent_id = parent.item_id if isinstance(parent, Context) else Context.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.context.item_id, child_id=child_id, visible=False)


@receiver(pre_delete, sender=TaskInstance)
@disable_for_loaddata
def delete_parent(sender, instance, **kwargs):
    """
    When the given task instance is deleted, delete also the corresponding item
    relations.
    """
    ItemRelation.objects.filter(child_id=instance.item_id).delete()

<filename>proso_tasks/models.py<gh_stars>1-10
from jsonfield import JSONField
from django.db import models
from proso.django.models import ModelDiffMixin
from proso_models.models import Item, ItemRelation, Answer
from django.db.models.signals import pre_save, post_save, pre_delete
from django.dispatch import receiver
from proso.django.models import disable_for_loaddata
import logging


LOGGER = logging.getLogger('django.request')


class TaskManager(models.Manager):

    def prepare_related(self):
        return self


class Task(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_tasks")
    lang = models.CharField(max_length=2)
    content = JSONField()
    active = models.BooleanField(default=True)

    objects = TaskManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_task",
            "lang": self.lang,
            "content": self.content,
        }

        return json

    def __str__(self):
        return "Task {0.pk} ({0.lang})".format(self)


class ContextManager(models.Manager):

    def prepare_related(self):
        return self


class Context(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_contexts")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    content = JSONField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = ContextManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False, with_content=True):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_context",
            "lang": self.lang,
            "name": self.name,
        }
        if with_content:
            json['content'] = self.content
        return json

    def __str__(self):
        return "{0.name} ({0.lang})".format(self)


class TaskInstanceManager(models.Manager):

    def prepare_related(self):
        return self.select_related('context', 'task')


class TaskInstance(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_instances")
    lang = models.CharField(max_length=2)
    task = models.ForeignKey(Task, related_name="instances")
    context = models.ForeignKey(Context, related_name="instances")
    description = JSONField(null=True, blank=True)
    active = models.BooleanField(default=True)

    objects = TaskInstanceManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False, contexts=True):
        data = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_instance",
            "active": self.active,
            "lang": self.lang,
            "task": self.task.to_json(nested=True),
            "description": self.description
        }
        if not nested and contexts:
            data["context"] = self.context.to_json(nested=True)
        else:
            data["context_id"] = self.context_id
        return data

    def __str__(self):
        return "{0.task} - {0.context}".format(self)


class SkillManager(models.Manager):

    def prepare_related(self):
        return self


class Skill(models.Model, ModelDiffMixin):
    identifier = models.SlugField()
    item = models.ForeignKey(Item, null=True, default=None, related_name="task_skills")
    lang = models.CharField(max_length=2)
    name = models.TextField()
    active = models.BooleanField(default=True)

    objects = SkillManager()

    class Meta:
        unique_together = ('identifier', 'lang')

    def to_json(self, nested=False):
        json = {
            "id": self.pk,
            "identifier": self.identifier,
            "item_id": self.item_id,
            "object_type": "task_skill",
            "lang": self.lang,
            "name": self.name,
        }
        return json

    def __str__(self):
        return "{0.name} ({0.lang})".format(self)


class TaskAnswerManager(models.Manager):

    def prepare_related(self):
        return self.select_related('context', 'metainfo')

    def from_json(self, json_object, practice_context, practice_set, user_id):
        json_object = dict(json_object)
        task_instance = TaskInstance.objects.get(pk=json_object['task_instance_id'])
        json_object['item_id'] = task_instance.item_id
        json_object['item_asked_id'] = task_instance.item_id
        json_object['item_answered_id'] = task_instance.item_id if json_object['correct'] else None
        json_object['lang'] = task_instance.lang
        answer = Answer.objects.from_json(json_object, practice_context, practice_set, user_id, object_class=TaskAnswer)
        if 'question' in json_object:
            answer.question = json_object['question']
        if 'answer' in json_object:
            answer.answer = json_object['answer']
        answer.save()
        return answer


class TaskAnswer(Answer):
    question = models.CharField(max_length=255, null=True, blank=True)
    answer = models.CharField(max_length=255, null=True, blank=True)

    objects = TaskAnswerManager()

    def to_json(self, nested=False):
        json = Answer.to_json(self, nested=nested)
        json['object_type'] = "task_answer"
        return json


@receiver(pre_save, sender=Task)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=TaskInstance)
@receiver(pre_save, sender=Skill)
@disable_for_loaddata
def create_items(sender, instance, **kwargs):
    """
    When one of the defined objects is created, initialize also its item.
    """
    if instance.item_id is None and instance.item is None:
        item = Item()
        if hasattr(instance, 'active'):
            item.active = getattr(instance, 'active')
        item.save()
        instance.item = item


@receiver(pre_save, sender=Task)
@receiver(pre_save, sender=Context)
@receiver(pre_save, sender=TaskInstance)
@receiver(pre_save, sender=Skill)
@disable_for_loaddata
def change_activity(sender, instance, **kwargs):
    if 'active' in instance.diff:
        instance.item.active = instance.active
        instance.item.save()


@receiver(post_save, sender=TaskInstance)
@disable_for_loaddata
def add_parent(sender, instance, **kwargs):
    """
    When a task instance is created, create also an item relation.
    """
    if not kwargs['created']:
        return
    for att in ['task', 'context']:
        parent = getattr(instance, att).item_id
        child = instance.item_id
        ItemRelation.objects.get_or_create(
            parent_id=parent,
            child_id=child,
            visible=True,
        )


@receiver(pre_save, sender=TaskInstance)
@disable_for_loaddata
def change_parent(sender, instance, **kwargs):
    """
    When the given task instance has changed. Look at task and context and change
    the corresponding item relation.
    """
    if instance.id is None:
        return
    if len({'task', 'task_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['task'][0] if 'task' in diff else diff['task_id'][0]
        parent_id = parent.item_id if isinstance(parent, Task) else Task.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.task.item_id, child_id=child_id, visible=True)
    if len({'context', 'context_id'} & set(instance.changed_fields)) != 0:
        diff = instance.diff
        parent = diff['context'][0] if 'context' in diff else diff['context_id'][0]
        parent_id = parent.item_id if isinstance(parent, Context) else Context.objects.get(pk=parent).item_id
        child_id = instance.item_id
        ItemRelation.objects.filter(parent_id=parent_id, child_id=child_id).delete()
        ItemRelation.objects.create(parent_id=instance.context.item_id, child_id=child_id, visible=False)


@receiver(pre_delete, sender=TaskInstance)
@disable_for_loaddata
def delete_parent(sender, instance, **kwargs):
    """
    When the given task instance is deleted, delete also the corresponding item
    relations.
    """
    ItemRelation.objects.filter(child_id=instance.item_id).delete()

When one of the defined objects is created, initialize also its item. When a task instance is created, create also an item relation. When the given task instance has changed. Look at task and context and change     the corresponding item relation. When the given task instance is deleted, delete also the corresponding item     relations.