<commit_before>#!/usr/bin/python
# vim: ts=4 sw=4 et
"""Query class for vctools.  All methods that obtain info should go here."""
from __future__ import division
from __future__ import print_function
from pyVmomi import vim # pylint: disable=no-name-in-module

class Query(object):
    """
    Class handles queries for information regarding for vms, datastores
    and networks.
    """
    def __init__(self):
        pass

    @classmethod
    def disk_size_format(cls, num):
        """
        Method converts datastore size in bytes to human readable format.

        Args:
            num (int): Number
        """

        for attr in ['bytes', 'KB', 'MB', 'GB', 'TB']:
            if num < 1024.0:
                return '%3.2f %s' % (num, attr)
            num /= 1024.0

    @classmethod
    def create_container(cls, s_instance, *args):
        """
        Wrapper method for creating managed objects inside vim.view.ViewManager.

        Args:
            s_instance (obj): ServiceInstance
            args(list):
        """
        if hasattr(s_instance, 'content'):
            if hasattr(s_instance.content, 'viewManager'):
                return s_instance.content.viewManager.CreateContainerView(*args)
            else:
                raise Exception
        else:
            raise Exception

    @classmethod
    def get_obj(cls, container, name):
        """
        Returns an object inside of ContainerView if it matches name.

        Args:
            container (obj):  Container object
            name (str):       Name of Container
        """

        for obj in container:
            if obj.name == name:
                return obj

    @classmethod
    def list_obj_attrs(cls, container, attr, view=True):
        """
        Returns a list of attributes inside of container.

        Args:
            container (obj):  Container object
            attr (str):       Name of attribute within Container
            view (bool):      True appends view attribute to Container
        """
        if view:
            return [getattr(obj, attr) for obj in container.view]
        else:
            return [getattr(obj, attr) for obj in container]

    @classmethod
    def folders_lookup(cls, container, datacenter, name):
        """
        Returns the object for a folder name.  Currently, it only searches for
        the folder through one level of subfolders. This method is needed for
        building new virtual machines.

        Args:
            container (obj):  Container object
            datacenter (str): Name of datacenter
            name (str):       Name of folder
        """

        obj = Query.get_obj(container, datacenter)

        if hasattr(obj, 'vmFolder'):
            for folder in obj.vmFolder.childEntity:
                if hasattr(folder, 'childType'):
                    if folder.name == name:
                        return folder
                if hasattr(folder, 'childEntity'):
                    for item in folder.childEntity:
                        if hasattr(item, 'childType'):
                            if item.name == name:
                                return item

    @classmethod
    def list_vm_folders(cls, container, datacenter):
        """
        Returns a list of Virtual Machine folders.  Sub folders will be listed
        with its parent -> subfolder. Currently it only searches for one
        level of subfolders.

        Args:
            container (obj):  Container object
            datacenter (str): Name of datacenter
        """
        obj = Query.get_obj(container, datacenter)
        folders = []

        if hasattr(obj, 'vmFolder'):
            for folder in obj.vmFolder.childEntity:
                if hasattr(folder, 'childType'):
                    folders.append(folder.name)
                if hasattr(folder, 'childEntity'):
                    for item in folder.childEntity:
                        if hasattr(item, 'childType'):
                            folders.append(item.parent.name+' -> '+item.name)
        return folders

    @classmethod
    def datastore_most_space(cls, container, cluster):
        """
        Attempts to find the datastore with the most free space.

        Args:
            container (obj): Container object
            cluster (str):   Name of cluster

        """
        obj = Query.get_obj(container, cluster)
        datastores = {}
        for datastore in obj.datastore:
            # if datastore is a VMware File System
            if datastore.summary.type == 'VMFS':
                free = int(datastore.summary.freeSpace)
                datastores.update({datastore.name:free})

        most = max(datastores.itervalues())
        for key, value in datastores.iteritems():
            if value == most:
                print(key)

    @classmethod
    def return_datastores(cls, container, cluster, header=True):
        """
        Returns a summary of disk space for datastores listed inside a
        cluster. Identical to list_datastore_info, but returns the datastores
        as an list object instead of printing them to stdout.

        Args:
            container (obj): Container object
            cluster (str):   Name of cluster
            header (bool):   Enables a header of info to datastore list.
        """

        obj = Query.get_obj(container, cluster)

        datastore_info = []

        if header:
            header = [
                'Datastore', 'Capacity', 'Provisioned', 'Pct', 'Free Space', 'Pct'
            ]
            datastore_info.append(header)

        for datastore in obj.datastore:
            info = []
            # type is long(bytes)
            free = int(datastore.summary.freeSpace)
            capacity = int(datastore.summary.capacity)

            # uncommitted is sometimes None, so we'll convert that to 0.
            if not datastore.summary.uncommitted:
                uncommitted = int(0)
            else:
                uncommitted = int(datastore.summary.uncommitted)

            provisioned = int((capacity - free) + uncommitted)

            provisioned_pct = '{0:.2%}'.format((provisioned / capacity))
            free_pct = '{0:.2%}'.format((free / capacity))

            info.append(datastore.name)
            info.append(Query.disk_size_format(capacity))
            info.append(Query.disk_size_format(provisioned))
            info.append(provisioned_pct)
            info.append(Query.disk_size_format(free))
            info.append(free_pct)

            datastore_info.append(info)

        # sort by datastore name
        datastore_info.sort(key=lambda x: x[0])

        return datastore_info

    @classmethod
    def list_vm_info(cls, container, datacenter):
        """
        Returns a list of names for VMs located inside a datacenter.

        """

        obj = Query.get_obj(container, datacenter)

        vms = {}

        # recurse through datacenter object attributes looking for vms.
        if hasattr(obj, 'vmFolder'):
            for virtmachine in obj.vmFolder.childEntity:
                # pylint: disable=protected-access
                if hasattr(virtmachine, 'childEntity'):
                    for virt in virtmachine.childEntity:
                        vms.update({virt.name:virt._moId})
                else:
                    vms.update({virtmachine.name:virt._moId})

        return vms

    @classmethod
    def get_vmid_by_name(cls, container, datacenter, name):
        """
        Returns the moId of matched name

        """

        obj = Query.get_obj(container, datacenter)

        # recurse through datacenter object attributes looking for vm that
        # matches hostname.
        if hasattr(obj, 'vmFolder'):
            for virtmachine in obj.vmFolder.childEntity:
                # pylint: disable=protected-access
                if hasattr(virtmachine, 'childEntity'):
                    for virt in virtmachine.childEntity:
                        if virt.name == name:
                            return virt._moId
                else:
                    if virt.name == name:
                        return virt._moId

    @classmethod
    def get_key(cls, obj, query):
        """
        Method will attempt to return the key associated with device so it can
        be used to edit existing devices. It will loop through all possible
        devices and return the key that matches query with the label attribute.

        Example:
            To get the key for the cdrom device, assuming obj is a
            VirtualMachine object

            get_key(virtual_machine, 'CD/DVD')

        Args:
            obj (obj): VirtualMachine object
            query (str): A string representation of the object attribute.

        Returns:
            keys (tuple): A tuple container the key and controllerKey associated
                with the device
        """

        if hasattr(obj, 'config'):
            for item in obj.config.hardware.device:
                if query in item.deviceInfo.label:
                    key = item.key
                    controller_key = item.controllerKey

        return (key, controller_key)

    @classmethod
    def list_guestids(cls):
        """
        Method will return an array of all supported guestids
        Returns:
            guestids (list): An ordered list of supported guestids
        """
        guestids = []
        for key in vim.vm.GuestOsDescriptor.GuestOsIdentifier.__dict__:
            guestids.append(key)

        guestids.sort()

        return guestids

<commit_msg>query: Raising better exception value in create_containers<commit_after>#!/usr/bin/python
# vim: ts=4 sw=4 et
"""Query class for vctools.  All methods that obtain info should go here."""
from __future__ import division
from __future__ import print_function
from pyVmomi import vim # pylint: disable=no-name-in-module

