Ejemplo n.º 1
0
class EventCategory(MPTTModel):
    """
    The category of an event.

    :name: The name of the category.
    :slug: The slug of the category.
    :parent: Allows you to create hierarchies of event categories.

    """
    name = models.CharField(_('Name'), max_length=255)

    slug = AutoSlugField(_('Slug'), populate_from='name')

    parent = TreeForeignKey('self',
                            verbose_name=_('Parent'),
                            null=True,
                            blank=True,
                            related_name='children',
                            db_index=True)

    order = models.PositiveSmallIntegerField(blank=False, default=0)

    objects = TreeManager()

    class Meta:
        ordering = ('lft', )
        verbose_name = _('Event Category')
        verbose_name_plural = _('Event Categories')

    class MPTTMeta:
        order_insertion_by = ('order', )

    def __str__(self):
        return self.name
Ejemplo n.º 2
0
class Category(Post, MPTTModel):  # Для блога
    parent = TreeForeignKey('self',
                            null=True,
                            blank=True,
                            related_name='children',
                            db_index=True)

    objects = TreeManager()

    def make_alias(self):
        return ''
Ejemplo n.º 3
0
class GLAccountType(MPTTModel):
    id = models.SlugField(primary_key=True, max_length=200)
    parent = TreeForeignKey('self',
                            null=True,
                            blank=True,
                            related_name='children')
    account_type_name = models.CharField(max_length=200)
    description = RichTextField(blank=True, null=True)
    creation_date = models.DateTimeField(blank=True, null=True, editable=False)
    update_date = models.DateTimeField(blank=True, null=True, editable=False)
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               blank=True,
                               null=True,
                               editable=False)

    objects = TreeManager()

    def save(self, *args, **kwargs):
        if self.creation_date is None:
            self.creation_date = timezone.now()
        self.update_date = timezone.now()
        self.author = get_request().user
        super(GLAccountType, self).save(*args, **kwargs)

    def __str__(self):
        return "GL Account Type " + self.id

    class Meta:
        app_label = 'accounting'
        db_table = 'acc_gl_account_type'
        verbose_name = _(__name__ + ".table_name")
        verbose_name_plural = _(__name__ + ".table_name_plural")

    class MPTTMeta:
        order_insertion_by = ['id']
        tree_manager_name = 'objects'
