<commit_before>"""
Django storage backends for Open edX.
"""

import beeline

from django.conf import settings
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.files.storage import get_storage_class, FileSystemStorage
from django.core.cache import caches
from django.utils.deconstruct import deconstructible
from django.utils.lru_cache import lru_cache
from pipeline.storage import NonPackagingMixin
from require.storage import OptimizedFilesMixin
from storages.backends.s3boto3 import S3Boto3Storage

from openedx.core.djangoapps.theming.storage import ThemeCachedFilesMixin, ThemePipelineMixin, ThemeMixin


class PipelineForgivingMixin(object):
    """
    An extension of the django-pipeline storage backend which forgives missing files.
    """
    def hashed_name(self, name, content=None, **kwargs):
        try:
            out = super(PipelineForgivingMixin, self).hashed_name(name, content, **kwargs)
        except ValueError:
            # This means that a file could not be found, and normally this would
            # cause a fatal error, which seems rather excessive given that
            # some packages have missing files in their css all the time.
            out = name
        return out

    def stored_name(self, name):
        try:
            out = super(PipelineForgivingMixin, self).stored_name(name)
        except ValueError:
            # This means that a file could not be found, and normally this would
            # cause a fatal error, which seems rather excessive given that
            # some packages have missing files in their css all the time.
            out = name
        return out


class ProductionMixin(
        PipelineForgivingMixin,
        OptimizedFilesMixin,
        ThemePipelineMixin,
        ThemeCachedFilesMixin,
        ThemeMixin,
):
    """
    This class combines several mixins that provide additional functionality, and
    can be applied over an existing Storage.
    We use this version on production.
    """
    def __init__(self, *args, **kwargs):
        kwargs.update(settings.STATICFILES_STORAGE_KWARGS.get(settings.STATICFILES_STORAGE, {}))
        super(ProductionMixin, self).__init__(*args, **kwargs)


class ProductionStorage(ProductionMixin, StaticFilesStorage):
    pass


class ProductionS3Storage(ProductionMixin, S3Boto3Storage):  # pylint: disable=abstract-method

    @beeline.traced('ProductionS3Storage.url')
    def url(self, name, force=False):
        """
        Return the non-hashed URL in DEBUG mode with cache support.

        Tahoe: RED-1961 This method is created for Tahoe to address mysteriously missing cache.
        """
        beeline.add_context_field('ProductionS3Storage.file_name', name)
        beeline.add_context_field('ProductionS3Storage.force', force)

        static_files_cache = caches['staticfiles']
        cache_entry_name = 'ProductionS3Storage.staticfiles_cache.{}'.format(name)
        beeline.add_context_field('ProductionS3Storage.staticfiles_cache', cache_entry_name)

        url = static_files_cache.get(cache_entry_name)
        if not url:
            beeline.add_context_field('ProductionS3Storage.cached', False)
            url = super().url(name, force)
            static_files_cache.set(cache_entry_name, url)
        else:
            beeline.add_context_field('ProductionS3Storage.cached', True)

        beeline.add_context_field('ProductionS3Storage.hashed_url', url)
        return url


class DevelopmentStorage(
        NonPackagingMixin,
        ThemePipelineMixin,
        ThemeMixin,
        StaticFilesStorage
):
    """
    This class combines Django's StaticFilesStorage class with several mixins
    that provide additional functionality. We use this version for development,
    so that we can skip packaging and optimization.
    """
    pass


@deconstructible
class OverwriteStorage(FileSystemStorage):
    """
    FileSystemStorage subclass which automatically overwrites any previous
    file with the same name; used in test runs to avoid test file proliferation.<commit_msg>remove honeycomb instrumentation for ProductionS3Storage.url<commit_after>"""
Django storage backends for Open edX.
"""

import beeline

from django.conf import settings
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.files.storage import get_storage_class, FileSystemStorage
from django.core.cache import caches
from django.utils.deconstruct import deconstructible
from django.utils.lru_cache import lru_cache
from pipeline.storage import NonPackagingMixin
from require.storage import OptimizedFilesMixin
from storages.backends.s3boto3 import S3Boto3Storage

from openedx.core.djangoapps.theming.storage import ThemeCachedFilesMixin, ThemePipelineMixin, ThemeMixin


class PipelineForgivingMixin(object):
    """
    An extension of the django-pipeline storage backend which forgives missing files.
    """
    def hashed_name(self, name, content=None, **kwargs):
        try:
            out = super(PipelineForgivingMixin, self).hashed_name(name, content, **kwargs)
        except ValueError:
            # This means that a file could not be found, and normally this would
            # cause a fatal error, which seems rather excessive given that
            # some packages have missing files in their css all the time.
            out = name
        return out

    def stored_name(self, name):
        try:
            out = super(PipelineForgivingMixin, self).stored_name(name)
        except ValueError:
            # This means that a file could not be found, and normally this would
            # cause a fatal error, which seems rather excessive given that
            # some packages have missing files in their css all the time.
            out = name
        return out


class ProductionMixin(
        PipelineForgivingMixin,
        OptimizedFilesMixin,
        ThemePipelineMixin,
        ThemeCachedFilesMixin,
        ThemeMixin,
):
    """
    This class combines several mixins that provide additional functionality, and
    can be applied over an existing Storage.
    We use this version on production.
    """
    def __init__(self, *args, **kwargs):
        kwargs.update(settings.STATICFILES_STORAGE_KWARGS.get(settings.STATICFILES_STORAGE, {}))
        super(ProductionMixin, self).__init__(*args, **kwargs)


class ProductionStorage(ProductionMixin, StaticFilesStorage):
    pass


class ProductionS3Storage(ProductionMixin, S3Boto3Storage):  # pylint: disable=abstract-method

    def url(self, name, force=False):
        """
        Return the non-hashed URL in DEBUG mode with cache support.

        Tahoe: RED-1961 This method is created for Tahoe to address mysteriously missing cache.
        """

        static_files_cache = caches['staticfiles']
        cache_entry_name = 'ProductionS3Storage.staticfiles_cache.{}'.format(name)
        beeline.add_context_field('ProductionS3Storage.staticfiles_cache', cache_entry_name)

        url = static_files_cache.get(cache_entry_name)
        if not url:
            url = super().url(name, force)
            static_files_cache.set(cache_entry_name, url)
        return url


class DevelopmentStorage(
        NonPackagingMixin,
        ThemePipelineMixin,
        ThemeMixin,
        StaticFilesStorage
):
    """
    This class combines Django's StaticFilesStorage class with several mixins
    that provide additional functionality. We use this version for development,
    so that we can skip packaging and optimization.
    """
    pass


@deconstructible
class OverwriteStorage(FileSystemStorage):
    """
    FileSystemStorage subclass which automatically overwrites any previous
    file with the same name; used in test runs to avoid test file proliferation.
{'fraction_non_alphanumeric': 0.06551457312354914, 'fraction_numerical': 0.004255867939128192, 'mean_word_length': 3.282164549972391, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '6683236', 'n_tokens_mistral': 2087, 'n_tokens_neox': 2002, 'n_words': 641}