class Query(object):
    """
    Class handles queries for information regarding for vms, datastores
    and networks.
    """
    def __init__(self):
        pass

    @classmethod
    def disk_size_format(cls, num):
        """
        Method converts datastore size in bytes to human readable format.

        Args:
            num (int): Number
        """

        for attr in ['bytes', 'KB', 'MB', 'GB', 'TB']:
            if num < 1024.0:
                return '%3.2f %s' % (num, attr)
            num /= 1024.0

    @classmethod
    def create_container(cls, s_instance, *args):
        """
        Wrapper method for creating managed objects inside vim.view.ViewManager.

        Args:
            s_instance (obj): ServiceInstance
            args(list):
        """
        if hasattr(s_instance, 'content'):
            if hasattr(s_instance.content, 'viewManager'):
                return s_instance.content.viewManager.CreateContainerView(*args)
            else:
                raise ValueError
        else:
            raise ValueError

    @classmethod
    def get_obj(cls, container, name):
        """
        Returns an object inside of ContainerView if it matches name.

        Args:
            container (obj):  Container object
            name (str):       Name of Container
        """

        for obj in container:
            if obj.name == name:
                return obj

    @classmethod
    def list_obj_attrs(cls, container, attr, view=True):
        """
        Returns a list of attributes inside of container.

        Args:
            container (obj):  Container object
            attr (str):       Name of attribute within Container
            view (bool):      True appends view attribute to Container
        """
        if view:
            return [getattr(obj, attr) for obj in container.view]
        else:
            return [getattr(obj, attr) for obj in container]

    @classmethod
    def folders_lookup(cls, container, datacenter, name):
        """
        Returns the object for a folder name.  Currently, it only searches for
        the folder through one level of subfolders. This method is needed for
        building new virtual machines.

        Args:
            container (obj):  Container object
            datacenter (str): Name of datacenter
            name (str):       Name of folder
        """

        obj = Query.get_obj(container, datacenter)

        if hasattr(obj, 'vmFolder'):
            for folder in obj.vmFolder.childEntity:
                if hasattr(folder, 'childType'):
                    if folder.name == name:
                        return folder
                if hasattr(folder, 'childEntity'):
                    for item in folder.childEntity:
                        if hasattr(item, 'childType'):
                            if item.name == name:
                                return item

    @classmethod
    def list_vm_folders(cls, container, datacenter):
        """
        Returns a list of Virtual Machine folders.  Sub folders will be listed
        with its parent -> subfolder. Currently it only searches for one
        level of subfolders.

        Args:
            container (obj):  Container object
            datacenter (str): Name of datacenter
        """
        obj = Query.get_obj(container, datacenter)
        folders = []

        if hasattr(obj, 'vmFolder'):
            for folder in obj.vmFolder.childEntity:
                if hasattr(folder, 'childType'):
                    folders.append(folder.name)
                if hasattr(folder, 'childEntity'):
                    for item in folder.childEntity:
                        if hasattr(item, 'childType'):
                            folders.append(item.parent.name+' -> '+item.name)
        return folders

    @classmethod
    def datastore_most_space(cls, container, cluster):
        """
        Attempts to find the datastore with the most free space.

        Args:
            container (obj): Container object
            cluster (str):   Name of cluster

        """
        obj = Query.get_obj(container, cluster)
        datastores = {}
        for datastore in obj.datastore:
            # if datastore is a VMware File System
            if datastore.summary.type == 'VMFS':
                free = int(datastore.summary.freeSpace)
                datastores.update({datastore.name:free})

        most = max(datastores.itervalues())
        for key, value in datastores.iteritems():
            if value == most:
                print(key)

    @classmethod
    def return_datastores(cls, container, cluster, header=True):
        """
        Returns a summary of disk space for datastores listed inside a
        cluster. Identical to list_datastore_info, but returns the datastores
        as an list object instead of printing them to stdout.

        Args:
            container (obj): Container object
            cluster (str):   Name of cluster
            header (bool):   Enables a header of info to datastore list.
        """

        obj = Query.get_obj(container, cluster)

        datastore_info = []

        if header:
            header = [
                'Datastore', 'Capacity', 'Provisioned', 'Pct', 'Free Space', 'Pct'
            ]
            datastore_info.append(header)

        for datastore in obj.datastore:
            info = []
            # type is long(bytes)
            free = int(datastore.summary.freeSpace)
            capacity = int(datastore.summary.capacity)

            # uncommitted is sometimes None, so we'll convert that to 0.
            if not datastore.summary.uncommitted:
                uncommitted = int(0)
            else:
                uncommitted = int(datastore.summary.uncommitted)

            provisioned = int((capacity - free) + uncommitted)

            provisioned_pct = '{0:.2%}'.format((provisioned / capacity))
            free_pct = '{0:.2%}'.format((free / capacity))

            info.append(datastore.name)
            info.append(Query.disk_size_format(capacity))
            info.append(Query.disk_size_format(provisioned))
            info.append(provisioned_pct)
            info.append(Query.disk_size_format(free))
            info.append(free_pct)

            datastore_info.append(info)

        # sort by datastore name
        datastore_info.sort(key=lambda x: x[0])

        return datastore_info

    @classmethod
    def list_vm_info(cls, container, datacenter):
        """
        Returns a list of names for VMs located inside a datacenter.

        """

        obj = Query.get_obj(container, datacenter)

        vms = {}

        # recurse through datacenter object attributes looking for vms.
        if hasattr(obj, 'vmFolder'):
            for virtmachine in obj.vmFolder.childEntity:
                # pylint: disable=protected-access
                if hasattr(virtmachine, 'childEntity'):
                    for virt in virtmachine.childEntity:
                        vms.update({virt.name:virt._moId})
                else:
                    vms.update({virtmachine.name:virt._moId})

        return vms

    @classmethod
    def get_vmid_by_name(cls, container, datacenter, name):
        """
        Returns the moId of matched name

        """

        obj = Query.get_obj(container, datacenter)

        # recurse through datacenter object attributes looking for vm that
        # matches hostname.
        if hasattr(obj, 'vmFolder'):
            for virtmachine in obj.vmFolder.childEntity:
                # pylint: disable=protected-access
                if hasattr(virtmachine, 'childEntity'):
                    for virt in virtmachine.childEntity:
                        if virt.name == name:
                            return virt._moId
                else:
                    if virt.name == name:
                        return virt._moId

    @classmethod
    def get_key(cls, obj, query):
        """
        Method will attempt to return the key associated with device so it can
        be used to edit existing devices. It will loop through all possible
        devices and return the key that matches query with the label attribute.

        Example:
            To get the key for the cdrom device, assuming obj is a
            VirtualMachine object

            get_key(virtual_machine, 'CD/DVD')

        Args:
            obj (obj): VirtualMachine object
            query (str): A string representation of the object attribute.

        Returns:
            keys (tuple): A tuple container the key and controllerKey associated
                with the device
        """

        if hasattr(obj, 'config'):
            for item in obj.config.hardware.device:
                if query in item.deviceInfo.label:
                    key = item.key
                    controller_key = item.controllerKey

        return (key, controller_key)

    @classmethod
    def list_guestids(cls):
        """
        Method will return an array of all supported guestids
        Returns:
            guestids (list): An ordered list of supported guestids
        """
        guestids = []
        for key in vim.vm.GuestOsDescriptor.GuestOsIdentifier.__dict__:
            guestids.append(key)

        guestids.sort()

        return guestids

<|endoftext|>


<commit_before>import os
from ConfigParser import ConfigParser
from datetime import datetime
from dateutil.parser import parser
from dateutil.tz import tzlocal

import requests
import rumps

date_parser = parser()

NIGHTSCOUT_URL = '/api/v1/entries.json'
UPDATE_FREQUENCY = 20
MAX_SECONDS_TO_SHOW_DELTA = 600
HISTORY_LENGTH = 3

class NightscoutConfig(object):
    FILENAME = 'config'
    SECTION = 'NightscoutMenubar'
    HOST = 'nightscout_host'

    def __init__(self, app_name):
        self.config_path = os.path.join(rumps.application_support(app_name), self.FILENAME)
        self.config = ConfigParser()
        self.config.read([self.config_path])
        if not self.config.has_section(self.SECTION):
            self.config.add_section(self.SECTION)
        if not self.config.has_option(self.SECTION, self.HOST):
            self.set_host('')

    def get_host(self):
        return self.config.get(self.SECTION, self.HOST)

    def set_host(self, host):
        self.config.set(self.SECTION, self.HOST, host)
        with open(self.config_path, 'w') as f:
            self.config.write(f)

def get_bg_history_json():
    return filter(
        lambda e: e['type'] == 'sgv',
        requests.get(config.get_host() + NIGHTSCOUT_URL).json()
    )

def get_menubar_text(bgs):
    last, second_to_last = bgs[0:2]
    if (date_parser.parse(last['dateString']) - date_parser.parse(second_to_last['dateString'])).seconds <= MAX_SECONDS_TO_SHOW_DELTA:
        direction = ('+' if last['sgv'] >= second_to_last['sgv'] else '') + str(last['sgv'] - second_to_last['sgv'])
    else:
        direction = '?'
    return "{sgv} ({direction}) [{time_ago}]".format(
        sgv=last['sgv'],
        direction=direction,
        time_ago=pretty_time_ago(last['dateString']),
    )

def get_history_menu_items(bgs):
    return [
        '{sgv} [{time_ago}]'.format(
            sgv=e['sgv'],
            time_ago=pretty_time_ago(e['dateString'],
        ))
        for e in bgs[1:HISTORY_LENGTH + 1]
    ]

def pretty_time_ago(date_str):
    seconds = (datetime.now().replace(tzinfo=tzlocal()) - date_parser.parse(date_str)).seconds
    if seconds >= 3600:
        return "%s hr" % (seconds / 3600)
    elif seconds >= 60:
        return "%s min" % (seconds / 60)
    else:
        return "%s sec" % seconds

@rumps.timer(UPDATE_FREQUENCY)
def update_data(sender):
    try:
        bgs = get_bg_history_json()
        app.title = get_menubar_text(bgs)
        app.menu.clear()
        app.menu.update(get_history_menu_items(bgs) + post_history_menu_options() + [app.quit_button])
    except requests.exceptions.RequestException:
        app.title = "<Can't reach nightscout!>"
        app.menu.clear()
        app.menu.update(post_history_menu_options() + [app.quit_button])

def post_history_menu_options():
    return [
        None,
        rumps.MenuItem('Configuration', callback=configuration_window),
        None,
    ]

def configuration_window(sender):
    window = rumps.Window(
        title='Nightscout Menubar Configuration',
        message='Enter your nightscout URL below.\n\nIt probably looks like:\nhttps://SOMETHING.azurewebsites.net',
    )
    window.default_text = config.get_host()
    window.add_buttons('Cancel')

    response = window.run()
    if response.clicked == 1:
        config.set_host(response.text.strip())

if __name__ == "__main__":
    app = rumps.App('Nightscout Menubar', title='loading...')
    app.menu = ['loading...'] + post_history_menu_options()
    config = NightscoutConfig(app.name)
    app.run()
<commit_msg>Fetch new data immediately if NightScout URL is changed<commit_after>import os
from ConfigParser import ConfigParser
from datetime import datetime
from dateutil.parser import parser
from dateutil.tz import tzlocal

import requests
import rumps

date_parser = parser()

NIGHTSCOUT_URL = '/api/v1/entries.json'
UPDATE_FREQUENCY = 20
MAX_SECONDS_TO_SHOW_DELTA = 600
HISTORY_LENGTH = 3

class NightscoutConfig(object):
    FILENAME = 'config'
    SECTION = 'NightscoutMenubar'
    HOST = 'nightscout_host'

    def __init__(self, app_name):
        self.config_path = os.path.join(rumps.application_support(app_name), self.FILENAME)
        self.config = ConfigParser()
        self.config.read([self.config_path])
        if not self.config.has_section(self.SECTION):
            self.config.add_section(self.SECTION)
        if not self.config.has_option(self.SECTION, self.HOST):
            self.set_host('')

    def get_host(self):
        return self.config.get(self.SECTION, self.HOST)

    def set_host(self, host):
        self.config.set(self.SECTION, self.HOST, host)
        with open(self.config_path, 'w') as f:
            self.config.write(f)

def get_bg_history_json():
    return filter(
        lambda e: e['type'] == 'sgv',
        requests.get(config.get_host() + NIGHTSCOUT_URL).json()
    )

def get_menubar_text(bgs):
    last, second_to_last = bgs[0:2]
    if (date_parser.parse(last['dateString']) - date_parser.parse(second_to_last['dateString'])).seconds <= MAX_SECONDS_TO_SHOW_DELTA:
        direction = ('+' if last['sgv'] >= second_to_last['sgv'] else '') + str(last['sgv'] - second_to_last['sgv'])
    else:
        direction = '?'
    return "{sgv} ({direction}) [{time_ago}]".format(
        sgv=last['sgv'],
        direction=direction,
        time_ago=pretty_time_ago(last['dateString']),
    )

def get_history_menu_items(bgs):
    return [
        '{sgv} [{time_ago}]'.format(
            sgv=e['sgv'],
            time_ago=pretty_time_ago(e['dateString'],
        ))
        for e in bgs[1:HISTORY_LENGTH + 1]
    ]

def pretty_time_ago(date_str):
    seconds = (datetime.now().replace(tzinfo=tzlocal()) - date_parser.parse(date_str)).seconds
    if seconds >= 3600:
        return "%s hr" % (seconds / 3600)
    elif seconds >= 60:
        return "%s min" % (seconds / 60)
    else:
        return "%s sec" % seconds

@rumps.timer(UPDATE_FREQUENCY)
def update_data(sender):
    try:
        bgs = get_bg_history_json()
        app.title = get_menubar_text(bgs)
        app.menu.clear()
        app.menu.update(get_history_menu_items(bgs) + post_history_menu_options() + [app.quit_button])
    except requests.exceptions.RequestException:
        app.title = "<Can't reach nightscout!>"
        app.menu.clear()
        app.menu.update(post_history_menu_options() + [app.quit_button])

