<commit_before>from datetime import datetime, timedelta

from django.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator
from django.conf import settings
from django.db import models
from django.http import Http404
from django.shortcuts import redirect
from django.template.defaultfilters import slugify
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _

from ella.core.models import Listing, Category, Publishable
from ella.core.cache import get_cached_object_or_404, cache_this
from ella.core import custom_urls
from ella.core.conf import core_settings
from ella.core.managers import ListingHandler

__docformat__ = "restructuredtext en"

# local cache for get_content_type()
CONTENT_TYPE_MAPPING = {}

class EllaCoreView(object):
    ' Base class for class-based views used in ella.core.views. '

    # name of the template to be passed into get_templates
    template_name = 'TEMPLATE'

    def get_context(self, request, **kwargs):
        """
        Return a dictionary that will be then passed into the template as context.

        :Parameters:
            - `request`: current request

        :Returns:
            Dictionary with all the data
        """
        raise NotImplementedError()

    def get_templates(self, context, template_name=None):
        " Extract parameters for `get_templates` from the context. "
        if not template_name:
            template_name = self.template_name
        kw = {}
        if 'object' in context:
            o = context['object']
            kw['slug'] = o.slug

        if context.get('content_type', False):
            ct = context['content_type']
            kw['app_label'] = ct.app_label
            kw['model_label'] = ct.model

        return get_templates(template_name, category=context['category'], **kw)

    def render(self, request, context, template):
        return TemplateResponse(request, template, context)

    def __call__(self, request, **kwargs):
        context = self.get_context(request, **kwargs)
        return self.render(request, context, self.get_templates(context))

class ObjectDetail(EllaCoreView):
    """
    Renders a page for publishable.  If ``url_remainder`` is specified, tries to
    locate custom view via :meth:`DetailDispatcher.call_view`. If
    :meth:`DetailDispatcher.has_custom_detail` returns ``True``, calls
    :meth:`DetailDispatcher.call_custom_detail`. Otherwise renders a template
    with context containing:

    * object: ``Publishable`` instance representing the URL accessed
    * category: ``Category`` of the ``object``
    * content_type_name: slugified plural verbose name of the publishable's content type
    * content_type: ``ContentType`` of the publishable

    The template is chosen based on the object in question (the first one that matches is used):

    * ``page/category/<tree_path>/content_type/<app>.<model>/<slug>/object.html``
    * ``page/category/<tree_path>/content_type/<app>.<model>/object.html``
    * ``page/category/<tree_path>/object.html``
    * ``page/content_type/<app>.<model>/object.html``
    * ``page/object.html``

    :param request: ``HttpRequest`` from Django
    :param category: ``Category.tree_path`` (empty if home category)
    :param content_type: slugified ``verbose_name_plural`` of the target model
    :param year month day: date matching the `publish_from` field of the `Publishable` object
    :param slug: slug of the `Publishable`
    :param url_remainder: url after the object's url, used to locate custom views in `custom_urls.resolver`

    :raises Http404: if the URL is not valid and/or doesn't correspond to any valid `Publishable`
    """
    template_name = 'object.html'
    def __call__(self, request, category, content_type, slug, year=None, month=None, day=None, id=None, url_remainder=None):
        context = self.get_context(request, category, content_type, slug, year, month, day, id)

        obj = context['object']

        if obj.static and slug != obj.slug:
            return redirect(obj.get_absolute_url(), permanent=True)

        # check for custom actions
        if url_remainder:
            return custom_urls.resolver.call_custom_view(request, obj, url_remainder, context)
        elif custom_urls.resolver.has_custom_detail(obj):
            return custom_urls.resolver.call_custom_detail(request, context)

        return self.render(request, context, self.get_templates(context))

    def get_context(self, request, category, content_type, slug, year, month, day, id):
        ct = get_content_type(content_type)

        try:
            cat = Category.objects.get_by_tree_path(category)
        except Category.DoesNotExist:
            raise Http404('Category with given tree_path doesn\'t exist.')

        if year:
            publishable = get_cached_object_or_404(Publishable,
                        publish_from__year=year,
                        publish_from__month=month,
                        publish_from__day=day,
                        content_type=ct,
                        category=cat,
                        slug=slug,
                        static=False
                    )
        else:
            publishable = get_cached_object_or_404(Publishable, pk=id)
            if publishable.category_id != cat.pk or publishable.content_type_id != ct.id or not publishable.static:
                raise Http404()

        # save existing object to preserve memory and SQL
        publishable.category = cat
        publishable.content_type = ct


        if not (publishable.is_published() or request.user.is_staff):
            # future publish, render if accessed by logged in staff member
            raise Http404

        obj = publishable.target

        context = {
                'object' : obj,
                'category' : cat,
                'content_type_name' : content_type,
                'content_type' : ct,
            }

        return context

