<commit_before>import os
import subprocess

import unittest

CURR_DIR = os.path.dirname(os.path.realpath(__file__))

class TestCli(unittest.TestCase):
    def getTestCaseNames(self):
        pass

    # Naming makes sure this test is run first. There's no reason to run the rest of the tests if installation failed
    def test00_installation(self):
        self.assertEqual(subprocess.call(['jpp', '--version']), 0, 'jpp not installed. Please run "pip install jpp" '
                                                                   'and try again.')

    def test_help(self):
        help_message = subprocess.check_output(['jpp', '-h'])
        self.assertRegex(help_message, '^usage:')

    def test_no_args(self):
        subprocess.check_call(['jpp'], cwd=CURR_DIR)

    def test_parse_specific_file(self):
        subprocess.check_call(['jpp', 'other.jpp'], cwd=CURR_DIR)

    def test_path_option(self):
        subprocess.check_call(['jpp', '--path', os.path.join(CURR_DIR, 'sub_path'), 'sub_other.jpp'], cwd=CURR_DIR)

    def test_compact_path(self):
        cmd_out = subprocess.check_output(['jpp', '--compact-print', 'compact_test.jpp'], cwd=CURR_DIR)
        # Make sure output is a one-liner
        self.assertEqual(cmd_out.count(b'\n'), 0)

    def test_user_input(self):
        subprocess.check_call(['jpp', '--user-input', '{"bar": "baz"}'], cwd=CURR_DIR)

def main():
    unittest.main()

if __name__ == '__main__':
    main()
<commit_msg>Minor refactor: removed redundant method<commit_after>import os
import subprocess

import unittest

CURR_DIR = os.path.dirname(os.path.realpath(__file__))

class TestCli(unittest.TestCase):
    # Naming makes sure this test is run first. There's no reason to run the rest of the tests if installation failed
    def test00_installation(self):
        self.assertEqual(subprocess.call(['jpp', '--version']), 0, 'jpp not installed. Please run "pip install jpp" '
                                                                   'and try again.')

    def test_help(self):
        help_message = subprocess.check_output(['jpp', '-h'])
        self.assertRegex(help_message, '^usage:')

    def test_no_args(self):
        subprocess.check_call(['jpp'], cwd=CURR_DIR)

    def test_parse_specific_file(self):
        subprocess.check_call(['jpp', 'other.jpp'], cwd=CURR_DIR)

    def test_path_option(self):
        subprocess.check_call(['jpp', '--path', os.path.join(CURR_DIR, 'sub_path'), 'sub_other.jpp'], cwd=CURR_DIR)

    def test_compact_path(self):
        cmd_out = subprocess.check_output(['jpp', '--compact-print', 'compact_test.jpp'], cwd=CURR_DIR)
        # Make sure output is a one-liner
        self.assertEqual(cmd_out.count(b'\n'), 0)

    def test_user_input(self):
        subprocess.check_call(['jpp', '--user-input', '{"bar": "baz"}'], cwd=CURR_DIR)

def main():
    unittest.main()

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>from django.utils.html import escape
from django.utils.encoding import smart_unicode, StrAndUnicode
from django.utils.functional import Promise

def flatatt(attrs):
    """
    Convert a dictionary of attributes to a single string.
    The returned string will contain a leading space followed by key="value",
    XML-style pairs.  It is assumed that the keys do not need to be XML-escaped.
    If the passed dictionary is empty, then return an empty string.
    """
    return u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])

class ErrorDict(dict, StrAndUnicode):
    """
    A collection of errors that knows how to display itself in various formats.

    The dictionary keys are the field names, and the values are the errors.
    """
    def __unicode__(self):
        return self.as_ul()

    def as_ul(self):
        if not self: return u''
        return u'
%s
' % ''.join([u'
%s%s
' % (k, smart_unicode(v)) for k, v in self.items()])

    def as_text(self):
        return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u'  * %s' % smart_unicode(i) for i in v])) for k, v in self.items()])

class ErrorList(list, StrAndUnicode):
    """
    A collection of errors that knows how to display itself in various formats.
    """
    def __unicode__(self):
        return self.as_ul()

    def as_ul(self):
        if not self: return u''
        return u'
%s
' % ''.join([u'
%s
' % smart_unicode(e) for e in self])

    def as_text(self):
        if not self: return u''
        return u'\n'.join([u'* %s' % smart_unicode(e) for e in self])

class ValidationError(Exception):
    def __init__(self, message):
        "ValidationError can be passed a string or a list."
        if isinstance(message, list):
            self.messages = ErrorList([smart_unicode(msg) for msg in message])
        else:
            assert isinstance(message, (basestring, Promise)), ("%s should be a basestring or lazy translation" % repr(message))
            message = smart_unicode(message)
            self.messages = ErrorList([message])

    def __str__(self):
        # This is needed because, without a __str__(), printing an exception
        # instance would result in this:
        # AttributeError: ValidationError instance has no attribute 'args'
        # See http://www.python.org/doc/current/tut/node10.html#handling
        return repr(self.messages)
<commit_msg>Fixed some Python 2.3 unicode conversion problems. Uncovered by the tests, but that was just a sign of a real bug (luckily!).<commit_after>from django.utils.html import escape
from django.utils.encoding import smart_unicode, StrAndUnicode, force_unicode
from django.utils.functional import Promise

def flatatt(attrs):
    """
    Convert a dictionary of attributes to a single string.
    The returned string will contain a leading space followed by key="value",
    XML-style pairs.  It is assumed that the keys do not need to be XML-escaped.
    If the passed dictionary is empty, then return an empty string.
    """
    return u''.join([u' %s="%s"' % (k, escape(v)) for k, v in attrs.items()])

class ErrorDict(dict, StrAndUnicode):
    """
    A collection of errors that knows how to display itself in various formats.

    The dictionary keys are the field names, and the values are the errors.
    """
    def __unicode__(self):
        return self.as_ul()

    def as_ul(self):
        if not self: return u''
        return u'
%s
' % ''.join([u'
%s%s
' % (k, force_unicode(v)) for k, v in self.items()])

    def as_text(self):
        return u'\n'.join([u'* %s\n%s' % (k, u'\n'.join([u'  * %s' % force_unicode(i) for i in v])) for k, v in self.items()])

class ErrorList(list, StrAndUnicode):
    """
    A collection of errors that knows how to display itself in various formats.
    """
    def __unicode__(self):
        return self.as_ul()

    def as_ul(self):
        if not self: return u''
        return u'
%s
' % ''.join([u'
%s
' % force_unicode(e) for e in self])

    def as_text(self):
        if not self: return u''
        return u'\n'.join([u'* %s' % force_unicode(e) for e in self])

class ValidationError(Exception):
    def __init__(self, message):
        "ValidationError can be passed a string or a list."
        if isinstance(message, list):
            self.messages = ErrorList([smart_unicode(msg) for msg in message])
        else:
            assert isinstance(message, (basestring, Promise)), ("%s should be a basestring or lazy translation" % repr(message))
            message = smart_unicode(message)
            self.messages = ErrorList([message])

    def __str__(self):
        # This is needed because, without a __str__(), printing an exception
        # instance would result in this:
        # AttributeError: ValidationError instance has no attribute 'args'
        # See http://www.python.org/doc/current/tut/node10.html#handling
        return repr(self.messages)

<|endoftext|>


<commit_before>"""
Init
"""
from __future__ import unicode_literals

import datetime
import os
import subprocess

VERSION = (2, 2, 12, 'final', 0)

def get_version(version=None):
    """
    Returns a PEP 386-compliant version number from VERSION.
    """
    if not version:
        version = VERSION
    else:
        assert len(version) == 5
        assert version[3] in ('alpha', 'beta', 'rc', 'final')

    # Now build the two parts of the version number:
    # main = X.Y[.Z]
    # sub = .devN - for pre-alpha releases
    #     | {a|b|c}N - for alpha, beta and rc releases

    parts = 2 if version[2] == 0 else 3
    main = '.'.join(str(x) for x in version[:parts])

    sub = ''
    if version[3] == 'alpha' and version[4] == 0:
        git_changeset = get_git_changeset()
        if git_changeset:
            sub = '.dev%s' % git_changeset

    elif version[3] != 'final':
        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
        sub = mapping[version[3]] + str(version[4])

    return str(main + sub)

def get_git_changeset():
    """
    Returns a numeric identifier of the latest git changeset.

    The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format.
    This value isn't guaranteed to be unique, but collisions are very unlikely,
    so it's sufficient for generating the development version numbers.
    """
    repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    git_log = subprocess.Popen('git log --pretty=format:%ct --quiet -1 HEAD',
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                               shell=True, cwd=repo_dir,
                               universal_newlines=True)
    timestamp = git_log.communicate()[0]

    try:
        timestamp = datetime.datetime.utcfromtimestamp(int(timestamp))
    except ValueError:
        return None

    return timestamp.strftime('%Y%m%d%H%M%S')
<commit_msg>Set version 2.2.13<commit_after>"""
Init
"""
from __future__ import unicode_literals