def post_history_menu_options():
    return [
        None,
        rumps.MenuItem('Configuration', callback=configuration_window),
        None,
    ]

def configuration_window(sender):
    window = rumps.Window(
        title='Nightscout Menubar Configuration',
        message='Enter your nightscout URL below.\n\nIt probably looks like:\nhttps://SOMETHING.azurewebsites.net',
    )
    window.default_text = config.get_host()
    window.add_buttons('Cancel')

    response = window.run()
    if response.clicked == 1:
        config.set_host(response.text.strip())
        update_data(None)

if __name__ == "__main__":
    app = rumps.App('Nightscout Menubar', title='loading...')
    app.menu = ['loading...'] + post_history_menu_options()
    config = NightscoutConfig(app.name)
    app.run()
<|endoftext|>


<commit_before>from __future__ import unicode_literals

import datetime
import json
import os
import requests
import scorched.connection
import unittest

from unittest import mock

HTTPBIN = os.environ.get('HTTPBIN_URL', 'https://httpbin.org/')
# Issue #1483: Make sure the URL always has a trailing slash
HTTPBIN = HTTPBIN.rstrip('/') + '/'

def httpbin(*suffix):
    """Returns url for HTTPBIN resource."""
    return requests.compat.urljoin(HTTPBIN, '/'.join(suffix))

class TestConnection(unittest.TestCase):

    def _make_connection(self, url="http://localhost:8983/solr",
                         http_connection=None, mode="r", retry_timeout=-1,
                         max_length_get_url=2048):

        sc = scorched.connection.SolrConnection(
            url=url,
            http_connection=http_connection,
            mode=mode,
            retry_timeout=retry_timeout,
            max_length_get_url=max_length_get_url)

        return sc

    def test_readable(self):
        sc = self._make_connection()
        self.assertRaises(TypeError, sc.update, {})

    def test_writeable(self):
        sc = self._make_connection(mode="w")
        self.assertRaises(TypeError, sc.mlt, [])
        self.assertRaises(TypeError, sc.select, {})

    def test_mlt(self):
        sc = self._make_connection(mode="")
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [])
            # test content
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [],
                              content="fooo")
        # test post building
        sc = self._make_connection(max_length_get_url=0)
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [],
                              content="fooo")

    def test_select(self):
        sc = self._make_connection(max_length_get_url=0)
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.select, [])

    def test_no_body_response_error(self):
        sc = self._make_connection(mode="")
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.update, {"foo": 2})
            self.assertRaises(scorched.exc.SolrError, sc.update, {})

    def test_request(self):
        sc = self._make_connection(url="http://localhost:1234/none", mode="")
        self.assertRaises(Exception, sc.request, (), {})

    def test_url_for_update(self):
        dsn = "http://localhost:1234/none"
        sc = self._make_connection(url=dsn)
        ret = sc.url_for_update()

        def dsn_url(path):
            return "%s%s" % (dsn, path)

        self.assertEqual(ret, dsn_url("/update/json"))
        # commitwithin
        ret = sc.url_for_update(commitWithin=2)
        self.assertEqual(ret, dsn_url("/update/json?commitWithin=2"))
        self.assertRaises(ValueError, sc.url_for_update, commitWithin="a")
        self.assertRaises(ValueError, sc.url_for_update, commitWithin=-1)
        # softCommit
        ret = sc.url_for_update(softCommit=True)
        self.assertEqual(ret, dsn_url("/update/json?softCommit=true"))
        ret = sc.url_for_update(softCommit=False)
        self.assertEqual(ret, dsn_url("/update/json?softCommit=false"))
        # optimize
        ret = sc.url_for_update(optimize=True)
        self.assertEqual(ret, dsn_url("/update/json?optimize=true"))
        ret = sc.url_for_update(optimize=False)
        self.assertEqual(ret, dsn_url("/update/json?optimize=false"))
        # waitSearcher
        ret = sc.url_for_update(waitSearcher=True)
        self.assertEqual(ret, dsn_url("/update/json?waitSearcher=true"))
        ret = sc.url_for_update(waitSearcher=False)
        self.assertEqual(ret, dsn_url("/update/json?waitSearcher=false"))
        # expungeDeletes
        ret = sc.url_for_update(commit=True, expungeDeletes=True)
        self.assertEqual(
            ret, dsn_url("/update/json?commit=true&expungeDeletes=true"))
        ret = sc.url_for_update(commit=True, expungeDeletes=False)
        self.assertEqual(
            ret, dsn_url("/update/json?commit=true&expungeDeletes=false"))
        self.assertRaises(ValueError, sc.url_for_update, expungeDeletes=True)
        # maxSegments
        ret = sc.url_for_update(optimize=True, maxSegments=2)
        self.assertEqual(
            ret, dsn_url("/update/json?maxSegments=2&optimize=true"))
        self.assertRaises(
            ValueError, sc.url_for_update, optimize=True, maxSegments="a")
        self.assertRaises(
            ValueError, sc.url_for_update, optimize=True, maxSegments=-1)
        self.assertRaises(ValueError, sc.url_for_update, maxSegments=2)

    def test_select_timeout(self):
        dsn = "http://localhost:1234/none"
        # max_length_get_url=99999: httbin doesn't support POST
        sc = scorched.connection.SolrConnection(
            url=dsn, http_connection=None, mode="", retry_timeout=-1,
            max_length_get_url=99999, search_timeout=3.0)
        sc.select_url = httpbin('delay/2')
        # delay 2.0s < 3.0s timeout, ok
        resp = sc.select([])
        self.assertTrue(json.loads(resp)['url'].startswith(sc.select_url))
        # delay 2.0s > 1.0s timeout, raise ReadTimeout
        sc.search_timeout = 1.0
        self.assertRaises(requests.exceptions.ReadTimeout, sc.select, [])
        sc.search_timeout = (5.0, 1.0)  # (connect, read)
        self.assertRaises(requests.exceptions.ReadTimeout, sc.select, [])
        # delay 2.0s < 3.0s timeout, ok
        sc.search_timeout = (1.0, 3.0)  # (connect, read)
        resp = sc.select([])
        self.assertTrue(json.loads(resp)['url'].startswith(sc.select_url))
        # Connecting to an invalid port should raise a ConnectionError
        sc.select_url = "https://httpbin.org:1/none/select"
        sc.search_timeout = 1.0
        self.assertRaises(requests.exceptions.ConnectTimeout, sc.select, [])
        sc.search_timeout = (1.0, 5.0)
        self.assertRaises(requests.exceptions.ConnectTimeout, sc.select, [])

    def test_basic_auth(self):
        hc = requests.Session()
        hc.auth = ('joe', 'Secret')

        dsn = "http://localhost:1234/none"
        sc = self._make_connection(url=dsn, http_connection=hc)
        sc.select_url = httpbin('/basic-auth/{0}/{1}'.format(*hc.auth))

        resp = sc.select([])
        self.assertTrue(json.loads(resp)['authenticated'])

class TestSolrInterface(unittest.TestCase):

    def _make_one(self):
        import scorched.connection
        import scorched.tests.schema
        with mock.patch('scorched.connection.SolrInterface.init_schema') as \
                init_schema:
            init_schema.return_value = scorched.tests.schema.schema
            si = scorched.connection.SolrInterface(
                'http://localhost:2222/mysolr')
        return si

    def test__should_skip_value(self):
        sc = self._make_one()
        self.assertTrue(sc._should_skip_value(None))
        self.assertTrue(sc._should_skip_value({'set': None}))
        self.assertFalse(sc._should_skip_value(1))
        self.assertFalse(sc._should_skip_value({'set': 1}))

    def test__prepare_docs_does_not_alter_given_docs(self):
        sc = self._make_one()
        today = datetime.datetime.utcnow()
        docs = [{'last_modified': today}]
        sc._prepare_docs(docs)
        self.assertEqual(docs, [{'last_modified': today}])

    def test__prepare_docs_converts_datetime(self):
        sc = self._make_one()
        dt = datetime.datetime(2014, 2, 18, 12, 12, 10)
        docs = [{'last_modified': dt}]
        result = sc._prepare_docs(docs)
        self.assertEqual(result[0]['last_modified'], "2014-02-18T12:12:10Z")

    def test__prepare_docs_converts_datetime_atomic_update(self):
        sc = self._make_one()
        dt = datetime.datetime(2014, 2, 18, 12, 12, 10)
        docs = [{'last_modified': {'set': dt}}]
        result = sc._prepare_docs(docs)
        self.assertEqual(
            result[0]['last_modified']['set'],
            '2014-02-18T12:12:10Z',
        )
<commit_msg>Remove __future__<commit_after>import datetime
import json
import os
import requests
import scorched.connection
import unittest

from unittest import mock

HTTPBIN = os.environ.get('HTTPBIN_URL', 'https://httpbin.org/')
# Issue #1483: Make sure the URL always has a trailing slash
HTTPBIN = HTTPBIN.rstrip('/') + '/'

def httpbin(*suffix):
    """Returns url for HTTPBIN resource."""
    return requests.compat.urljoin(HTTPBIN, '/'.join(suffix))