def archive_year_cache_key(self, category):
    return 'core.archive_year:%d' % category.pk

class ListContentType(EllaCoreView):
    """
    List objects' listings according to the parameters. If no filtering is
    applied (including pagination), the category's title page is rendered. The
    template used depends on ``template`` attribute for category being rendered.
    Default template is ``category.html``, so it would look like this:

    * ``page/category/<tree_path>/category.html``
    * ``page/category.html``
    
    If custom template is selected, let's say ``static_page.html``, it would
    result in:
    
    * ``page/category/<tree_path>/static_page.html``
    * ``page/static_page.html``

    If filtering is active, an archive template gets rendered:

    * ``page/category/<tree_path>/content_type/<app>.<model>/listing.html``
    * ``page/category/<tree_path>/listing.html``
    * ``page/content_type/<app>.<model>/listing.html``
    * ``page/listing.html``

    The context contains:

    * ``category``
    * ``listings``: list of ``Listing`` objects ordered by date

    * ``page``: ``django.core.paginator.Page`` instance
    * ``is_paginated``: ``True`` if there are more pages
    * ``results_per_page``: number of objects on one page

    * ``content_type``: ``ContentType`` of the objects, if filtered on content type
    * ``content_type_name``: name of the objects' type, if filtered on content type


    :param category: ``tree_path`` of the ``Category``, root category is used if empty
    :param year, month, day: date matching the ``publish_from`` field of the ``Listing`` object.
    :param content_type: slugified verbose_name_plural of the target model, if omitted all content_types are listed
    :param page_no: which page to display
    :keyword paginate_by: number of records in one page

    All parameters are optional, filtering is done on those supplied

    :raises Http404: if the specified category or content_type does not exist or if the given date is malformed.
    """
    template_name = 'listing.html'
    empty_homepage_template_name = 'debug/empty_homepage.html'

    class EmptyHomepageException(Exception): pass

    def __call__(self, request, **kwargs):
        try:
            context = self.get_context(request, **kwargs)
            template_name = context['category'].template
            if core_settings.ARCHIVE_TEMPLATE and not context.get('is_title_page'):
                template_name = self.template_name
            return self.render(request, context, self.get_templates(context, template_name))
        except self.EmptyHomepageException:
            return self.render(request, {}, self.empty_homepage_template_name)

    def _handle_404(self, explanation, is_homepage):
        if settings.DEBUG is True and is_homepage:
            raise self.EmptyHomepageException()
        raise Http404(explanation)

    @cache_this(archive_year_cache_key, timeout=60 * 60 * 24)
    def _archive_entry_year(self, category):
        " Return ARCHIVE_ENTRY_YEAR from settings (if exists) or year of the newest object in category "
        year = getattr(settings, 'ARCHIVE_ENTRY_YEAR', None)
        if not year:
            now = datetime.now()
            try:
                year = Listing.objects.filter(
                        category__site__id=settings.SITE_ID,
                        category__tree_path__startswith=category.tree_path,
                        publish_from__lte=now
                    ).values('publish_from')[0]['publish_from'].year
            except:
                year = now.year
        return year

    def get_context(self, request, category='', year=None, month=None, \
        day=None, content_type=None, paginate_by=core_settings.CATEGORY_LISTINGS_PAGINATE_BY):
        # pagination
        if 'p' in request.GET and request.GET['p'].isdigit():
            page_no = int(request.GET['p'])
        else:
            page_no = 1

        # if we are not on the first page, display a different template
        category_title_page = page_no == 1 and not year

        # Homepage is considered when category is empty (~ no slug) and no
        # filtering is used.
        # 
        # Homepage behaves differently on 404 with DEBUG mode to let user 
        # know everything is fine instead of 404. Also, indication of 
        # homepage is added to context, it's usually good to know, if your
        # on homepage, right? :)
        #
        # @see: _handle_404()
        is_homepage = not bool(category) and page_no == 1 and year is None

        kwa = {}
        if day:
            try:
                start_day = datetime(int(year), int(month), int(day))
            except ValueError:
                return self._handle_404(_('Invalid day value %r') % day,
                    is_homepage)
            kwa['date_range'] = (start_day, start_day + timedelta(seconds=24 * 3600 - 1))
        elif month:
            try:
                start_day = datetime(int(year), int(month), 1)
            except ValueError:
                return self._handle_404(_('Invalid month value %r') % month,
                    is_homepage)
            kwa['date_range'] = (start_day, (start_day + timedelta(days=32)).replace(day=1) - timedelta(seconds=1))
        elif year:
            try:
                start_day = datetime(int(year), 1, 1)
            except ValueError:
                return self._handle_404(_('Invalid year value %r') % month,
                    is_homepage)
            kwa['date_range'] = (start_day, (start_day + timedelta(days=370)).replace(day=1) - timedelta(seconds=1))

        try:
            cat = Category.objects.get_by_tree_path(category)<commit_msg>Catch OverflowError caused by incorrect date values<commit_after>from datetime import datetime, timedelta

