<commit_before># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------

from __future__ import absolute_import

import os
import re

from django.db import models
from django.db.models.signals import post_delete
from django.dispatch.dispatcher import receiver
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

from feincms import settings
from feincms.models import ExtensionsMixin
from feincms.translations import (TranslatedObjectMixin, Translation,
    TranslatedObjectManager)

from . import logger

# ------------------------------------------------------------------------
class CategoryManager(models.Manager):
    """
    Simple manager which exists only to supply ``.select_related("parent")``
    on querysets since we can't even __unicode__ efficiently without it.
    """
    def get_query_set(self):
        return super(CategoryManager, self).get_query_set().select_related("parent")

# ------------------------------------------------------------------------
class Category(models.Model):
    """
    These categories are meant primarily for organizing media files in the
    library.
    """

    title = models.CharField(_('title'), max_length=200)
    parent = models.ForeignKey('self', blank=True, null=True,
        related_name='children', limit_choices_to={'parent__isnull': True},
        verbose_name=_('parent'))

    slug = models.SlugField(_('slug'), max_length=150)

    class Meta:
        ordering = ['parent__title', 'title']
        verbose_name = _('category')
        verbose_name_plural = _('categories')

    objects = CategoryManager()

    def __unicode__(self):
        if self.parent_id:
            return u'%s - %s' % (self.parent.title, self.title)

        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)

        super(Category, self).save(*args, **kwargs)
    save.alters_data = True

    def path_list(self):
        if self.parent is None:
            return [ self ]
        p = self.parent.path_list()
        p.append(self)
        return p

    def path(self):
        return ' - '.join((f.title for f in self.path_list()))

# ------------------------------------------------------------------------
class MediaFileBase(models.Model, ExtensionsMixin, TranslatedObjectMixin):
    """
    Abstract media file class. Includes the :class:`feincms.models.ExtensionsMixin`
    because of the (handy) extension mechanism.
    """

    file = models.FileField(_('file'), max_length=255, upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO)
    type = models.CharField(_('file type'), max_length=12, editable=False, choices=())
    created = models.DateTimeField(_('created'), editable=False, default=timezone.now)
    copyright = models.CharField(_('copyright'), max_length=200, blank=True)
    file_size  = models.IntegerField(_("file size"), blank=True, null=True, editable=False)

    categories = models.ManyToManyField(Category, verbose_name=_('categories'),
                                        blank=True, null=True)
    categories.category_filter = True

    class Meta:
        abstract = True
        ordering = ['-created']
        verbose_name = _('media file')
        verbose_name_plural = _('media files')

    objects = TranslatedObjectManager()

    filetypes = [ ]
    filetypes_dict = { }

    @classmethod
    def reconfigure(cls, upload_to=None, storage=None):
        f = cls._meta.get_field('file')
        # Ugh. Copied relevant parts from django/db/models/fields/files.py
        # FileField.__init__ (around line 225)
        if storage:
            f.storage = storage
        if upload_to:
            f.upload_to = upload_to
            if callable(upload_to):
                f.generate_filename = upload_to

    @classmethod
    def register_filetypes(cls, *types):
        cls.filetypes[0:0] = types
        choices = [ t[0:2] for t in cls.filetypes ]
        cls.filetypes_dict = dict(choices)
        cls._meta.get_field('type').choices[:] = choices

    def __init__(self, *args, **kwargs):
        super(MediaFileBase, self).__init__(*args, **kwargs)
        if self.file:
            self._original_file_name = self.file.name

    def __unicode__(self):
        trans = None

        try:
            trans = self.translation
        except models.ObjectDoesNotExist:
            pass
        except AttributeError:
            pass

        if trans:
            trans = unicode(trans)
            if trans.strip():
                return trans
        return os.path.basename(self.file.name)

    def get_absolute_url(self):
        return self.file.url

    def determine_file_type(self, name):
        """
        >>> t = MediaFileBase()
        >>> t.determine_file_type('foobar.jpg')
        'image'
        >>> t.determine_file_type('foobar.PDF')
        'pdf'
        >>> t.determine_file_type('foobar.jpg.pdf')
        'pdf'
        >>> t.determine_file_type('foobar.jgp')
        'other'
        >>> t.determine_file_type('foobar-jpg')
        'other'
        """
        for type_key, type_name, type_test in self.filetypes:
            if type_test(name):
                return type_key
        return self.filetypes[-1][0]

    def save(self, *args, **kwargs):
        if not self.id and not self.created:
            self.created = timezone.now()

        self.type = self.determine_file_type(self.file.name)
        if self.file:
            try:
                self.file_size = self.file.size
            except (OSError, IOError, ValueError), e:
                logger.error("Unable to read file size for %s: %s" % (self, e))

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

        logger.info("Saved mediafile %d (%s, type %s, %d bytes)" % (self.id, self.file.name, self.type, self.file_size))

        # User uploaded a new file. Try to get rid of the old file in
        # storage, to avoid having orphaned files hanging around.
        if getattr(self, '_original_file_name', None):
            if self.file.name != self._original_file_name:
                self.delete_mediafile(self._original_file_name)

        self.purge_translation_cache()
    save.alters_data = True

    def delete_mediafile(self, name=None):
        if name is None:
            name = self.file.name
        try:
            self.file.storage.delete(name)
        except Exception, e:
            logger.warn("Cannot delete media file %s: %s" % (name, e))