class TestConnection(unittest.TestCase):

    def _make_connection(self, url="http://localhost:8983/solr",
                         http_connection=None, mode="r", retry_timeout=-1,
                         max_length_get_url=2048):

        sc = scorched.connection.SolrConnection(
            url=url,
            http_connection=http_connection,
            mode=mode,
            retry_timeout=retry_timeout,
            max_length_get_url=max_length_get_url)

        return sc

    def test_readable(self):
        sc = self._make_connection()
        self.assertRaises(TypeError, sc.update, {})

    def test_writeable(self):
        sc = self._make_connection(mode="w")
        self.assertRaises(TypeError, sc.mlt, [])
        self.assertRaises(TypeError, sc.select, {})

    def test_mlt(self):
        sc = self._make_connection(mode="")
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [])
            # test content
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [],
                              content="fooo")
        # test post building
        sc = self._make_connection(max_length_get_url=0)
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.mlt, [],
                              content="fooo")

    def test_select(self):
        sc = self._make_connection(max_length_get_url=0)
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.select, [])

    def test_no_body_response_error(self):
        sc = self._make_connection(mode="")
        with mock.patch.object(requests.Session, 'request',
                               return_value=mock.Mock(status_code=500)):
            self.assertRaises(scorched.exc.SolrError, sc.update, {"foo": 2})
            self.assertRaises(scorched.exc.SolrError, sc.update, {})

    def test_request(self):
        sc = self._make_connection(url="http://localhost:1234/none", mode="")
        self.assertRaises(Exception, sc.request, (), {})

    def test_url_for_update(self):
        dsn = "http://localhost:1234/none"
        sc = self._make_connection(url=dsn)
        ret = sc.url_for_update()

        def dsn_url(path):
            return "%s%s" % (dsn, path)

        self.assertEqual(ret, dsn_url("/update/json"))
        # commitwithin
        ret = sc.url_for_update(commitWithin=2)
        self.assertEqual(ret, dsn_url("/update/json?commitWithin=2"))
        self.assertRaises(ValueError, sc.url_for_update, commitWithin="a")
        self.assertRaises(ValueError, sc.url_for_update, commitWithin=-1)
        # softCommit
        ret = sc.url_for_update(softCommit=True)
        self.assertEqual(ret, dsn_url("/update/json?softCommit=true"))
        ret = sc.url_for_update(softCommit=False)
        self.assertEqual(ret, dsn_url("/update/json?softCommit=false"))
        # optimize
        ret = sc.url_for_update(optimize=True)
        self.assertEqual(ret, dsn_url("/update/json?optimize=true"))
        ret = sc.url_for_update(optimize=False)
        self.assertEqual(ret, dsn_url("/update/json?optimize=false"))
        # waitSearcher
        ret = sc.url_for_update(waitSearcher=True)
        self.assertEqual(ret, dsn_url("/update/json?waitSearcher=true"))
        ret = sc.url_for_update(waitSearcher=False)
        self.assertEqual(ret, dsn_url("/update/json?waitSearcher=false"))
        # expungeDeletes
        ret = sc.url_for_update(commit=True, expungeDeletes=True)
        self.assertEqual(
            ret, dsn_url("/update/json?commit=true&expungeDeletes=true"))
        ret = sc.url_for_update(commit=True, expungeDeletes=False)
        self.assertEqual(
            ret, dsn_url("/update/json?commit=true&expungeDeletes=false"))
        self.assertRaises(ValueError, sc.url_for_update, expungeDeletes=True)
        # maxSegments
        ret = sc.url_for_update(optimize=True, maxSegments=2)
        self.assertEqual(
            ret, dsn_url("/update/json?maxSegments=2&optimize=true"))
        self.assertRaises(
            ValueError, sc.url_for_update, optimize=True, maxSegments="a")
        self.assertRaises(
            ValueError, sc.url_for_update, optimize=True, maxSegments=-1)
        self.assertRaises(ValueError, sc.url_for_update, maxSegments=2)

    def test_select_timeout(self):
        dsn = "http://localhost:1234/none"
        # max_length_get_url=99999: httbin doesn't support POST
        sc = scorched.connection.SolrConnection(
            url=dsn, http_connection=None, mode="", retry_timeout=-1,
            max_length_get_url=99999, search_timeout=3.0)
        sc.select_url = httpbin('delay/2')
        # delay 2.0s < 3.0s timeout, ok
        resp = sc.select([])
        self.assertTrue(json.loads(resp)['url'].startswith(sc.select_url))
        # delay 2.0s > 1.0s timeout, raise ReadTimeout
        sc.search_timeout = 1.0
        self.assertRaises(requests.exceptions.ReadTimeout, sc.select, [])
        sc.search_timeout = (5.0, 1.0)  # (connect, read)
        self.assertRaises(requests.exceptions.ReadTimeout, sc.select, [])
        # delay 2.0s < 3.0s timeout, ok
        sc.search_timeout = (1.0, 3.0)  # (connect, read)
        resp = sc.select([])
        self.assertTrue(json.loads(resp)['url'].startswith(sc.select_url))
        # Connecting to an invalid port should raise a ConnectionError
        sc.select_url = "https://httpbin.org:1/none/select"
        sc.search_timeout = 1.0
        self.assertRaises(requests.exceptions.ConnectTimeout, sc.select, [])
        sc.search_timeout = (1.0, 5.0)
        self.assertRaises(requests.exceptions.ConnectTimeout, sc.select, [])

    def test_basic_auth(self):
        hc = requests.Session()
        hc.auth = ('joe', 'Secret')

        dsn = "http://localhost:1234/none"
        sc = self._make_connection(url=dsn, http_connection=hc)
        sc.select_url = httpbin('/basic-auth/{0}/{1}'.format(*hc.auth))

        resp = sc.select([])
        self.assertTrue(json.loads(resp)['authenticated'])

class TestSolrInterface(unittest.TestCase):

    def _make_one(self):
        import scorched.connection
        import scorched.tests.schema
        with mock.patch('scorched.connection.SolrInterface.init_schema') as \
                init_schema:
            init_schema.return_value = scorched.tests.schema.schema
            si = scorched.connection.SolrInterface(
                'http://localhost:2222/mysolr')
        return si

    def test__should_skip_value(self):
        sc = self._make_one()
        self.assertTrue(sc._should_skip_value(None))
        self.assertTrue(sc._should_skip_value({'set': None}))
        self.assertFalse(sc._should_skip_value(1))
        self.assertFalse(sc._should_skip_value({'set': 1}))

    def test__prepare_docs_does_not_alter_given_docs(self):
        sc = self._make_one()
        today = datetime.datetime.utcnow()
        docs = [{'last_modified': today}]
        sc._prepare_docs(docs)
        self.assertEqual(docs, [{'last_modified': today}])

    def test__prepare_docs_converts_datetime(self):
        sc = self._make_one()
        dt = datetime.datetime(2014, 2, 18, 12, 12, 10)
        docs = [{'last_modified': dt}]
        result = sc._prepare_docs(docs)
        self.assertEqual(result[0]['last_modified'], "2014-02-18T12:12:10Z")

    def test__prepare_docs_converts_datetime_atomic_update(self):
        sc = self._make_one()
        dt = datetime.datetime(2014, 2, 18, 12, 12, 10)
        docs = [{'last_modified': {'set': dt}}]
        result = sc._prepare_docs(docs)
        self.assertEqual(
            result[0]['last_modified']['set'],
            '2014-02-18T12:12:10Z',
        )
<|endoftext|>


<commit_before># Create your views here.

import time
import re
import json

from django.utils.datastructures import MultiValueDict
from django import forms
from django.conf import settings
from django.http import HttpResponse

import jingo
from tower import ugettext as _
from flatqs import flatten

from sumo.utils import paginate
from sumo.models import ForumThread, WikiPage, Forum, Category
from .clients import ForumClient, WikiClient, SearchError
from .utils import crc32
import search as constants

def jsonp_is_valid(func):
    func_regex = re.compile(r'^[a-zA-Z_\$][a-zA-Z0-9_\$]*'
        + r'(\[[a-zA-Z0-9_\$]*\])*(\.[a-zA-Z0-9_\$]+(\[[a-zA-Z0-9_\$]*\])*)*$')
    return func_regex.match(func)

def search(request):
    """Performs search or displays the search form"""

    # set up query variables
    q = request.GET.get('q', '')

    locale = request.GET.get('locale', request.locale)
    language = request.GET.get('language', locale)

    search_locale = (crc32(language),)

    where = int(request.GET.get('w', constants.WHERE_ALL))

    page = int(request.GET.get('page', 1))
    page = max(page, 1)
    offset = (page - 1) * settings.SEARCH_RESULTS_PER_PAGE

    # set up form with default values
    search_params = request.GET.copy()
    if not search_params.getlist('category'):
        search_params.setlist('category', settings.SEARCH_DEFAULT_CATEGORIES)
    if not search_params.getlist('forum'):
        search_params.setlist('forum', settings.SEARCH_DEFAULT_FORUMS)
    if not search_params.getlist('language'):
        search_params.setlist('language', [language])
    search_form = SearchForm(search_params)

    # no query or advanced search?
    # => return empty form
    if (not q or (request.GET.get('a', '0') == '1')):
        return jingo.render(request, 'form.html',
            {'advanced': request.GET.get('a'), 'request': request,
            'w': where, 'search_form': search_form})

    # get language name for display in template
    if settings.LANGUAGES.get(language):
        lang_name = settings.LANGUAGES[language]
    else:
        lang_name = ''

    documents = []

    try:
        if (where & constants.WHERE_WIKI):
            wc = WikiClient()  # Wiki SearchClient instance
            filters_w = []  # Filters for the wiki search

            # Category filter
            categories = map(int, search_params.getlist('category'))
            filters_w.append({
                'filter': 'category',
                'value': [x for x in categories if x > 0],
            })

            exclude_categories = [abs(x) for x in categories if x < 0]
            if exclude_categories:
                filters_w.append({
                    'filter': 'category',
                    'value': exclude_categories,
                    'exclude': True,
                })

            # Locale filter
            filters_w.append({
                'filter': 'locale',
                'value': search_locale,
            })

            # Tag filter
            tag = [t.strip() for t in request.GET.get('tag', '').split()]
            if tag:
                tag = map(crc32, tag)
                for t in tag:
                    filters_w.append({
                        'filter': 'tag',
                        'value': (t,),
                    })

            # Execute the query and append to documents
            documents += wc.query(q, filters_w)

        if (where & constants.WHERE_FORUM):
            fc = ForumClient()  # Forum SearchClient instance
            filters_f = []  # Filters for the forum search

            # Forum filter
            filters_f.append({
                'filter': 'forumId',
                'value': map(int, search_params.getlist('forum')),
            })

            # Status filter
            status = int(request.GET.get('status', 0))
            # No replies case is not stored in status
            if status == constants.STATUS_ALIAS_NR:
                filters_f.append({
                    'filter': 'replies',
                    'value': (0,),
                })

                # Avoid filtering by status
                status = None

            if status:
                filters_f.append({
                    'filter': 'status',
                    'value': constants.STATUS_ALIAS_REVERSE[status],
                })

            # Author filter
            if request.GET.get('author'):
                filters_f.append({
                    'filter': 'author_ord',
                    'value': (crc32(request.GET.get('author')),
                               crc32(request.GET.get('author') + ' (anon)'),),
                })

            unix_now = int(time.time())
            # Created filter
            created = int(request.GET.get('created', 0))
            created_date = request.GET.get('created_date', '')

            if not created_date:
                # No date => no filtering
                created = None
            else:
                try:
                    created_date = int(time.mktime(
                        time.strptime(created_date, '%m/%d/%Y'),
                        ))
                except ValueError:
                    created = None

            if created == constants.CREATED_BEFORE:
                filters_f.append({
                    'range': True,
                    'filter': 'created',
                    'min': 0,
                    'max': created_date,
                })
            elif created == constants.CREATED_AFTER:
                filters_f.append({
                    'range': True,
                    'filter': 'created',
                    'min': created_date,
                    'max': int(unix_now),
                })

            # Last modified filter
            lastmodif = int(request.GET.get('lastmodif', 0))

            if lastmodif:
                filters_f.append({
                    'range': True,
                    'filter': 'last_updated',
                    'min': unix_now - constants.LUP_MULTIPLIER * lastmodif,
                    'max': unix_now,
                })

            # Sort results by
            sortby = int(request.GET.get('sortby', 0))
            if sortby == constants.SORTBY_CREATED:
                fc.set_sort_mode(constants.SORTBY_MODE, 'created')
            elif sortby == constants.SORTBY_LASTMODIF:
                fc.set_sort_mode(constants.SORTBY_MODE, 'last_updated')
            elif sortby == constants.SORTBY_REPLYCOUNT:
                fc.set_sort_mode(constants.SORTBY_MODE, 'replies')

            documents += fc.query(q, filters_f)
    except SearchError:
        return jingo.render(request, 'down.html', {}, status=503)

    pages = paginate(request, documents, settings.SEARCH_RESULTS_PER_PAGE)

    results = []
    for i in range(offset, offset + settings.SEARCH_RESULTS_PER_PAGE):
        try:
            if documents[i]['attrs'].get('category', False):
                wiki_page = WikiPage.objects.get(pk=documents[i]['id'])
                result = {'search_summary': wc.excerpt(wiki_page.data, q),
                    'url': wiki_page.get_url(),
                    'title': wiki_page.name,
                    }
                results.append(result)
            else:
                forum_thread = ForumThread.objects.get(pk=documents[i]['id'])
                result = {'search_summary': fc.excerpt(forum_thread.data, q),
                    'url': forum_thread.get_url(),
                    'title': forum_thread.name,
                    }
                results.append(result)
        except IndexError:
            break
        except (WikiPage.DoesNotExist, ForumThread.DoesNotExist):
            continue

    refine_query = MultiValueDict()
    for name, field in search_form.fields.items():
        refine_query[name] = request.GET.getlist(name)

    refine_query = '?a=1&w=' + str(where) + '&' \
        + flatten(refine_query, encode=False)

    if request.GET.get('format') == 'json':
        callback = request.GET.get('callback', '').strip()
        # Check callback is valid
        if callback and not jsonp_is_valid(callback):
                return HttpResponse('', mimetype='application/x-javascript',
                    status=400)

        data = {}
        data['results'] = results
        data['total'] = len(documents)
        data['query'] = q
        if not results:
            data['message'] = _('No pages matched the search criteria')
        json_data = json.dumps(data)
        if callback:
            json_data = callback + '(' + json_data + ');'
            response = HttpResponse(json_data,
                mimetype='application/x-javascript')
        else:
            response = HttpResponse(json_data,
                mimetype='application/json')

        return response

    return jingo.render(request, 'results.html',
        {'num_results': len(documents), 'results': results, 'q': q,
          'pages': pages, 'w': where, 'refine_query': refine_query,
          'search_form': search_form, 'lang_name': lang_name, })