import datetime
import os
import subprocess

VERSION = (2, 2, 13, 'final', 0)

def get_version(version=None):
    """
    Returns a PEP 386-compliant version number from VERSION.
    """
    if not version:
        version = VERSION
    else:
        assert len(version) == 5
        assert version[3] in ('alpha', 'beta', 'rc', 'final')

    # Now build the two parts of the version number:
    # main = X.Y[.Z]
    # sub = .devN - for pre-alpha releases
    #     | {a|b|c}N - for alpha, beta and rc releases

    parts = 2 if version[2] == 0 else 3
    main = '.'.join(str(x) for x in version[:parts])

    sub = ''
    if version[3] == 'alpha' and version[4] == 0:
        git_changeset = get_git_changeset()
        if git_changeset:
            sub = '.dev%s' % git_changeset

    elif version[3] != 'final':
        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
        sub = mapping[version[3]] + str(version[4])

    return str(main + sub)

def get_git_changeset():
    """
    Returns a numeric identifier of the latest git changeset.

    The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format.
    This value isn't guaranteed to be unique, but collisions are very unlikely,
    so it's sufficient for generating the development version numbers.
    """
    repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    git_log = subprocess.Popen('git log --pretty=format:%ct --quiet -1 HEAD',
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                               shell=True, cwd=repo_dir,
                               universal_newlines=True)
    timestamp = git_log.communicate()[0]

    try:
        timestamp = datetime.datetime.utcfromtimestamp(int(timestamp))
    except ValueError:
        return None

    return timestamp.strftime('%Y%m%d%H%M%S')
<|endoftext|>


<commit_before>import mimetypes
from datetime import datetime
from django.core.files.base import ContentFile
from django.core.files.storage import Storage
from azure import WindowsAzureMissingResourceError
from azure.storage.blobservice import BlobService
from . import settings as ls

class AzureStorage(Storage):
    """
    A storage that sends files to a Microsoft Azure container.
    """
    account_name = ls.AZURE_ACCOUNT_NAME
    account_key = ls.AZURE_ACCOUNT_KEY

    def __init__(self, container=ls.AZURE_DEFAULT_CONTAINER,
            cdn_host=ls.AZURE_CDN_HOST, protocol=ls.AZURE_DEFAULT_PROTOCOL,
            allow_override=ls.AZURE_BLOB_OVERWRITE):
        self._service = None
        self.container = container
        self.cdn_host = cdn_host
        self.protocol = protocol
        self.allow_override = allow_override

    def _clean_name(self, name):
        return name.replace("\\", "/")

    def _get_properties(self, name):
        return self.service.get_blob_properties(self.container, name)

    def _open(self, name, mode='rb'):
        return ContentFile(self.service.get_blob(self.container, name))

    def _save(self, name, content):
        if hasattr(content.file, 'content_type'):
            content_type = content.file.content_type
        else:
            content_type = mimetypes.guess_type(name)[0] or \
                    u'application/octet-stream'

        self.service.put_blob(self.container, name, content.read(),
                              x_ms_blob_type='BlockBlob',
                              x_ms_blob_content_type=content_type)
        return name

    def delete(self, name):
        try:
            self.service.delete_blob(self.container, name)
        except WindowsAzureMissingResourceError:
            pass

    def exists(self, name):
        try:
            self._get_properties(name)
        except WindowsAzureMissingResourceError:
            return False
        else:
            return True

    def get_available_name(self, name):
        if self.allow_override:
            return self._clean_name(name)
        return super(AzureStorage, self).get_available_name(name)

    def listdir(self, path):
        dirs, files = set(), []
        base_parts = path.split("/") if path and path != '' else []
        for blob in self.service.list_blobs(self.container,
                prefix=path if path != '' else None):
            parts = blob.name.split("/")[len(base_parts):]
            if len(parts) == 1:
                files.append(parts[-1])
            elif len(parts) > 1:
                dirs.add(parts[0])
        return (list(dirs), files)

    def modified_time(self, name):
        return datetime.strptime(self._get_properties(name)['last-modified'],
                                 ls.TIMESTAMP_FORMAT)

    @property
    def service(self):
        if self._service is None:
            self._service = BlobService(self.account_name,
                    self.account_key, self.protocol)
        return self._service

    def size(self, name):
        return len(self._open(name))

    def url(self, name):
        return '{0}://{1}/{2}/{3}'.format(self.protocol, self.cdn_host,
                                          self.container, name) \
                if self.cdn_host else self.service.make_blob_url(
                        container_name=self.container, blob_name=name)

class StaticFilesAzureStorage(AzureStorage):
    """
    Default Azure STATICFILES_STORAGE. It's generally expensive
    since it asks the clous for every operation.
    """
    def __init__(self, *args, **kwargs):
        super(StaticFilesAzureStorage, self).__init__(*args, **kwargs)
        self.container = ls.AZURE_STATIC_FILES_CONTAINER

try:
    from compressor.storage import CompressorFileStorage # @UnresolvedImport
except:
    pass

else:
    class CachedAzureStorage(CompressorFileStorage):
        """
        A storage that sends files to Azure as well.
        It returns the remote url to be suitable for static files.
        To be used along with django-compressor. Ie, in settings:

        STATICFILES_STORAGE = 'django_azure.storage.CachedAzureStorage'
        COMPRESS_STORAGE = 'django_azure.storage.CachedAzureStorage'
        """
        def __init__(self, *args, **kwargs):
            super(CachedAzureStorage, self).__init__(*args, **kwargs)
            self.remote_storage = AzureStorage(
                    container=ls.AZURE_STATIC_FILES_CONTAINER,
                    allow_override=True)()

        def save(self, name, content):
            """
            Save in both storages
            """
            # get the remote name
            name = self.remote_storage.save(name, content)
            # ...and then save locally
            super(CachedAzureStorage, self).save(name, content)
            return name

        def delete(self, name):
            """
            Delete in both storages
            """
            super(CachedAzureStorage, self).save(name)
            self.remote_storage.delete(name)

        def url(self, name):
            """
            Return the Azure url
            """
            return self.remote_storage.url(name)
<commit_msg>minor fix #2<commit_after>import mimetypes
from datetime import datetime
from django.core.files.base import ContentFile
from django.core.files.storage import Storage
from azure import WindowsAzureMissingResourceError
from azure.storage.blobservice import BlobService
from . import settings as ls

class AzureStorage(Storage):
    """
    A storage that sends files to a Microsoft Azure container.
    """
    account_name = ls.AZURE_ACCOUNT_NAME
    account_key = ls.AZURE_ACCOUNT_KEY

    def __init__(self, container=ls.AZURE_DEFAULT_CONTAINER,
            cdn_host=ls.AZURE_CDN_HOST, protocol=ls.AZURE_DEFAULT_PROTOCOL,
            allow_override=ls.AZURE_BLOB_OVERWRITE):
        self._service = None
        self.container = container
        self.cdn_host = cdn_host
        self.protocol = protocol
        self.allow_override = allow_override

    def _clean_name(self, name):
        return name.replace("\\", "/")

    def _get_properties(self, name):
        return self.service.get_blob_properties(self.container, name)

    def _open(self, name, mode='rb'):
        return ContentFile(self.service.get_blob(self.container, name))

    def _save(self, name, content):
        if hasattr(content.file, 'content_type'):
            content_type = content.file.content_type
        else:
            content_type = mimetypes.guess_type(name)[0] or \
                    u'application/octet-stream'

        self.service.put_blob(self.container, name, content.read(),
                              x_ms_blob_type='BlockBlob',
                              x_ms_blob_content_type=content_type)
        return name

    def delete(self, name):
        try:
            self.service.delete_blob(self.container, name)
        except WindowsAzureMissingResourceError:
            pass

    def exists(self, name):
        try:
            self._get_properties(name)
        except WindowsAzureMissingResourceError:
            return False
        else:
            return True

    def get_available_name(self, name):
        if self.allow_override:
            return self._clean_name(name)
        return super(AzureStorage, self).get_available_name(name)

    def listdir(self, path):
        dirs, files = set(), []
        base_parts = path.split("/") if path and path != '' else []
        for blob in self.service.list_blobs(self.container,
                prefix=path if path != '' else None):
            parts = blob.name.split("/")[len(base_parts):]
            if len(parts) == 1:
                files.append(parts[-1])
            elif len(parts) > 1:
                dirs.add(parts[0])
        return (list(dirs), files)

    def modified_time(self, name):
        return datetime.strptime(self._get_properties(name)['last-modified'],
                                 ls.TIMESTAMP_FORMAT)

    @property
    def service(self):
        if self._service is None:
            self._service = BlobService(self.account_name,
                    self.account_key, self.protocol)
        return self._service

    def size(self, name):
        return len(self._open(name))

    def url(self, name):
        return '{0}://{1}/{2}/{3}'.format(self.protocol, self.cdn_host,
                                          self.container, name) \
                if self.cdn_host else self.service.make_blob_url(
                        container_name=self.container, blob_name=name)

