<commit_before>import kerberos
import re
import logging

from requests.auth import AuthBase
from requests.models import Response
from requests.compat import urlparse, StringIO
from requests.structures import CaseInsensitiveDict
from requests.cookies import cookiejar_from_dict

from .exceptions import MutualAuthenticationError

log = logging.getLogger(__name__)

# Different types of mutual authentication:
#  with mutual_authentication set to REQUIRED, all responses will be
#   authenticated with the exception of errors. Errors will have their contents
#   and headers stripped. If a non-error response cannot be authenticated, a
#   MutualAuthenticationError exception will be raised.
# with mutual_authentication set to OPTIONAL, mutual authentication will be
#   attempted if supported, and if supported and failed, a
#   MutualAuthenticationError exception will be raised. Responses which do not
#   support mutual authentication will be returned directly to the user.
# with mutual_authentication set to DISABLED, mutual authentication will not be
#   attempted, even if supported.
REQUIRED = 1
OPTIONAL = 2
DISABLED = 3

class SanitizedResponse(Response):
    """The :class:`Response <Response>` object, which contains a server's
    response to an HTTP request.

    This differs from `requests.models.Response` in that it's headers and
    content have been sanitized. This is only used for HTTP Error messages
    which do not support mutual authentication when mutual authentication is
    required."""

    def __init__(self, response):
        super(SanitizedResponse, self).__init__()
        self.status_code = response.status_code
        self.encoding = response.encoding
        self.raw = response.raw
        self.reason = response.reason
        self.url = response.url
        self.request = response.request
        self.connection = response.connection
        self._content_consumed = True

        self._content = ""
        self.cookies = cookiejar_from_dict({})
        self.headers = CaseInsensitiveDict()
        self.headers['content-length'] = '0'
        for header in ('date', 'server'):
            if header in response.headers:
                self.headers[header] = response.headers[header]

def _negotiate_value(response):
    """Extracts the gssapi authentication token from the appropriate header"""
    if hasattr(_negotiate_value, 'regex'):
        regex = _negotiate_value.regex
    else:
        # There's no need to re-compile this EVERY time it is called. Compile
        # it once and you won't have the performance hit of the compilation.
        regex = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
        _negotiate_value.regex = regex

    authreq = response.headers.get('www-authenticate', None)

    if authreq:
        match_obj = regex.search(authreq)
        if match_obj:
            return match_obj.group(1)

    return None

class HTTPKerberosAuth(AuthBase):
    """Attaches HTTP GSSAPI/Kerberos Authentication to the given Request
    object."""
    def __init__(self, mutual_authentication=REQUIRED):
        self.context = {}
        self.mutual_authentication = mutual_authentication

    def generate_request_header(self, response):
        """
        Generates the GSSAPI authentication token with kerberos.

        If any GSSAPI step fails, return None.

        """
        host = urlparse(response.url).netloc

        try:
            result, self.context[host] = kerberos.authGSSClientInit(
                "HTTP@{0}".format(host))
        except kerberos.GSSError as e:
            log.error("generate_request_header(): authGSSClientInit() failed:")
            log.exception(e)
            return None

        if result < 1:
            log.error("generate_request_header(): authGSSClientInit() failed: "
                      "{0}".format(result))
            return None

        try:
            result = kerberos.authGSSClientStep(self.context[host],
                                                _negotiate_value(response))
        except kerberos.GSSError as e:
            log.error("generate_request_header(): authGSSClientStep() failed:")
            log.exception(e)
            return None

        if result < 0:
            log.error("generate_request_header(): authGSSClientStep() failed: "
                      "{0}".format(result))
            return None

        gss_response = kerberos.authGSSClientResponse(self.context[host])

        return "Negotiate {0}".format(gss_response)

    def authenticate_user(self, response, **kwargs):
        """Handles user authentication with gssapi/kerberos"""

        auth_header = self.generate_request_header(response)
        if auth_header is None:
            # GSS Failure, return existing response
            return response

        log.debug("authenticate_user(): Authorization header: {0}".format(
            auth_header))
        response.request.headers['Authorization'] = auth_header

        # Consume the content so we can reuse the connection for the next
        # request.
        response.content
        response.raw.release_conn()

        _r = response.connection.send(response.request, **kwargs)
        _r.history.append(response)

        log.debug("authenticate_user(): returning {0}".format(_r))
        return _r

    def handle_401(self, response, **kwargs):
        """Handles 401's, attempts to use gssapi/kerberos authentication"""

        log.debug("handle_401(): Handling: 401")
        if _negotiate_value(response) is not None:
            _r = self.authenticate_user(response, **kwargs)
            log.debug("handle_401(): returning {0}".format(_r))
            return _r
        else:
            log.debug("handle_401(): Kerberos is not supported")
            log.debug("handle_401(): returning {0}".format(response))
            return response

    def handle_other(self, response):
        """Handles all responses with the exception of 401s.

        This is necessary so that we can authenticate responses if requested"""

        log.debug("handle_other(): Handling: %d" % response.status_code)

        if self.mutual_authentication in (REQUIRED, OPTIONAL):

            is_http_error = response.status_code >= 400

            if _negotiate_value(response) is not None:
                log.debug("handle_other(): Authenticating the server")
                if not self.authenticate_server(response):
                    # Mutual authentication failure when mutual auth is wanted,
                    # raise an exception so the user doesn't use an untrusted
                    # response.
                    log.error("handle_other(): Mutual authentication failed")
                    raise MutualAuthenticationError("Unable to authenticate "
                                                    "{0}".format(response))

                # Authentication successful
                log.debug("handle_other(): returning {0}".format(response))
                return response

            elif is_http_error or self.mutual_authentication == OPTIONAL:
                log.error("handle_other(): Mutual authentication unavailable "
                          "on {0} response".format(response.status_code))

                if self.mutual_authentication == REQUIRED:
                    return SanitizedResponse(response)
                else:
                    return response
            else:
                # Unable to attempt mutual authentication when mutual auth is
                # required, raise an exception so the user doesnt use an
                # untrusted response.
                log.error("handle_other(): Mutual authentication failed")
                raise MutualAuthenticationError("Unable to authenticate "
                                                "{0}".format(response))
        else:
            log.debug("handle_other(): returning {0}".format(response))
            return response

    def authenticate_server(self, response):
        """
        Uses GSSAPI to authenticate the server.

        Returns True on success, False on failure.
        """

        log.debug("authenticate_server(): Authenticate header: {0}".format(
            _negotiate_value(response)))

        host = urlparse(response.url).netloc

        result = kerberos.authGSSClientStep(self.context[host],
                                            _negotiate_value(response))
        if result < 1:
            log.error("auhenticate_server(): authGSSClientStep() failed: "
                      "{0}".format(result))
            return False

        log.debug("authenticate_server(): returning {0}".format(response))
        return True

    def handle_response(self, response, **kwargs):
        """Takes the given response and tries kerberos-auth, as needed."""

        if response.status_code == 401:
            _r = self.handle_401(response, **kwargs)
            log.debug("handle_response(): returning {0}".format(_r))
            return _r
        else:
            _r = self.handle_other(response)
            log.debug("handle_response(): returning {0}".format(_r))
            return _r

    def deregister(self, response):
        """Deregisters the response handler"""
        response.request.deregister_hook('response', self.handle_response)

    def __call__(self, request):
        request.register_hook('response', self.handle_response)
        return request
<commit_msg>Handle additional uncaught GSSErrors<commit_after>import kerberos
import re
import logging

from requests.auth import AuthBase
from requests.models import Response
from requests.compat import urlparse, StringIO
from requests.structures import CaseInsensitiveDict
from requests.cookies import cookiejar_from_dict

from .exceptions import MutualAuthenticationError

log = logging.getLogger(__name__)

# Different types of mutual authentication:
#  with mutual_authentication set to REQUIRED, all responses will be
#   authenticated with the exception of errors. Errors will have their contents
#   and headers stripped. If a non-error response cannot be authenticated, a
#   MutualAuthenticationError exception will be raised.
# with mutual_authentication set to OPTIONAL, mutual authentication will be
#   attempted if supported, and if supported and failed, a
#   MutualAuthenticationError exception will be raised. Responses which do not
#   support mutual authentication will be returned directly to the user.
# with mutual_authentication set to DISABLED, mutual authentication will not be
#   attempted, even if supported.
REQUIRED = 1
OPTIONAL = 2
DISABLED = 3