Ejemplo n.º 4
0
class ContentNode(MPTTModel, models.Model):
    """
    By default, all nodes have a title and can be used as a topic.
    """
    # The id should be the same between the content curation server and Kolibri.
    id = UUIDField(primary_key=True, default=uuid.uuid4)

    # the content_id is used for tracking a user's interaction with a piece of
    # content, in the face of possibly many copies of that content. When a user
    # interacts with a piece of content, all substantially similar pieces of
    # content should be marked as such as well. We track these "substantially
    # similar" types of content by having them have the same content_id.
    content_id = UUIDField(primary_key=False,
                           default=uuid.uuid4,
                           editable=False,
                           db_index=True)
    node_id = UUIDField(primary_key=False, default=uuid.uuid4, editable=False)

    # TODO: disallow nulls once existing models have been set
    original_channel_id = UUIDField(
        primary_key=False, editable=False, null=True,
        db_index=True)  # Original channel copied from
    source_channel_id = UUIDField(primary_key=False, editable=False,
                                  null=True)  # Immediate channel copied from
    original_source_node_id = UUIDField(
        primary_key=False, editable=False, null=True, db_index=True
    )  # Original node_id of node copied from (TODO: original_node_id clashes with original_node field - temporary)
    source_node_id = UUIDField(
        primary_key=False, editable=False,
        null=True)  # Immediate node_id of node copied from

    # Fields specific to content generated by Ricecooker
    source_id = models.CharField(max_length=200, blank=True, null=True)
    source_domain = models.CharField(max_length=300, blank=True, null=True)

    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    kind = models.ForeignKey('ContentKind',
                             related_name='contentnodes',
                             db_index=True)
    license = models.ForeignKey('License', null=True, blank=True)
    license_description = models.CharField(max_length=400,
                                           null=True,
                                           blank=True)
    prerequisite = models.ManyToManyField(
        'self',
        related_name='is_prerequisite_of',
        through='PrerequisiteContentRelationship',
        symmetrical=False,
        blank=True)
    is_related = models.ManyToManyField('self',
                                        related_name='relate_to',
                                        through='RelatedContentRelationship',
                                        symmetrical=False,
                                        blank=True)
    language = models.ForeignKey('Language',
                                 null=True,
                                 blank=True,
                                 related_name='content_language')
    parent = TreeForeignKey('self',
                            null=True,
                            blank=True,
                            related_name='children',
                            db_index=True)
    tags = models.ManyToManyField(ContentTag,
                                  symmetrical=False,
                                  related_name='tagged_content',
                                  blank=True)
    sort_order = models.FloatField(
        max_length=50,
        default=1,
        verbose_name=_("sort order"),
        help_text=_("Ascending, lowest number shown first"))
    copyright_holder = models.CharField(
        max_length=200,
        null=True,
        blank=True,
        default="",
        help_text=_("Organization of person who holds the essential rights"))
    cloned_source = TreeForeignKey('self',
                                   on_delete=models.SET_NULL,
                                   null=True,
                                   blank=True,
                                   related_name='clones')
    original_node = TreeForeignKey('self',
                                   on_delete=models.SET_NULL,
                                   null=True,
                                   blank=True,
                                   related_name='duplicates')
    thumbnail_encoding = models.TextField(blank=True, null=True)

    created = models.DateTimeField(auto_now_add=True,
                                   verbose_name=_("created"))
    modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
    published = models.BooleanField(default=False)
    publishing = models.BooleanField(default=False)

    changed = models.BooleanField(default=True, db_index=True)
    extra_fields = models.TextField(blank=True, null=True)
    author = models.CharField(max_length=200,
                              blank=True,
                              default="",
                              help_text=_("Person who created content"),
                              null=True)

    role_visibility = models.CharField(max_length=50,
                                       choices=roles.choices,
                                       default=roles.LEARNER)
    freeze_authoring_data = models.BooleanField(default=False)

    objects = TreeManager()

    @raise_if_unsaved
    def get_root(self):
        # Only topics can be root nodes
        if not self.parent and self.kind_id != content_kinds.TOPIC:
            return self
        return super(ContentNode, self).get_root()

    def __init__(self, *args, **kwargs):
        super(ContentNode, self).__init__(*args, **kwargs)
        self._original_fields = self._as_dict(
        )  # Fast way to keep track of updates (no need to query db again)

    def _as_dict(self):
        return dict([(f.name, getattr(self, f.name))
                     for f in self._meta.local_fields if not f.rel])

    def get_changed_fields(self):
        """ Returns a dictionary of all of the changed (dirty) fields """
        new_state = self._as_dict()
        return dict([(key, value)
                     for key, value in self._original_fields.iteritems()
                     if value != new_state[key]])

    def get_tree_data(self, include_self=True):
        if not include_self:
            return [c.get_tree_data() for c in self.children.all()]
        elif self.kind_id == content_kinds.TOPIC:
            return {
                "title": self.title,
                "kind": self.kind_id,
                "children": [c.get_tree_data() for c in self.children.all()],
                "node_id": self.node_id,
            }
        elif self.kind_id == content_kinds.EXERCISE:
            return {
                "title": self.title,
                "kind": self.kind_id,
                "count": self.assessment_items.count(),
                "node_id": self.node_id,
            }
        else:
            return {
                "title":
                self.title,
                "kind":
                self.kind_id,
                "file_size":
                self.files.values('file_size').aggregate(
                    size=Sum('file_size'))['size'],
                "node_id":
                self.node_id,
            }

    def get_node_tree_data(self):
        nodes = []
        for child in self.children.all():
            if child.kind_id == content_kinds.TOPIC:
                nodes.append({
                    "title": child.title,
                    "kind": child.kind_id,
                    "node_id": child.node_id,
                })
            elif child.kind_id == content_kinds.EXERCISE:
                nodes.append({
                    "title": child.title,
                    "kind": child.kind_id,
                    "count": child.assessment_items.count(),
                })
            else:
                nodes.append({
                    "title":
                    child.title,
                    "kind":
                    child.kind_id,
                    "file_size":
                    child.files.values('file_size').aggregate(
                        size=Sum('file_size'))['size'],
                })
        return nodes

    def get_original_node(self):
        original_node = self.original_node or self
        if self.original_channel_id and self.original_source_node_id:
            original_tree_id = Channel.objects.select_related("main_tree").get(
                pk=self.original_channel_id).main_tree.tree_id
            original_node = ContentNode.objects.filter(tree_id=original_tree_id, node_id=self.original_source_node_id).first() or \
                            ContentNode.objects.filter(tree_id=original_tree_id, content_id=self.content_id).first() or self
        return original_node

    def get_associated_presets(self):
        key = "associated_presets_{}".format(self.kind_id)
        cached_data = cache.get(key)
        if cached_data:
            return cached_data
        presets = FormatPreset.objects.filter(kind=self.kind).values()
        cache.set(key, presets, None)
        return presets

    def get_prerequisites(self):
        prerequisite_mapping = {}
        prerequisites = self.prerequisite.all()
        prereqlist = list(prerequisites)
        for prereq in prerequisites:
            prlist, prereqmapping = prereq.get_prerequisites()
            prerequisite_mapping.update({prereq.pk: prereqmapping})
            prereqlist.extend(prlist)
        return prereqlist, prerequisite_mapping

    def get_postrequisites(self):
        postrequisite_mapping = {}
        postrequisites = self.is_prerequisite_of.all()
        postreqlist = list(postrequisites)
        for postreq in postrequisites:
            prlist, postreqmapping = postreq.get_postrequisites()
            postrequisite_mapping.update({postreq.pk: postreqmapping})
            postreqlist.extend(prlist)
        return postreqlist, postrequisite_mapping

    def get_channel(self):
        try:
            root = self.get_root()
            return root.channel_main.first() or root.channel_chef.first(
            ) or root.channel_trash.first() or root.channel_staging.first(
            ) or root.channel_previous.first()
        except (ObjectDoesNotExist, MultipleObjectsReturned, AttributeError):
            return None

    def save(self, *args, **kwargs):
        if kwargs.get('request'):
            request = kwargs.pop('request')
            channel = self.get_channel()
            request.user.can_edit(channel and channel.pk)

        self.changed = self.changed or len(self.get_changed_fields()) > 0

        # Detect if node has been moved to another tree
        if self.pk and ContentNode.objects.filter(pk=self.pk).exists():
            original = ContentNode.objects.get(pk=self.pk)
            if original.parent and original.parent_id != self.parent_id and not original.parent.changed:
                original.parent.changed = True
                original.parent.save()

        if self.original_node is None:
            self.original_node = self
        if self.cloned_source is None:
            self.cloned_source = self

        # TODO: This SIGNIFICANTLY slows down the creation flow
        #   Avoid calling get_channel() (db read)
        channel = (
            self.parent and self.parent.get_channel()) or self.get_channel(
            )  # Check parent first otherwise new content won't have root
        if self.original_channel_id is None:
            self.original_channel_id = channel.id if channel else None
        if self.source_channel_id is None:
            self.source_channel_id = channel.id if channel else None
        if self.original_source_node_id is None:
            self.original_source_node_id = self.node_id
        if self.source_node_id is None:
            self.source_node_id = self.node_id

        super(ContentNode, self).save(*args, **kwargs)

        try:
            # During saving for fixtures, this fails to find the root node
            root = self.get_root()
            if self.is_prerequisite_of.exists() and (
                    root.channel_trash.exists()
                    or root.user_clipboard.exists()):
                PrerequisiteContentRelationship.objects.filter(
                    Q(prerequisite_id=self.id)
                    | Q(target_node_id=self.id)).delete()
        except ContentNode.DoesNotExist:
            pass

    class MPTTMeta:
        order_insertion_by = ['sort_order']

    class Meta:
        verbose_name = _("Topic")
        verbose_name_plural = _("Topics")