class StaticFilesAzureStorage(AzureStorage):
    """
    Default Azure STATICFILES_STORAGE. It's generally expensive
    since it asks the clous for every operation.
    """
    def __init__(self, *args, **kwargs):
        super(StaticFilesAzureStorage, self).__init__(*args, **kwargs)
        self.container = ls.AZURE_STATIC_FILES_CONTAINER

try:
    from compressor.storage import CompressorFileStorage # @UnresolvedImport
except:
    pass

else:
    class CachedAzureStorage(CompressorFileStorage):
        """
        A storage that sends files to Azure as well.
        It returns the remote url to be suitable for static files.
        To be used along with django-compressor. Ie, in settings:

        STATICFILES_STORAGE = 'django_azure.storage.CachedAzureStorage'
        COMPRESS_STORAGE = 'django_azure.storage.CachedAzureStorage'
        """
        def __init__(self, *args, **kwargs):
            super(CachedAzureStorage, self).__init__(*args, **kwargs)
            self.remote_storage = AzureStorage(
                    container=ls.AZURE_STATIC_FILES_CONTAINER,
                    allow_override=True)

        def save(self, name, content):
            """
            Save in both storages
            """
            # get the remote name
            name = self.remote_storage.save(name, content)
            # ...and then save locally
            super(CachedAzureStorage, self).save(name, content)
            return name

        def delete(self, name):
            """
            Delete in both storages
            """
            super(CachedAzureStorage, self).save(name)
            self.remote_storage.delete(name)

        def url(self, name):
            """
            Return the Azure url
            """
            return self.remote_storage.url(name)
<|endoftext|>


<commit_before>from selenium import webdriver

driver = webdriver.Firefox()
driver.get("https://marketplace-dev.allizom.org/")

driver.find_element_by_link_text('Popular').click()
assert "Popular | Firefox Marketplace" in driver.title

driver.find_elements_by_css_selector(a:contains('spicy-cookie-6')).click()
driver.find.send_keys(Keys.RETURN)

driver.find_elements_by_css_selector(a:contains('Load more')).click()
assert "loadmore loading" in driver.page_source

driver.find_element_by_xpath("//input[@name='email']").send_keys('bishnucit@gmail.com')
driver.find_elements_by_css_selector(a:contains('Sign me up')).click()
assert "privacy" in driver.page_source
driver.find_element_by_xpath("//input[@name='privacy']").click()
driver.find_elements_by_css_selector(a:contains('Sign me up')).click()

assert "success" in driver.page_source

driver.close()
<commit_msg>Update 2<commit_after>from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Firefox()
driver.get("https://marketplace-dev.allizom.org/")
driver.implicitly_wait(12)
driver.find_element_by_xpath("//*[@id='site-header']/mkt-header-nav/li[2]/a").click()
assert "Popular | Firefox Marketplace" in driver.title

driver.find_element_by_xpath("//*[@id='ph_1']/ul/li[25]/button").click()

driver.find_element_by_xpath("//*[@id='newsletter-footer']/div/form/input").send_keys('bishnucit1@gmail.com')
driver.find_elements_by_xpath("//*[@id='newsletter-footer']/div/form/button[1]").click()
assert "privacy" in driver.page_source
driver.find_element_by_xpath("//*[@id='id_newsletter_privacy']").click()
driver.find_elements_by_xpath("//*[@id='newsletter-footer']/div/form/button[1]").click()
driver.implicitly_wait(5)

assert "success" in driver.page_source

driver.close()
<|endoftext|>


<commit_before># Copyright (c) 2013, Raphael Kimmig
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.  Redistributions in binary
# form must reproduce the above copyright notice, this list of conditions and
# the following disclaimer in the documentation and/or other materials provided
# with the distribution.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
# AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__ALL__ = ['reverse']

from django.core.urlresolvers import reverse as django_reverse

def reverse(url_name, *args, **kwargs):
    if 'args' in kwargs or 'kwargs' in kwargs:
        raise Exception("You are using django_helpful.reverse, you can pass "
                        "arguments directly.")
    return django_reverse(url_name, args=args, kwargs=kwargs)
<commit_msg>django 2+ compat<commit_after># Copyright (c) 2013, Raphael Kimmig
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.  Redistributions in binary
# form must reproduce the above copyright notice, this list of conditions and
# the following disclaimer in the documentation and/or other materials provided
# with the distribution.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
# AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__ALL__ = ['reverse']

try:
    from django.core.urlresolvers import reverse as django_reverse
except ImportError:
    from django.urls import reverse as django_reverse

def reverse(url_name, *args, **kwargs):
    if 'args' in kwargs or 'kwargs' in kwargs:
        raise Exception("You are using django_helpful.reverse, you can pass "
                        "arguments directly.")
    return django_reverse(url_name, args=args, kwargs=kwargs)
<|endoftext|>


<commit_before>#     Copyright 2014 Yelp, Inc.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
"""
.. module: security_monkey.watchers.redshift
    :platform: Unix

.. version:: $$VERSION$$
.. moduleauthor:: Ivan Leichtling <ivanlei@yelp.com> @c0wl

"""

from security_monkey.watcher import Watcher
from security_monkey.watcher import ChangeItem
from security_monkey.constants import TROUBLE_REGIONS
from security_monkey.exceptions import BotoConnectionIssue
from security_monkey import app

from boto.redshift import regions

class Redshift(Watcher):
    index = 'redshift'
    i_am_singular = 'Redshift Cluster'
    i_am_plural = 'Redshift Clusters'

    def __init__(self, accounts=None, debug=False):
        super(Redshift, self).__init__(accounts=accounts, debug=debug)

    def slurp(self):
        """
        :returns: item_list - list of Redshift Policies.
        :returns: exception_map - A dict where the keys are a tuple containing the
            location of the exception and the value is the actual exception

        """
        self.prep_for_slurp()
        from security_monkey.common.sts_connect import connect
        item_list = []
        exception_map = {}
        for account in self.accounts:
            for region in regions():
                app.logger.debug("Checking {}/{}/{}".format(self.index, account, region.name))
                try:
                    redshift = connect(account, 'redshift', region=region)

                    all_clusters = []
                    marker = None
                    while True:
                        response = self.wrap_aws_rate_limited_call(
                            redshift.describe_clusters,
                            marker=marker
                        )
                        all_clusters.extend(response['DescribeClustersResponse']['DescribeClustersResult']['Clusters'])
                        if response['DescribeClustersResponse']['DescribeClustersResult']['Marker'] is not None:
                            marker = response['DescribeClustersResponse']['DescribeClustersResult']['Marker']
                        else:
                            break

                except Exception as e:
                    if region.name not in TROUBLE_REGIONS:
                        exc = BotoConnectionIssue(str(e), 'redshift', account, region.name)
                        self.slurp_exception((self.index, account, region.name), exc, exception_map)
                    continue
                app.logger.debug("Found {} {}".format(len(all_clusters), Redshift.i_am_plural))
                for cluster in all_clusters:
                    cluster_id = cluster['ClusterIdentifier']
                    if self.check_ignore_list(cluster_id):
                        continue

                    item = RedshiftCluster(region=region.name, account=account, name=cluster_id, config=dict(cluster))
                    item_list.append(item)

        return item_list, exception_map

class RedshiftCluster(ChangeItem):
    def __init__(self, region=None, account=None, name=None, config={}):
        super(RedshiftCluster, self).__init__(
            index=Redshift.index,
            region=region,
            account=account,
            name=name,
            new_config=config)
<commit_msg>Fixes #292 - adds ephemeral sections to the redshift watcher<commit_after>#     Copyright 2014 Yelp, Inc.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
"""
.. module: security_monkey.watchers.redshift
    :platform: Unix

.. version:: $$VERSION$$
.. moduleauthor:: Ivan Leichtling <ivanlei@yelp.com> @c0wl

"""

from security_monkey.watcher import Watcher
from security_monkey.watcher import ChangeItem
from security_monkey.constants import TROUBLE_REGIONS
from security_monkey.exceptions import BotoConnectionIssue
from security_monkey import app

from boto.redshift import regions