class SanitizedResponse(Response):
    """The :class:`Response <Response>` object, which contains a server's
    response to an HTTP request.

    This differs from `requests.models.Response` in that it's headers and
    content have been sanitized. This is only used for HTTP Error messages
    which do not support mutual authentication when mutual authentication is
    required."""

    def __init__(self, response):
        super(SanitizedResponse, self).__init__()
        self.status_code = response.status_code
        self.encoding = response.encoding
        self.raw = response.raw
        self.reason = response.reason
        self.url = response.url
        self.request = response.request
        self.connection = response.connection
        self._content_consumed = True

        self._content = ""
        self.cookies = cookiejar_from_dict({})
        self.headers = CaseInsensitiveDict()
        self.headers['content-length'] = '0'
        for header in ('date', 'server'):
            if header in response.headers:
                self.headers[header] = response.headers[header]

def _negotiate_value(response):
    """Extracts the gssapi authentication token from the appropriate header"""
    if hasattr(_negotiate_value, 'regex'):
        regex = _negotiate_value.regex
    else:
        # There's no need to re-compile this EVERY time it is called. Compile
        # it once and you won't have the performance hit of the compilation.
        regex = re.compile('(?:.*,)*\s*Negotiate\s*([^,]*),?', re.I)
        _negotiate_value.regex = regex

    authreq = response.headers.get('www-authenticate', None)

    if authreq:
        match_obj = regex.search(authreq)
        if match_obj:
            return match_obj.group(1)

    return None

class HTTPKerberosAuth(AuthBase):
    """Attaches HTTP GSSAPI/Kerberos Authentication to the given Request
    object."""
    def __init__(self, mutual_authentication=REQUIRED):
        self.context = {}
        self.mutual_authentication = mutual_authentication

    def generate_request_header(self, response):
        """
        Generates the GSSAPI authentication token with kerberos.

        If any GSSAPI step fails, return None.

        """
        host = urlparse(response.url).netloc

        try:
            result, self.context[host] = kerberos.authGSSClientInit(
                "HTTP@{0}".format(host))
        except kerberos.GSSError as e:
            log.error("generate_request_header(): authGSSClientInit() failed:")
            log.exception(e)
            return None

        if result < 1:
            log.error("generate_request_header(): authGSSClientInit() failed: "
                      "{0}".format(result))
            return None

        try:
            result = kerberos.authGSSClientStep(self.context[host],
                                                _negotiate_value(response))
        except kerberos.GSSError as e:
            log.error("generate_request_header(): authGSSClientStep() failed:")
            log.exception(e)
            return None

        if result < 0:
            log.error("generate_request_header(): authGSSClientStep() failed: "
                      "{0}".format(result))
            return None

        try:
            gss_response = kerberos.authGSSClientResponse(self.context[host])
        except kerberos.GSSError as e:
            log.error("generate_request_header(): authGSSClientResponse() "
                      "failed:")
            log.exception(e)
            return None

        return "Negotiate {0}".format(gss_response)

    def authenticate_user(self, response, **kwargs):
        """Handles user authentication with gssapi/kerberos"""

        auth_header = self.generate_request_header(response)
        if auth_header is None:
            # GSS Failure, return existing response
            return response

        log.debug("authenticate_user(): Authorization header: {0}".format(
            auth_header))
        response.request.headers['Authorization'] = auth_header

        # Consume the content so we can reuse the connection for the next
        # request.
        response.content
        response.raw.release_conn()

        _r = response.connection.send(response.request, **kwargs)
        _r.history.append(response)

        log.debug("authenticate_user(): returning {0}".format(_r))
        return _r

    def handle_401(self, response, **kwargs):
        """Handles 401's, attempts to use gssapi/kerberos authentication"""

        log.debug("handle_401(): Handling: 401")
        if _negotiate_value(response) is not None:
            _r = self.authenticate_user(response, **kwargs)
            log.debug("handle_401(): returning {0}".format(_r))
            return _r
        else:
            log.debug("handle_401(): Kerberos is not supported")
            log.debug("handle_401(): returning {0}".format(response))
            return response

    def handle_other(self, response):
        """Handles all responses with the exception of 401s.

        This is necessary so that we can authenticate responses if requested"""

        log.debug("handle_other(): Handling: %d" % response.status_code)

        if self.mutual_authentication in (REQUIRED, OPTIONAL):

            is_http_error = response.status_code >= 400

            if _negotiate_value(response) is not None:
                log.debug("handle_other(): Authenticating the server")
                if not self.authenticate_server(response):
                    # Mutual authentication failure when mutual auth is wanted,
                    # raise an exception so the user doesn't use an untrusted
                    # response.
                    log.error("handle_other(): Mutual authentication failed")
                    raise MutualAuthenticationError("Unable to authenticate "
                                                    "{0}".format(response))

                # Authentication successful
                log.debug("handle_other(): returning {0}".format(response))
                return response

            elif is_http_error or self.mutual_authentication == OPTIONAL:
                log.error("handle_other(): Mutual authentication unavailable "
                          "on {0} response".format(response.status_code))

                if self.mutual_authentication == REQUIRED:
                    return SanitizedResponse(response)
                else:
                    return response
            else:
                # Unable to attempt mutual authentication when mutual auth is
                # required, raise an exception so the user doesnt use an
                # untrusted response.
                log.error("handle_other(): Mutual authentication failed")
                raise MutualAuthenticationError("Unable to authenticate "
                                                "{0}".format(response))
        else:
            log.debug("handle_other(): returning {0}".format(response))
            return response

    def authenticate_server(self, response):
        """
        Uses GSSAPI to authenticate the server.

        Returns True on success, False on failure.
        """

        log.debug("authenticate_server(): Authenticate header: {0}".format(
            _negotiate_value(response)))

        host = urlparse(response.url).netloc

        try:
            result = kerberos.authGSSClientStep(self.context[host],
                                                _negotiate_value(response))
        except kerberos.GSSError as e:
            log.error("authenticate_server(): authGSSClientStep() failed:")
            log.exception(e)
            return False

        if result < 1:
            log.error("auhenticate_server(): authGSSClientStep() failed: "
                      "{0}".format(result))
            return False

        log.debug("authenticate_server(): returning {0}".format(response))
        return True

    def handle_response(self, response, **kwargs):
        """Takes the given response and tries kerberos-auth, as needed."""

        if response.status_code == 401:
            _r = self.handle_401(response, **kwargs)
            log.debug("handle_response(): returning {0}".format(_r))
            return _r
        else:
            _r = self.handle_other(response)
            log.debug("handle_response(): returning {0}".format(_r))
            return _r

    def deregister(self, response):
        """Deregisters the response handler"""
        response.request.deregister_hook('response', self.handle_response)

    def __call__(self, request):
        request.register_hook('response', self.handle_response)
        return request
<|endoftext|>


<commit_before># mako/lookup.py
# Copyright (C) 2006-2011 the Mako authors and contributors <see AUTHORS file>
#
# This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

import os, stat, posixpath, re
from mako import exceptions, util
from mako.template import Template

try:
    import threading
except:
    import dummy_threading as threading

class TemplateCollection(object):
    """Represent a collection of :class:`.Template` objects,
    identifiable via uri.

    A :class:`.TemplateCollection` is linked to the usage of
    all template tags that address other templates, such
    as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.
    The ``file`` attribute of each of those tags refers
    to a string URI that is passed to that :class:`.Template`
    object's :class:`.TemplateCollection` for resolution.

    :class:`.TemplateCollection` is an abstract class,
    with the usual default implementation being :class:`.TemplateLookup`.

     """

    def has_template(self, uri):
        """Return ``True`` if this :class:`.TemplateLookup` is
        capable of returning a :class:`.Template` object for the
        given URL.

        :param uri: String uri of the template to be resolved.

        """
        try:
            self.get_template(uri)
            return True
        except exceptions.TemplateLookupException:
            return False

    def get_template(self, uri, relativeto=None):
        """Return a :class:`.Template` object corresponding to the given
        URL.

        The default implementation raises
        :class:`.NotImplementedError`. Implementations should
        raise :class:`.TemplateLookupException` if the given uri
        cannot be resolved.

        :param uri: String uri of the template to be resolved.
        :param relativeto: if present, the given URI is assumed to
         be relative to this uri.

        """
        raise NotImplementedError()

    def filename_to_uri(self, uri, filename):
        """Convert the given filename to a uri relative to
           this TemplateCollection."""

        return uri

    def adjust_uri(self, uri, filename):
        """Adjust the given uri based on the calling filename.

        When this method is called from the runtime, the
        'filename' parameter is taken directly to the 'filename'
        attribute of the calling template. Therefore a custom
        TemplateCollection subclass can place any string
        identifier desired in the "filename" parameter of the
        Template objects it constructs and have them come back
        here.

        """
        return uri