Ejemplo n.º 5
0
class Header(BaseAccountModel):
    """Groups Accounts Together."""

    parent = TreeForeignKey('self', blank=True, null=True)
    active = models.BooleanField(default=True)

    objects = TreeManager()

    def __unicode__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('accounts.views.show_accounts_chart',
                       args=[str(self.slug)])

    def account_number(self):
        tree = self.get_root().get_descendants(include_self=True)
        number = list(tree).index(self)
        return number

    def get_account_balance(self):
        """Traverse child Headers and Accounts to generate the current balance.

        :returns: The Value Balance of all :class:`Accounts<Account>` and
                :class:`Headers<Header>` under this Header.
        :rtype: :class:`decimal.Decimal`
        """
        balance = Decimal("0.00")
        child_headers = self.get_children()
        for header in child_headers:
            balance += header.get_account_balance()
        for account in self.account_set.all():
            balance += account.get_balance()
        return balance

    def _calculate_full_number(self):
        """Use type and tree position to generate full account number"""
        if self.parent:
            full_number = "{0}-{1:02d}000".format(self.type,
                                                  self.account_number())
        else:
            full_number = "{0}-00000".format(self.type)
        return full_number

    def _get_change_tree(self):
        """Get extra :class:`Headers<Header>` and :class:`Accounts<Account>`.

        A change in a :class:`Header` may cause changes in the number of Headers
        up to it's grandfather.

        We only save one :class:`Account` under each :class:`Header` because
        each :class:`Account` will save it's siblings.

        :returns: Additional instances to save.
        :rtype: list of :class:`Headers<Header>` and :class:`Accounts<Account>`

        """
        if self.parent and self.parent.parent:
            headers_to_change = list(self.parent.parent.get_descendants())
        else:
            headers_to_change = list(Header.objects.filter(type=self.type))
        accounts_to_change = [account for header in headers_to_change for
                              account in list(header.account_set.all())[-1:]]
        return headers_to_change + accounts_to_change