class Redshift(Watcher):
    index = 'redshift'
    i_am_singular = 'Redshift Cluster'
    i_am_plural = 'Redshift Clusters'

    def __init__(self, accounts=None, debug=False):
        super(Redshift, self).__init__(accounts=accounts, debug=debug)
        self.honor_ephemerals = True
        self.ephemeral_paths = [
            "RestoreStatus",
            "ClusterStatus",
            "ClusterParameterGroups$ParameterApplyStatus",
            "ClusterParameterGroups$ClusterParameterStatusList$ParameterApplyErrorDescription",
            "ClusterParameterGroups$ClusterParameterStatusList$ParameterApplyStatus",
            "ClusterRevisionNumber"
        ]

    def slurp(self):
        """
        :returns: item_list - list of Redshift Policies.
        :returns: exception_map - A dict where the keys are a tuple containing the
            location of the exception and the value is the actual exception

        """
        self.prep_for_slurp()
        from security_monkey.common.sts_connect import connect
        item_list = []
        exception_map = {}
        for account in self.accounts:
            for region in regions():
                app.logger.debug("Checking {}/{}/{}".format(self.index, account, region.name))
                try:
                    redshift = connect(account, 'redshift', region=region)

                    all_clusters = []
                    marker = None
                    while True:
                        response = self.wrap_aws_rate_limited_call(
                            redshift.describe_clusters,
                            marker=marker
                        )
                        all_clusters.extend(response['DescribeClustersResponse']['DescribeClustersResult']['Clusters'])
                        if response['DescribeClustersResponse']['DescribeClustersResult']['Marker'] is not None:
                            marker = response['DescribeClustersResponse']['DescribeClustersResult']['Marker']
                        else:
                            break

                except Exception as e:
                    if region.name not in TROUBLE_REGIONS:
                        exc = BotoConnectionIssue(str(e), 'redshift', account, region.name)
                        self.slurp_exception((self.index, account, region.name), exc, exception_map)
                    continue
                app.logger.debug("Found {} {}".format(len(all_clusters), Redshift.i_am_plural))
                for cluster in all_clusters:
                    cluster_id = cluster['ClusterIdentifier']
                    if self.check_ignore_list(cluster_id):
                        continue

                    item = RedshiftCluster(region=region.name, account=account, name=cluster_id, config=dict(cluster))
                    item_list.append(item)

        return item_list, exception_map

class RedshiftCluster(ChangeItem):
    def __init__(self, region=None, account=None, name=None, config={}):
        super(RedshiftCluster, self).__init__(
            index=Redshift.index,
            region=region,
            account=account,
            name=name,
            new_config=config)
<|endoftext|>


<commit_before>import re

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch, reverse
from django.utils.encoding import force_unicode
from django.utils.functional import memoize
from django.utils.importlib import import_module
from django.utils.regex_helper import normalize

from django_hosts.defaults import host as host_cls

_hostconf_cache = {}
_hostconf_module_cache = {}
_host_patterns_cache = {}
_host_cache = {}

def get_hostconf():
    try:
        return settings.ROOT_HOSTCONF
    except AttributeError:
        raise ImproperlyConfigured("Missing ROOT_HOSTCONF setting")
get_hostconf = memoize(get_hostconf, _hostconf_cache, 0)

def get_hostconf_module(hostconf=None):
    if hostconf is None:
        hostconf = get_hostconf()
    return import_module(hostconf)
get_hostconf_module = memoize(get_hostconf_module, _hostconf_module_cache, 1)

def get_host(name):
    for host in get_host_patterns():
        if host.name == name:
            return host
    raise NoReverseMatch("No host called '%s' exists" % name)
get_host = memoize(get_host, _host_cache, 1)

def get_host_patterns():
    hostconf = get_hostconf()
    module = get_hostconf_module(hostconf)
    try:
        return module.host_patterns
    except AttributeError:
        raise ImproperlyConfigured("Missing host_patterns in '%s'" %
                                   hostconf)
get_host_patterns = memoize(get_host_patterns, _host_patterns_cache, 0)

def clear_host_caches():
    global _hostconf_cache, _hostconf_module_cache, _host_patterns_cache, _host_cache
    _hostconf_cache.clear()
    _hostconf_module_cache.clear()
    _host_patterns_cache.clear()
    _host_cache.clear()

def reverse_host(host, args=None, kwargs=None):
    """
    Given the host name and the appropriate parameters,
    reverses the host, e.g.::

        >>> from django.conf import settings
        >>> settings.ROOT_HOSTCONF = 'mysite.hosts'
        >>> settings.PARENT_HOST = 'example.com'
        >>> from django_hosts.reverse import reverse_host
        >>> reverse_host('with_username', 'jezdez')
        'jezdez.example.com'

    :param name: the name of the host as specified in the hostconf
    :args: the host arguments to use to find a matching entry in the hostconf
    :kwargs: similar to args but key value arguments
    :raises django.core.urlresolvers.NoReverseMatch: if no host matches
    :rtype: reversed hostname
    """
    if args and kwargs:
        raise ValueError("Don't mix *args and **kwargs in call to reverse()!")

    args = args or ()
    kwargs = kwargs or {}

    if not isinstance(host, host_cls):
        host = get_host(host)

    unicode_args = [force_unicode(x) for x in args]
    unicode_kwargs = dict(((k, force_unicode(v))
                          for (k, v) in kwargs.iteritems()))

    for result, params in normalize(host.regex):
        if args:
            if len(args) != len(params):
                continue
            candidate = result % dict(zip(params, unicode_args))
        else:
            if set(kwargs.keys()) != set(params):
                continue
            candidate = result % unicode_kwargs

        if re.match(host.regex, candidate, re.UNICODE):  # pragma: no cover
            parent_host = getattr(settings, 'PARENT_HOST', '').lstrip('.')
            if parent_host:
                # only add the parent host when needed (aka www-less domain)
                if candidate and candidate != parent_host:
                    candidate = '%s.%s' % (candidate, parent_host)
                else:
                    candidate = parent_host
            return candidate

    raise NoReverseMatch("Reverse host for '%s' with arguments '%s' "
                         "and keyword arguments '%s' not found." %
                         (host.name, args, kwargs))

def reverse_full(host, view,
                 host_args=None, host_kwargs=None,
                 view_args=None, view_kwargs=None):
    """
    Given the host and view name and the appropriate parameters,
    reverses the fully qualified URL, e.g.::

        >>> from django.conf import settings
        >>> settings.ROOT_HOSTCONF = 'mysite.hosts'
        >>> settings.PARENT_HOST = 'example.com'
        >>> from django_hosts.reverse import reverse_full
        >>> reverse_full('www', 'about')
        '//www.example.com/about/'

    You can set the used scheme in the host object.

    :param host: the name of the host
    :param view: the name of the view
    :host_args: the host arguments
    :host_kwargs: the host keyed arguments
    :view_args: the arguments of the view
    :view_kwargs: the keyed arguments of the view
    :rtype: fully qualified URL with path
    """
    host = get_host(host)
    host_part = reverse_host(host,
                             args=host_args,
                             kwargs=host_kwargs)
    path_part = reverse(view,
                        args=view_args or (),
                        kwargs=view_kwargs or {},
                        urlconf=host.urlconf)
    return '%s%s%s' % (host.scheme, host_part, path_part)
<commit_msg>Fixed an import.<commit_after>import re

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import NoReverseMatch, reverse
from django.utils.encoding import force_text
from django.utils.functional import memoize
from django.utils.importlib import import_module
from django.utils.regex_helper import normalize

from django_hosts.defaults import host as host_cls

_hostconf_cache = {}
_hostconf_module_cache = {}
_host_patterns_cache = {}
_host_cache = {}

def get_hostconf():
    try:
        return settings.ROOT_HOSTCONF
    except AttributeError:
        raise ImproperlyConfigured("Missing ROOT_HOSTCONF setting")
get_hostconf = memoize(get_hostconf, _hostconf_cache, 0)

def get_hostconf_module(hostconf=None):
    if hostconf is None:
        hostconf = get_hostconf()
    return import_module(hostconf)
get_hostconf_module = memoize(get_hostconf_module, _hostconf_module_cache, 1)

def get_host(name):
    for host in get_host_patterns():
        if host.name == name:
            return host
    raise NoReverseMatch("No host called '%s' exists" % name)
get_host = memoize(get_host, _host_cache, 1)

def get_host_patterns():
    hostconf = get_hostconf()
    module = get_hostconf_module(hostconf)
    try:
        return module.host_patterns
    except AttributeError:
        raise ImproperlyConfigured("Missing host_patterns in '%s'" %
                                   hostconf)
get_host_patterns = memoize(get_host_patterns, _host_patterns_cache, 0)

def clear_host_caches():
    global _hostconf_cache, _hostconf_module_cache, _host_patterns_cache, _host_cache
    _hostconf_cache.clear()
    _hostconf_module_cache.clear()
    _host_patterns_cache.clear()
    _host_cache.clear()