from django.contrib.contenttypes.models import ContentType
from django.core.paginator import Paginator
from django.conf import settings
from django.db import models
from django.http import Http404
from django.shortcuts import redirect
from django.template.defaultfilters import slugify
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _

from ella.core.models import Listing, Category, Publishable
from ella.core.cache import get_cached_object_or_404, cache_this
from ella.core import custom_urls
from ella.core.conf import core_settings
from ella.core.managers import ListingHandler

__docformat__ = "restructuredtext en"

# local cache for get_content_type()
CONTENT_TYPE_MAPPING = {}

class EllaCoreView(object):
    ' Base class for class-based views used in ella.core.views. '

    # name of the template to be passed into get_templates
    template_name = 'TEMPLATE'

    def get_context(self, request, **kwargs):
        """
        Return a dictionary that will be then passed into the template as context.

        :Parameters:
            - `request`: current request

        :Returns:
            Dictionary with all the data
        """
        raise NotImplementedError()

    def get_templates(self, context, template_name=None):
        " Extract parameters for `get_templates` from the context. "
        if not template_name:
            template_name = self.template_name
        kw = {}
        if 'object' in context:
            o = context['object']
            kw['slug'] = o.slug

        if context.get('content_type', False):
            ct = context['content_type']
            kw['app_label'] = ct.app_label
            kw['model_label'] = ct.model

        return get_templates(template_name, category=context['category'], **kw)

    def render(self, request, context, template):
        return TemplateResponse(request, template, context)

    def __call__(self, request, **kwargs):
        context = self.get_context(request, **kwargs)
        return self.render(request, context, self.get_templates(context))