class TemplateLookup(TemplateCollection):
    """Represent a collection of templates that locates template source files
    from the local filesystem.

    The primary argument is the ``directories`` argument, the list of
    directories to search::

        lookup = TemplateLookup(["/path/to/templates"])
        some_template = lookup.get_template("/index.html")

    The :class:`.TemplateLookup` can also be given :class:`.Template` objects
    programatically using :meth:`.put_string` or :meth:`.put_template`::

        lookup = TemplateLookup()
        lookup.put_string("base.html", '''
            ${self.next()}
        ''')
        lookup.put_string("hello.html", '''
            <%include file='base.html'/>

            Hello, world !
        ''')

    :param directories: A list of directory names which will be
     searched for a particular template URI. The URI is appended
     to each directory and the filesystem checked.

    :param collection_size: Approximate size of the collection used
     to store templates. If left at its default of -1, the size
     is unbounded, and a plain Python dictionary is used to
     relate URI strings to :class:`.Template` instances.
     Otherwise, a least-recently-used cache object is used which
     will maintain the size of the collection approximately to
     the number given.

    :param filesystem_checks: When at its default value of ``True``,
     each call to :meth:`TemplateLookup.get_template()` will
     compare the filesystem last modified time to the time in
     which an existing :class:`.Template` object was created.
     This allows the :class:`.TemplateLookup` to regenerate a
     new :class:`.Template` whenever the original source has
     been updated. Set this to ``False`` for a very minor
     performance increase.

    :param modulename_callable: A callable which, when present,
     is passed the path of the source file as well as the
     requested URI, and then returns the full path of the
     generated Python module file. This is used to inject
     alternate schemes for Pyhton module location. If left at
     its default of ``None``, the built in system of generation
     based on ``module_directory`` plus ``uri`` is used.

    All other keyword parameters available for
    :class:`.Template` are mirrored here. When new
    :class:`.Template` objects are created, the keywords
    established with this :class:`.TemplateLookup` are passed on
    to each new :class:`.Template`.

    """

    def __init__(self,
                        directories=None,
                        module_directory=None,
                        filesystem_checks=True,
                        collection_size=-1,
                        format_exceptions=False,
                        error_handler=None,
                        disable_unicode=False,
                        output_encoding='ascii',
                        encoding_errors='strict',
                        cache_type=None,
                        cache_dir=None, cache_url=None,
                        cache_enabled=True,
                        modulename_callable=None,
                        default_filters=None,
                        buffer_filters=(),
                        strict_undefined=False,
                        imports=None,
                        input_encoding=None,
                        preprocessor=None):

        self.directories = [posixpath.normpath(d) for d in
                            util.to_list(directories, ())
                            ]
        self.module_directory = module_directory
        self.modulename_callable = modulename_callable
        self.filesystem_checks = filesystem_checks
        self.collection_size = collection_size

        self.template_args = {
            'format_exceptions':format_exceptions,
            'error_handler':error_handler,
            'disable_unicode':disable_unicode,
            'output_encoding':output_encoding,
            'encoding_errors':encoding_errors,
            'input_encoding':input_encoding,
            'module_directory':module_directory,
            'cache_type':cache_type,
            'cache_dir':cache_dir or module_directory,
            'cache_url':cache_url,
            'cache_enabled':cache_enabled,
            'default_filters':default_filters,
            'buffer_filters':buffer_filters,
            'strict_undefined':strict_undefined,
            'imports':imports,
            'preprocessor':preprocessor}

        if collection_size == -1:
            self._collection = {}
            self._uri_cache = {}
        else:
            self._collection = util.LRUCache(collection_size)
            self._uri_cache = util.LRUCache(collection_size)
        self._mutex = threading.Lock()

    def get_template(self, uri):
        """Return a :class:`.Template` object corresponding to the given
        URL.

        Note the "relativeto" argument is not supported here at the moment.

        """

        try:
            if self.filesystem_checks:
                return self._check(uri, self._collection[uri])
            else:
                return self._collection[uri]
        except KeyError:
            u = re.sub(r'^\/+', '', uri)
            for dir in self.directories:
                srcfile = posixpath.normpath(posixpath.join(dir, u))
                if os.path.isfile(srcfile):
                    return self._load(srcfile, uri)
            else:
                raise exceptions.TopLevelLookupException(
                                    "Cant locate template for uri %r" % uri)

    def adjust_uri(self, uri, relativeto):
        """adjust the given uri based on the given relative uri."""

        if uri[0] != '/':
            if relativeto is not None:
                return posixpath.join(posixpath.dirname(relativeto), uri)
            else:
                return '/' + uri
        else:
            return uri

    def filename_to_uri(self, filename):
        """Convert the given filename to a uri relative to
           this TemplateCollection."""

        try:
            return self._uri_cache[filename]
        except KeyError:
            value = self._relativeize(filename)
            self._uri_cache[filename] = value
            return value

    def _relativeize(self, filename):
        """Return the portion of a filename that is 'relative'
           to the directories in this lookup.

        """

        filename = posixpath.normpath(filename)
        for dir in self.directories:
            if filename[0:len(dir)] == dir:
                return filename[len(dir):]
        else:
            return None

    def _load(self, filename, uri):
        self._mutex.acquire()
        try:
            try:
                # try returning from collection one
                # more time in case concurrent thread already loaded
                return self._collection[uri]
            except KeyError:
                pass
            try:
                if self.modulename_callable is not None:
                    module_filename = self.modulename_callable(filename, uri)
                else:
                    module_filename = None
                self._collection[uri] = template = Template(
                                        uri=uri,
                                        filename=posixpath.normpath(filename),
                                        lookup=self,
                                        module_filename=module_filename,
                                        **self.template_args)
                return template
            except:
                # if compilation fails etc, ensure
                # template is removed from collection,
                # re-raise
                self._collection.pop(uri, None)
                raise
        finally:
            self._mutex.release()

    def _check(self, uri, template):
        if template.filename is None:
            return template

        try:
            template_stat = os.stat(template.filename)
            if template.module._modified_time < \
                        template_stat[stat.ST_MTIME]:
                self._collection.pop(uri, None)
                return self._load(template.filename, uri)
            else:
                return template
        except OSError:
            self._collection.pop(uri, None)
            raise exceptions.TemplateLookupException(
                                "Cant locate template for uri %r" % uri)

    def put_string(self, uri, text):
        """Place a new :class:`.Template` object into this
        :class:`.TemplateLookup`, based on the given string of
        text.

        """
        self._collection[uri] = Template(
                                    text,
                                    lookup=self,
                                    uri=uri,
                                    **self.template_args)

    def put_template(self, uri, template):
        """Place a new :class:`.Template` object into this
        :class:`.TemplateLookup`, based on the given
        :class:`.Template` object.

        """
        self._collection[uri] = template

<commit_msg>- use the _uri_cache for adjust_uri(), as this is called each time there's a namespace or include<commit_after># mako/lookup.py
# Copyright (C) 2006-2011 the Mako authors and contributors <see AUTHORS file>
#
# This module is part of Mako and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

import os, stat, posixpath, re
from mako import exceptions, util
from mako.template import Template

try:
    import threading
except:
    import dummy_threading as threading

class TemplateCollection(object):
    """Represent a collection of :class:`.Template` objects,
    identifiable via uri.

    A :class:`.TemplateCollection` is linked to the usage of
    all template tags that address other templates, such
    as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.
    The ``file`` attribute of each of those tags refers
    to a string URI that is passed to that :class:`.Template`
    object's :class:`.TemplateCollection` for resolution.

    :class:`.TemplateCollection` is an abstract class,
    with the usual default implementation being :class:`.TemplateLookup`.

     """

    def has_template(self, uri):
        """Return ``True`` if this :class:`.TemplateLookup` is
        capable of returning a :class:`.Template` object for the
        given URL.

        :param uri: String uri of the template to be resolved.

        """
        try:
            self.get_template(uri)
            return True
        except exceptions.TemplateLookupException:
            return False

    def get_template(self, uri, relativeto=None):
        """Return a :class:`.Template` object corresponding to the given
        URL.

        The default implementation raises
        :class:`.NotImplementedError`. Implementations should
        raise :class:`.TemplateLookupException` if the given uri
        cannot be resolved.

        :param uri: String uri of the template to be resolved.
        :param relativeto: if present, the given URI is assumed to
         be relative to this uri.

        """
        raise NotImplementedError()

    def filename_to_uri(self, uri, filename):
        """Convert the given filename to a uri relative to
           this TemplateCollection."""

        return uri

    def adjust_uri(self, uri, filename):
        """Adjust the given uri based on the calling filename.

        When this method is called from the runtime, the
        'filename' parameter is taken directly to the 'filename'
        attribute of the calling template. Therefore a custom
        TemplateCollection subclass can place any string
        identifier desired in the "filename" parameter of the
        Template objects it constructs and have them come back
        here.

        """
        return uri