def reverse_host(host, args=None, kwargs=None):
    """
    Given the host name and the appropriate parameters,
    reverses the host, e.g.::

        >>> from django.conf import settings
        >>> settings.ROOT_HOSTCONF = 'mysite.hosts'
        >>> settings.PARENT_HOST = 'example.com'
        >>> from django_hosts.reverse import reverse_host
        >>> reverse_host('with_username', 'jezdez')
        'jezdez.example.com'

    :param name: the name of the host as specified in the hostconf
    :args: the host arguments to use to find a matching entry in the hostconf
    :kwargs: similar to args but key value arguments
    :raises django.core.urlresolvers.NoReverseMatch: if no host matches
    :rtype: reversed hostname
    """
    if args and kwargs:
        raise ValueError("Don't mix *args and **kwargs in call to reverse()!")

    args = args or ()
    kwargs = kwargs or {}

    if not isinstance(host, host_cls):
        host = get_host(host)

    unicode_args = [force_text(x) for x in args]
    unicode_kwargs = dict(((k, force_text(v))
                          for (k, v) in kwargs.iteritems()))

    for result, params in normalize(host.regex):
        if args:
            if len(args) != len(params):
                continue
            candidate = result % dict(zip(params, unicode_args))
        else:
            if set(kwargs.keys()) != set(params):
                continue
            candidate = result % unicode_kwargs

        if re.match(host.regex, candidate, re.UNICODE):  # pragma: no cover
            parent_host = getattr(settings, 'PARENT_HOST', '').lstrip('.')
            if parent_host:
                # only add the parent host when needed (aka www-less domain)
                if candidate and candidate != parent_host:
                    candidate = '%s.%s' % (candidate, parent_host)
                else:
                    candidate = parent_host
            return candidate

    raise NoReverseMatch("Reverse host for '%s' with arguments '%s' "
                         "and keyword arguments '%s' not found." %
                         (host.name, args, kwargs))

def reverse_full(host, view,
                 host_args=None, host_kwargs=None,
                 view_args=None, view_kwargs=None):
    """
    Given the host and view name and the appropriate parameters,
    reverses the fully qualified URL, e.g.::

        >>> from django.conf import settings
        >>> settings.ROOT_HOSTCONF = 'mysite.hosts'
        >>> settings.PARENT_HOST = 'example.com'
        >>> from django_hosts.reverse import reverse_full
        >>> reverse_full('www', 'about')
        '//www.example.com/about/'

    You can set the used scheme in the host object.

    :param host: the name of the host
    :param view: the name of the view
    :host_args: the host arguments
    :host_kwargs: the host keyed arguments
    :view_args: the arguments of the view
    :view_kwargs: the keyed arguments of the view
    :rtype: fully qualified URL with path
    """
    host = get_host(host)
    host_part = reverse_host(host,
                             args=host_args,
                             kwargs=host_kwargs)
    path_part = reverse(view,
                        args=view_args or (),
                        kwargs=view_kwargs or {},
                        urlconf=host.urlconf)
    return '%s%s%s' % (host.scheme, host_part, path_part)
<|endoftext|>


<commit_before># Create your views here.

from django.core.urlresolvers import reverse_lazy
from django.contrib.auth.decorators import login_required
from django.views.generic.base import RedirectView
from django.db.models.loading import get_model

from .models import Transaction

import conf

class TransactionCompletedView(RedirectView):
    permanent = False
    url = reverse_lazy(conf.PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL)

    def get(self, request, *args, **kwargs):
        '''
        After Pesapal processes the transaction this will save the transaction and then redirect
        to whatever reidrect URL in your settings as `PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL`.

        For further processing just create a `post_save` signal on the `Transaction` model.
        '''
        transaction_id = request.GET.get('pesapal_transaction_tracking_id', '')
        merchant_reference = request.GET.get('pesapal_merchant_reference', '')

        if transaction_id and merchant_reference:
            transaction, created = Transaction.objects.get_or_create(pk=merchant_reference, pesapal_transaction_id=transaction_id)

        return super(TransactionCompletedView, self).get(request, *args, **kwargs)
<commit_msg>Refer to merchant_reference explicitly<commit_after># Create your views here.

from django.core.urlresolvers import reverse_lazy
from django.contrib.auth.decorators import login_required
from django.views.generic.base import RedirectView
from django.db.models.loading import get_model

from .models import Transaction

import conf

class TransactionCompletedView(RedirectView):
    permanent = False
    url = reverse_lazy(conf.PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL)

    def get(self, request, *args, **kwargs):
        '''
        After Pesapal processes the transaction this will save the transaction and then redirect
        to whatever reidrect URL in your settings as `PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL`.

        For further processing just create a `post_save` signal on the `Transaction` model.
        '''
        transaction_id = request.GET.get('pesapal_transaction_tracking_id', '')
        merchant_reference = request.GET.get('pesapal_merchant_reference', '')

        if transaction_id and merchant_reference:
            transaction, created = Transaction.objects.get_or_create(merchant_reference=merchant_reference, pesapal_transaction=transaction_id)

        return super(TransactionCompletedView, self).get(request, *args, **kwargs)
<|endoftext|>


<commit_before>from django.utils.timesince import timesince
from django.core.urlresolvers import reverse
from restlib import http, resources
from avocado.store.forms import PerspectiveForm, SessionPerspectiveForm

__all__ = ('PerspectiveResource', 'SessionPerspectiveResource', 'PerspectiveResourceCollection')

class PerspectiveResource(resources.ModelResource):
    """The standard resource for perspectives. The API is currently limited to
    prevent overwriting of an existing perspective. Simple ``write`` operations
    targeting descriptive information (e.g. name) can be performed at any time.
    """
    model = 'avocado.Perspective'

    default_for_related = False

    fields = (':pk', 'name', 'description', 'keywords', 'timesince', 'modified')

    @classmethod
    def timesince(self, obj):
        if obj.modified:
            return '%s ago' % timesince(obj.modified)

    @classmethod
    def queryset(self, request):
        return self.model.objects.filter(user=request.user, session=False)

    def DELETE(self, request, pk):
        "Deletes a perspective and deferences the object from the session."
        instance = request.session['perspective']

        # ensure to deference the session
        if instance.references(pk):
            instance.deference(delete=True)
        else:
            reference = self.queryset(request).filter(pk=pk)
            reference.delete()

        # nothing to see..
        return http.NO_CONTENT

    def GET(self, request, pk):
        "Fetches a perspective and sets the session perspective to be a proxy."
        instance = request.session['perspective']

        # if this object is already referenced by the session, simple return
        if not instance.references(pk):
            # attempt to fetch the requested object
            reference = self.get(request, pk=pk)
            if not reference:
                return http.NOT_FOUND

            reference.reset(instance)
        else:
            reference = instance.reference

        return reference

    def PUT(self, request, pk):
        """Explicitly updates an existing object given the request data. The
        data that can be updated via the request is limited to simple
        description data. Note, that if there are any pending changes applied
        via the session, these will be saved as well.
        """
        instance = request.session['perspective']

        if instance.references(pk):
            referenced = True
            reference = instance.reference
        else:
            referenced = False
            reference = self.get(request, pk=pk)
            if not reference:
                return http.NOT_FOUND

        form = PerspectiveForm(request.data, instance=reference)

        if form.is_valid():
            form.save()
            # if this is referenced by the session, update the session
            # instance to reflect this change. this only needs to be a
            # shallow reset since a PUT only updates local attributes
            if referenced:
                reference.reset(instance)
            return reference

        return form.errors

class SessionPerspectiveResource(resources.ModelResource):
    model = 'avocado.Perspective'

    fields = (':pk', 'name', 'description', 'keywords', 'store', 'header',
        'has_changed', 'timesince', 'modified')

    @classmethod
    def timesince(self, obj):
        if obj.modified:
            return '%s ago' % timesince(obj.modified)

    def GET(self, request):
        "Return this session's current perspective."
        return request.session['perspective']

    def PUT(self, request):
        instance = request.session['perspective']
        data = request.data

        # see if the json object is only the ``store``
        if data.has_key('columns') or data.has_key('ordering'):
            data = {'store': data}

        store = data.get('store', None)

        if store is not None:
            if not instance.is_valid(store):
                return http.BAD_REQUEST
            if not instance.has_permission(store, request.user):
                return http.UNAUTHORIZED

        # checked if this session references an existing perspective. if so
        # the changes will be applied on the referenced object as a "soft"
        # save. the only caveat is if changes are pending and this request
        # changes the name. if this case, a new perspective is saved
        form = SessionPerspectiveForm(data, instance=instance)

        if form.is_valid():
            form.save()
            return instance

        return form.errors

class PerspectiveResourceCollection(resources.ModelResourceCollection):
    resource = PerspectiveResource

<commit_msg>Added rudimentary PATCH support<commit_after>from django.utils.timesince import timesince
from restlib import http, resources
from avocado.store.forms import PerspectiveForm, SessionPerspectiveForm

__all__ = ('PerspectiveResource', 'SessionPerspectiveResource', 'PerspectiveResourceCollection')

PATCH_OPERATIONS = ('add', 'remove', 'replace')