class ObjectDetail(EllaCoreView):
    """
    Renders a page for publishable.  If ``url_remainder`` is specified, tries to
    locate custom view via :meth:`DetailDispatcher.call_view`. If
    :meth:`DetailDispatcher.has_custom_detail` returns ``True``, calls
    :meth:`DetailDispatcher.call_custom_detail`. Otherwise renders a template
    with context containing:

    * object: ``Publishable`` instance representing the URL accessed
    * category: ``Category`` of the ``object``
    * content_type_name: slugified plural verbose name of the publishable's content type
    * content_type: ``ContentType`` of the publishable

    The template is chosen based on the object in question (the first one that matches is used):

    * ``page/category/<tree_path>/content_type/<app>.<model>/<slug>/object.html``
    * ``page/category/<tree_path>/content_type/<app>.<model>/object.html``
    * ``page/category/<tree_path>/object.html``
    * ``page/content_type/<app>.<model>/object.html``
    * ``page/object.html``

    :param request: ``HttpRequest`` from Django
    :param category: ``Category.tree_path`` (empty if home category)
    :param content_type: slugified ``verbose_name_plural`` of the target model
    :param year month day: date matching the `publish_from` field of the `Publishable` object
    :param slug: slug of the `Publishable`
    :param url_remainder: url after the object's url, used to locate custom views in `custom_urls.resolver`

    :raises Http404: if the URL is not valid and/or doesn't correspond to any valid `Publishable`
    """
    template_name = 'object.html'
    def __call__(self, request, category, content_type, slug, year=None, month=None, day=None, id=None, url_remainder=None):
        context = self.get_context(request, category, content_type, slug, year, month, day, id)

        obj = context['object']

        if obj.static and slug != obj.slug:
            return redirect(obj.get_absolute_url(), permanent=True)

        # check for custom actions
        if url_remainder:
            return custom_urls.resolver.call_custom_view(request, obj, url_remainder, context)
        elif custom_urls.resolver.has_custom_detail(obj):
            return custom_urls.resolver.call_custom_detail(request, context)

        return self.render(request, context, self.get_templates(context))

    def get_context(self, request, category, content_type, slug, year, month, day, id):
        ct = get_content_type(content_type)

        try:
            cat = Category.objects.get_by_tree_path(category)
        except Category.DoesNotExist:
            raise Http404('Category with given tree_path doesn\'t exist.')

        if year:
            publishable = get_cached_object_or_404(Publishable,
                        publish_from__year=year,
                        publish_from__month=month,
                        publish_from__day=day,
                        content_type=ct,
                        category=cat,
                        slug=slug,
                        static=False
                    )
        else:
            publishable = get_cached_object_or_404(Publishable, pk=id)
            if publishable.category_id != cat.pk or publishable.content_type_id != ct.id or not publishable.static:
                raise Http404()

        # save existing object to preserve memory and SQL
        publishable.category = cat
        publishable.content_type = ct


        if not (publishable.is_published() or request.user.is_staff):
            # future publish, render if accessed by logged in staff member
            raise Http404

        obj = publishable.target

        context = {
                'object' : obj,
                'category' : cat,
                'content_type_name' : content_type,
                'content_type' : ct,
            }

        return context

def archive_year_cache_key(self, category):
    return 'core.archive_year:%d' % category.pk