class TemplateLookup(TemplateCollection):
    """Represent a collection of templates that locates template source files
    from the local filesystem.

    The primary argument is the ``directories`` argument, the list of
    directories to search::

        lookup = TemplateLookup(["/path/to/templates"])
        some_template = lookup.get_template("/index.html")

    The :class:`.TemplateLookup` can also be given :class:`.Template` objects
    programatically using :meth:`.put_string` or :meth:`.put_template`::

        lookup = TemplateLookup()
        lookup.put_string("base.html", '''
            ${self.next()}
        ''')
        lookup.put_string("hello.html", '''
            <%include file='base.html'/>

            Hello, world !
        ''')

    :param directories: A list of directory names which will be
     searched for a particular template URI. The URI is appended
     to each directory and the filesystem checked.

    :param collection_size: Approximate size of the collection used
     to store templates. If left at its default of -1, the size
     is unbounded, and a plain Python dictionary is used to
     relate URI strings to :class:`.Template` instances.
     Otherwise, a least-recently-used cache object is used which
     will maintain the size of the collection approximately to
     the number given.

    :param filesystem_checks: When at its default value of ``True``,
     each call to :meth:`TemplateLookup.get_template()` will
     compare the filesystem last modified time to the time in
     which an existing :class:`.Template` object was created.
     This allows the :class:`.TemplateLookup` to regenerate a
     new :class:`.Template` whenever the original source has
     been updated. Set this to ``False`` for a very minor
     performance increase.

    :param modulename_callable: A callable which, when present,
     is passed the path of the source file as well as the
     requested URI, and then returns the full path of the
     generated Python module file. This is used to inject
     alternate schemes for Pyhton module location. If left at
     its default of ``None``, the built in system of generation
     based on ``module_directory`` plus ``uri`` is used.

    All other keyword parameters available for
    :class:`.Template` are mirrored here. When new
    :class:`.Template` objects are created, the keywords
    established with this :class:`.TemplateLookup` are passed on
    to each new :class:`.Template`.

    """

    def __init__(self,
                        directories=None,
                        module_directory=None,
                        filesystem_checks=True,
                        collection_size=-1,
                        format_exceptions=False,
                        error_handler=None,
                        disable_unicode=False,
                        output_encoding='ascii',
                        encoding_errors='strict',
                        cache_type=None,
                        cache_dir=None, cache_url=None,
                        cache_enabled=True,
                        modulename_callable=None,
                        default_filters=None,
                        buffer_filters=(),
                        strict_undefined=False,
                        imports=None,
                        input_encoding=None,
                        preprocessor=None):

        self.directories = [posixpath.normpath(d) for d in
                            util.to_list(directories, ())
                            ]
        self.module_directory = module_directory
        self.modulename_callable = modulename_callable
        self.filesystem_checks = filesystem_checks
        self.collection_size = collection_size

        self.template_args = {
            'format_exceptions':format_exceptions,
            'error_handler':error_handler,
            'disable_unicode':disable_unicode,
            'output_encoding':output_encoding,
            'encoding_errors':encoding_errors,
            'input_encoding':input_encoding,
            'module_directory':module_directory,
            'cache_type':cache_type,
            'cache_dir':cache_dir or module_directory,
            'cache_url':cache_url,
            'cache_enabled':cache_enabled,
            'default_filters':default_filters,
            'buffer_filters':buffer_filters,
            'strict_undefined':strict_undefined,
            'imports':imports,
            'preprocessor':preprocessor}

        if collection_size == -1:
            self._collection = {}
            self._uri_cache = {}
        else:
            self._collection = util.LRUCache(collection_size)
            self._uri_cache = util.LRUCache(collection_size)
        self._mutex = threading.Lock()

    def get_template(self, uri):
        """Return a :class:`.Template` object corresponding to the given
        URL.

        Note the "relativeto" argument is not supported here at the moment.

        """

        try:
            if self.filesystem_checks:
                return self._check(uri, self._collection[uri])
            else:
                return self._collection[uri]
        except KeyError:
            u = re.sub(r'^\/+', '', uri)
            for dir in self.directories:
                srcfile = posixpath.normpath(posixpath.join(dir, u))
                if os.path.isfile(srcfile):
                    return self._load(srcfile, uri)
            else:
                raise exceptions.TopLevelLookupException(
                                    "Cant locate template for uri %r" % uri)

    def adjust_uri(self, uri, relativeto):
        """adjust the given uri based on the given relative uri."""

        key = (uri, relativeto)
        if key in self._uri_cache:
            return self._uri_cache[key]

        if uri[0] != '/':
            if relativeto is not None:
                v = self._uri_cache[key] = posixpath.join(posixpath.dirname(relativeto), uri)
            else:
                v = self._uri_cache[key] = '/' + uri
        else:
            v = self._uri_cache[key] = uri
        return v

    def filename_to_uri(self, filename):
        """Convert the given filename to a uri relative to
           this TemplateCollection."""

        try:
            return self._uri_cache[filename]
        except KeyError:
            value = self._relativeize(filename)
            self._uri_cache[filename] = value
            return value

    def _relativeize(self, filename):
        """Return the portion of a filename that is 'relative'
           to the directories in this lookup.

        """

        filename = posixpath.normpath(filename)
        for dir in self.directories:
            if filename[0:len(dir)] == dir:
                return filename[len(dir):]
        else:
            return None

    def _load(self, filename, uri):
        self._mutex.acquire()
        try:
            try:
                # try returning from collection one
                # more time in case concurrent thread already loaded
                return self._collection[uri]
            except KeyError:
                pass
            try:
                if self.modulename_callable is not None:
                    module_filename = self.modulename_callable(filename, uri)
                else:
                    module_filename = None
                self._collection[uri] = template = Template(
                                        uri=uri,
                                        filename=posixpath.normpath(filename),
                                        lookup=self,
                                        module_filename=module_filename,
                                        **self.template_args)
                return template
            except:
                # if compilation fails etc, ensure
                # template is removed from collection,
                # re-raise
                self._collection.pop(uri, None)
                raise
        finally:
            self._mutex.release()

    def _check(self, uri, template):
        if template.filename is None:
            return template

        try:
            template_stat = os.stat(template.filename)
            if template.module._modified_time < \
                        template_stat[stat.ST_MTIME]:
                self._collection.pop(uri, None)
                return self._load(template.filename, uri)
            else:
                return template
        except OSError:
            self._collection.pop(uri, None)
            raise exceptions.TemplateLookupException(
                                "Cant locate template for uri %r" % uri)

    def put_string(self, uri, text):
        """Place a new :class:`.Template` object into this
        :class:`.TemplateLookup`, based on the given string of
        text.

        """
        self._collection[uri] = Template(
                                    text,
                                    lookup=self,
                                    uri=uri,
                                    **self.template_args)

    def put_template(self, uri, template):
        """Place a new :class:`.Template` object into this
        :class:`.TemplateLookup`, based on the given
        :class:`.Template` object.

        """
        self._collection[uri] = template

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import re
from typing import List

REPLY_PATTERNS = [
    "^On (.*) wrote:$",  # apple mail/gmail reply
    "^Am (.*) schrieb (.*):$",  # German
    "^Le (.*) a écrit :$",  # French
    "El (.*) escribió:$",  # Spanish
    "^(.*) написал\(а\):$",  # Russian
    "^Den (.*) skrev (.*):$",  # Swedish
    "^Em (.*) escreveu:$",  # Brazillian portuguese
    "([0-9]{4}/[0-9]{1,2}/[0-9]{1,2}) (.* <.*@.*>)$",  # gmail (?) reply
]

REPLY_DATE_SPLIT_REGEX = re.compile(r"^(.*(:[0-9]{2}( [apAP]\.?[mM]\.?)?)), (.*)?$")

FORWARD_MESSAGES = [
    # apple mail forward
    "Begin forwarded message",
    "Anfang der weitergeleiteten E-Mail",
    "Début du message réexpédié",
    "Inicio del mensaje reenviado",
    # gmail/evolution forward
    "Forwarded [mM]essage",
    "Mensaje reenviado",
    "Vidarebefordrat meddelande",
    # outlook
    "Original [mM]essage",
    "Ursprüngliche Nachricht",
    "Mensaje [oO]riginal",
    # Thunderbird forward
    "Message transféré",
    # mail.ru forward (Russian)
    "Пересылаемое сообщение",
]