class SearchForm(forms.Form):
    q = forms.CharField()

    # KB form data
    tag = forms.CharField(label=_('Tags'))

    language = forms.ChoiceField(label=_('Language'),
        choices=[(i, settings.LANGUAGES[i]) for i in settings.LANGUAGES])

    categories = []
    for cat in Category.objects.all():
        categories.append((cat.categId, cat.name))
    category = forms.MultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
        label=_('Category'), choices=categories)

    # Forum form data
    status = forms.ChoiceField(label=_('Post status'),
        choices=constants.STATUS_LIST)
    author = forms.CharField()

    created = forms.ChoiceField(label=_('Created'),
        choices=constants.CREATED_LIST)
    created_date = forms.CharField()

    lastmodif = forms.ChoiceField(label=_('Last updated'),
        choices=constants.LUP_LIST)
    sortby = forms.ChoiceField(label=_('Sort results by'),
        choices=constants.SORTBY_LIST)

    forums = []
    for f in Forum.objects.all():
        forums.append((f.forumId, f.name))
    forum = forms.MultipleChoiceField(label=_('Search in forum'),
        choices=forums)
<commit_msg>Just needed to call .lower() to get the correct language name.<commit_after># Create your views here.

import time
import re
import json

from django.utils.datastructures import MultiValueDict
from django import forms
from django.conf import settings
from django.http import HttpResponse

import jingo
from tower import ugettext as _
from flatqs import flatten

from sumo.utils import paginate
from sumo.models import ForumThread, WikiPage, Forum, Category
from .clients import ForumClient, WikiClient, SearchError
from .utils import crc32
import search as constants

def jsonp_is_valid(func):
    func_regex = re.compile(r'^[a-zA-Z_\$][a-zA-Z0-9_\$]*'
        + r'(\[[a-zA-Z0-9_\$]*\])*(\.[a-zA-Z0-9_\$]+(\[[a-zA-Z0-9_\$]*\])*)*$')
    return func_regex.match(func)

def search(request):
    """Performs search or displays the search form"""

    # set up query variables
    q = request.GET.get('q', '')

    locale = request.GET.get('locale', request.locale)
    language = request.GET.get('language', locale)

    search_locale = (crc32(language),)

    where = int(request.GET.get('w', constants.WHERE_ALL))

    page = int(request.GET.get('page', 1))
    page = max(page, 1)
    offset = (page - 1) * settings.SEARCH_RESULTS_PER_PAGE

    # set up form with default values
    search_params = request.GET.copy()
    if not search_params.getlist('category'):
        search_params.setlist('category', settings.SEARCH_DEFAULT_CATEGORIES)
    if not search_params.getlist('forum'):
        search_params.setlist('forum', settings.SEARCH_DEFAULT_FORUMS)
    if not search_params.getlist('language'):
        search_params.setlist('language', [language])
    search_form = SearchForm(search_params)

    # no query or advanced search?
    # => return empty form
    if (not q or (request.GET.get('a', '0') == '1')):
        return jingo.render(request, 'form.html',
            {'advanced': request.GET.get('a'), 'request': request,
            'w': where, 'search_form': search_form})

    # get language name for display in template
    lang = language.lower()
    if settings.LANGUAGES.get(lang):
        lang_name = settings.LANGUAGES[lang]
    else:
        lang_name = ''

    documents = []

    try:
        if (where & constants.WHERE_WIKI):
            wc = WikiClient()  # Wiki SearchClient instance
            filters_w = []  # Filters for the wiki search

            # Category filter
            categories = map(int, search_params.getlist('category'))
            filters_w.append({
                'filter': 'category',
                'value': [x for x in categories if x > 0],
            })

            exclude_categories = [abs(x) for x in categories if x < 0]
            if exclude_categories:
                filters_w.append({
                    'filter': 'category',
                    'value': exclude_categories,
                    'exclude': True,
                })

            # Locale filter
            filters_w.append({
                'filter': 'locale',
                'value': search_locale,
            })

            # Tag filter
            tag = [t.strip() for t in request.GET.get('tag', '').split()]
            if tag:
                tag = map(crc32, tag)
                for t in tag:
                    filters_w.append({
                        'filter': 'tag',
                        'value': (t,),
                    })

            # Execute the query and append to documents
            documents += wc.query(q, filters_w)

        if (where & constants.WHERE_FORUM):
            fc = ForumClient()  # Forum SearchClient instance
            filters_f = []  # Filters for the forum search

            # Forum filter
            filters_f.append({
                'filter': 'forumId',
                'value': map(int, search_params.getlist('forum')),
            })

            # Status filter
            status = int(request.GET.get('status', 0))
            # No replies case is not stored in status
            if status == constants.STATUS_ALIAS_NR:
                filters_f.append({
                    'filter': 'replies',
                    'value': (0,),
                })

                # Avoid filtering by status
                status = None

            if status:
                filters_f.append({
                    'filter': 'status',
                    'value': constants.STATUS_ALIAS_REVERSE[status],
                })

            # Author filter
            if request.GET.get('author'):
                filters_f.append({
                    'filter': 'author_ord',
                    'value': (crc32(request.GET.get('author')),
                               crc32(request.GET.get('author') + ' (anon)'),),
                })

            unix_now = int(time.time())
            # Created filter
            created = int(request.GET.get('created', 0))
            created_date = request.GET.get('created_date', '')

            if not created_date:
                # No date => no filtering
                created = None
            else:
                try:
                    created_date = int(time.mktime(
                        time.strptime(created_date, '%m/%d/%Y'),
                        ))
                except ValueError:
                    created = None

            if created == constants.CREATED_BEFORE:
                filters_f.append({
                    'range': True,
                    'filter': 'created',
                    'min': 0,
                    'max': created_date,
                })
            elif created == constants.CREATED_AFTER:
                filters_f.append({
                    'range': True,
                    'filter': 'created',
                    'min': created_date,
                    'max': int(unix_now),
                })

            # Last modified filter
            lastmodif = int(request.GET.get('lastmodif', 0))

            if lastmodif:
                filters_f.append({
                    'range': True,
                    'filter': 'last_updated',
                    'min': unix_now - constants.LUP_MULTIPLIER * lastmodif,
                    'max': unix_now,
                })

            # Sort results by
            sortby = int(request.GET.get('sortby', 0))
            if sortby == constants.SORTBY_CREATED:
                fc.set_sort_mode(constants.SORTBY_MODE, 'created')
            elif sortby == constants.SORTBY_LASTMODIF:
                fc.set_sort_mode(constants.SORTBY_MODE, 'last_updated')
            elif sortby == constants.SORTBY_REPLYCOUNT:
                fc.set_sort_mode(constants.SORTBY_MODE, 'replies')

            documents += fc.query(q, filters_f)
    except SearchError:
        return jingo.render(request, 'down.html', {}, status=503)

    pages = paginate(request, documents, settings.SEARCH_RESULTS_PER_PAGE)

    results = []
    for i in range(offset, offset + settings.SEARCH_RESULTS_PER_PAGE):
        try:
            if documents[i]['attrs'].get('category', False):
                wiki_page = WikiPage.objects.get(pk=documents[i]['id'])
                result = {'search_summary': wc.excerpt(wiki_page.data, q),
                    'url': wiki_page.get_url(),
                    'title': wiki_page.name,
                    }
                results.append(result)
            else:
                forum_thread = ForumThread.objects.get(pk=documents[i]['id'])
                result = {'search_summary': fc.excerpt(forum_thread.data, q),
                    'url': forum_thread.get_url(),
                    'title': forum_thread.name,
                    }
                results.append(result)
        except IndexError:
            break
        except (WikiPage.DoesNotExist, ForumThread.DoesNotExist):
            continue

    refine_query = MultiValueDict()
    for name, field in search_form.fields.items():
        refine_query[name] = request.GET.getlist(name)

    refine_query = '?a=1&w=' + str(where) + '&' \
        + flatten(refine_query, encode=False)

    if request.GET.get('format') == 'json':
        callback = request.GET.get('callback', '').strip()
        # Check callback is valid
        if callback and not jsonp_is_valid(callback):
                return HttpResponse('', mimetype='application/x-javascript',
                    status=400)

        data = {}
        data['results'] = results
        data['total'] = len(documents)
        data['query'] = q
        if not results:
            data['message'] = _('No pages matched the search criteria')
        json_data = json.dumps(data)
        if callback:
            json_data = callback + '(' + json_data + ');'
            response = HttpResponse(json_data,
                mimetype='application/x-javascript')
        else:
            response = HttpResponse(json_data,
                mimetype='application/json')

        return response

    return jingo.render(request, 'results.html',
        {'num_results': len(documents), 'results': results, 'q': q,
          'pages': pages, 'w': where, 'refine_query': refine_query,
          'search_form': search_form, 'lang_name': lang_name, })