Ejemplo n.º 6
0
class Album(MPTTModel):
    """Model representing an album"""
    gallery = models.ForeignKey(Gallery)

    parent = TreeForeignKey('self',
                            null=True,
                            blank=True,
                            related_name='children')

    name = models.CharField(_('name'), max_length=250)

    description = models.TextField(_('description'), blank=True)

    image = FileBrowseField(_('image'),
                            max_length=255,
                            null=True,
                            blank=True,
                            default=None)

    template_name = models.CharField(
        _('template'),
        max_length=255,
        help_text=_('Template used to render the album'),
        choices=settings.PORTICUS_ALBUM_TEMPLATE_CHOICES,
        default=settings.PORTICUS_ALBUM_TEMPLATE_DEFAULT)

    publish = models.BooleanField(_('published'),
                                  choices=PUBLISHED_CHOICES,
                                  default=True)

    priority = models.IntegerField(_('display priority'), default=100)

    creation_date = models.DateTimeField(_('creation date'), editable=False)

    slug = models.SlugField(_('slug'), unique=True, max_length=100)

    def __unicode__(self):
        return self.name

    objects = TreeManager()
    published = AlbumPublishedManager()

    #@models.permalink
    #def get_absolute_url(self):
    #return ('porticus:album-detail', (self.gallery.slug, self.slug,))

    def get_tags(self):
        """
        Return a queryset of tags used from album's ressources
        """
        return Tag.objects.get_for_object(self)

    def get_published_children(self):
        """
        Return all ressources for the album and all its children
        """
        return self.get_children().filter(publish=True)

    def get_published_descendants(self):
        """
        Return all ressources for the album and its direct descendants
        """
        return self.get_descendants().filter(publish=True)

    def get_published_ressources(self):
        """
        Return all ressources for the album
        """
        return self.ressource_set.filter(publish=True).order_by(
            'priority', 'name')

    def save(self, *args, **kwargs):
        """
        Fill 'creation_date' attribute on first create
        """
        if self.creation_date is None:
            self.creation_date = tz_now()

        super(Album, self).save(*args, **kwargs)

    class Meta:
        verbose_name = _('album')
        verbose_name_plural = _('albums')

    class MPTTMeta:
        order_insertion_by = ['gallery', 'priority', 'name']