# We yield this pattern to simulate Outlook forward styles. It is also used for
# some emails forwarded by Yahoo.
FORWARD_LINE = "________________________________"

FORWARD_PATTERNS = (
    [
        "^{}$".format(FORWARD_LINE),
    ]
    + ["^---+ ?%s ?---+$" % p for p in FORWARD_MESSAGES]
    + ["^%s:$" % p for p in FORWARD_MESSAGES]
)

FORWARD_STYLES = [
    # Outlook
    "border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in",
]

HEADER_RE = re.compile(r"\*?([-\w ]+):\*?(.*)$", re.UNICODE)

HEADER_MAP = {
    "from": "from",
    "von": "from",
    "de": "from",
    "от кого": "from",
    "från": "from",
    "to": "to",
    "an": "to",
    "para": "to",
    "à": "to",
    "pour": "to",
    "кому": "to",
    "till": "to",
    "cc": "cc",
    "kopie": "cc",
    "kopia": "cc",
    "bcc": "bcc",
    "cco": "bcc",
    "blindkopie": "bcc",
    "reply-to": "reply-to",
    "antwort an": "reply-to",
    "répondre à": "reply-to",
    "responder a": "reply-to",
    "date": "date",
    "sent": "date",
    "received": "date",
    "datum": "date",
    "gesendet": "date",
    "enviado el": "date",
    "enviados": "date",
    "fecha": "date",
    "дата": "date",
    "subject": "subject",
    "betreff": "subject",
    "asunto": "subject",
    "objet": "subject",
    "sujet": "subject",
    "тема": "subject",
    "ämne": "subject",
}

COMPILED_PATTERN_MAP = {
    "reply": [re.compile(regex) for regex in REPLY_PATTERNS],
    "forward": [re.compile(regex) for regex in FORWARD_PATTERNS],
}

COMPILED_PATTERNS: List[re.Pattern] = sum(COMPILED_PATTERN_MAP.values(), [])

MULTIPLE_WHITESPACE_RE = re.compile("\s+")

# Amount to lines to join to check for potential wrapped patterns in plain text
# messages.
MAX_WRAP_LINES = 2

# minimum number of headers that we recognize
MIN_HEADER_LINES = 2

# minimum number of lines to recognize a quoted block
MIN_QUOTED_LINES = 3

# Characters at the end of line where we join lines without adding a space.
# For example, "John <\njohn@example>" becomes "John <john@example>", but
# "John\nDoe" becomes "John Doe".
STRIP_SPACE_CHARS = "<([{\"'"
<commit_msg>fix bad escapes for regex string<commit_after># -*- coding: utf-8 -*-

import re
from typing import List

REPLY_PATTERNS = [
    "^On (.*) wrote:$",  # apple mail/gmail reply
    "^Am (.*) schrieb (.*):$",  # German
    "^Le (.*) a écrit :$",  # French
    "El (.*) escribió:$",  # Spanish
    r"^(.*) написал\(а\):$",  # Russian
    "^Den (.*) skrev (.*):$",  # Swedish
    "^Em (.*) escreveu:$",  # Brazillian portuguese
    "([0-9]{4}/[0-9]{1,2}/[0-9]{1,2}) (.* <.*@.*>)$",  # gmail (?) reply
]

REPLY_DATE_SPLIT_REGEX = re.compile(r"^(.*(:[0-9]{2}( [apAP]\.?[mM]\.?)?)), (.*)?$")

FORWARD_MESSAGES = [
    # apple mail forward
    "Begin forwarded message",
    "Anfang der weitergeleiteten E-Mail",
    "Début du message réexpédié",
    "Inicio del mensaje reenviado",
    # gmail/evolution forward
    "Forwarded [mM]essage",
    "Mensaje reenviado",
    "Vidarebefordrat meddelande",
    # outlook
    "Original [mM]essage",
    "Ursprüngliche Nachricht",
    "Mensaje [oO]riginal",
    # Thunderbird forward
    "Message transféré",
    # mail.ru forward (Russian)
    "Пересылаемое сообщение",
]

# We yield this pattern to simulate Outlook forward styles. It is also used for
# some emails forwarded by Yahoo.
FORWARD_LINE = "________________________________"

FORWARD_PATTERNS = (
    [
        "^{}$".format(FORWARD_LINE),
    ]
    + ["^---+ ?%s ?---+$" % p for p in FORWARD_MESSAGES]
    + ["^%s:$" % p for p in FORWARD_MESSAGES]
)

FORWARD_STYLES = [
    # Outlook
    "border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in",
]

HEADER_RE = re.compile(r"\*?([-\w ]+):\*?(.*)$", re.UNICODE)

HEADER_MAP = {
    "from": "from",
    "von": "from",
    "de": "from",
    "от кого": "from",
    "från": "from",
    "to": "to",
    "an": "to",
    "para": "to",
    "à": "to",
    "pour": "to",
    "кому": "to",
    "till": "to",
    "cc": "cc",
    "kopie": "cc",
    "kopia": "cc",
    "bcc": "bcc",
    "cco": "bcc",
    "blindkopie": "bcc",
    "reply-to": "reply-to",
    "antwort an": "reply-to",
    "répondre à": "reply-to",
    "responder a": "reply-to",
    "date": "date",
    "sent": "date",
    "received": "date",
    "datum": "date",
    "gesendet": "date",
    "enviado el": "date",
    "enviados": "date",
    "fecha": "date",
    "дата": "date",
    "subject": "subject",
    "betreff": "subject",
    "asunto": "subject",
    "objet": "subject",
    "sujet": "subject",
    "тема": "subject",
    "ämne": "subject",
}

COMPILED_PATTERN_MAP = {
    "reply": [re.compile(regex) for regex in REPLY_PATTERNS],
    "forward": [re.compile(regex) for regex in FORWARD_PATTERNS],
}

COMPILED_PATTERNS: List[re.Pattern] = sum(COMPILED_PATTERN_MAP.values(), [])

MULTIPLE_WHITESPACE_RE = re.compile(r"\s+")

# Amount to lines to join to check for potential wrapped patterns in plain text
# messages.
MAX_WRAP_LINES = 2

# minimum number of headers that we recognize
MIN_HEADER_LINES = 2

# minimum number of lines to recognize a quoted block
MIN_QUOTED_LINES = 3

# Characters at the end of line where we join lines without adding a space.
# For example, "John <\njohn@example>" becomes "John <john@example>", but
# "John\nDoe" becomes "John Doe".
STRIP_SPACE_CHARS = r"<([{\"'"
<|endoftext|>


<commit_before>from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from datetime import datetime
import logging

from models import Submission,Grader
import util
import json

log = logging.getLogger(__name__)

@csrf_exempt
@login_required
def submit(request):
    '''
    Xqueue pull script posts objects here.
    Input:

    request - dict with keys xqueue_header and xqueue_body
    xqueue_header needs submission_id,submission_key,queue_name
    xqueue_body needs grader_payload, student_info, student_response, max_score
    grader_payload needs location, course_id,problem_id,grader
    student_info needs anonymous_student_id, submission_time

    Output:
    Returns status code indicating success (0) or failure (1) and message
    '''
    if request.method != 'POST':
        return HttpResponse(util.compose_reply(False, "'submit' must use HTTP POST"))
    else:
        #Minimal parsing of reply
        log.debug(request.POST)
        reply_is_valid, header, body = _is_valid_reply(request.POST.copy())
        log.debug("Header: {0}\n Body: {1}".format(header,body))

        if not reply_is_valid:
            log.error("Invalid xqueue object added: request_ip: {0} request.POST: {1}".format(
                util.get_request_ip(request),
                request.POST,
            ))
            return HttpResponse(util.compose_reply(False, 'Incorrect format'))
        else:
            try:
                #Retrieve individual values from xqueue body and header.
                prompt=util._value_or_default(body['grader_payload']['prompt'],"")
                rubric=util._value_or_default(body['grader_payload']['rubric'],"")
                student_id=util._value_or_default(body['student_info']['anonymous_student_id'])
                location=util._value_or_default(body['grader_payload']['location'])
                course_id=util._value_or_default(body['grader_payload']['course_id'])
                problem_id=util._value_or_default(body['grader_payload']['problem_id'],location)
                grader_settings=util._value_or_default(body['grader_payload']['grader'],"")
                student_response=util._value_or_default(body['student_response'])
                xqueue_submission_id=util._value_or_default(header['submission_id'])
                xqueue_submission_key=util._value_or_default(header['submission_key'])
                state_code="W"
                xqueue_queue_name=util._value_or_default(header["queue_name"])
                max_score=util._value_or_default(body['max_score'])

                submission_time_string=util._value_or_default(body['student_info']['submission_time'])
                student_submission_time=datetime.strptime(submission_time_string,"%Y%m%d%H%M%S")

                #Create submission object
                sub, created = Submission.objects.get_or_create(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    state=state_code,
                    student_response=student_response,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    max_score=max_score,
                )

            except Exception as err:
                xqueue_submission_id=util._value_or_default(header['submission_id'])
                xqueue_submission_key=util._value_or_default(header['submission_key'])
                log.error("Error creating submission and adding to database: sender: {0}, submission_id: {1}, submission_key: {2}".format(
                    util.get_request_ip(request),
                    xqueue_submission_id,
                    xqueue_submission_key,
                ))
                return HttpResponse(util.compose_reply(False,'Unable to create submission.'))

            #Handle submission and write to db
            success=handle_submission(sub)

            return HttpResponse(util.compose_reply(success=success, content=''))