class SearchForm(forms.Form):
    q = forms.CharField()

    # KB form data
    tag = forms.CharField(label=_('Tags'))

    language = forms.ChoiceField(label=_('Language'),
        choices=[(i, settings.LANGUAGES[i]) for i in settings.LANGUAGES])

    categories = []
    for cat in Category.objects.all():
        categories.append((cat.categId, cat.name))
    category = forms.MultipleChoiceField(
        widget=forms.CheckboxSelectMultiple,
        label=_('Category'), choices=categories)

    # Forum form data
    status = forms.ChoiceField(label=_('Post status'),
        choices=constants.STATUS_LIST)
    author = forms.CharField()

    created = forms.ChoiceField(label=_('Created'),
        choices=constants.CREATED_LIST)
    created_date = forms.CharField()

    lastmodif = forms.ChoiceField(label=_('Last updated'),
        choices=constants.LUP_LIST)
    sortby = forms.ChoiceField(label=_('Sort results by'),
        choices=constants.SORTBY_LIST)

    forums = []
    for f in Forum.objects.all():
        forums.append((f.forumId, f.name))
    forum = forms.MultipleChoiceField(label=_('Search in forum'),
        choices=forums)
<|endoftext|>


<commit_before>from datetime import datetime
import urllib
import urlparse

from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import comments as comment_app
from django.contrib.comments.views import comments
from django.contrib.comments.views.comments import CommentPostBadRequest, comment_done
from django.contrib.comments.views.utils import confirmation_view, next_redirect
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.http import (HttpResponse, HttpResponseBadRequest,
    HttpResponseForbidden)
from django.shortcuts import get_object_or_404, redirect, render_to_response
from django.template import RequestContext, loader
from django.utils.encoding import smart_unicode
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST

from voting.models import Vote

from osl_comments import signals
from osl_comments.forms import OslEditCommentForm
from osl_comments.models import (CommentsBannedFromIpAddress,
    CommentsPerPageForContentType, OslComment)
from osl_comments.templatetags import (EDIT_QUERY_STRING_KEY,
    REPLY_QUERY_STRING_KEY)

NO_PAGINATION_QUERY_STRING_KEY = 'np'

@login_required
def delete_comment(request, comment_id, next=None):
    """
    Deletes a comment. Confirmation on GET, action on POST.

    Templates: `comments/delete_by_user.html`,
               `comments/delete_by_user_forbidden.html`
    Context:
        comment
            the deleted `comments.comment` object
    """
    comment = get_object_or_404(comment_app.get_model(), pk=comment_id,
        site__pk=settings.SITE_ID)

    # Ensure requesting user is same user who posted comment
    if request.user != comment.user:
        t = loader.get_template('comments/delete_by_user_forbidden.html')
        c = RequestContext(request)
        return HttpResponseForbidden(t.render(c))

    # Check that user is not banned from commenting
    user_ip_address = request.META['REMOTE_ADDR']
    user_is_banned = \
        CommentsBannedFromIpAddress.objects.is_banned(user_ip_address)
    if user_is_banned:
        return HttpResponseForbidden()

    # Delete on POST
    if request.method == 'POST':
        comment.is_deleted_by_user = True
        comment.save()
        signals.comment_was_deleted_by_user.send(
            sender = comment.__class__,
            comment = comment,
            request = request
        )
        if request.is_ajax():
            return redirect('osl_comments.views.get_comment',
                comment_id=comment_id)
        else:
            return next_redirect(request.POST.copy(), next, delete_by_user_done,
            c=comment.pk)

    # Render a form on GET
    else:
        return render_to_response('comments/delete_by_user.html',
            {'comment': comment, "next": next},
            RequestContext(request)
        )

delete_by_user_done = confirmation_view(
    template = "comments/deleted_by_user.html",
    doc = 'Displays a "comment was deleted" success page.'
)

@login_required
@require_POST
def edit_comment(request, next=None):
    # get data
    data = request.POST.copy()
    next = data.get('next', next)

    # see if user wants to cancel
    cancel = 'cancel' in data
    if cancel:
        return redirect(data['cancel_url'])

    # get comment and raise error if id is wrong
    comment_id = data.get('comment_id')
    if comment_id is None:
        return CommentPostBadRequest("Missing comment id field.")
    try:
        comment = OslComment.objects.get(pk=comment_id)
    except ObjectDoesNotExist:
        return CommentPostBadRequest(
            "No comment matching with PK %r exists." % comment_id
        )
    except (ValueError, ValidationError) as e:
        return CommentPostBadRequest(
            "Attempting to get comment PK %r raised %s" %
            (escape(comment_id), e.__class__.__name__)
        )

    # ensure user editing is same user who posted comment
    if comment.user != request.user:
        return HttpResponseForbidden("Cannot edit another user's comment.")

    # does the user want to preview the comment?
    preview = 'preview' in data

    # get a special comment form for editing
    form = OslEditCommentForm(data=data)

    # If there are errors or if we requested a preview show the comment
    if form.errors or preview:
        template_list = [
            # These first two exist for purely historical reasons.
            # Django v1.0 and v1.1 allowed the underscore format for
            # preview templates, so we have to preserve that format.
            "comments/%s_%s_preview.html" % (comment.content_type.app_label, comment.content_type.model),
            "comments/%s_preview.html" % comment.content_type.app_label,
            # Now the usual directory based template heirarchy.
            "comments/%s/%s/preview.html" % (comment.content_type.app_label, comment.content_type.model),
            "comments/%s/preview.html" % comment.content_type.app_label,
            "comments/preview.html",
        ]
        return render_to_response(
            template_list, {
                "comment" : form.data.get("comment", ""),
                "form" : form,
                "next": next,
            },
            RequestContext(request, {})
        )

    responses = signals.comment_will_be_edited.send(
        sender  = comment.__class__,
        comment = comment,
        request = request
    )

    for (receiver, response) in responses:
        if response == False:
            return CommentPostBadRequest(
                "comment_will_be_edited receiver %r killed the comment" % receiver.__name__)

    # update comment content
    comment.comment = data.get('comment')
    comment.edit_timestamp = datetime.now()
    comment.save()

    signals.comment_was_edited.send(
        sender  = comment.__class__,
        comment = comment,
        request = request
    )

    if request.is_ajax():
        return redirect('osl_comments.views.get_comment', comment_id=comment.pk)
    else:
        return next_redirect(data, next, comment_edited)

comment_edited = confirmation_view(
    template = "comments/edit_confirmed.html",
    doc = """Display a "comment was edited" success page."""
)

@require_POST
@login_required
def flag(request, comment_id, next=None):
    if not request.is_ajax:
        return redirect('django.contrib.comments.views.moderation.flag',
            comment_id)

    from django.contrib.comment.views.moderation import perform_flag
    comment = get_object_or_404(OslComment, pk=comment_id,
        site__pk=settings.SITE_ID)
    perform_flag(request, comment)

    return HttpResponse(status=200)

def get_ajax_edit_form(request, comment_pk):
    comment = OslComment.objects.get(pk=comment_pk)
    return render_to_response(
        "comments/render_edit_form.html",
        {'comment': comment},
        RequestContext(request)
    )

def get_ajax_reply_form(request, obj_ctype_pk, obj_pk, comment_pk):
    obj_model = ContentType.objects.get(pk=obj_ctype_pk).model_class()
    obj = obj_model.objects.get(pk=obj_pk)
    comment = OslComment.objects.get(pk=comment_pk)
    return render_to_response(
        "comments/reply_form_container.html",
        {'object': obj, 'comment': comment},
        RequestContext(request)
    )

def get_comment(request, comment_id):
    comment = get_object_or_404(OslComment, pk=comment_id)
    return render_to_response('comments/comment.html',
        {'comment': comment, 'comments_enabled': True,
        'comment_parent': comment.parent_comment == None,
        'comment_score': Vote.objects.get_score(comment)['score'],
        'vote': Vote.objects.get_for_user(comment, request.user)},
        context_instance=RequestContext(request))

def get_comments(request, obj_ctype_pk, obj_pk, order_method, comments_enabled):
    """Renders a list of comments."""

    OFFSET_QUERY_STRING_KEY = 'offset'
    if OFFSET_QUERY_STRING_KEY not in request.GET:
        return HttpResponseBadRequest('Need to provide an offset key value in the query string')
    offset = request.GET[OFFSET_QUERY_STRING_KEY]
    offset = int(offset)

    comments_enabled = bool(comments_enabled)

    obj_ctype = ContentType.objects.get(pk=obj_ctype_pk)

    comment_list = list(OslComment.objects.get_comments(
        ctype=obj_ctype,
        object_pk=obj_pk,
        order_method=order_method,
        offset=offset
    ))

    comment_count = OslComment.objects.filter(
            content_type = obj_ctype,
            object_pk = smart_unicode(obj_pk),
            site__pk = settings.SITE_ID,
            is_public = True,
            inline_to_object = False
        ).count()
    num_comments_per_page = CommentsPerPageForContentType.objects.get_comments_per_page_for_content_type(
        obj_ctype)
    display_load_more = False
    if offset + num_comments_per_page < comment_count:
        display_load_more = True

    return render_to_response(
        'comments/inner_list.html',
        {
            'comment_list': comment_list,
            'comments_enabled': comments_enabled,
            'display_load_more': display_load_more,
            'sorted_by': order_method,
            'object_ctype_pk': obj_ctype_pk,
            'object_pk': obj_pk
        },
        RequestContext(request)
    )

@require_POST
@permission_required('comments.can_moderate')
def moderate(request, comment_id, next=None):
    if not request.is_ajax:
        return redirect('django.contrib.comments.views.moderation.delete',
            comment_id)

    from django.contrib.comments.views.moderation import perform_delete
    comment = get_object_or_404(OslComment, pk=comment_id,
        site__pk=settings.SITE_ID)
    perform_delete(request, comment)

    return redirect('osl_comments.views.get_comment', comment_id=comment_id)

@csrf_protect
@require_POST
def post_comment(request, next=None, using=None):
    """Wraps Django's post_comment view to handle the redirect better."""
    data = request.POST.copy()
    if 'cancel' in data:
        return redirect(data['cancel_url'])

    response = comments.post_comment(request, next, using)

    comment_pk = ''
    if response.status_code == 302:
        # Move the comment pk in the query string to the URL fragment
        # (and clear out delete and reply key values pairs as well)
        redirect_location = response['location']
        redirect_url = list(urlparse.urlparse(redirect_location))
        redirect_qs = urlparse.parse_qs(redirect_url[4])
        comment_pk = ''
        if 'c' in redirect_qs:
            comment_pk = redirect_qs['c'][0]
            del redirect_qs['c']
        if EDIT_QUERY_STRING_KEY in redirect_qs:
            del redirect_qs[EDIT_QUERY_STRING_KEY]
        if REPLY_QUERY_STRING_KEY in redirect_qs:
            del redirect_qs[REPLY_QUERY_STRING_KEY]
        redirect_url[4] = urllib.urlencode(redirect_qs, True)
        redirect_url[5] = ''.join(['c', comment_pk])
        response['location'] = urlparse.urlunparse(redirect_url)

        if request.is_ajax():
            return redirect(get_comment, comment_id=comment_pk)

    return response