class ListContentType(EllaCoreView):
    """
    List objects' listings according to the parameters. If no filtering is
    applied (including pagination), the category's title page is rendered. The
    template used depends on ``template`` attribute for category being rendered.
    Default template is ``category.html``, so it would look like this:

    * ``page/category/<tree_path>/category.html``
    * ``page/category.html``
    
    If custom template is selected, let's say ``static_page.html``, it would
    result in:
    
    * ``page/category/<tree_path>/static_page.html``
    * ``page/static_page.html``

    If filtering is active, an archive template gets rendered:

    * ``page/category/<tree_path>/content_type/<app>.<model>/listing.html``
    * ``page/category/<tree_path>/listing.html``
    * ``page/content_type/<app>.<model>/listing.html``
    * ``page/listing.html``

    The context contains:

    * ``category``
    * ``listings``: list of ``Listing`` objects ordered by date

    * ``page``: ``django.core.paginator.Page`` instance
    * ``is_paginated``: ``True`` if there are more pages
    * ``results_per_page``: number of objects on one page

    * ``content_type``: ``ContentType`` of the objects, if filtered on content type
    * ``content_type_name``: name of the objects' type, if filtered on content type


    :param category: ``tree_path`` of the ``Category``, root category is used if empty
    :param year, month, day: date matching the ``publish_from`` field of the ``Listing`` object.
    :param content_type: slugified verbose_name_plural of the target model, if omitted all content_types are listed
    :param page_no: which page to display
    :keyword paginate_by: number of records in one page

    All parameters are optional, filtering is done on those supplied

    :raises Http404: if the specified category or content_type does not exist or if the given date is malformed.
    """
    template_name = 'listing.html'
    empty_homepage_template_name = 'debug/empty_homepage.html'

    class EmptyHomepageException(Exception): pass

    def __call__(self, request, **kwargs):
        try:
            context = self.get_context(request, **kwargs)
            template_name = context['category'].template
            if core_settings.ARCHIVE_TEMPLATE and not context.get('is_title_page'):
                template_name = self.template_name
            return self.render(request, context, self.get_templates(context, template_name))
        except self.EmptyHomepageException:
            return self.render(request, {}, self.empty_homepage_template_name)

    def _handle_404(self, explanation, is_homepage):
        if settings.DEBUG is True and is_homepage:
            raise self.EmptyHomepageException()
        raise Http404(explanation)

    @cache_this(archive_year_cache_key, timeout=60 * 60 * 24)
    def _archive_entry_year(self, category):
        " Return ARCHIVE_ENTRY_YEAR from settings (if exists) or year of the newest object in category "
        year = getattr(settings, 'ARCHIVE_ENTRY_YEAR', None)
        if not year:
            now = datetime.now()
            try:
                year = Listing.objects.filter(
                        category__site__id=settings.SITE_ID,
                        category__tree_path__startswith=category.tree_path,
                        publish_from__lte=now
                    ).values('publish_from')[0]['publish_from'].year
            except:
                year = now.year
        return year

    def get_context(self, request, category='', year=None, month=None, \
        day=None, content_type=None, paginate_by=core_settings.CATEGORY_LISTINGS_PAGINATE_BY):
        # pagination
        if 'p' in request.GET and request.GET['p'].isdigit():
            page_no = int(request.GET['p'])
        else:
            page_no = 1

        # if we are not on the first page, display a different template
        category_title_page = page_no == 1 and not year

        # Homepage is considered when category is empty (~ no slug) and no
        # filtering is used.
        # 
        # Homepage behaves differently on 404 with DEBUG mode to let user 
        # know everything is fine instead of 404. Also, indication of 
        # homepage is added to context, it's usually good to know, if your
        # on homepage, right? :)
        #
        # @see: _handle_404()
        is_homepage = not bool(category) and page_no == 1 and year is None

        kwa = {}
        if day:
            try:
                start_day = datetime(int(year), int(month), int(day))
                kwa['date_range'] = (start_day, start_day + timedelta(seconds=24 * 3600 - 1))
            except (ValueError, OverflowError):
                return self._handle_404(_('Invalid day value %r') % day,
                    is_homepage)
        elif month:
            try:
                start_day = datetime(int(year), int(month), 1)
                kwa['date_range'] = (start_day, (start_day + timedelta(days=32)).replace(day=1) - timedelta(seconds=1))
            except (ValueError, OverflowError):
                return self._handle_404(_('Invalid month value %r') % month,
                    is_homepage)
        elif year:
            try:
                start_day = datetime(int(year), 1, 1)
                kwa['date_range'] = (start_day, (start_day + timedelta(days=370)).replace(day=1) - timedelta(seconds=1))
            except (ValueError, OverflowError):
                return self._handle_404(_('Invalid year value %r') % month,
                    is_homepage)

        try:
            cat = Category.objects.get_by_tree_path(category)
{'fraction_non_alphanumeric': 0.09474939317804369, 'fraction_numerical': 0.006813439509432355, 'mean_word_length': 2.6889726672950047, 'pattern_counts': {'":': 0, '<': 39, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2925751', 'n_tokens_mistral': 6715, 'n_tokens_neox': 6349, 'n_words': 1890}