def handle_submission(sub):
    """
    Handles a new submission.  Decides what the next grader should be and saves it.
    Input:
        sub - A Submission object from controller.models

    Output:
        True/False status code
    """
    try:
        #Assign whether grader should be ML or IN based on number of graded examples.
        subs_graded_by_instructor,subs_pending_instructor=util.subs_by_instructor(sub.location)

        #TODO: abstract out logic for assigning which grader to go with.
        if((subs_graded_by_instructor+subs_pending_instructor)>=settings.MIN_TO_USE_ML):
            sub.next_grader_type="ML"
        else:
            sub.next_grader_type="IN"

        sub.save()
        log.debug("Created successfully!")
    except:
        log.debug("Creation failed!")
        return False

    return True

def _is_valid_reply(external_reply):
    '''
    Check if external reply is in the right format
        1) Presence of 'xqueue_header' and 'xqueue_body'
        2) Presence of specific metadata in 'xqueue_header'
            ['submission_id', 'submission_key']

    Returns:
        is_valid:       Flag indicating success (Boolean)
        header :        header of the queue item
        body:           body of the queue item
    '''
    fail = (False,-1,'')

    try:
        header = json.loads(external_reply['xqueue_header'])
        body = json.loads(external_reply['xqueue_body'])
    except KeyError:
        log.debug("Cannot load header or body.")
        return fail

    if not isinstance(header,dict) or not isinstance(body,dict):
        return fail

    for tag in ['submission_id', 'submission_key', 'queue_name']:
        if not header.has_key(tag):
            log.debug("{0} not found in header".format(tag))
            return fail

    for tag in ['grader_payload', 'student_response', 'student_info']:
        if not body.has_key(tag):
            log.debug("{0} not found in body".format(tag))
            return fail

    try:
        body['grader_payload']=json.loads(body['grader_payload'])
        body['student_info']=json.loads(body['student_info'])
    except:
        log.debug("Cannot load payload or info.")
        return fail

    return True,header,body

<commit_msg>Change parameter name to grader settings.<commit_after>from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import timezone
from datetime import datetime
import logging

from models import Submission,Grader
import util
import json

log = logging.getLogger(__name__)

@csrf_exempt
@login_required
def submit(request):
    '''
    Xqueue pull script posts objects here.
    Input:

    request - dict with keys xqueue_header and xqueue_body
    xqueue_header needs submission_id,submission_key,queue_name
    xqueue_body needs grader_payload, student_info, student_response, max_score
    grader_payload needs location, course_id,problem_id,grader
    student_info needs anonymous_student_id, submission_time

    Output:
    Returns status code indicating success (0) or failure (1) and message
    '''
    if request.method != 'POST':
        return HttpResponse(util.compose_reply(False, "'submit' must use HTTP POST"))
    else:
        #Minimal parsing of reply
        log.debug(request.POST)
        reply_is_valid, header, body = _is_valid_reply(request.POST.copy())
        log.debug("Header: {0}\n Body: {1}".format(header,body))

        if not reply_is_valid:
            log.error("Invalid xqueue object added: request_ip: {0} request.POST: {1}".format(
                util.get_request_ip(request),
                request.POST,
            ))
            return HttpResponse(util.compose_reply(False, 'Incorrect format'))
        else:
            try:
                #Retrieve individual values from xqueue body and header.
                prompt=util._value_or_default(body['grader_payload']['prompt'],"")
                rubric=util._value_or_default(body['grader_payload']['rubric'],"")
                student_id=util._value_or_default(body['student_info']['anonymous_student_id'])
                location=util._value_or_default(body['grader_payload']['location'])
                course_id=util._value_or_default(body['grader_payload']['course_id'])
                problem_id=util._value_or_default(body['grader_payload']['problem_id'],location)
                grader_settings=util._value_or_default(body['grader_payload']['grader_settings'],"")
                student_response=util._value_or_default(body['student_response'])
                xqueue_submission_id=util._value_or_default(header['submission_id'])
                xqueue_submission_key=util._value_or_default(header['submission_key'])
                state_code="W"
                xqueue_queue_name=util._value_or_default(header["queue_name"])
                max_score=util._value_or_default(body['max_score'])

                submission_time_string=util._value_or_default(body['student_info']['submission_time'])
                student_submission_time=datetime.strptime(submission_time_string,"%Y%m%d%H%M%S")

                #Create submission object
                sub, created = Submission.objects.get_or_create(
                    prompt=prompt,
                    rubric=rubric,
                    student_id=student_id,
                    problem_id=problem_id,
                    state=state_code,
                    student_response=student_response,
                    student_submission_time=student_submission_time,
                    xqueue_submission_id=xqueue_submission_id,
                    xqueue_submission_key=xqueue_submission_key,
                    xqueue_queue_name=xqueue_queue_name,
                    location=location,
                    course_id=course_id,
                    max_score=max_score,
                )

            except Exception as err:
                xqueue_submission_id=util._value_or_default(header['submission_id'])
                xqueue_submission_key=util._value_or_default(header['submission_key'])
                log.error("Error creating submission and adding to database: sender: {0}, submission_id: {1}, submission_key: {2}".format(
                    util.get_request_ip(request),
                    xqueue_submission_id,
                    xqueue_submission_key,
                ))
                return HttpResponse(util.compose_reply(False,'Unable to create submission.'))

            #Handle submission and write to db
            success=handle_submission(sub)

            return HttpResponse(util.compose_reply(success=success, content=''))

def handle_submission(sub):
    """
    Handles a new submission.  Decides what the next grader should be and saves it.
    Input:
        sub - A Submission object from controller.models

    Output:
        True/False status code
    """
    try:
        #Assign whether grader should be ML or IN based on number of graded examples.
        subs_graded_by_instructor,subs_pending_instructor=util.subs_by_instructor(sub.location)

        #TODO: abstract out logic for assigning which grader to go with.
        if((subs_graded_by_instructor+subs_pending_instructor)>=settings.MIN_TO_USE_ML):
            sub.next_grader_type="ML"
        else:
            sub.next_grader_type="IN"

        sub.save()
        log.debug("Created successfully!")
    except:
        log.debug("Creation failed!")
        return False

    return True

def _is_valid_reply(external_reply):
    '''
    Check if external reply is in the right format
        1) Presence of 'xqueue_header' and 'xqueue_body'
        2) Presence of specific metadata in 'xqueue_header'
            ['submission_id', 'submission_key']

    Returns:
        is_valid:       Flag indicating success (Boolean)
        header :        header of the queue item
        body:           body of the queue item
    '''
    fail = (False,-1,'')

    try:
        header = json.loads(external_reply['xqueue_header'])
        body = json.loads(external_reply['xqueue_body'])
    except KeyError:
        log.debug("Cannot load header or body.")
        return fail

    if not isinstance(header,dict) or not isinstance(body,dict):
        return fail

    for tag in ['submission_id', 'submission_key', 'queue_name']:
        if not header.has_key(tag):
            log.debug("{0} not found in header".format(tag))
            return fail

    for tag in ['grader_payload', 'student_response', 'student_info']:
        if not body.has_key(tag):
            log.debug("{0} not found in body".format(tag))
            return fail

    try:
        body['grader_payload']=json.loads(body['grader_payload'])
        body['student_info']=json.loads(body['student_info'])
    except:
        log.debug("Cannot load payload or info.")
        return fail

    return True,header,body