def redirect_view(request, content_type_id, object_id):
    """
    Used instead of standard comments-url-redirect view to allow for no
    pagination.
    """
    from django.contrib.contenttypes.views import shortcut
    response = shortcut(request, content_type_id, object_id)
    original_url = response['location']

    # Add in no pagination query string key
    url_list = list(urlparse.urlparse(original_url))
    url = url_list[:4]
    query_string = url_list[4]
    fragment = url_list[5]

    query_string_dict = urlparse.parse_qs(query_string)
    query_string_dict.update({NO_PAGINATION_QUERY_STRING_KEY: '1'})
    new_qs = urllib.urlencode(query_string_dict)

    url.extend([new_qs, fragment])
    url_string = urlparse.urlunparse(url)
    response['location'] = url_string
    return response

@login_required
@permission_required('osl_comments.can_ban')
def update_ip_address_ban(request, comment_id, next=None):
    try:
        comment = OslComment.objects.get(pk=comment_id)
    except OslComment.DoesNotExist:
        return HttpResponseBadRequest()

    if request.method == 'GET':
        try:
            banned = \
                CommentsBannedFromIpAddress.objects.get(
                ip_address=comment.ip_address).comments_banned
        except CommentsBannedFromIpAddress.DoesNotExist:
            banned = False
        return render_to_response('comments/update_ip_address_ban.html',
            {'banned': banned},
            RequestContext(request)
        )

    if request.method == 'POST':
        data = request.POST
        banned_str = data.get('ban', None)
        if banned_str == 'True':
            banned = True
        elif banned_str == 'False':
            banned = False
        else:
            return HttpResponseBadRequest()

        try:
            ban = CommentsBannedFromIpAddress.objects.get(
                ip_address=comment.ip_address)
        except CommentsBannedFromIpAddress.DoesNotExist:
            ban = CommentsBannedFromIpAddress(ip_address=comment.ip_address)
        ban.comments_banned = banned
        ban.save()

        signals.ip_address_ban_was_updated.send(
            sender = comment.__class__,
            banned = banned,
            request = request
        )

        return next_redirect(request.POST.copy(), next,
            update_ip_address_ban_done)

update_ip_address_ban_done = confirmation_view(
    template = "comments/update_ip_address_ban_done.html",
    doc = """Display a "ip address ban updated" success page."""
)

<commit_msg>Return 404 status if user tries to edit non-existant comment<commit_after>from datetime import datetime
import urllib
import urlparse

from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib import comments as comment_app
from django.contrib.comments.views import comments
from django.contrib.comments.views.comments import CommentPostBadRequest, comment_done
from django.contrib.comments.views.utils import confirmation_view, next_redirect
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.http import (HttpResponse, HttpResponseBadRequest,
    HttpResponseForbidden, HttpResponseNotFound)
from django.shortcuts import get_object_or_404, redirect, render_to_response
from django.template import RequestContext, loader
from django.utils.encoding import smart_unicode
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST

from voting.models import Vote

from osl_comments import signals
from osl_comments.forms import OslEditCommentForm
from osl_comments.models import (CommentsBannedFromIpAddress,
    CommentsPerPageForContentType, OslComment)
from osl_comments.templatetags import (EDIT_QUERY_STRING_KEY,
    REPLY_QUERY_STRING_KEY)

NO_PAGINATION_QUERY_STRING_KEY = 'np'

@login_required
def delete_comment(request, comment_id, next=None):
    """
    Deletes a comment. Confirmation on GET, action on POST.

    Templates: `comments/delete_by_user.html`,
               `comments/delete_by_user_forbidden.html`
    Context:
        comment
            the deleted `comments.comment` object
    """
    comment = get_object_or_404(comment_app.get_model(), pk=comment_id,
        site__pk=settings.SITE_ID)

    # Ensure requesting user is same user who posted comment
    if request.user != comment.user:
        t = loader.get_template('comments/delete_by_user_forbidden.html')
        c = RequestContext(request)
        return HttpResponseForbidden(t.render(c))

    # Check that user is not banned from commenting
    user_ip_address = request.META['REMOTE_ADDR']
    user_is_banned = \
        CommentsBannedFromIpAddress.objects.is_banned(user_ip_address)
    if user_is_banned:
        return HttpResponseForbidden()

    # Delete on POST
    if request.method == 'POST':
        comment.is_deleted_by_user = True
        comment.save()
        signals.comment_was_deleted_by_user.send(
            sender = comment.__class__,
            comment = comment,
            request = request
        )
        if request.is_ajax():
            return redirect('osl_comments.views.get_comment',
                comment_id=comment_id)
        else:
            return next_redirect(request.POST.copy(), next, delete_by_user_done,
            c=comment.pk)

    # Render a form on GET
    else:
        return render_to_response('comments/delete_by_user.html',
            {'comment': comment, "next": next},
            RequestContext(request)
        )

delete_by_user_done = confirmation_view(
    template = "comments/deleted_by_user.html",
    doc = 'Displays a "comment was deleted" success page.'
)

@login_required
@require_POST
def edit_comment(request, next=None):
    # get data
    data = request.POST.copy()
    next = data.get('next', next)

    # see if user wants to cancel
    cancel = 'cancel' in data
    if cancel:
        return redirect(data['cancel_url'])

    # get comment and raise error if id is wrong
    comment_id = data.get('comment_id')
    if comment_id is None:
        return CommentPostBadRequest("Missing comment id field.")
    try:
        comment = OslComment.objects.get(pk=comment_id)
    except ObjectDoesNotExist:
        return HttpResponseNotFound(
            "No comment matching with PK %r exists." % comment_id
        )
    except (ValueError, ValidationError) as e:
        return CommentPostBadRequest(
            "Attempting to get comment PK %r raised %s" %
            (escape(comment_id), e.__class__.__name__)
        )

    # ensure user editing is same user who posted comment
    if comment.user != request.user:
        return HttpResponseForbidden("Cannot edit another user's comment.")

    # does the user want to preview the comment?
    preview = 'preview' in data

    # get a special comment form for editing
    form = OslEditCommentForm(data=data)

    # If there are errors or if we requested a preview show the comment
    if form.errors or preview:
        template_list = [
            # These first two exist for purely historical reasons.
            # Django v1.0 and v1.1 allowed the underscore format for
            # preview templates, so we have to preserve that format.
            "comments/%s_%s_preview.html" % (comment.content_type.app_label, comment.content_type.model),
            "comments/%s_preview.html" % comment.content_type.app_label,
            # Now the usual directory based template heirarchy.
            "comments/%s/%s/preview.html" % (comment.content_type.app_label, comment.content_type.model),
            "comments/%s/preview.html" % comment.content_type.app_label,
            "comments/preview.html",
        ]
        return render_to_response(
            template_list, {
                "comment" : form.data.get("comment", ""),
                "form" : form,
                "next": next,
            },
            RequestContext(request, {})
        )

    responses = signals.comment_will_be_edited.send(
        sender  = comment.__class__,
        comment = comment,
        request = request
    )

    for (receiver, response) in responses:
        if response == False:
            return CommentPostBadRequest(
                "comment_will_be_edited receiver %r killed the comment" % receiver.__name__)

    # update comment content
    comment.comment = data.get('comment')
    comment.edit_timestamp = datetime.now()
    comment.save()

    signals.comment_was_edited.send(
        sender  = comment.__class__,
        comment = comment,
        request = request
    )

    if request.is_ajax():
        return redirect('osl_comments.views.get_comment', comment_id=comment.pk)
    else:
        return next_redirect(data, next, comment_edited)

comment_edited = confirmation_view(
    template = "comments/edit_confirmed.html",
    doc = """Display a "comment was edited" success page."""
)

@require_POST
@login_required
def flag(request, comment_id, next=None):
    if not request.is_ajax:
        return redirect('django.contrib.comments.views.moderation.flag',
            comment_id)

    from django.contrib.comment.views.moderation import perform_flag
    comment = get_object_or_404(OslComment, pk=comment_id,
        site__pk=settings.SITE_ID)
    perform_flag(request, comment)

    return HttpResponse(status=200)

def get_ajax_edit_form(request, comment_pk):
    comment = OslComment.objects.get(pk=comment_pk)
    return render_to_response(
        "comments/render_edit_form.html",
        {'comment': comment},
        RequestContext(request)
    )

def get_ajax_reply_form(request, obj_ctype_pk, obj_pk, comment_pk):
    obj_model = ContentType.objects.get(pk=obj_ctype_pk).model_class()
    obj = obj_model.objects.get(pk=obj_pk)
    comment = OslComment.objects.get(pk=comment_pk)
    return render_to_response(
        "comments/reply_form_container.html",
        {'object': obj, 'comment': comment},
        RequestContext(request)
    )

def get_comment(request, comment_id):
    comment = get_object_or_404(OslComment, pk=comment_id)
    return render_to_response('comments/comment.html',
        {'comment': comment, 'comments_enabled': True,
        'comment_parent': comment.parent_comment == None,
        'comment_score': Vote.objects.get_score(comment)['score'],
        'vote': Vote.objects.get_for_user(comment, request.user)},
        context_instance=RequestContext(request))

def get_comments(request, obj_ctype_pk, obj_pk, order_method, comments_enabled):
    """Renders a list of comments."""

    OFFSET_QUERY_STRING_KEY = 'offset'
    if OFFSET_QUERY_STRING_KEY not in request.GET:
        return HttpResponseBadRequest('Need to provide an offset key value in the query string')
    offset = request.GET[OFFSET_QUERY_STRING_KEY]
    offset = int(offset)

    comments_enabled = bool(comments_enabled)

    obj_ctype = ContentType.objects.get(pk=obj_ctype_pk)

    comment_list = list(OslComment.objects.get_comments(
        ctype=obj_ctype,
        object_pk=obj_pk,
        order_method=order_method,
        offset=offset
    ))

    comment_count = OslComment.objects.filter(
            content_type = obj_ctype,
            object_pk = smart_unicode(obj_pk),
            site__pk = settings.SITE_ID,
            is_public = True,
            inline_to_object = False
        ).count()
    num_comments_per_page = CommentsPerPageForContentType.objects.get_comments_per_page_for_content_type(
        obj_ctype)
    display_load_more = False
    if offset + num_comments_per_page < comment_count:
        display_load_more = True

    return render_to_response(
        'comments/inner_list.html',
        {
            'comment_list': comment_list,
            'comments_enabled': comments_enabled,
            'display_load_more': display_load_more,
            'sorted_by': order_method,
            'object_ctype_pk': obj_ctype_pk,
            'object_pk': obj_pk
        },
        RequestContext(request)
    )

@require_POST
@permission_required('comments.can_moderate')
def moderate(request, comment_id, next=None):
    if not request.is_ajax:
        return redirect('django.contrib.comments.views.moderation.delete',
            comment_id)

    from django.contrib.comments.views.moderation import perform_delete
    comment = get_object_or_404(OslComment, pk=comment_id,
        site__pk=settings.SITE_ID)
    perform_delete(request, comment)

    return redirect('osl_comments.views.get_comment', comment_id=comment_id)

