Source code for mg.orga.tree.models

# MG_GPL_HEADER_BEGIN
# 
# This file is part of Media Gallery, GNU GPLv2.
# 
# Media Gallery is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
# 
# Media Gallery is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Media Gallery; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# 
# MG_GPL_HEADER_END

__author__ = "Media Gallery contributors <mg@lists.ssji.net>"
__copyright__ = "(c) 2010"
__credits__ = ["Dimitri Refauvelet", "Nicolas Pouillon"]


from django.db import models
from django.db import transaction
from django.db.models import F, Q
from django.db.models.signals import post_save, pre_delete

from django.contrib.auth.models import User

from mg.core.element.models import *

__all__ = [
    'Presence', 'Directory', 'Reacheable',
    ]

class Presence(models.Model):
[docs] """ This is a Many2Many "through" table, adding an ordering constraint for simple presence of an Element in a Directory """ directory = models.ForeignKey('Directory', related_name = 'directories') element = models.ForeignKey(Element) order = models.IntegerField(db_index = True) class Meta: unique_together = ( ('directory', 'element'), ('directory', 'order'), ) ordering = ("directory", "order") def __unicode__(self): return u'%s/%s'%(self.directory.name, self.element.name) class Directory(Element):
[docs] """ This is a Directory of Elements, and it is itself an element. """ children = models.ManyToManyField(Element, through = Presence, related_name = "parents") end = models.DateTimeField(null = True, blank = True) @property def children_elements(self):
[docs] return self.children.all().order_by('presence__order') class Meta:
verbose_name_plural = "Directories" def append(self, elem, offset = 0):
[docs] """ Adds an element to the direcotry :param elem: Element to add to this directory """ om = Presence.objects\ .filter(directory = self)\ .order_by("-order") if om: omm = om[0].order + 1 else: omm = 1 p = Presence(directory = self, element = elem, order = omm+offset) p.save() def remove(self, elem):
[docs] """ Removes an element form the Directory :param elem: Element to remove from this directory """ Presence.objects \ .filter(directory = self, element = elem) \ .delete() def rm_rf(self):
""" Deletes all children of this directory (and itself), deleting elements not referenced by other directories. """ for c in self.children.all(): if c.parents.all().count() == 1: if c.type == "directory": c.actual_element.rm_rf() else: c.delete() self.delete() def thumbnail_get(self): try: r = self.subtree_element_get_random() except IndexError, e: return None return r.thumbnail_get() def subtree_element_get_random(self):
[docs] filters = ( Q(parents = self) |Q(parents__reacheable_parents__arity__gt = 0, parents__reacheable_parents__parent = self) ) return Element.objects.filter(filters)\ .exclude(type = "directory").order_by('?')[0] Element.register_type("directory", Directory)
class ReacheableManager(models.Manager): """ Manager for Reacheable model, which only returns reacheabilities where at least one link exists. """ def get_query_set(self): return super(ReacheableManager, self).get_query_set().filter(arity__gt = 0) class Reacheable(models.Model):
[docs] """ Reacheability is a table associating a directory to another directory, with the constraint there is at least one path from "parent" to "child" throught the tree. """ objects = ReacheableManager() any = models.Manager() parent = models.ForeignKey(Directory, related_name = "reacheable_children") child = models.ForeignKey(Directory, related_name = "reacheable_parents") arity = models.SmallIntegerField(db_index = True) class Meta: unique_together = ( ('parent', 'child'), ) def __unicode__(self): return "%s -> %s %d"%(self.parent, self.child, self.arity) @classmethod @transaction.autocommit def ref(cls, parent, child): assert isinstance(parent, Directory), parent assert isinstance(child, Directory), child if 0 == cls.any\ .filter(parent = parent, child = child)\ .update(arity = F('arity')+1): cls(parent = parent, child = child, arity = 1).save() @classmethod @transaction.autocommit def unref(cls, parent, child): assert isinstance(parent, Directory), parent assert isinstance(child, Directory), child assert 1 == cls.any\ .filter(parent = parent, child = child)\ .update(arity = F('arity')-1) @classmethod def presence_add(cls, sender, instance, created, **kwargs): if not instance.element.type == "directory" or not created: return left = instance.directory right = instance.element.directory assert isinstance(left, Directory), left assert isinstance(right, Directory), right lefts = list(Directory.objects.filter(reacheable_children__child = left)) rights = list(Directory.objects.filter(reacheable_parents__parent = right)) for lefter in lefts: cls.ref(lefter, right) for righter in rights: cls.ref(lefter, righter) for righter in rights: cls.ref(left, righter) cls.ref(left, right) @classmethod def presence_rem(cls, sender, instance, **kwargs): if not instance.element.type == "directory": return left = instance.directory right = instance.element.directory for lefter in left.reacheable_parents.all(): for righter in Directory.objects.filter(reacheable_parents = right): cls.unref(lefter, righter) cls.unref(left, right) post_save.connect(Reacheable.presence_add, sender = Presence)
pre_delete.connect(Reacheable.presence_rem, sender = Presence)