<|endoftext|>


<commit_before>import os
import logging
import datetime
import webapp2

from google.appengine.api import memcache
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

import tba_config

from base_controller import CacheableHandler
from consts.event_type import EventType
from consts.notification_type import NotificationType
from helpers.event_helper import EventHelper

from models.event import Event
from models.insight import Insight
from models.team import Team
from models.sitevar import Sitevar

def render_static(page):
    memcache_key = "main_%s" % page
    html = memcache.get(memcache_key)

    if html is None:
        path = os.path.join(os.path.dirname(__file__), "../templates/%s.html" % page)
        html = template.render(path, {})
        if tba_config.CONFIG["memcache"]:
            memcache.set(memcache_key, html, 86400)

    return html

class MainKickoffHandler(CacheableHandler):
    CACHE_VERSION = 3
    CACHE_KEY_FORMAT = "main_kickoff"

    def __init__(self, *args, **kw):
        super(MainKickoffHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        kickoff_datetime_est = datetime.datetime(2015, 1, 3, 10, 30)
        kickoff_datetime_utc = kickoff_datetime_est + datetime.timedelta(hours=5)

        is_kickoff = datetime.datetime.now() >= kickoff_datetime_est - datetime.timedelta(days=1)  # turn on 1 day before

        self.template_values.update({
            'is_kickoff': is_kickoff,
            'kickoff_datetime_est': kickoff_datetime_est,
            'kickoff_datetime_utc': kickoff_datetime_utc,
        })

        path = os.path.join(os.path.dirname(__file__), "../templates/index_kickoff.html")
        return template.render(path, self.template_values)

class MainBuildseasonHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_buildseason"

    def __init__(self, *args, **kw):
        super(MainBuildseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        endbuild_datetime_est = datetime.datetime(2015, 2, 17, 23, 59)
        endbuild_datetime_utc = endbuild_datetime_est + datetime.timedelta(hours=5)

        self.template_values.update({
            'endbuild_datetime_est': endbuild_datetime_est,
            'endbuild_datetime_utc': endbuild_datetime_utc
        })

        path = os.path.join(os.path.dirname(__file__), "../templates/index_buildseason.html")
        return template.render(path, self.template_values)

class MainChampsHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_champs"

    def __init__(self, *args, **kw):
        super(MainChampsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        year = datetime.datetime.now().year
        event_keys = Event.query(Event.year == year, Event.event_type_enum.IN(EventType.CMP_EVENT_TYPES)).fetch(100, keys_only=True)
        events = [event_key.get() for event_key in event_keys]

        self.template_values.update({
            "events": events,
        })

        insights = ndb.get_multi([ndb.Key(Insight, Insight.renderKeyName(year, insight_name)) for insight_name in Insight.INSIGHT_NAMES.values()])
        for insight in insights:
            if insight:
                self.template_values[insight.name] = insight

        path = os.path.join(os.path.dirname(__file__), '../templates/index_champs.html')
        return template.render(path, self.template_values)

class MainCompetitionseasonHandler(CacheableHandler):
    CACHE_VERSION = 5
    CACHE_KEY_FORMAT = "main_competitionseason"

    def __init__(self, *args, **kw):
        super(MainCompetitionseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()

        self.template_values.update({
            "events": week_events,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/index_competitionseason.html')
        return template.render(path, self.template_values)

class MainInsightsHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_insights"

    def __init__(self, *args, **kw):
        super(MainInsightsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()
        self.template_values.update({
            "events": week_events,
        })

        insights = ndb.get_multi([ndb.Key(Insight, Insight.renderKeyName(2014, insight_name)) for insight_name in Insight.INSIGHT_NAMES.values()])
        for insight in insights:
            if insight:
                self.template_values[insight.name] = insight

        path = os.path.join(os.path.dirname(__file__), '../templates/index_insights.html')
        return template.render(path, self.template_values)

class MainOffseasonHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_offseason"

    def __init__(self, *args, **kw):
        super(MainOffseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()
        self.template_values.update({
            "events": week_events,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/index_offseason.html')
        return template.render(path, self.template_values)

class ContactHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_contact"

    def __init__(self, *args, **kw):
        super(ContactHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/contact.html")
        return template.render(path, self.template_values)

class HashtagsHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_hashtags"

    def __init__(self, *args, **kw):
        super(HashtagsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/hashtags.html")
        return template.render(path, self.template_values)

class AboutHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_about"

    def __init__(self, *args, **kw):
        super(AboutHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/about.html")
        return template.render(path, self.template_values)

class ThanksHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_thanks"

    def __init__(self, *args, **kw):
        super(ThanksHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/thanks.html")
        return template.render(path, self.template_values)

class OprHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_opr"

    def __init__(self, *args, **kw):
        super(OprHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/opr.html")
        return template.render(path, self.template_values)

class SearchHandler(webapp2.RequestHandler):
    def get(self):
        try:
            q = self.request.get("q")
            logging.info("search query: %s" % q)
            if q.isdigit():
                team_id = "frc%s" % q
                team = Team.get_by_id(team_id)
                if team:
                    self.redirect(team.details_url)
                    return None
            elif len(q) in {3, 4, 5}:  # event shorts are between 3 and 5 characters long
                year = datetime.datetime.now().year  # default to current year
                event_id = "%s%s" % (year, q)
                event = Event.get_by_id(event_id)
                if event:
                    self.redirect(event.details_url)
                    return None
        except Exception, e:
            logging.warning("warning: %s" % e)
        finally:
            self.response.out.write(render_static("search"))

class GamedayHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_gameday"

    def __init__(self, *args, **kw):
        super(GamedayHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts')
        special_webcasts_temp = special_webcasts_future.get_result()
        if special_webcasts_temp:
            special_webcasts_temp = special_webcasts_temp.contents
        else:
            special_webcasts_temp = {}
        special_webcasts = []
        for webcast in special_webcasts_temp.values():
            toAppend = {}
            for key, value in webcast.items():
                toAppend[str(key)] = str(value)
            special_webcasts.append(toAppend)

        ongoing_events = []
        ongoing_events_w_webcasts = []
        week_events = EventHelper.getWeekEvents()
        for event in week_events:
            if event.now:
                ongoing_events.append(event)
                if event.webcast:
                    valid = []
                    for webcast in event.webcast:
                        if 'type' in webcast and 'channel' in webcast:
                            event_webcast = {'event': event}
                            valid.append(event_webcast)
                    # Add webcast numbers if more than one for an event
                    if len(valid) > 1:
                        count = 1
                        for event in valid:
                            event['count'] = count
                            count += 1
                    ongoing_events_w_webcasts += valid

        self.template_values.update({
            'special_webcasts': special_webcasts,
            'ongoing_events': ongoing_events,
            'ongoing_events_w_webcasts': ongoing_events_w_webcasts
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/gameday.html')
        return template.render(path, self.template_values)

class PageNotFoundHandler(webapp2.RequestHandler):
    def get(self, *args):
        self.error(404)
        self.response.out.write(render_static("404"))

class InternalServerErrorHandler(webapp2.RequestHandler):
    def get(self, *args):
        self.error(500)
        self.response.out.write(render_static("500"))

class WebcastsHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_webcasts"

    def __init__(self, *args, **kw):
        super(WebcastsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        year = datetime.datetime.now().year
        event_keys = Event.query(Event.year == year).order(Event.start_date).fetch(500, keys_only=True)
        events = ndb.get_multi(event_keys)

        self.template_values.update({
            'events': events,
            'year': year,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/webcasts.html')
        return template.render(path, self.template_values)

class RecordHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_record"

    def __init__(self, *args, **kw):
        super(RecordHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/record.html")
        return template.render(path, self.template_values)

class ApiDocumentationHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "api_docs"

    def __init__(self, *args, **kw):
        super(ApiDocumentationHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/apidocs.html")
        return template.render(path, self.template_values)

class WebhookDocumentationHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "webhook_docs"

    def __init__(self, *args, **kw):
        super(WebhookDocumentationHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        self.template_values['enabled'] = NotificationType.enabled_notifications
        self.template_values['types'] = NotificationType.types
        path = os.path.join(os.path.dirname(__file__), "../templates/webhookdocs.html")
        return template.render(path, self.template_values)
<commit_msg>Reduce gameday cache time<commit_after>import os
import logging
import datetime
import webapp2

from google.appengine.api import memcache
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template

import tba_config

from base_controller import CacheableHandler
from consts.event_type import EventType
from consts.notification_type import NotificationType
from helpers.event_helper import EventHelper

from models.event import Event
from models.insight import Insight
from models.team import Team
from models.sitevar import Sitevar

def render_static(page):
    memcache_key = "main_%s" % page
    html = memcache.get(memcache_key)

    if html is None:
        path = os.path.join(os.path.dirname(__file__), "../templates/%s.html" % page)
        html = template.render(path, {})
        if tba_config.CONFIG["memcache"]:
            memcache.set(memcache_key, html, 86400)

    return html

class MainKickoffHandler(CacheableHandler):
    CACHE_VERSION = 3
    CACHE_KEY_FORMAT = "main_kickoff"

    def __init__(self, *args, **kw):
        super(MainKickoffHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        kickoff_datetime_est = datetime.datetime(2015, 1, 3, 10, 30)
        kickoff_datetime_utc = kickoff_datetime_est + datetime.timedelta(hours=5)

        is_kickoff = datetime.datetime.now() >= kickoff_datetime_est - datetime.timedelta(days=1)  # turn on 1 day before

        self.template_values.update({
            'is_kickoff': is_kickoff,
            'kickoff_datetime_est': kickoff_datetime_est,
            'kickoff_datetime_utc': kickoff_datetime_utc,
        })

        path = os.path.join(os.path.dirname(__file__), "../templates/index_kickoff.html")
        return template.render(path, self.template_values)

class MainBuildseasonHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_buildseason"

    def __init__(self, *args, **kw):
        super(MainBuildseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        endbuild_datetime_est = datetime.datetime(2015, 2, 17, 23, 59)
        endbuild_datetime_utc = endbuild_datetime_est + datetime.timedelta(hours=5)

        self.template_values.update({
            'endbuild_datetime_est': endbuild_datetime_est,
            'endbuild_datetime_utc': endbuild_datetime_utc
        })

        path = os.path.join(os.path.dirname(__file__), "../templates/index_buildseason.html")
        return template.render(path, self.template_values)

class MainChampsHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_champs"

    def __init__(self, *args, **kw):
        super(MainChampsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        year = datetime.datetime.now().year
        event_keys = Event.query(Event.year == year, Event.event_type_enum.IN(EventType.CMP_EVENT_TYPES)).fetch(100, keys_only=True)
        events = [event_key.get() for event_key in event_keys]

        self.template_values.update({
            "events": events,
        })

        insights = ndb.get_multi([ndb.Key(Insight, Insight.renderKeyName(year, insight_name)) for insight_name in Insight.INSIGHT_NAMES.values()])
        for insight in insights:
            if insight:
                self.template_values[insight.name] = insight

        path = os.path.join(os.path.dirname(__file__), '../templates/index_champs.html')
        return template.render(path, self.template_values)

class MainCompetitionseasonHandler(CacheableHandler):
    CACHE_VERSION = 5
    CACHE_KEY_FORMAT = "main_competitionseason"

    def __init__(self, *args, **kw):
        super(MainCompetitionseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()

        self.template_values.update({
            "events": week_events,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/index_competitionseason.html')
        return template.render(path, self.template_values)

class MainInsightsHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_insights"

    def __init__(self, *args, **kw):
        super(MainInsightsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()
        self.template_values.update({
            "events": week_events,
        })

        insights = ndb.get_multi([ndb.Key(Insight, Insight.renderKeyName(2014, insight_name)) for insight_name in Insight.INSIGHT_NAMES.values()])
        for insight in insights:
            if insight:
                self.template_values[insight.name] = insight

        path = os.path.join(os.path.dirname(__file__), '../templates/index_insights.html')
        return template.render(path, self.template_values)

class MainOffseasonHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_offseason"

    def __init__(self, *args, **kw):
        super(MainOffseasonHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24

    def _render(self, *args, **kw):
        week_events = EventHelper.getWeekEvents()
        self.template_values.update({
            "events": week_events,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/index_offseason.html')
        return template.render(path, self.template_values)

class ContactHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_contact"

    def __init__(self, *args, **kw):
        super(ContactHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/contact.html")
        return template.render(path, self.template_values)

class HashtagsHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_hashtags"

    def __init__(self, *args, **kw):
        super(HashtagsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/hashtags.html")
        return template.render(path, self.template_values)

class AboutHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_about"

    def __init__(self, *args, **kw):
        super(AboutHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/about.html")
        return template.render(path, self.template_values)

class ThanksHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_thanks"

    def __init__(self, *args, **kw):
        super(ThanksHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/thanks.html")
        return template.render(path, self.template_values)

class OprHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_opr"

    def __init__(self, *args, **kw):
        super(OprHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/opr.html")
        return template.render(path, self.template_values)

class SearchHandler(webapp2.RequestHandler):
    def get(self):
        try:
            q = self.request.get("q")
            logging.info("search query: %s" % q)
            if q.isdigit():
                team_id = "frc%s" % q
                team = Team.get_by_id(team_id)
                if team:
                    self.redirect(team.details_url)
                    return None
            elif len(q) in {3, 4, 5}:  # event shorts are between 3 and 5 characters long
                year = datetime.datetime.now().year  # default to current year
                event_id = "%s%s" % (year, q)
                event = Event.get_by_id(event_id)
                if event:
                    self.redirect(event.details_url)
                    return None
        except Exception, e:
            logging.warning("warning: %s" % e)
        finally:
            self.response.out.write(render_static("search"))

class GamedayHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_gameday"

    def __init__(self, *args, **kw):
        super(GamedayHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60

    def _render(self, *args, **kw):
        special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts')
        special_webcasts_temp = special_webcasts_future.get_result()
        if special_webcasts_temp:
            special_webcasts_temp = special_webcasts_temp.contents
        else:
            special_webcasts_temp = {}
        special_webcasts = []
        for webcast in special_webcasts_temp.values():
            toAppend = {}
            for key, value in webcast.items():
                toAppend[str(key)] = str(value)
            special_webcasts.append(toAppend)

        ongoing_events = []
        ongoing_events_w_webcasts = []
        week_events = EventHelper.getWeekEvents()
        for event in week_events:
            if event.now:
                ongoing_events.append(event)
                if event.webcast:
                    valid = []
                    for webcast in event.webcast:
                        if 'type' in webcast and 'channel' in webcast:
                            event_webcast = {'event': event}
                            valid.append(event_webcast)
                    # Add webcast numbers if more than one for an event
                    if len(valid) > 1:
                        count = 1
                        for event in valid:
                            event['count'] = count
                            count += 1
                    ongoing_events_w_webcasts += valid

        self.template_values.update({
            'special_webcasts': special_webcasts,
            'ongoing_events': ongoing_events,
            'ongoing_events_w_webcasts': ongoing_events_w_webcasts
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/gameday.html')
        return template.render(path, self.template_values)

class PageNotFoundHandler(webapp2.RequestHandler):
    def get(self, *args):
        self.error(404)
        self.response.out.write(render_static("404"))

class InternalServerErrorHandler(webapp2.RequestHandler):
    def get(self, *args):
        self.error(500)
        self.response.out.write(render_static("500"))

class WebcastsHandler(CacheableHandler):
    CACHE_VERSION = 2
    CACHE_KEY_FORMAT = "main_webcasts"

    def __init__(self, *args, **kw):
        super(WebcastsHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        year = datetime.datetime.now().year
        event_keys = Event.query(Event.year == year).order(Event.start_date).fetch(500, keys_only=True)
        events = ndb.get_multi(event_keys)

        self.template_values.update({
            'events': events,
            'year': year,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/webcasts.html')
        return template.render(path, self.template_values)

class RecordHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "main_record"

    def __init__(self, *args, **kw):
        super(RecordHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/record.html")
        return template.render(path, self.template_values)

class ApiDocumentationHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "api_docs"

    def __init__(self, *args, **kw):
        super(ApiDocumentationHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        path = os.path.join(os.path.dirname(__file__), "../templates/apidocs.html")
        return template.render(path, self.template_values)

class WebhookDocumentationHandler(CacheableHandler):
    CACHE_VERSION = 1
    CACHE_KEY_FORMAT = "webhook_docs"

    def __init__(self, *args, **kw):
        super(WebhookDocumentationHandler, self).__init__(*args, **kw)
        self._cache_expiration = 60 * 60 * 24 * 7

    def _render(self, *args, **kw):
        self.template_values['enabled'] = NotificationType.enabled_notifications
        self.template_values['types'] = NotificationType.types
        path = os.path.join(os.path.dirname(__file__), "../templates/webhookdocs.html")
        return template.render(path, self.template_values)
<|endoftext|>