@csrf_protect
@require_POST
def post_comment(request, next=None, using=None):
    """Wraps Django's post_comment view to handle the redirect better."""
    data = request.POST.copy()
    if 'cancel' in data:
        return redirect(data['cancel_url'])

    response = comments.post_comment(request, next, using)

    comment_pk = ''
    if response.status_code == 302:
        # Move the comment pk in the query string to the URL fragment
        # (and clear out delete and reply key values pairs as well)
        redirect_location = response['location']
        redirect_url = list(urlparse.urlparse(redirect_location))
        redirect_qs = urlparse.parse_qs(redirect_url[4])
        comment_pk = ''
        if 'c' in redirect_qs:
            comment_pk = redirect_qs['c'][0]
            del redirect_qs['c']
        if EDIT_QUERY_STRING_KEY in redirect_qs:
            del redirect_qs[EDIT_QUERY_STRING_KEY]
        if REPLY_QUERY_STRING_KEY in redirect_qs:
            del redirect_qs[REPLY_QUERY_STRING_KEY]
        redirect_url[4] = urllib.urlencode(redirect_qs, True)
        redirect_url[5] = ''.join(['c', comment_pk])
        response['location'] = urlparse.urlunparse(redirect_url)

        if request.is_ajax():
            return redirect(get_comment, comment_id=comment_pk)

    return response

def redirect_view(request, content_type_id, object_id):
    """
    Used instead of standard comments-url-redirect view to allow for no
    pagination.
    """
    from django.contrib.contenttypes.views import shortcut
    response = shortcut(request, content_type_id, object_id)
    original_url = response['location']

    # Add in no pagination query string key
    url_list = list(urlparse.urlparse(original_url))
    url = url_list[:4]
    query_string = url_list[4]
    fragment = url_list[5]

    query_string_dict = urlparse.parse_qs(query_string)
    query_string_dict.update({NO_PAGINATION_QUERY_STRING_KEY: '1'})
    new_qs = urllib.urlencode(query_string_dict)

    url.extend([new_qs, fragment])
    url_string = urlparse.urlunparse(url)
    response['location'] = url_string
    return response

@login_required
@permission_required('osl_comments.can_ban')
def update_ip_address_ban(request, comment_id, next=None):
    try:
        comment = OslComment.objects.get(pk=comment_id)
    except OslComment.DoesNotExist:
        return HttpResponseBadRequest()

    if request.method == 'GET':
        try:
            banned = \
                CommentsBannedFromIpAddress.objects.get(
                ip_address=comment.ip_address).comments_banned
        except CommentsBannedFromIpAddress.DoesNotExist:
            banned = False
        return render_to_response('comments/update_ip_address_ban.html',
            {'banned': banned},
            RequestContext(request)
        )

    if request.method == 'POST':
        data = request.POST
        banned_str = data.get('ban', None)
        if banned_str == 'True':
            banned = True
        elif banned_str == 'False':
            banned = False
        else:
            return HttpResponseBadRequest()

        try:
            ban = CommentsBannedFromIpAddress.objects.get(
                ip_address=comment.ip_address)
        except CommentsBannedFromIpAddress.DoesNotExist:
            ban = CommentsBannedFromIpAddress(ip_address=comment.ip_address)
        ban.comments_banned = banned
        ban.save()

        signals.ip_address_ban_was_updated.send(
            sender = comment.__class__,
            banned = banned,
            request = request
        )

        return next_redirect(request.POST.copy(), next,
            update_ip_address_ban_done)

update_ip_address_ban_done = confirmation_view(
    template = "comments/update_ip_address_ban_done.html",
    doc = """Display a "ip address ban updated" success page."""
)

<|endoftext|>


<commit_before>from datetime import datetime
from os import environ as env
import json
import logging

import requests

from ghtools import __version__

log = logging.getLogger(__name__)

KNOWN_GITHUBS = {
    'public': 'https://api.github.com'
}

class ClientError(Exception):
    pass

class APIError(Exception):
    pass

class GithubAPIClient(object):
    def __init__(self, root=None, nickname='public'):
        self._session = requests.Session()

        self.nickname = nickname
        self.token = self._env('oauth_token')

        # Try to ascertain GitHub API root
        if root is not None:
            self.root = root
        elif self._env('api_root') is not None:
            self.root = self._env('api_root')
        else:
            try:
                self.root = KNOWN_GITHUBS[self.nickname]
            except KeyError:
                msg = "No known API root for nickname '{0}'. Perhaps you need to set ${1}?".format(
                    self.nickname,
                    envkey(self.nickname, 'api_root')
                )
                raise ClientError(msg)

        # Use specified SSL CA Bundle if provided
        ca_bundle = self._env('ca_bundle')
        if ca_bundle is not None:
            self._session.verify = ca_bundle

        log.debug("Created %s", self)

    def _env(self, key, default=None):
        return env.get(
            envkey(self.nickname, key),
            default
        )

    def _req(self, method, url, _raise=True, *args, **kwargs):
        res = self._session.request(method, url, *args, **kwargs)
        if _raise:
            custom_raise_for_status(res)
        return res

    def _url(self, path):
        return self.root + path

    def login(self, username, password, scopes=None):
        if scopes is None:
            scopes = []

        ver = __version__
        now = datetime.utcnow().replace(microsecond=0)

        note = 'ghtools {0} (created {1})'.format(ver, now.isoformat())

        data = {
            'note': note,
            'scopes': scopes,
        }

        res = self.post(
            '/authorizations',
            auth=(username, password),
            data=json.dumps(data)
        )

        self.token = res.json['token']

    @property
    def token(self):
        return self._token

    @token.setter
    def token(self, tok):
        self._token = tok
        self._session.headers['Authorization'] = 'token {0}'.format(self._token)

    @property
    def logged_in(self):
        return self.token is not None

    def delete(self, url, *args, **kwargs):
        return self._req('delete', self._url(url), *args, **kwargs)

    def get(self, url, *args, **kwargs):
        return self._req('get', self._url(url), *args, **kwargs)

    def patch(self, url, *args, **kwargs):
        return self._req('patch', self._url(url), *args, **kwargs)

    def post(self, url, *args, **kwargs):
        return self._req('post', self._url(url), *args, **kwargs)

    def put(self, url, *args, **kwargs):
        return self._req('put', self._url(url), *args, **kwargs)

    def paged_get(self, url, *args, **kwargs):
        for res in paged(self._session, 'get', self._url(url)):
            for repo in res.json:
                yield repo

    def __str__(self):
        return '<GithubAPIClient {0}={1}>'.format(self.nickname, self.root)

def envkey(nickname, key):
    return 'GITHUB_{0}_{1}'.format(nickname.upper(), key.upper())

def paged(session, method, url, **kwargs):
    res = session.request(method, url, **kwargs)
    custom_raise_for_status(res)

    while True:
        yield res
        try:
            next_page = res.links['next']
        except KeyError:
            break
        else:
            res = session.request(method, next_page['url'], **kwargs)
            custom_raise_for_status(res)

def custom_raise_for_status(res):
    try:
        res.raise_for_status()
    except requests.RequestException as err:
        newerr = APIError(err)
        for k, v in newerr.__dict__.items():
            setattr(newerr, k, v)
<commit_msg>Raise APIError after it's been created.<commit_after>from datetime import datetime
from os import environ as env
import json
import logging

import requests

from ghtools import __version__

log = logging.getLogger(__name__)

KNOWN_GITHUBS = {
    'public': 'https://api.github.com'
}

class ClientError(Exception):
    pass

class APIError(Exception):
    pass

class GithubAPIClient(object):
    def __init__(self, root=None, nickname='public'):
        self._session = requests.Session()

        self.nickname = nickname
        self.token = self._env('oauth_token')

        # Try to ascertain GitHub API root
        if root is not None:
            self.root = root
        elif self._env('api_root') is not None:
            self.root = self._env('api_root')
        else:
            try:
                self.root = KNOWN_GITHUBS[self.nickname]
            except KeyError:
                msg = "No known API root for nickname '{0}'. Perhaps you need to set ${1}?".format(
                    self.nickname,
                    envkey(self.nickname, 'api_root')
                )
                raise ClientError(msg)

        # Use specified SSL CA Bundle if provided
        ca_bundle = self._env('ca_bundle')
        if ca_bundle is not None:
            self._session.verify = ca_bundle

        log.debug("Created %s", self)

    def _env(self, key, default=None):
        return env.get(
            envkey(self.nickname, key),
            default
        )

    def _req(self, method, url, _raise=True, *args, **kwargs):
        res = self._session.request(method, url, *args, **kwargs)
        if _raise:
            custom_raise_for_status(res)
        return res

    def _url(self, path):
        return self.root + path

    def login(self, username, password, scopes=None):
        if scopes is None:
            scopes = []

        ver = __version__
        now = datetime.utcnow().replace(microsecond=0)

        note = 'ghtools {0} (created {1})'.format(ver, now.isoformat())

        data = {
            'note': note,
            'scopes': scopes,
        }

        res = self.post(
            '/authorizations',
            auth=(username, password),
            data=json.dumps(data)
        )

        self.token = res.json['token']

    @property
    def token(self):
        return self._token

    @token.setter
    def token(self, tok):
        self._token = tok
        self._session.headers['Authorization'] = 'token {0}'.format(self._token)

    @property
    def logged_in(self):
        return self.token is not None

    def delete(self, url, *args, **kwargs):
        return self._req('delete', self._url(url), *args, **kwargs)

    def get(self, url, *args, **kwargs):
        return self._req('get', self._url(url), *args, **kwargs)

    def patch(self, url, *args, **kwargs):
        return self._req('patch', self._url(url), *args, **kwargs)

    def post(self, url, *args, **kwargs):
        return self._req('post', self._url(url), *args, **kwargs)

    def put(self, url, *args, **kwargs):
        return self._req('put', self._url(url), *args, **kwargs)

    def paged_get(self, url, *args, **kwargs):
        for res in paged(self._session, 'get', self._url(url)):
            for repo in res.json:
                yield repo

    def __str__(self):
        return '<GithubAPIClient {0}={1}>'.format(self.nickname, self.root)

def envkey(nickname, key):
    return 'GITHUB_{0}_{1}'.format(nickname.upper(), key.upper())

def paged(session, method, url, **kwargs):
    res = session.request(method, url, **kwargs)
    custom_raise_for_status(res)

    while True:
        yield res
        try:
            next_page = res.links['next']
        except KeyError:
            break
        else:
            res = session.request(method, next_page['url'], **kwargs)
            custom_raise_for_status(res)

def custom_raise_for_status(res):
    try:
        res.raise_for_status()
    except requests.RequestException as err:
        newerr = APIError(err)
        for k, v in err.__dict__.items():
            setattr(newerr, k, v)
        raise newerr
<|endoftext|>