# ------------------------------------------------------------------------
MediaFileBase.register_filetypes(
        # Should we be using imghdr.what instead of extension guessing?<commit_msg>don't crash if a mediafile object is created without an existing file<commit_after># ------------------------------------------------------------------------
# coding=utf-8
# ------------------------------------------------------------------------

from __future__ import absolute_import

import os
import re

from django.db import models
from django.db.models.signals import post_delete
from django.dispatch.dispatcher import receiver
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

from feincms import settings
from feincms.models import ExtensionsMixin
from feincms.translations import (TranslatedObjectMixin, Translation,
    TranslatedObjectManager)

from . import logger

# ------------------------------------------------------------------------
class CategoryManager(models.Manager):
    """
    Simple manager which exists only to supply ``.select_related("parent")``
    on querysets since we can't even __unicode__ efficiently without it.
    """
    def get_query_set(self):
        return super(CategoryManager, self).get_query_set().select_related("parent")

# ------------------------------------------------------------------------
class Category(models.Model):
    """
    These categories are meant primarily for organizing media files in the
    library.
    """

    title = models.CharField(_('title'), max_length=200)
    parent = models.ForeignKey('self', blank=True, null=True,
        related_name='children', limit_choices_to={'parent__isnull': True},
        verbose_name=_('parent'))

    slug = models.SlugField(_('slug'), max_length=150)

    class Meta:
        ordering = ['parent__title', 'title']
        verbose_name = _('category')
        verbose_name_plural = _('categories')

    objects = CategoryManager()

    def __unicode__(self):
        if self.parent_id:
            return u'%s - %s' % (self.parent.title, self.title)

        return self.title

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)

        super(Category, self).save(*args, **kwargs)
    save.alters_data = True

    def path_list(self):
        if self.parent is None:
            return [ self ]
        p = self.parent.path_list()
        p.append(self)
        return p

    def path(self):
        return ' - '.join((f.title for f in self.path_list()))

# ------------------------------------------------------------------------
class MediaFileBase(models.Model, ExtensionsMixin, TranslatedObjectMixin):
    """
    Abstract media file class. Includes the :class:`feincms.models.ExtensionsMixin`
    because of the (handy) extension mechanism.
    """

    file = models.FileField(_('file'), max_length=255, upload_to=settings.FEINCMS_MEDIALIBRARY_UPLOAD_TO)
    type = models.CharField(_('file type'), max_length=12, editable=False, choices=())
    created = models.DateTimeField(_('created'), editable=False, default=timezone.now)
    copyright = models.CharField(_('copyright'), max_length=200, blank=True)
    file_size  = models.IntegerField(_("file size"), blank=True, null=True, editable=False)

    categories = models.ManyToManyField(Category, verbose_name=_('categories'),
                                        blank=True, null=True)
    categories.category_filter = True

    class Meta:
        abstract = True
        ordering = ['-created']
        verbose_name = _('media file')
        verbose_name_plural = _('media files')

    objects = TranslatedObjectManager()

    filetypes = [ ]
    filetypes_dict = { }

    @classmethod
    def reconfigure(cls, upload_to=None, storage=None):
        f = cls._meta.get_field('file')
        # Ugh. Copied relevant parts from django/db/models/fields/files.py
        # FileField.__init__ (around line 225)
        if storage:
            f.storage = storage
        if upload_to:
            f.upload_to = upload_to
            if callable(upload_to):
                f.generate_filename = upload_to

    @classmethod
    def register_filetypes(cls, *types):
        cls.filetypes[0:0] = types
        choices = [ t[0:2] for t in cls.filetypes ]
        cls.filetypes_dict = dict(choices)
        cls._meta.get_field('type').choices[:] = choices

    def __init__(self, *args, **kwargs):
        super(MediaFileBase, self).__init__(*args, **kwargs)
        if self.file:
            self._original_file_name = self.file.name

    def __unicode__(self):
        trans = None

        try:
            trans = self.translation
        except models.ObjectDoesNotExist:
            pass
        except AttributeError:
            pass

        if trans:
            trans = unicode(trans)
            if trans.strip():
                return trans
        return os.path.basename(self.file.name)

    def get_absolute_url(self):
        return self.file.url

    def determine_file_type(self, name):
        """
        >>> t = MediaFileBase()
        >>> t.determine_file_type('foobar.jpg')
        'image'
        >>> t.determine_file_type('foobar.PDF')
        'pdf'
        >>> t.determine_file_type('foobar.jpg.pdf')
        'pdf'
        >>> t.determine_file_type('foobar.jgp')
        'other'
        >>> t.determine_file_type('foobar-jpg')
        'other'
        """
        for type_key, type_name, type_test in self.filetypes:
            if type_test(name):
                return type_key
        return self.filetypes[-1][0]

    def save(self, *args, **kwargs):
        if not self.id and not self.created:
            self.created = timezone.now()

        self.type = self.determine_file_type(self.file.name)
        if self.file:
            try:
                self.file_size = self.file.size
            except (OSError, IOError, ValueError), e:
                logger.error("Unable to read file size for %s: %s" % (self, e))

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

        logger.info("Saved mediafile %d (%s, type %s, %d bytes)" % (self.id, self.file.name, self.type, self.file_size or 0))

        # User uploaded a new file. Try to get rid of the old file in
        # storage, to avoid having orphaned files hanging around.
        if getattr(self, '_original_file_name', None):
            if self.file.name != self._original_file_name:
                self.delete_mediafile(self._original_file_name)

        self.purge_translation_cache()
    save.alters_data = True

    def delete_mediafile(self, name=None):
        if name is None:
            name = self.file.name
        try:
            self.file.storage.delete(name)
        except Exception, e:
            logger.warn("Cannot delete media file %s: %s" % (name, e))

# ------------------------------------------------------------------------
MediaFileBase.register_filetypes(
        # Should we be using imghdr.what instead of extension guessing?
{'fraction_non_alphanumeric': 0.16590029924823005, 'fraction_numerical': 0.0035763812860375157, 'mean_word_length': 2.0571173583221776, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1869017', 'n_tokens_mistral': 3876, 'n_tokens_neox': 3743, 'n_words': 912}