class PerspectiveResource(resources.ModelResource):
    """The standard resource for perspectives. The API is currently limited to
    prevent overwriting of an existing perspective. Simple ``write`` operations
    targeting descriptive information (e.g. name) can be performed at any time.
    """
    model = 'avocado.Perspective'

    default_for_related = False

    fields = (':pk', 'name', 'description', 'keywords', 'timesince', 'modified')

    middleware = (
        'serrano.api.middleware.NeverCache',
    ) + resources.Resource.middleware

    @classmethod
    def timesince(self, obj):
        if obj.modified:
            return '%s ago' % timesince(obj.modified)

    @classmethod
    def queryset(self, request):
        return self.model.objects.filter(user=request.user, session=False)

    def DELETE(self, request, pk):
        "Deletes a perspective and deferences the object from the session."
        instance = request.session['perspective']

        # ensure to deference the session
        if instance.references(pk):
            instance.deference(delete=True)
        else:
            reference = self.queryset(request).filter(pk=pk)
            reference.delete()

        # nothing to see..
        return http.NO_CONTENT

    def GET(self, request, pk):
        "Fetches a perspective and sets the session perspective to be a proxy."
        instance = request.session['perspective']

        # if this object is already referenced by the session, simple return
        if not instance.references(pk):
            # attempt to fetch the requested object
            reference = self.get(request, pk=pk)
            if not reference:
                return http.NOT_FOUND

            reference.reset(instance)
        else:
            reference = instance.reference

        return reference

    def PUT(self, request, pk):
        """Explicitly updates an existing object given the request data. The
        data that can be updated via the request is limited to simple
        description data. Note, that if there are any pending changes applied
        via the session, these will be saved as well.
        """
        instance = request.session['perspective']

        if instance.references(pk):
            referenced = True
            reference = instance.reference
        else:
            referenced = False
            reference = self.get(request, pk=pk)
            if not reference:
                return http.NOT_FOUND

        form = PerspectiveForm(request.data, instance=reference)

        if form.is_valid():
            form.save()
            # if this is referenced by the session, update the session
            # instance to reflect this change. this only needs to be a
            # shallow reset since a PUT only updates local attributes
            if referenced:
                reference.reset(instance)
            return reference

        return form.errors

class SessionPerspectiveResource(resources.ModelResource):
    model = 'avocado.Perspective'

    fields = (':pk', 'name', 'description', 'keywords', 'store', 'header',
        'has_changed', 'timesince', 'modified')

    @classmethod
    def timesince(self, obj):
        if obj.modified:
            return '%s ago' % timesince(obj.modified)

    def GET(self, request):
        "Return this session's current perspective."
        return request.session['perspective']

    def PUT(self, request):
        instance = request.session['perspective']
        data = request.data

        # see if the json object is only the ``store``
        if data.has_key('columns') or data.has_key('ordering'):
            data = {'store': data}

        store = data.get('store', None)

        if store is not None:
            if not instance.is_valid(store):
                return http.BAD_REQUEST
            if not instance.has_permission(store, request.user):
                return http.UNAUTHORIZED

        # checked if this session references an existing perspective. if so
        # the changes will be applied on the referenced object as a "soft"
        # save. the only caveat is if changes are pending and this request
        # changes the name. if this case, a new perspective is saved
        form = SessionPerspectiveForm(data, instance=instance)

        if form.is_valid():
            form.save()
            return instance

        return form.errors

    def PATCH(self, request):
        instance = request.session['perspective']

        if len(request.data) != 1:
            return http.UNPROCESSABLE_ENTITY

        # XXX: until we move to the jsonpatch library..
        if not request.data.has_key('replace'):
            return http.UNPROCESSABLE_ENTITY

        store = request.data['replace']

        if store is not None:
            if not instance.is_valid({'store': store}):
                return http.BAD_REQUEST
            if not instance.has_permission({'store': store}, request.user):
                return http.UNAUTHORIZED

        instance.write(store)
        instance.save()
        return instance

class PerspectiveResourceCollection(resources.ModelResourceCollection):
    resource = PerspectiveResource

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Django-Select2 Widgets.

These components are responsible for rendering
the necessary HTML data markups. Since this whole
package is to render choices using Select2 JavaScript
library, hence these components are meant to be used
with choice fields.

Widgets are generally of two types:

    1. **Light** --
    They are not meant to be used when there
    are too many options, say, in thousands.
    This is because all those options would
    have to be pre-rendered onto the page
    and JavaScript would be used to search
    through them. Said that, they are also one
    the most easiest to use. They are a
    drop-in-replacement for Django's default
    select widgets.

    2. **Heavy** --
    They are suited for scenarios when the number of options
    are large and need complex queries (from maybe different
    sources) to get the options.
    This dynamic fetching of options undoubtedly requires
    Ajax communication with the server. Django-Select2 includes
    a helper JS file which is included automatically,
    so you need not worry about writing any Ajax related JS code.
    Although on the server side you do need to create a view
    specifically to respond to the queries.

    3. **Model** --
    Model-widgets are a further specialized versions of Heavies.
    These do not require views to serve Ajax requests.
    When they are instantiated, they register themselves
    with one central view which handles Ajax requests for them.

Heavy widgets have the word 'Heavy' in their name.
Light widgets are normally named, i.e. there is no
'Light' word in their names.

.. inheritance-diagram:: django_select2.forms
    :parts: 1