Ejemplo n.º 7
0
class ContentNode(MPTTModel, models.Model):
    """
    By default, all nodes have a title and can be used as a topic.
    """
    # The id should be the same between the content curation server and Kolibri.
    id = UUIDField(primary_key=True, default=uuid.uuid4)

    # the content_id is used for tracking a user's interaction with a piece of
    # content, in the face of possibly many copies of that content. When a user
    # interacts with a piece of content, all substantially similar pieces of
    # content should be marked as such as well. We track these "substantially
    # similar" types of content by having them have the same content_id.
    content_id = UUIDField(primary_key=False,
                           default=uuid.uuid4,
                           editable=False)
    node_id = UUIDField(primary_key=False, default=uuid.uuid4, editable=False)

    # TODO: disallow nulls once existing models have been set
    original_channel_id = UUIDField(
        primary_key=False, editable=False, null=True,
        db_index=True)  # Original channel copied from
    source_channel_id = UUIDField(primary_key=False, editable=False,
                                  null=True)  # Immediate channel copied from
    original_source_node_id = UUIDField(
        primary_key=False, editable=False, null=True, db_index=True
    )  # Original node_id of node copied from (TODO: original_node_id clashes with original_node field - temporary)
    source_node_id = UUIDField(
        primary_key=False, editable=False,
        null=True)  # Immediate node_id of node copied from

    # Fields specific to content generated by Ricecooker
    source_id = models.CharField(max_length=200, blank=True, null=True)
    source_domain = models.CharField(max_length=300, blank=True, null=True)

    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    kind = models.ForeignKey('ContentKind',
                             related_name='contentnodes',
                             db_index=True)
    license = models.ForeignKey('License',
                                null=True,
                                default=settings.DEFAULT_LICENSE)
    license_description = models.CharField(max_length=400,
                                           null=True,
                                           blank=True)
    prerequisite = models.ManyToManyField(
        'self',
        related_name='is_prerequisite_of',
        through='PrerequisiteContentRelationship',
        symmetrical=False,
        blank=True)
    is_related = models.ManyToManyField('self',
                                        related_name='relate_to',
                                        through='RelatedContentRelationship',
                                        symmetrical=False,
                                        blank=True)
    parent = TreeForeignKey('self',
                            null=True,
                            blank=True,
                            related_name='children',
                            db_index=True)
    tags = models.ManyToManyField(ContentTag,
                                  symmetrical=False,
                                  related_name='tagged_content',
                                  blank=True)
    sort_order = models.FloatField(
        max_length=50,
        default=1,
        verbose_name=_("sort order"),
        help_text=_("Ascending, lowest number shown first"))
    copyright_holder = models.CharField(
        max_length=200,
        null=True,
        blank=True,
        default="",
        help_text=_("Organization of person who holds the essential rights"))
    cloned_source = TreeForeignKey('self',
                                   on_delete=models.SET_NULL,
                                   null=True,
                                   blank=True,
                                   related_name='clones')
    original_node = TreeForeignKey('self',
                                   on_delete=models.SET_NULL,
                                   null=True,
                                   blank=True,
                                   related_name='duplicates')

    created = models.DateTimeField(auto_now_add=True,
                                   verbose_name=_("created"))
    modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
    published = models.BooleanField(default=False)

    changed = models.BooleanField(default=True, db_index=True)
    extra_fields = models.TextField(blank=True, null=True)
    author = models.CharField(max_length=200,
                              blank=True,
                              default="",
                              help_text=_("Person who created content"),
                              null=True)

    objects = TreeManager()

    def get_original_node(self):
        original_node = self.original_node or self
        if self.original_channel_id and self.original_source_node_id:
            original_channel = Channel.objects.select_related("main_tree").get(
                pk=self.original_channel_id)
            original_node = ContentNode.objects.filter(
                tree_id=original_channel.main_tree.tree_id,
                node_id=self.original_source_node_id).first() or self
        return original_node

    def get_associated_presets(self):
        key = "associated_presets_{}".format(self.kind_id)
        cached_data = cache.get(key)
        if cached_data:
            return cached_data
        presets = FormatPreset.objects.filter(kind=self.kind).values()
        cache.set(key, presets, None)
        return presets

    def get_channel(self):
        try:
            root = self.get_root()
            return root.channel_main.first() or root.channel_chef.first(
            ) or root.channel_trash.first() or root.channel_staging.first(
            ) or root.channel_previous.first()
        except ObjectDoesNotExist:
            return None

    def save(self, *args, **kwargs):
        # Detect if node has been moved to another tree
        if self.pk and ContentNode.objects.filter(pk=self.pk).exists():
            original = ContentNode.objects.get(pk=self.pk)
            if original.parent and original.parent_id != self.parent_id and not original.parent.changed:
                original.parent.changed = True
                original.parent.save()

        super(ContentNode, self).save(*args, **kwargs)

        post_save_changes = False
        if self.original_node is None:
            self.original_node = self
            post_save_changes = True
        if self.cloned_source is None:
            self.cloned_source = self
            post_save_changes = True

        if self.original_channel_id is None and self.get_channel():
            self.original_channel_id = self.get_channel().id
            post_save_changes = True
        if self.source_channel_id is None and self.get_channel():
            self.source_channel_id = self.get_channel().id
            post_save_changes = True

        if self.original_source_node_id is None:
            self.original_source_node_id = self.node_id
            post_save_changes = True
        if self.source_node_id is None:
            self.source_node_id = self.node_id
            post_save_changes = True

        if post_save_changes:
            self.save()

    class MPTTMeta:
        order_insertion_by = ['sort_order']

    class Meta:
        verbose_name = _("Topic")
        verbose_name_plural = _("Topics")