"""
from __future__ import absolute_import, unicode_literals

from functools import reduce
from itertools import chain

from django import forms
from django.core import signing
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.forms.models import ModelChoiceIterator
from django.utils.encoding import force_text
from django.contrib.staticfiles.templatetags.staticfiles import static

from .cache import cache
from .conf import settings

class Select2Mixin(object):
    """
    The base mixin of all Select2 widgets.

    This mixin is responsible for rendering the necessary
    data attributes for select2 as well as adding the static
    form media.
    """

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Add select2 data attributes."""
        attrs = super(Select2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
        if self.is_required:
            attrs.setdefault('data-allow-clear', 'false')
        else:
            attrs.setdefault('data-allow-clear', 'true')
            attrs.setdefault('data-placeholder', '')

        attrs.setdefault('data-minimum-input-length', 0)
        if 'class' in attrs:
            attrs['class'] += ' django-select2'
        else:
            attrs['class'] = 'django-select2'
        return attrs

    def render_options(self, choices, selected_choices):
        """Render options including an empty one, if the field is not required."""
        output = '<option></option>' if not self.is_required else ''
        output += super(Select2Mixin, self).render_options(choices, selected_choices)
        return output

    def _get_media(self):
        """
        Construct Media as a dynamic property.

        .. Note:: For more information visit
            https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
        """
        return forms.Media(
            js=(settings.SELECT2_JS_URL,
                static('django_select2/django_select2.js')),
            css={'screen': (settings.SELECT2_CSS_URL,)}
        )

    media = property(_get_media)

class Select2TagMixin(object):
    """Mixin to add select2 tag functionality."""

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Add select2's tag attributes."""
        self.attrs.setdefault('data-minimum-input-length', 1)
        self.attrs.setdefault('data-tags', 'true')
        self.attrs.setdefault('data-token-separators', [",", " "])
        return super(Select2TagMixin, self).build_attrs(extra_attrs, **kwargs)

class Select2Widget(Select2Mixin, forms.Select):
    """
    Select2 drop in widget.

    Example usage::

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': Select2Widget
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(widget=Select2Widget)

    """

    pass

class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for multiple select.

    Works just like :class:`.Select2Widget` but for multi select.
    """

    pass

class Select2TagWidget(Select2TagMixin, Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for for tagging.

    Example for :class:`.django.contrib.postgres.fields.ArrayField`::

        class MyWidget(Select2TagWidget):

            def value_from_datadict(self, data, files, name):
                values = super(MyWidget, self).value_from_datadict(data, files, name):
                return ",".join(values)

    """

    pass

class HeavySelect2Mixin(Select2Mixin):
    """Mixin that adds select2's ajax options and registers itself on django's cache."""

    def __init__(self, **kwargs):
        """
        Return HeavySelect2Mixin.

        :param data_view: url pattern name
        :type data_view: str
        :param data_url: url
        :type data_url: str
        :return:
        """
        self.data_view = kwargs.pop('data_view', None)
        self.data_url = kwargs.pop('data_url', None)
        if not (self.data_view or self.data_url):
            raise ValueError('You must ether specify "data_view" or "data_url".')
        self.userGetValTextFuncName = kwargs.pop('userGetValTextFuncName', 'null')
        super(HeavySelect2Mixin, self).__init__(**kwargs)

    def get_url(self):
        """Return url from instance or by reversing :attr:`.data_view`."""
        if self.data_url:
            return self.data_url
        return reverse_lazy(self.data_view)

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Set select2's ajax attributes."""
        attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)

        # encrypt instance Id
        self.widget_id = signing.dumps(id(self))

        attrs['data-field_id'] = self.widget_id
        attrs.setdefault('data-ajax--url', self.get_url())
        attrs.setdefault('data-ajax--cache', "true")
        attrs.setdefault('data-ajax--type', "GET")
        attrs.setdefault('data-minimum-input-length', 2)

        attrs['class'] += ' django-select2-heavy'
        return attrs

    def render(self, name, value, attrs=None, choices=()):
        """Render widget and register it in Django's cache."""
        output = super(HeavySelect2Mixin, self).render(name, value, attrs=attrs, choices=choices)
        self.set_to_cache()
        return output

    def _get_cache_key(self):
        return "%s%s" % (settings.SELECT2_CACHE_PREFIX, id(self))

    def set_to_cache(self):
        """Add widget object to Djnago's cache."""
        cache.set(self._get_cache_key(), self)

    def render_options(self, choices, selected_choices):
        """Render only selected options."""
        choices = chain(choices, self.choices)
        output = ['<option></option>' if not self.is_required else '']
        choices = {(k, v) for k, v in choices if k in selected_choices}
        selected_choices = {force_text(v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

class HeavySelect2Widget(HeavySelect2Mixin, forms.Select):
    """
    Select2 widget with AJAX support that registers itself to Django's Cache.

    Usage example::

        class MyWidget(HeavySelectWidget):
            data_view = 'my_view_name'

    or::

        class MyForm(forms.Form):
            my_field = forms.ChoicesField(
                widget=HeavySelectWidget(
                    data_url='/url/to/json/response'
                )
            )

    """

    pass

class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
    """Select2 multi select widget similar to :class:`.HeavySelect2Widget`."""

    pass

class HeavySelect2TagWidget(Select2TagMixin, HeavySelect2MultipleWidget):
    """Select2 tag widget."""

    pass

# Auto Heavy widgets

class ModelSelect2Mixin(object):
    """Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."""

    model = None
    queryset = None
    search_fields = []
    """
    Model lookups that are used to filter the queryset.

    Example::

        search_fields = [
                'title__icontains',
            ]

    """

    max_results = 25
    """Maximal results returned by :class:`.AutoResponseView`."""

    def __init__(self, *args, **kwargs):
        """
        Overwrite class parameters if passed as keyword arguments.

        :param model: model to select choices from
        :type model: django.db.models.Model
        :param queryset: queryset to select choices from
        :type queryset: django.db.models.query.QuerySet
        :param search_fields: list of model lookup strings
        :type search_fields: list
        :param max_results: max. JsonResponse view page size
        :type max_results: int
        """
        self.model = kwargs.pop('model', self.model)
        self.queryset = kwargs.pop('queryset', self.queryset)
        self.search_fields = kwargs.pop('search_fields', self.search_fields)
        self.max_results = kwargs.pop('max_results', self.max_results)
        defaults = {'data_view': 'django_select2-json'}
        defaults.update(kwargs)
        super(ModelSelect2Mixin, self).__init__(*args, **defaults)

    def set_to_cache(self):
        """
        Add widget's attributes to Djnago's cache.

        Split the queryset, to not pickle the result set.
        """
        queryset = self.get_queryset()
        cache.set(self._get_cache_key(), {
            'queryset':
                [
                    queryset.none(),
                    queryset.query,
                ],
            'cls': self.__class__,
            'search_fields': self.search_fields,
            'max_results': self.max_results,
        })

    def filter_queryset(self, term, queryset=None):
        """
        Return queryset filtered by search_fields matching the passed term.

        :param term: Search term
        :type term: str
        :return: Filtered queryset
        :rtype: :class:`.django.db.models.QuerySet`
        """
        if queryset is None:
            queryset = self.get_queryset()
        search_fields = self.get_search_fields()
        select = Q()
        term = term.replace('\t', ' ')
        term = term.replace('\n', ' ')
        for t in [t for t in term.split(' ') if not t == '']:
            select &= reduce(lambda x, y: x | Q(**{y: t}), search_fields,
                             Q(**{search_fields[0]: t}))
        return queryset.filter(select).distinct()

    def get_queryset(self):
        """
        Return queryset based on :attr:`.queryset` or :attr:`.model`.

        :return: queryset of available choices
        :rtype: :class:`.django.db.models.QuerySet`
        """
        if self.queryset is not None:
            queryset = self.queryset
        elif self.model is not None:
            queryset = self.model._default_manager.all()
        else:
            raise NotImplementedError(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset, or override "
                "%(cls)s.get_queryset()." % {
                    'cls': self.__class__.__name__
                }
            )
        return queryset

    def get_search_fields(self):
        """Return list of lookup names."""
        if self.search_fields:
            return self.search_fields
        raise NotImplementedError('%s, must implement "search_fields".' % self.__class__.__name__)

    def render_options(self, choices, selected_choices):
        """Render only selected options and set queryset from :class:`ModelChoicesIterator`."""
        output = ['<option></option>' if not self.is_required else '']
        if isinstance(self.choices, ModelChoiceIterator):
            if not self.queryset:
                self.queryset = self.choices.queryset
            selected_choices = {c for c in selected_choices
                                if c not in self.choices.field.empty_values}
            choices = {self.choices.choice(obj)
                       for obj in self.choices.queryset.filter(pk__in=selected_choices)}
        else:
            choices = chain(choices, self.choices)
            choices = {(k, v) for k, v in choices if k in selected_choices}
        selected_choices = {force_text(v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget):
    """
    Select2 drop in model select widget.

    Example usage::

        class MyWidget(ModelSelect2Widget):
            search_fields = [
                'title__icontains',
            ]

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': MyWidget,
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(
                widget=ModelSelect2Widget(
                    model=MyOtherModel,
                    search_fields=['title__icontains']
                )
            )

    .. tip:: The ModelSelect2(Multiple)Widget will try
        to get the queryset from the fields choices.
        Therefore you don't need to define a queryset,
        if you just drop in the widget for a ForeignKey field.
    """

    pass

class ModelSelect2MultipleWidget(ModelSelect2Mixin, HeavySelect2MultipleWidget):
    """
    Select2 drop in model multiple select widget.

    Works just like :class:`.ModelSelect2Widget` but for multi select.
    """

    pass

class ModelSelect2TagWidget(Select2TagMixin, ModelSelect2MultipleWidget):
    """
    Select2 model widget with tag support.

    This it not a simple drop in widget.
    It requires to implement you own :func:`.value_from_datadict`
    that adds missing tags to you queryset.

    Example::

        class MyModelSelect2TagWidget(ModelSelect2TagWidget):
            queryset = MyModel.objects.all()

            def value_from_datadict(self, data, files, name):
                values = super().value_from_datadict(self, data, files, name)
                qs = self.queryset.filter(**{'pk__in': list(values)})
                pks = set(force_text(getattr(o, pk)) for o in qs)
                cleaned_values = []
                for val in value:
                    if force_text(val) not in pks:
                        val = queryset.create(title=val).pk
                    cleaned_values.append(val)
                return cleaned_values

    """

    pass
<commit_msg>Remove caching as not all models can be pickle'd<commit_after># -*- coding: utf-8 -*-
"""
Django-Select2 Widgets.

These components are responsible for rendering
the necessary HTML data markups. Since this whole
package is to render choices using Select2 JavaScript
library, hence these components are meant to be used
with choice fields.

Widgets are generally of two types:

    1. **Light** --
    They are not meant to be used when there
    are too many options, say, in thousands.
    This is because all those options would
    have to be pre-rendered onto the page
    and JavaScript would be used to search
    through them. Said that, they are also one
    the most easiest to use. They are a
    drop-in-replacement for Django's default
    select widgets.

    2. **Heavy** --
    They are suited for scenarios when the number of options
    are large and need complex queries (from maybe different
    sources) to get the options.
    This dynamic fetching of options undoubtedly requires
    Ajax communication with the server. Django-Select2 includes
    a helper JS file which is included automatically,
    so you need not worry about writing any Ajax related JS code.
    Although on the server side you do need to create a view
    specifically to respond to the queries.

    3. **Model** --
    Model-widgets are a further specialized versions of Heavies.
    These do not require views to serve Ajax requests.
    When they are instantiated, they register themselves
    with one central view which handles Ajax requests for them.

Heavy widgets have the word 'Heavy' in their name.
Light widgets are normally named, i.e. there is no
'Light' word in their names.

.. inheritance-diagram:: django_select2.forms
    :parts: 1

"""
from __future__ import absolute_import, unicode_literals

from functools import reduce
from itertools import chain

from django import forms
from django.core import signing
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.forms.models import ModelChoiceIterator
from django.utils.encoding import force_text
from django.contrib.staticfiles.templatetags.staticfiles import static

from .conf import settings

class Select2Mixin(object):
    """
    The base mixin of all Select2 widgets.

    This mixin is responsible for rendering the necessary
    data attributes for select2 as well as adding the static
    form media.
    """

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Add select2 data attributes."""
        attrs = super(Select2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
        if self.is_required:
            attrs.setdefault('data-allow-clear', 'false')
        else:
            attrs.setdefault('data-allow-clear', 'true')
            attrs.setdefault('data-placeholder', '')

        attrs.setdefault('data-minimum-input-length', 0)
        if 'class' in attrs:
            attrs['class'] += ' django-select2'
        else:
            attrs['class'] = 'django-select2'
        return attrs

    def render_options(self, choices, selected_choices):
        """Render options including an empty one, if the field is not required."""
        output = '<option></option>' if not self.is_required else ''
        output += super(Select2Mixin, self).render_options(choices, selected_choices)
        return output

    def _get_media(self):
        """
        Construct Media as a dynamic property.

        .. Note:: For more information visit
            https://docs.djangoproject.com/en/1.8/topics/forms/media/#media-as-a-dynamic-property
        """
        return forms.Media(
            js=(settings.SELECT2_JS_URL,
                static('django_select2/django_select2.js')),
            css={'screen': (settings.SELECT2_CSS_URL,)}
        )

    media = property(_get_media)

class Select2TagMixin(object):
    """Mixin to add select2 tag functionality."""

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Add select2's tag attributes."""
        self.attrs.setdefault('data-minimum-input-length', 1)
        self.attrs.setdefault('data-tags', 'true')
        self.attrs.setdefault('data-token-separators', [",", " "])
        return super(Select2TagMixin, self).build_attrs(extra_attrs, **kwargs)

class Select2Widget(Select2Mixin, forms.Select):
    """
    Select2 drop in widget.

    Example usage::

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': Select2Widget
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(widget=Select2Widget)

    """

    pass

class Select2MultipleWidget(Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for multiple select.

    Works just like :class:`.Select2Widget` but for multi select.
    """

    pass

class Select2TagWidget(Select2TagMixin, Select2Mixin, forms.SelectMultiple):
    """
    Select2 drop in widget for for tagging.

    Example for :class:`.django.contrib.postgres.fields.ArrayField`::

        class MyWidget(Select2TagWidget):

            def value_from_datadict(self, data, files, name):
                values = super(MyWidget, self).value_from_datadict(data, files, name):
                return ",".join(values)

    """

    pass

class HeavySelect2Mixin(Select2Mixin):
    """Mixin that adds select2's ajax options."""

    def __init__(self, **kwargs):
        """
        Return HeavySelect2Mixin.

        :param data_view: url pattern name
        :type data_view: str
        :param data_url: url
        :type data_url: str
        :return:
        """
        self.data_view = kwargs.pop('data_view', None)
        self.data_url = kwargs.pop('data_url', None)
        if not (self.data_view or self.data_url):
            raise ValueError('You must ether specify "data_view" or "data_url".')
        self.userGetValTextFuncName = kwargs.pop('userGetValTextFuncName', 'null')
        super(HeavySelect2Mixin, self).__init__(**kwargs)

    def get_url(self):
        """Return url from instance or by reversing :attr:`.data_view`."""
        if self.data_url:
            return self.data_url
        return reverse_lazy(self.data_view)

    def build_attrs(self, extra_attrs=None, **kwargs):
        """Set select2's ajax attributes."""
        attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)

        # encrypt instance Id
        self.widget_id = signing.dumps(id(self))

        attrs['data-field_id'] = self.widget_id
        attrs.setdefault('data-ajax--url', self.get_url())
        attrs.setdefault('data-ajax--cache', "true")
        attrs.setdefault('data-ajax--type', "GET")
        attrs.setdefault('data-minimum-input-length', 2)

        attrs['class'] += ' django-select2-heavy'
        return attrs

    def render(self, name, value, attrs=None, choices=()):
        """Render widget."""
        return super(HeavySelect2Mixin, self).render(name, value, attrs=attrs, choices=choices)

    def render_options(self, choices, selected_choices):
        """Render only selected options."""
        choices = chain(choices, self.choices)
        output = ['<option></option>' if not self.is_required else '']
        choices = {(k, v) for k, v in choices if k in selected_choices}
        selected_choices = {force_text(v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

class HeavySelect2Widget(HeavySelect2Mixin, forms.Select):
    """
    Select2 widget with AJAX support.

    Usage example::

        class MyWidget(HeavySelectWidget):
            data_view = 'my_view_name'

    or::

        class MyForm(forms.Form):
            my_field = forms.ChoicesField(
                widget=HeavySelectWidget(
                    data_url='/url/to/json/response'
                )
            )

    """

    pass

class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
    """Select2 multi select widget similar to :class:`.HeavySelect2Widget`."""

    pass

class HeavySelect2TagWidget(Select2TagMixin, HeavySelect2MultipleWidget):
    """Select2 tag widget."""

    pass

# Auto Heavy widgets

class ModelSelect2Mixin(object):
    """Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."""

    model = None
    queryset = None
    search_fields = []
    """
    Model lookups that are used to filter the queryset.

    Example::

        search_fields = [
                'title__icontains',
            ]

    """

    max_results = 25
    """Maximal results returned by :class:`.AutoResponseView`."""

    def __init__(self, *args, **kwargs):
        """
        Overwrite class parameters if passed as keyword arguments.

        :param model: model to select choices from
        :type model: django.db.models.Model
        :param queryset: queryset to select choices from
        :type queryset: django.db.models.query.QuerySet
        :param search_fields: list of model lookup strings
        :type search_fields: list
        :param max_results: max. JsonResponse view page size
        :type max_results: int
        """
        self.model = kwargs.pop('model', self.model)
        self.queryset = kwargs.pop('queryset', self.queryset)
        self.search_fields = kwargs.pop('search_fields', self.search_fields)
        self.max_results = kwargs.pop('max_results', self.max_results)
        defaults = {'data_view': 'django_select2-json'}
        defaults.update(kwargs)
        super(ModelSelect2Mixin, self).__init__(*args, **defaults)

    def filter_queryset(self, term, queryset=None):
        """
        Return queryset filtered by search_fields matching the passed term.

        :param term: Search term
        :type term: str
        :return: Filtered queryset
        :rtype: :class:`.django.db.models.QuerySet`
        """
        if queryset is None:
            queryset = self.get_queryset()
        search_fields = self.get_search_fields()
        select = Q()
        term = term.replace('\t', ' ')
        term = term.replace('\n', ' ')
        for t in [t for t in term.split(' ') if not t == '']:
            select &= reduce(lambda x, y: x | Q(**{y: t}), search_fields,
                             Q(**{search_fields[0]: t}))
        return queryset.filter(select).distinct()

    def get_queryset(self):
        """
        Return queryset based on :attr:`.queryset` or :attr:`.model`.

        :return: queryset of available choices
        :rtype: :class:`.django.db.models.QuerySet`
        """
        if self.queryset is not None:
            queryset = self.queryset
        elif self.model is not None:
            queryset = self.model._default_manager.all()
        else:
            raise NotImplementedError(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset, or override "
                "%(cls)s.get_queryset()." % {
                    'cls': self.__class__.__name__
                }
            )
        return queryset

    def get_search_fields(self):
        """Return list of lookup names."""
        if self.search_fields:
            return self.search_fields
        raise NotImplementedError('%s, must implement "search_fields".' % self.__class__.__name__)

    def render_options(self, choices, selected_choices):
        """Render only selected options and set queryset from :class:`ModelChoicesIterator`."""
        output = ['<option></option>' if not self.is_required else '']
        if isinstance(self.choices, ModelChoiceIterator):
            if not self.queryset:
                self.queryset = self.choices.queryset
            selected_choices = {c for c in selected_choices
                                if c not in self.choices.field.empty_values}
            choices = {self.choices.choice(obj)
                       for obj in self.choices.queryset.filter(pk__in=selected_choices)}
        else:
            choices = chain(choices, self.choices)
            choices = {(k, v) for k, v in choices if k in selected_choices}
        selected_choices = {force_text(v) for v in selected_choices}
        for option_value, option_label in choices:
            output.append(self.render_option(selected_choices, option_value, option_label))
        return '\n'.join(output)

class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget):
    """
    Select2 drop in model select widget.

    Example usage::

        class MyWidget(ModelSelect2Widget):
            search_fields = [
                'title__icontains',
            ]

        class MyModelForm(forms.ModelForm):
            class Meta:
                model = MyModel
                fields = ('my_field', )
                widgets = {
                    'my_field': MyWidget,
                }

    or::

        class MyForm(forms.Form):
            my_choice = forms.ChoiceField(
                widget=ModelSelect2Widget(
                    model=MyOtherModel,
                    search_fields=['title__icontains']
                )
            )

    .. tip:: The ModelSelect2(Multiple)Widget will try
        to get the queryset from the fields choices.
        Therefore you don't need to define a queryset,
        if you just drop in the widget for a ForeignKey field.
    """

    pass

class ModelSelect2MultipleWidget(ModelSelect2Mixin, HeavySelect2MultipleWidget):
    """
    Select2 drop in model multiple select widget.

    Works just like :class:`.ModelSelect2Widget` but for multi select.
    """

    pass

class ModelSelect2TagWidget(Select2TagMixin, ModelSelect2MultipleWidget):
    """
    Select2 model widget with tag support.

    This it not a simple drop in widget.
    It requires to implement you own :func:`.value_from_datadict`
    that adds missing tags to you queryset.

    Example::

        class MyModelSelect2TagWidget(ModelSelect2TagWidget):
            queryset = MyModel.objects.all()

            def value_from_datadict(self, data, files, name):
                values = super().value_from_datadict(self, data, files, name)
                qs = self.queryset.filter(**{'pk__in': list(values)})
                pks = set(force_text(getattr(o, pk)) for o in qs)
                cleaned_values = []
                for val in value:
                    if force_text(val) not in pks:
                        val = queryset.create(title=val).pk
                    cleaned_values.append(val)
                return cleaned_values

    """

    pass
<|endoftext|>