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

""" Selenium element operations library """
from time import sleep
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys

from Framework.Utils.print_Utils import print_error, print_info, print_exception
from Framework.Utils.data_Utils import get_object_from_datarepository
from Framework.ClassUtils.WSelenium.element_locator import ElementLocator

try:
    from selenium.webdriver.remote.webelement import WebElement
    from selenium.common.exceptions import NoSuchElementException
    from selenium.common.exceptions import StaleElementReferenceException
except ImportError as exception:
    print_exception(exception)

EL = ElementLocator()
ACTIONS = {'click': '_click_element',
           'double_click': '_double_click_element',
           'send_keys': '_send_keys',
           'type': '_type_keys',
           'fill': '_type_keys',
           'clear_text': '_clear_text',
           'get_text': '_get_text',
           'clear': '_clear_text',
           'drag_and_drop': '_drag_and_drop',
           'mouse_over':'_mouse_over',
           'drag_and_drop_by_offset':'_drag_and_drop_by_offset',
           'get_property': '_get_property',
           'check_property': '_check_property',
           'perform_keypress': '_perform_keypress'
           }
KEYS = {'ADD': Keys.ADD, 'ALT': Keys.ALT, 'ARROW_DOWN': Keys.ARROW_DOWN,
        'ARROW_LEFT': Keys.ARROW_LEFT, 'ARROW_RIGHT': Keys.ARROW_RIGHT, 'ARROW_UP': Keys.ARROW_UP,
        'BACKSPACE': Keys.BACK_SPACE, 'CANCEL': Keys.CANCEL, 'CLEAR': Keys.CLEAR,
        'COMMAND': Keys.COMMAND, 'CONTROL': Keys.CONTROL, 'DECIMAL': Keys.DECIMAL,
        'DELETE': Keys.DELETE, 'DIVIDE': Keys.DIVIDE, 'DOWN': Keys.DOWN, 'END': Keys.END,
        'ENTER': Keys.RETURN, 'EQUALS': Keys.EQUALS, 'ESCAPE': Keys.ESCAPE, 'F1': Keys.F1,
        'F10': Keys.F10, 'F11': Keys.F11, 'F12': Keys.F12, 'F2': Keys.F2, 'F3': Keys.F3, 'F4': Keys.F4,
        'F5': Keys.F5, 'F6': Keys.F6, 'F7': Keys.F7, 'F8': Keys.F8, 'F9': Keys.F9,
        'HELP': Keys.HELP, 'HOME': Keys.HOME, 'INSERT': Keys.INSERT, 'LEFT': Keys.LEFT,
        'LEFT_ALT': Keys.LEFT_ALT, 'LEFT_CONTROL': Keys.LEFT_CONTROL, 'LEFT_SHIFT': Keys.LEFT_SHIFT,
        'META': Keys.META, 'MULTIPLY': Keys.MULTIPLY, 'NULL': Keys.NULL, 'NUMPAD0': Keys.NUMPAD0,
        'NUMPAD1': Keys.NUMPAD1, 'NUMPAD2': Keys.NUMPAD2, 'NUMPAD3': Keys.NUMPAD3,
        'NUMPAD4': Keys.NUMPAD4, 'NUMPAD5': Keys.NUMPAD5, 'NUMPAD6': Keys.NUMPAD6,
        'NUMPAD7': Keys.NUMPAD7, 'NUMPAD8': Keys.NUMPAD8,  'NUMPAD9': Keys.NUMPAD9,
        'PAGE_DOWN': Keys.PAGE_DOWN, 'PAGE_UP': Keys.PAGE_UP, 'PAUSE': Keys.PAUSE,
        'RETURN': Keys.RETURN, 'RIGHT': Keys.RIGHT, 'SEMICOLON': Keys.SEMICOLON, 'SEPARATOR': Keys.SEPARATOR,
        'SHIFT': Keys.SHIFT, 'SPACE': Keys.SPACE, 'SUBTRACT': Keys.SUBTRACT, 'TAB': Keys.TAB,
         'UP': Keys.UP
         }

class ElementOperations():
    """ Element operations """

    def __init__(self, *args, **kwargs):
        """ constructor """
        pass

    def perform_element_action(self, element_or_browser, locator=None,
                               action=None, **kwargs):
        """Generic method to perform specific actions on an element
        :Currently supported actions and the values that they take """
        browser = kwargs.get('browser')
        status = True
        if action != "perform_keypress":
            element = self._get_element(element_or_browser, locator)
        else:
            element = element_or_browser
        if element:
            action_function = self._get_action_function(action.lower())
            if not action_function:
                print_error((action + " is not a supported a supported value."))
            else:
                count = 0
                while (count <= 3):
                    try:
                        if action == "get_text":
                            status, value = action_function(element, **kwargs)
                            if status is True:
                                return status, value
                            else:
                                count = count + 1
                                status = self.wait_time(browser, locator,
                                                        action)
                                if status is True:
                                    return status
                        else:
                            status = action_function(element, **kwargs)
                            if status is True:
                                return status
                            else:
                                count = count + 1
                                status = self.wait_time(browser, locator,
                                                        action)
                                if status is True:
                                    return status
                    except StaleElementReferenceException:
                        status = False
                        try:
                            count = count + 1
                            status = self.wait_time(browser, locator, action)
                            if status is True:
                                return status
                        except StaleElementReferenceException:
                            status = False
                    except Exception as exception:
                        status = False
                        print_exception(exception)
                        return status
                else:
                    print_error("StaleElementReferenceException occured."
                                "Tried three times to locate the element")
                    status = False
        else:
            status = False
            print_error("Provide a valid WebElement to perform "
                        "a {0} operation got {1}".format(action, element))
        return status

    def wait_time(self, browser, locator, action):
        """ wait time to find the element again """
        print_info("waiting for 3 seconds before retrying")
        sleep(3)
        status = self._stale_element_exception(browser, locator, action)
        return status

    def get_page_source(self, browser):
        '''
        Get page source of the browser
        '''
        return browser.getPageSource()

    def verify_text(self, **kwargs):
        """stores the text from element in data repository with var variable
        and verifies if it is same as expected if expected is provided
        :Arguments:
            1. var = variable in which to store the text
            2. expected = value to compare with as a list separated by comma
        """
        status = True
        value = get_object_from_datarepository(kwargs.get('var'))
        expected = kwargs.get('expected').split(',')
        if value not in expected:
            print_error("element text expected to be <<{}>> "
                        "but found to be <<{}>>".format(', '.join(expected),
                                                        value))
            status = False

        return status

# Private methods

    def _stale_element_exception(self, browser, locator, action, **kwargs):
        element = EL.get_element(browser, locator)
        if element is not None:
            action_function = self._get_action_function(action.lower())
            status = action_function(element, **kwargs)
            return status

    def _get_element(self, element_or_browser, locator):
        """Get the element based on the provided input"""
        value = None
        if isinstance(element_or_browser, WebElement):
            value = element_or_browser
        else:
            value = EL.get_element(element_or_browser, locator)
        return value

    def _mouse_over(self, element, **kwargs ):
        """Moving the mouse to the middle of an element """
        status = False
        print_info("mouse over operation")
        browser_instance = kwargs.get('browser')
        if element is not None:
            ActionChains(browser_instance).move_to_element(element).perform()
            status = True
        return status

    def _get_action_function(self, action):
        """Gets the function call corresponding to the
        action to be performed"""
        action_function = ACTIONS.get(action.lower().replace(' ', ''), None)
        return getattr(self, action_function) if action_function else None

    def _click_element(self, element, **kwargs):
        """ Clicks on the provided element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        print_info("Click on element")
        if element is not None:
            element.click()
        else:
            status = False
        return status

    def _double_click_element(self, element, **kwargs):
        """ Double clicks on the provided element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        print_info("Double click on element")
        if element is not None:
            browser_instance = kwargs.get('browser')
            ActionChains(browser_instance).double_click(element)
        else:
            status = False
        return status

    def _type_keys(self, element, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. element = a valid WebElement
            2. value = a string that has to be typed into the element.
        """
        status = True
        if element is not None:
            value = kwargs.get('value', '')
            print_info("Sending '{0}' to element".format(value))
            element.send_keys(value)
        else:
            status = False
        return status

    def _send_keys(self, element, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. element = a valid WebElement
            2. value = a Keys object that has to be sent to the element.
        """
        status = True
        value = kwargs.get('value', '')
        try:
            KEYS[value.upper()]
        except KeyError:
            print_error("{0} is not supported by Selenium.".format(value))
            status = False
        else:
            print_info("Type text='{0}' into element".format(value))
            element.send_keys(KEYS[value.upper()])
        return status

    def _drag_and_drop(self, source, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. source = a valid WebElement
            2. target = a valid WebElement
        """
        status = False
        browser_instance = kwargs.get('browser')
        target = self._get_element(browser_instance,
                                   kwargs.get('target_locator'))
        if source is not None and target is not None:
            ActionChains(browser_instance).drag_and_drop(source,
                                                         target).perform()
            status = True
        return status

    def _drag_and_drop_by_offset(self, source, **kwargs):
        """Holds down the left mouse button on the source element,
           then moves to the target offset and releases the mouse button
        :Arguments:
            1. source  = a valid WebElement
            2. xoffset = X offset to move to
            3. yoffset = Y offset to move to
        """
        status = False
        xoffset = kwargs.get('xoffset')
        yoffset = kwargs.get('yoffset')
        browser_instance = kwargs.get('browser')
        actions = ActionChains(browser_instance)
        if source is not None and xoffset is not None and yoffset is not None:
            actions.drag_and_drop_by_offset(source, xoffset, yoffset).perform()
            status = True
        return status

    def _clear_text(self, element, **kwargs):
        """Clears the text if it is a text element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        if element is not None:
            print_info("Clear element")
            element.clear()
        else:
            status = False
        return status

    def _get_text(self, element, **kwargs):
        """gets the text from element
        :Arguments:
            1. element = a valid WebElement
        """
        print_info("get element text")
        print_info("tag: "+element.tag_name)
        if element.tag_name == "input":
            value = element.get_attribute("value")
        else:
            value = element.text
        if value is not None:
            status = True
        print_info("The text for this element is {}".format(value))
        return status, value

    def _get_property(self, element, **kwargs):
        status = True
        if element is not None:
            attribute_name = kwargs.get('attribute_name')
            attr_properties = element.get_attribute(attribute_name)
            if attr_properties is not None:
                print_info("The properties of the attribute '{0}' "
                           "are {1}".format(attribute_name, attr_properties))
            else:
                print_error("Could not find attribute '{0}', hence could not "
                            "retrieve its properties.".format(attribute_name))
                status = False
        else:
            status = False
        return status

    def _check_property(self, element, **kwargs):
        status = True
        if element is not None:
            attribute_name = kwargs.get('attribute_name')
            property_name = kwargs.get('property_name')
            attr_properties = element.get_attribute(attribute_name)
            if attr_properties is not None:
                if property_name in attr_properties:
                    print_info("{0} has a property called {1}. Verification "
                               "success!".format(attribute_name, property_name))
                else:
                    print_error("{0} does not have a property called {1}. "
                                "Verification failed!".format(attribute_name,
                                                              property_name))
                    status = False
            else:
                print_error("Could not find attribute '{0}', hence could not "
                            "retrieve its properties.".format(attribute_name))
                status = False
        else:
            status = False
        return status

    def _perform_keypress(self, element, **kwargs):
        """
        This function expects to receive a browser instance through the
        "browser" argument and a key "keys" through the kwargs.

        The value for "keys" would be a list of keys tha need to pressed.

        """
        status = True
        flag = False
        keys = kwargs.get('keys')
        actions = ActionChains(element)
        for key in keys:
            try:
                selenium_key = KEYS[key.upper()]
            except KeyError:
                print_error("{0} is not supported by Selenium.".format(key))
                status = False
            else:
                flag = True
                actions.send_keys(selenium_key)
        if flag:
            actions.perform()
            sleep(2)

        return status
<commit_msg>modified function<commit_after>'''
Copyright 2017, Fujitsu Network Communications, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
'''

""" Selenium element operations library """
from time import sleep
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys

from Framework.Utils.print_Utils import print_error, print_info, print_exception
from Framework.Utils.data_Utils import get_object_from_datarepository
from Framework.ClassUtils.WSelenium.element_locator import ElementLocator

try:
    from selenium.webdriver.remote.webelement import WebElement
    from selenium.common.exceptions import NoSuchElementException
    from selenium.common.exceptions import StaleElementReferenceException
except ImportError as exception:
    print_exception(exception)

EL = ElementLocator()
ACTIONS = {'click': '_click_element',
           'double_click': '_double_click_element',
           'send_keys': '_send_keys',
           'type': '_type_keys',
           'fill': '_type_keys',
           'clear_text': '_clear_text',
           'get_text': '_get_text',
           'clear': '_clear_text',
           'drag_and_drop': '_drag_and_drop',
           'mouse_over':'_mouse_over',
           'drag_and_drop_by_offset':'_drag_and_drop_by_offset',
           'get_property': '_get_property',
           'check_property': '_check_property',
           'perform_keypress': '_perform_keypress'
           }
KEYS = {'ADD': Keys.ADD, 'ALT': Keys.ALT, 'ARROW_DOWN': Keys.ARROW_DOWN,
        'ARROW_LEFT': Keys.ARROW_LEFT, 'ARROW_RIGHT': Keys.ARROW_RIGHT, 'ARROW_UP': Keys.ARROW_UP,
        'BACKSPACE': Keys.BACK_SPACE, 'CANCEL': Keys.CANCEL, 'CLEAR': Keys.CLEAR,
        'COMMAND': Keys.COMMAND, 'CONTROL': Keys.CONTROL, 'DECIMAL': Keys.DECIMAL,
        'DELETE': Keys.DELETE, 'DIVIDE': Keys.DIVIDE, 'DOWN': Keys.DOWN, 'END': Keys.END,
        'ENTER': Keys.RETURN, 'EQUALS': Keys.EQUALS, 'ESCAPE': Keys.ESCAPE, 'F1': Keys.F1,
        'F10': Keys.F10, 'F11': Keys.F11, 'F12': Keys.F12, 'F2': Keys.F2, 'F3': Keys.F3, 'F4': Keys.F4,
        'F5': Keys.F5, 'F6': Keys.F6, 'F7': Keys.F7, 'F8': Keys.F8, 'F9': Keys.F9,
        'HELP': Keys.HELP, 'HOME': Keys.HOME, 'INSERT': Keys.INSERT, 'LEFT': Keys.LEFT,
        'LEFT_ALT': Keys.LEFT_ALT, 'LEFT_CONTROL': Keys.LEFT_CONTROL, 'LEFT_SHIFT': Keys.LEFT_SHIFT,
        'META': Keys.META, 'MULTIPLY': Keys.MULTIPLY, 'NULL': Keys.NULL, 'NUMPAD0': Keys.NUMPAD0,
        'NUMPAD1': Keys.NUMPAD1, 'NUMPAD2': Keys.NUMPAD2, 'NUMPAD3': Keys.NUMPAD3,
        'NUMPAD4': Keys.NUMPAD4, 'NUMPAD5': Keys.NUMPAD5, 'NUMPAD6': Keys.NUMPAD6,
        'NUMPAD7': Keys.NUMPAD7, 'NUMPAD8': Keys.NUMPAD8,  'NUMPAD9': Keys.NUMPAD9,
        'PAGE_DOWN': Keys.PAGE_DOWN, 'PAGE_UP': Keys.PAGE_UP, 'PAUSE': Keys.PAUSE,
        'RETURN': Keys.RETURN, 'RIGHT': Keys.RIGHT, 'SEMICOLON': Keys.SEMICOLON, 'SEPARATOR': Keys.SEPARATOR,
        'SHIFT': Keys.SHIFT, 'SPACE': Keys.SPACE, 'SUBTRACT': Keys.SUBTRACT, 'TAB': Keys.TAB,
         'UP': Keys.UP
         }

class ElementOperations():
    """ Element operations """

    def __init__(self, *args, **kwargs):
        """ constructor """
        pass

    def perform_element_action(self, element_or_browser, locator=None,
                               action=None, **kwargs):
        """Generic method to perform specific actions on an element
        :Currently supported actions and the values that they take """
        browser = kwargs.get('browser')
        status = True
        if action != "perform_keypress":
            element = self._get_element(element_or_browser, locator)
        else:
            element = element_or_browser
        if element:
            action_function = self._get_action_function(action.lower())
            if not action_function:
                print_error((action + " is not a supported "
                             "a supported value."))
            else:
                count = 0
                while (count <= 3):
                    try:
                        if action == "get_text":
                            status, value = action_function(element, **kwargs)
                            if status is True:
                                return status, value
                            else:
                                count = count + 1
                                status = self.wait_time(browser, locator,
                                                        action)
                                if status is True:
                                    return status
                        else:
                            status = action_function(element, **kwargs)
                            if status is True:
                                return status
                            else:
                                count = count + 1
                                status = self.wait_time(browser, locator,
                                                        action)
                                if status is True:
                                    return status
                    except StaleElementReferenceException:
                        status = False
                        try:
                            count = count + 1
                            if action == "get_text":
                                print_info("waiting for 3 seconds "
                                           "before retrying")
                                sleep(3)
                                status, value = action_function(element,
                                                                **kwargs)
                                if status is True:
                                    return status, value
                            else:
                                status = self.wait_time(browser,
                                                        locator,
                                                        action)
                                if status is True:
                                    return status
                        except StaleElementReferenceException:
                            status = False
                    except Exception as exception:
                        status = False
                        print_exception(exception)
                        return status
                else:
                    print_error("StaleElementReferenceException occured."
                                "Tried three times to locate the element")
                    status = False
        else:
            status = False
            print_error("Provide a valid WebElement to perform "
                        "a {0} operation got {1}".format(action, element))
        return status

    def wait_time(self, browser, locator, action):
        """ wait time to find the element again """
        print_info("waiting for 3 seconds before retrying")
        sleep(3)
        status = self._stale_element_exception(browser, locator, action)
        return status

    def get_page_source(self, browser):
        '''
        Get page source of the browser
        '''
        return browser.getPageSource()

    def verify_text(self, **kwargs):
        """stores the text from element in data repository with var variable
        and verifies if it is same as expected if expected is provided
        :Arguments:
            1. var = variable in which to store the text
            2. expected = value to compare with as a list separated by comma
        """
        status = True
        value = get_object_from_datarepository(kwargs.get('var'))
        expected = kwargs.get('expected').split(',')
        if value not in expected:
            print_error("element text expected to be <<{}>> "
                        "but found to be <<{}>>".format(', '.join(expected),
                                                        value))
            status = False

        return status

# Private methods

    def _stale_element_exception(self, browser, locator, action, **kwargs):
        element = EL.get_element(browser, locator)
        if element is not None:
            action_function = self._get_action_function(action.lower())
            status = action_function(element, **kwargs)
            return status

    def _get_element(self, element_or_browser, locator):
        """Get the element based on the provided input"""
        value = None
        if isinstance(element_or_browser, WebElement):
            value = element_or_browser
        else:
            value = EL.get_element(element_or_browser, locator)
        return value

    def _mouse_over(self, element, **kwargs ):
        """Moving the mouse to the middle of an element """
        status = False
        print_info("mouse over operation")
        browser_instance = kwargs.get('browser')
        if element is not None:
            ActionChains(browser_instance).move_to_element(element).perform()
            status = True
        return status

    def _get_action_function(self, action):
        """Gets the function call corresponding to the
        action to be performed"""
        action_function = ACTIONS.get(action.lower().replace(' ', ''), None)
        return getattr(self, action_function) if action_function else None

    def _click_element(self, element, **kwargs):
        """ Clicks on the provided element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        print_info("Click on element")
        if element is not None:
            element.click()
        else:
            status = False
        return status

    def _double_click_element(self, element, **kwargs):
        """ Double clicks on the provided element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        print_info("Double click on element")
        if element is not None:
            browser_instance = kwargs.get('browser')
            ActionChains(browser_instance).double_click(element)
        else:
            status = False
        return status

    def _type_keys(self, element, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. element = a valid WebElement
            2. value = a string that has to be typed into the element.
        """
        status = True
        if element is not None:
            value = kwargs.get('value', '')
            print_info("Sending '{0}' to element".format(value))
            element.send_keys(value)
        else:
            status = False
        return status

    def _send_keys(self, element, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. element = a valid WebElement
            2. value = a Keys object that has to be sent to the element.
        """
        status = True
        value = kwargs.get('value', '')
        try:
            KEYS[value.upper()]
        except KeyError:
            print_error("{0} is not supported by Selenium.".format(value))
            status = False
        else:
            print_info("Type text='{0}' into element".format(value))
            element.send_keys(KEYS[value.upper()])
        return status

    def _drag_and_drop(self, source, **kwargs):
        """Send values to a particular element,
        simulates typing into a element
        :Arguments:
            1. source = a valid WebElement
            2. target = a valid WebElement
        """
        status = False
        browser_instance = kwargs.get('browser')
        target = self._get_element(browser_instance,
                                   kwargs.get('target_locator'))
        if source is not None and target is not None:
            ActionChains(browser_instance).drag_and_drop(source,
                                                         target).perform()
            status = True
        return status

    def _drag_and_drop_by_offset(self, source, **kwargs):
        """Holds down the left mouse button on the source element,
           then moves to the target offset and releases the mouse button
        :Arguments:
            1. source  = a valid WebElement
            2. xoffset = X offset to move to
            3. yoffset = Y offset to move to
        """
        status = False
        xoffset = kwargs.get('xoffset')
        yoffset = kwargs.get('yoffset')
        browser_instance = kwargs.get('browser')
        actions = ActionChains(browser_instance)
        if source is not None and xoffset is not None and yoffset is not None:
            actions.drag_and_drop_by_offset(source, xoffset, yoffset).perform()
            status = True
        return status

    def _clear_text(self, element, **kwargs):
        """Clears the text if it is a text element
        :Arguments:
            1. element = a valid WebElement
        """
        status = True
        if element is not None:
            print_info("Clear element")
            element.clear()
        else:
            status = False
        return status

    def _get_text(self, element, **kwargs):
        """gets the text from element
        :Arguments:
            1. element = a valid WebElement
        """
        print_info("get element text")
        print_info("tag: "+element.tag_name)
        if element.tag_name == "input":
            value = element.get_attribute("value")
        else:
            value = element.text
        if value is not None:
            status = True
        print_info("The text for this element is {}".format(value))
        return status, value

    def _get_property(self, element, **kwargs):
        status = True
        if element is not None:
            attribute_name = kwargs.get('attribute_name')
            attr_properties = element.get_attribute(attribute_name)
            if attr_properties is not None:
                print_info("The properties of the attribute '{0}' "
                           "are {1}".format(attribute_name, attr_properties))
            else:
                print_error("Could not find attribute '{0}', hence could not "
                            "retrieve its properties.".format(attribute_name))
                status = False
        else:
            status = False
        return status

    def _check_property(self, element, **kwargs):
        status = True
        if element is not None:
            attribute_name = kwargs.get('attribute_name')
            property_name = kwargs.get('property_name')
            attr_properties = element.get_attribute(attribute_name)
            if attr_properties is not None:
                if property_name in attr_properties:
                    print_info("{0} has a property called {1}. Verification "
                               "success!".format(attribute_name, property_name))
                else:
                    print_error("{0} does not have a property called {1}. "
                                "Verification failed!".format(attribute_name,
                                                              property_name))
                    status = False
            else:
                print_error("Could not find attribute '{0}', hence could not "
                            "retrieve its properties.".format(attribute_name))
                status = False
        else:
            status = False
        return status

    def _perform_keypress(self, element, **kwargs):
        """
        This function expects to receive a browser instance through the
        "browser" argument and a key "keys" through the kwargs.

        The value for "keys" would be a list of keys tha need to pressed.

        """
        status = True
        flag = False
        keys = kwargs.get('keys')
        actions = ActionChains(element)
        for key in keys:
            try:
                selenium_key = KEYS[key.upper()]
            except KeyError:
                print_error("{0} is not supported by Selenium.".format(key))
                status = False
            else:
                flag = True
                actions.send_keys(selenium_key)
        if flag:
            actions.perform()
            sleep(2)

        return status
<|endoftext|>


<commit_before># Amara, universalsubtitles.org
#
# Copyright (C) 2014 Participatory Culture Foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see
# http://www.gnu.org/licenses/agpl-3.0.html.

"""externalsites.google -- Google API handling."""

from collections import namedtuple
from email.mime.multipart import MIMEMultipart, MIMEBase
from lxml import etree
import json
import logging
import urllib
import urlparse
import re

from django.conf import settings
from django.utils.translation import ugettext as _
import jwt
import requests
import pafy, isodate

from utils.subtitles import load_subtitles
from utils.text import fmt

class APIError(StandardError):
    """Error communicating with YouTube's API."""
    pass

class OAuthError(APIError):
    """Error handling YouTube's OAuth."""
    pass

OAuthCallbackData = namedtuple(
    'OAuthCallbackData', 'refresh_token access_token openid_id sub state')
YoutubeUserInfo = namedtuple('YoutubeUserInfo', 'channel_id username')
VideoInfo = namedtuple('VideoInfo',
                       'channel_id title description duration thumbnail_url')
OpenIDProfile = namedtuple('OpenIDProfile',
                           'sub email full_name first_name last_name')

logger = logging.getLogger('utils.youtube')

def youtube_scopes():
    return [
        "https://www.googleapis.com/auth/youtube",
        "https://www.googleapis.com/auth/youtube.force-ssl",
    ]

def request_token_url(redirect_uri, access_type, state, extra_scopes=()):
    """Get the URL to for the request token

    We should redirect the user's browser to this URL when trying to initiate
    OAuth authentication.

    The basic flow is that we send the user's browser to request_token_url,
    then youtube does it's OAuth stuff and sends the browser back to
    redirect_uri.  Then the calling code calls handle_callback() to process
    the OAuth data sent with the request to redirect_uri.

    :param redirect_uri: URI to redirect the user to after youtube
    authentication is complete
    :param state: dict of state info.  This will get returned back from
    handle_callback()
    """
    scopes = ['openid']
    scopes.extend(extra_scopes)
    redirect_uri_parsed = urlparse.urlparse(redirect_uri)

    params = {
        "client_id": settings.YOUTUBE_CLIENT_ID,
        "redirect_uri": redirect_uri,
        "scope": ' '.join(scopes),
        "state": json.dumps(state),
        "response_type": "code",
        "access_type": access_type,
        'openid.realm': "{}://{}/".format(redirect_uri_parsed.scheme,
                                          redirect_uri_parsed.netloc),
    }
    if access_type == 'offline':
        params["approval_prompt"] = "force"

    return ("https://accounts.google.com/o/oauth2/auth?" +
            urllib.urlencode(params))

def _oauth_token_post(**params):
    params["client_id"] = settings.YOUTUBE_CLIENT_ID
    params["client_secret"] = settings.YOUTUBE_CLIENT_SECRET

    response = requests.post("https://accounts.google.com/o/oauth2/token",
                             data=params, headers={
        "Content-Type": "application/x-www-form-urlencoded"
                             })

    if response.status_code != 200:
        logger.error("Error requesting Youtube OAuth token", extra={
                    "data": {
                        "sent_data": params,
                        "response": response.content
                    },
                })
        raise OAuthError('Authentication error')

    response_data = response.json()
    if response_data.get('error', None):
        logger.error("Error on requesting Youtube OAuth token", extra={
                    "data": {
                        "sent_data": params,
                        "response": response.content
                    },
                })
        raise OAuthError(response_data['error'])

    return response

def handle_callback(request, redirect_uri):
    """Handle the youtube oauth callback.

    :param request: djongo Request object
    :redirect_uri: same URI as as passed to request_token_url()

    :returns: OAuthCallbackData object
    """

    code = request.GET.get('code')
    error = request.GET.get('error')
    state = request.GET.get('state')

    if error is not None:
        raise OAuthError(fmt(_('Youtube error: %(error)s'), error=error))

    if code is None:
        logger.warn("handle_callback: no authorization code (%s)" %
                    request.GET)
        raise OAuthError(_('Error while linking.  Please try again.'))

    if state is not None:
        state = json.loads(state)

    # exchange the auth code for refresh/access tokens
    response = _oauth_token_post(code=code, grant_type='authorization_code',
                                 redirect_uri=redirect_uri)
    # decode the id_token.  We can skip verification since we used HTTPS to
    # connect to google
    response_data = response.json()
    token_data = jwt.decode(response_data['id_token'], verify=False)
    return OAuthCallbackData(
        response_data.get('refresh_token'),
        response_data['access_token'],
        token_data['openid_id'],
        token_data['sub'],
        state,
    )

def get_new_access_token(refresh_token):
    response = _oauth_token_post(grant_type='refresh_token',
                                 refresh_token=refresh_token)
    return response.json()['access_token']

def revoke_auth_token(refresh_token):
    requests.get('https://accounts.google.com/o/oauth2/revoke',
                 params={'token': refresh_token})

def multipart_format(parts):
    """Make a multipart message

    Args:
        parts: list of (content_type, data) tuples

    Returns:
        (headers, data) tuple
    """
    multi_message = MIMEMultipart('related')
    for content_type, data in parts:
        msg = MIMEBase(*content_type.split('/', 1))
        msg.set_payload(data)
        multi_message.attach(msg)

    body_lines = []
    in_headers = True
    last_key = None
    headers = {}
    for line in multi_message.as_string().splitlines(True):
        if in_headers:
            if line == '\n':
                in_headers = False
            elif line.startswith(' ') and last_key:
                headers[last_key] += line.rstrip()
            else:
                key, value = line[:-1].split(':')
                headers[key] = value.strip()
                last_key = key
        else:
            body_lines.append(line)
    return headers, ''.join(body_lines)

def _make_api_request(method, access_token, url, **kwargs):
    """Make a youtube API request

    Args:
        method: HTTP method to use
        access_token: access token to use, or None for APIs that don't need
            authentication
        **kwargs: args to send to requests.request()
    """
    if access_token is not None:
        if 'headers' not in kwargs:
            kwargs['headers'] = {}
        kwargs['headers']['Authorization'] = 'Bearer %s' % access_token
    else:
        if 'params' not in kwargs:
            kwargs['params'] = {}
        kwargs['params']['key'] = settings.YOUTUBE_API_KEY
    response = requests.request(method, url, **kwargs)
    if method == 'delete':
        expected_status_code = 204
    else:
        expected_status_code = 200
    if response.status_code != expected_status_code:
        try:
            errors = response.json()['error']['errors']
            message = ' '.join(e['reason'] for e in errors)
        except StandardError, e:
            logger.error("%s parsing youtube response (%s): %s" % (
                e, response.status_code, response.content))
            message = 'Unkown error'
        raise APIError(message)
    return response

def _make_youtube_api_request(method, access_token, url_path, **kwargs):
    url = 'https://www.googleapis.com/youtube/v3/' + url_path
    return _make_api_request(method, access_token, url, **kwargs)

def _make_youtube_upload_api_request(method, access_token, url_path, **kwargs):
    url = 'https://www.googleapis.com/upload/youtube/v3/' + url_path
    return _make_api_request(method, access_token, url, **kwargs)

def channel_get(access_token, part, channel_id=None):
    params = { 'part': ','.join(part) }
    if channel_id is None:
        params['mine'] = 'true'
    else:
        params['id'] = channel_id
    return _make_youtube_api_request('get', access_token, 'channels',
                                     params=params)

def video_get(access_token, video_id, part):
    return _make_youtube_api_request('get', access_token, 'videos', params={
        'id': video_id,
        'part': ','.join(part),
    })

def get_uploads_playlist_id(channel_id):
    response = channel_get(None, ['contentDetails'], channel_id)
    content_details = response.json()['items'][0]['contentDetails']
    return content_details['relatedPlaylists']['uploads']

def get_uploaded_video_ids(channel_id):
    playlist_id = get_uploads_playlist_id(channel_id)

    params = {
        'part': 'snippet',
        'playlistId': playlist_id
    }
    response = _make_youtube_api_request('get', None, 'playlistItems',
                                         params=params)
    rv = []
    for item in response.json()['items']:
        resource_id = item['snippet']['resourceId']
        if resource_id['kind'] == 'youtube#video':
            rv.append(resource_id['videoId'])
    return rv

def captions_list(access_token, video_id):
    """Fetch info on all non-ASR captions for a video

    Returns:
        List of (caption_id, language_code, name) tuples
    """
    response = _make_youtube_api_request(
        'get', access_token, 'captions', params={
            'videoId': video_id,
            'part': 'id,snippet',
        })
    return [
        (caption['id'], caption['snippet']['language'],
         caption['snippet']['name'])
        for caption in response.json()['items']
        if caption['snippet']['trackKind'] != 'ASR'
    ]

def captions_download(access_token, caption_id, format='ttml'):
    """Download a caption file."""
    response = _make_youtube_api_request('get', access_token,
                                         'captions/{}'.format(caption_id),
                                         params={'tfmt': format})
    return response.content

def captions_insert(access_token, video_id, language_code,
                    sub_content_type, sub_data):
    """Download a caption file."""
    caption_data = json.dumps({
        'snippet': {
            'videoId': video_id,
            'language': language_code,
            'name': '',
        }
    })

    headers, data = multipart_format([
        ('application/json', caption_data),
        (sub_content_type, sub_data)
    ])

    params = {
        'uploadType': 'multipart',
        'part': 'snippet',
    }
    response = _make_youtube_upload_api_request(
        'post', access_token, 'captions', params=params,
        headers=headers, data=data)
    return response.content

def captions_update(access_token, caption_id, sub_content_type, sub_data):
    """Download a caption file."""
    caption_data = json.dumps({
        'id': caption_id,
    })

    headers, data = multipart_format([
        ('application/json', caption_data),
        (sub_content_type, sub_data)
    ])

    params = {
        'uploadType': 'multipart',
        'part': 'id',
    }
    response = _make_youtube_upload_api_request(
        'put', access_token, 'captions', params=params,
        headers=headers, data=data)
    return response.content

def captions_delete(access_token, caption_id):
    response = _make_youtube_api_request(
        'delete', access_token, 'captions', params={'id': caption_id})
    return response.content

def video_put(access_token, video_id, **data):
    part = '.'.join(data.keys())
    data['id'] = video_id
    return _make_youtube_api_request('put', access_token, 'videos', params={
        'part': part,
    }, data=json.dumps(data), headers={
        'content-type': 'application/json'
    })

def get_youtube_user_info(access_token):
    """Get info about a user logged in with access_token

    google/youtube have this concept of "channel IDs" which uniquely identify
    users across youtube, google+, and presumably new services that get added.
    Alongside the channel ID is the channel title, which is a human-friendly
    name to display to the user.

    :returns: (channel_id, display_name) tuple
    """
    response = channel_get(access_token, part=['id','snippet'])
    channel = response.json()['items'][0]
    return YoutubeUserInfo(channel['id'], channel['snippet']['title'])

def get_openid_profile(access_token):
    """Get OpenID info from an access token

    :returns: OpenIDProfile
    """
    url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'
    response = _make_api_request('get', access_token, url)
    response_data = response.json()
    return OpenIDProfile(
        response_data['sub'],
        response_data['email'],
        response_data.get('name', ''),
        response_data.get('given_name', ''),
        response_data.get('family_name', ''),
    )

def get_video_info(video_id):
    try:
        logger.info("youtube.get_video_info()", extra={
            'stack': True,
        })
        return _get_video_info(video_id)
    except APIError as e:
        logger.error("Youtube API Error: %s, falling back", e)
        try:
            p = pafy.new(video_id)
            return VideoInfo(None, p.title, "", p.length, p.thumb)
        except:
            raise e

def _get_video_info(video_id):
    response = video_get(None, video_id, ['snippet', 'contentDetails'])
    try:
        response_data = response.json()
        snippet = response_data['items'][0]['snippet']
        content_details = response_data['items'][0]['contentDetails']
        return VideoInfo(snippet['channelId'],
                         snippet['title'],
                         snippet['description'],
                         isodate.parse_duration(content_details['duration']).total_seconds(),
                         snippet['thumbnails']['high']['url'])
    except StandardError, e:
        raise APIError("get_video_info: Unexpected content: %s" % e)

def get_direct_url_to_audio(video_id):
    """
    It does a request to google to retrieve the URL
    So that should be done in a backgound task
    """
    return pafy.new(video_id).getbestaudio().url

def update_video_description(video_id, access_token, description):
    # get the current snippet for the video
    response = video_get(access_token, video_id, ['snippet'])
    snippet = response.json()['items'][0]['snippet']
    # send back the snippet with the new description
    snippet['description'] = description
    video_put(access_token, video_id, snippet=snippet)
<commit_msg>Import more videos from youtube (#2456)<commit_after># Amara, universalsubtitles.org
#
# Copyright (C) 2014 Participatory Culture Foundation
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see
# http://www.gnu.org/licenses/agpl-3.0.html.

"""externalsites.google -- Google API handling."""

from collections import namedtuple
from email.mime.multipart import MIMEMultipart, MIMEBase
from lxml import etree
import json
import logging
import urllib
import urlparse
import re

from django.conf import settings
from django.utils.translation import ugettext as _
import jwt
import requests
import pafy, isodate

from utils.subtitles import load_subtitles
from utils.text import fmt

class APIError(StandardError):
    """Error communicating with YouTube's API."""
    pass

class OAuthError(APIError):
    """Error handling YouTube's OAuth."""
    pass

OAuthCallbackData = namedtuple(
    'OAuthCallbackData', 'refresh_token access_token openid_id sub state')
YoutubeUserInfo = namedtuple('YoutubeUserInfo', 'channel_id username')
VideoInfo = namedtuple('VideoInfo',
                       'channel_id title description duration thumbnail_url')
OpenIDProfile = namedtuple('OpenIDProfile',
                           'sub email full_name first_name last_name')

logger = logging.getLogger('utils.youtube')

def youtube_scopes():
    return [
        "https://www.googleapis.com/auth/youtube",
        "https://www.googleapis.com/auth/youtube.force-ssl",
    ]

def request_token_url(redirect_uri, access_type, state, extra_scopes=()):
    """Get the URL to for the request token

    We should redirect the user's browser to this URL when trying to initiate
    OAuth authentication.

    The basic flow is that we send the user's browser to request_token_url,
    then youtube does it's OAuth stuff and sends the browser back to
    redirect_uri.  Then the calling code calls handle_callback() to process
    the OAuth data sent with the request to redirect_uri.

    :param redirect_uri: URI to redirect the user to after youtube
    authentication is complete
    :param state: dict of state info.  This will get returned back from
    handle_callback()
    """
    scopes = ['openid']
    scopes.extend(extra_scopes)
    redirect_uri_parsed = urlparse.urlparse(redirect_uri)

    params = {
        "client_id": settings.YOUTUBE_CLIENT_ID,
        "redirect_uri": redirect_uri,
        "scope": ' '.join(scopes),
        "state": json.dumps(state),
        "response_type": "code",
        "access_type": access_type,
        'openid.realm': "{}://{}/".format(redirect_uri_parsed.scheme,
                                          redirect_uri_parsed.netloc),
    }
    if access_type == 'offline':
        params["approval_prompt"] = "force"

    return ("https://accounts.google.com/o/oauth2/auth?" +
            urllib.urlencode(params))

def _oauth_token_post(**params):
    params["client_id"] = settings.YOUTUBE_CLIENT_ID
    params["client_secret"] = settings.YOUTUBE_CLIENT_SECRET

    response = requests.post("https://accounts.google.com/o/oauth2/token",
                             data=params, headers={
        "Content-Type": "application/x-www-form-urlencoded"
                             })

    if response.status_code != 200:
        logger.error("Error requesting Youtube OAuth token", extra={
                    "data": {
                        "sent_data": params,
                        "response": response.content
                    },
                })
        raise OAuthError('Authentication error')

    response_data = response.json()
    if response_data.get('error', None):
        logger.error("Error on requesting Youtube OAuth token", extra={
                    "data": {
                        "sent_data": params,
                        "response": response.content
                    },
                })
        raise OAuthError(response_data['error'])

    return response

def handle_callback(request, redirect_uri):
    """Handle the youtube oauth callback.

    :param request: djongo Request object
    :redirect_uri: same URI as as passed to request_token_url()

    :returns: OAuthCallbackData object
    """

    code = request.GET.get('code')
    error = request.GET.get('error')
    state = request.GET.get('state')

    if error is not None:
        raise OAuthError(fmt(_('Youtube error: %(error)s'), error=error))

    if code is None:
        logger.warn("handle_callback: no authorization code (%s)" %
                    request.GET)
        raise OAuthError(_('Error while linking.  Please try again.'))

    if state is not None:
        state = json.loads(state)

    # exchange the auth code for refresh/access tokens
    response = _oauth_token_post(code=code, grant_type='authorization_code',
                                 redirect_uri=redirect_uri)
    # decode the id_token.  We can skip verification since we used HTTPS to
    # connect to google
    response_data = response.json()
    token_data = jwt.decode(response_data['id_token'], verify=False)
    return OAuthCallbackData(
        response_data.get('refresh_token'),
        response_data['access_token'],
        token_data['openid_id'],
        token_data['sub'],
        state,
    )

def get_new_access_token(refresh_token):
    response = _oauth_token_post(grant_type='refresh_token',
                                 refresh_token=refresh_token)
    return response.json()['access_token']

def revoke_auth_token(refresh_token):
    requests.get('https://accounts.google.com/o/oauth2/revoke',
                 params={'token': refresh_token})

def multipart_format(parts):
    """Make a multipart message

    Args:
        parts: list of (content_type, data) tuples

    Returns:
        (headers, data) tuple
    """
    multi_message = MIMEMultipart('related')
    for content_type, data in parts:
        msg = MIMEBase(*content_type.split('/', 1))
        msg.set_payload(data)
        multi_message.attach(msg)

    body_lines = []
    in_headers = True
    last_key = None
    headers = {}
    for line in multi_message.as_string().splitlines(True):
        if in_headers:
            if line == '\n':
                in_headers = False
            elif line.startswith(' ') and last_key:
                headers[last_key] += line.rstrip()
            else:
                key, value = line[:-1].split(':')
                headers[key] = value.strip()
                last_key = key
        else:
            body_lines.append(line)
    return headers, ''.join(body_lines)

def _make_api_request(method, access_token, url, **kwargs):
    """Make a youtube API request

    Args:
        method: HTTP method to use
        access_token: access token to use, or None for APIs that don't need
            authentication
        **kwargs: args to send to requests.request()
    """
    if access_token is not None:
        if 'headers' not in kwargs:
            kwargs['headers'] = {}
        kwargs['headers']['Authorization'] = 'Bearer %s' % access_token
    else:
        if 'params' not in kwargs:
            kwargs['params'] = {}
        kwargs['params']['key'] = settings.YOUTUBE_API_KEY
    response = requests.request(method, url, **kwargs)
    if method == 'delete':
        expected_status_code = 204
    else:
        expected_status_code = 200
    if response.status_code != expected_status_code:
        try:
            errors = response.json()['error']['errors']
            message = ' '.join(e['reason'] for e in errors)
        except StandardError, e:
            logger.error("%s parsing youtube response (%s): %s" % (
                e, response.status_code, response.content))
            message = 'Unkown error'
        raise APIError(message)
    return response

def _make_youtube_api_request(method, access_token, url_path, **kwargs):
    url = 'https://www.googleapis.com/youtube/v3/' + url_path
    return _make_api_request(method, access_token, url, **kwargs)

def _make_youtube_upload_api_request(method, access_token, url_path, **kwargs):
    url = 'https://www.googleapis.com/upload/youtube/v3/' + url_path
    return _make_api_request(method, access_token, url, **kwargs)

def channel_get(access_token, part, channel_id=None):
    params = { 'part': ','.join(part) }
    if channel_id is None:
        params['mine'] = 'true'
    else:
        params['id'] = channel_id
    return _make_youtube_api_request('get', access_token, 'channels',
                                     params=params)

def video_get(access_token, video_id, part):
    return _make_youtube_api_request('get', access_token, 'videos', params={
        'id': video_id,
        'part': ','.join(part),
    })

def get_uploads_playlist_id(channel_id):
    response = channel_get(None, ['contentDetails'], channel_id)
    content_details = response.json()['items'][0]['contentDetails']
    return content_details['relatedPlaylists']['uploads']

def get_uploaded_video_ids(channel_id):
    MAX_ITEMS = 1000

    playlist_id = get_uploads_playlist_id(channel_id)
    results, next_page_token = _get_uploaded_video_ids(playlist_id,
                                                               None)
    while next_page_token and len(results) < MAX_ITEMS:
        more_results, next_page_token = _get_uploaded_video_ids(
            playlist_id, next_page_token)
        results.extend(more_results)
    return results

def _get_uploaded_video_ids(playlist_id, page_token):
    """Fetches one page of results for get_uploaded_video_ids()."""
    params = {
        'part': 'snippet',
        'playlistId': playlist_id,
        'maxResults': 50,
    }
    if page_token:
        params['pageToken'] = page_token
    response = _make_youtube_api_request('get', None, 'playlistItems',
                                         params=params)
    response_data = response.json()
    rv = []
    for item in response_data['items']:
        resource_id = item['snippet']['resourceId']
        if resource_id['kind'] == 'youtube#video':
            rv.append(resource_id['videoId'])
    return rv, response_data.get('nextPageToken')

def captions_list(access_token, video_id):
    """Fetch info on all non-ASR captions for a video

    Returns:
        List of (caption_id, language_code, name) tuples
    """
    response = _make_youtube_api_request(
        'get', access_token, 'captions', params={
            'videoId': video_id,
            'part': 'id,snippet',
        })
    return [
        (caption['id'], caption['snippet']['language'],
         caption['snippet']['name'])
        for caption in response.json()['items']
        if caption['snippet']['trackKind'] != 'ASR'
    ]

def captions_download(access_token, caption_id, format='ttml'):
    """Download a caption file."""
    response = _make_youtube_api_request('get', access_token,
                                         'captions/{}'.format(caption_id),
                                         params={'tfmt': format})
    return response.content

def captions_insert(access_token, video_id, language_code,
                    sub_content_type, sub_data):
    """Download a caption file."""
    caption_data = json.dumps({
        'snippet': {
            'videoId': video_id,
            'language': language_code,
            'name': '',
        }
    })

    headers, data = multipart_format([
        ('application/json', caption_data),
        (sub_content_type, sub_data)
    ])

    params = {
        'uploadType': 'multipart',
        'part': 'snippet',
    }
    response = _make_youtube_upload_api_request(
        'post', access_token, 'captions', params=params,
        headers=headers, data=data)
    return response.content

def captions_update(access_token, caption_id, sub_content_type, sub_data):
    """Download a caption file."""
    caption_data = json.dumps({
        'id': caption_id,
    })

    headers, data = multipart_format([
        ('application/json', caption_data),
        (sub_content_type, sub_data)
    ])

    params = {
        'uploadType': 'multipart',
        'part': 'id',
    }
    response = _make_youtube_upload_api_request(
        'put', access_token, 'captions', params=params,
        headers=headers, data=data)
    return response.content

def captions_delete(access_token, caption_id):
    response = _make_youtube_api_request(
        'delete', access_token, 'captions', params={'id': caption_id})
    return response.content

def video_put(access_token, video_id, **data):
    part = '.'.join(data.keys())
    data['id'] = video_id
    return _make_youtube_api_request('put', access_token, 'videos', params={
        'part': part,
    }, data=json.dumps(data), headers={
        'content-type': 'application/json'
    })

def get_youtube_user_info(access_token):
    """Get info about a user logged in with access_token

    google/youtube have this concept of "channel IDs" which uniquely identify
    users across youtube, google+, and presumably new services that get added.
    Alongside the channel ID is the channel title, which is a human-friendly
    name to display to the user.

    :returns: (channel_id, display_name) tuple
    """
    response = channel_get(access_token, part=['id','snippet'])
    channel = response.json()['items'][0]
    return YoutubeUserInfo(channel['id'], channel['snippet']['title'])

def get_openid_profile(access_token):
    """Get OpenID info from an access token

    :returns: OpenIDProfile
    """
    url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'
    response = _make_api_request('get', access_token, url)
    response_data = response.json()
    return OpenIDProfile(
        response_data['sub'],
        response_data['email'],
        response_data.get('name', ''),
        response_data.get('given_name', ''),
        response_data.get('family_name', ''),
    )

def get_video_info(video_id):
    try:
        logger.info("youtube.get_video_info()", extra={
            'stack': True,
        })
        return _get_video_info(video_id)
    except APIError as e:
        logger.error("Youtube API Error: %s, falling back", e)
        try:
            p = pafy.new(video_id)
            return VideoInfo(None, p.title, "", p.length, p.thumb)
        except:
            raise e

def _get_video_info(video_id):
    response = video_get(None, video_id, ['snippet', 'contentDetails'])
    try:
        response_data = response.json()
        snippet = response_data['items'][0]['snippet']
        content_details = response_data['items'][0]['contentDetails']
        return VideoInfo(snippet['channelId'],
                         snippet['title'],
                         snippet['description'],
                         isodate.parse_duration(content_details['duration']).total_seconds(),
                         snippet['thumbnails']['high']['url'])
    except StandardError, e:
        raise APIError("get_video_info: Unexpected content: %s" % e)

def get_direct_url_to_audio(video_id):
    """
    It does a request to google to retrieve the URL
    So that should be done in a backgound task
    """
    return pafy.new(video_id).getbestaudio().url

def update_video_description(video_id, access_token, description):
    # get the current snippet for the video
    response = video_get(access_token, video_id, ['snippet'])
    snippet = response.json()['items'][0]['snippet']
    # send back the snippet with the new description
    snippet['description'] = description
    video_put(access_token, video_id, snippet=snippet)
<|endoftext|>


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

"""Runs plain netperf in a few modes.

docs:
http://www.netperf.org/svn/netperf2/tags/netperf-2.4.5/doc/netperf.html#TCP_005fRR
manpage: http://manpages.ubuntu.com/manpages/maverick/man1/netperf.1.html

Runs TCP_RR, TCP_CRR, and TCP_STREAM benchmarks from netperf across two
machines.
"""

import csv
import io
import json
import logging

from perfkitbenchmarker import configs
from perfkitbenchmarker import flags
from perfkitbenchmarker import sample
from perfkitbenchmarker import vm_util
from perfkitbenchmarker.linux_packages import netperf

flags.DEFINE_integer('netperf_max_iter', None,
                     'Maximum number of iterations to run during '
                     'confidence interval estimation. If unset, '
                     'a single iteration will be run.',
                     lower_bound=3, upper_bound=30)

flags.DEFINE_integer('netperf_test_length', 60,
                     'netperf test length, in seconds',
                     lower_bound=1)
flags.DEFINE_bool('netperf_enable_histograms', True,
                  'Determines whether latency histograms are '
                  'collected/reported.')

ALL_BENCHMARKS = ['TCP_RR', 'TCP_CRR', 'TCP_STREAM', 'UDP_RR']
flags.DEFINE_list('netperf_benchmarks', ALL_BENCHMARKS,
                  'The netperf benchmark(s) to run.')
flags.RegisterValidator(
    'netperf_benchmarks',
    lambda benchmarks: benchmarks and set(benchmarks).issubset(ALL_BENCHMARKS))

FLAGS = flags.FLAGS

BENCHMARK_NAME = 'netperf'
BENCHMARK_CONFIG = """
netperf:
  description: Run TCP_RR, TCP_CRR, UDP_RR and TCP_STREAM
  vm_groups:
    vm_1:
      vm_spec: *default_single_core
    vm_2:
      vm_spec: *default_single_core
"""

MBPS = 'Mbits/sec'
TRANSACTIONS_PER_SECOND = 'transactions_per_second'

COMMAND_PORT = 20000
DATA_PORT = 20001

def GetConfig(user_config):
  return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME)

def PrepareNetperf(vm):
  """Installs netperf on a single vm."""
  vm.Install('netperf')

def Prepare(benchmark_spec):
  """Install netperf on the target vm.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.
  """
  vms = benchmark_spec.vms
  vms = vms[:2]
  vm_util.RunThreaded(PrepareNetperf, vms)

  if vm_util.ShouldRunOnExternalIpAddress():
    vms[1].AllowPort(COMMAND_PORT)
    vms[1].AllowPort(DATA_PORT)

  vms[1].RemoteCommand('%s -p %s' %
                       (netperf.NETSERVER_PATH, COMMAND_PORT))

def RunNetperf(vm, benchmark_name, server_ip):
  """Spawns netperf on a remove VM, parses results.

  Args:
    vm: The VM that the netperf TCP_RR benchmark will be run upon.
    benchmark_name: The netperf benchmark to run, see the documentation.
    server_ip: A machine that is running netserver.

  Returns:
    A sample.Sample object with the result.
  """
  # Flags:
  # -o specifies keys to include in CSV output.
  # -j keeps additional latency numbers
  # -v sets the verbosity level so that netperf will print out histograms
  # -I specifies the confidence % and width - here 99% confidence that the true
  #    value is within +/- 2.5% of the reported value
  # -i specifies the maximum and minimum number of iterations.
  confidence = ('-I 99,5 -i {0},3'.format(FLAGS.netperf_max_iter)
                if FLAGS.netperf_max_iter else '')
  verbosity = '-v2 ' if FLAGS.netperf_enable_histograms else ''
  netperf_cmd = ('{netperf_path} -p {command_port} -j {verbosity}'
                 '-t {benchmark_name} -H {server_ip} -l {length} {confidence} '
                 ' -- '
                 '-P {data_port} '
                 '-o THROUGHPUT,THROUGHPUT_UNITS,P50_LATENCY,P90_LATENCY,'
                 'P99_LATENCY,STDDEV_LATENCY,'
                 'MIN_LATENCY,MAX_LATENCY,'
                 'CONFIDENCE_ITERATION,THROUGHPUT_CONFID').format(
                     netperf_path=netperf.NETPERF_PATH,
                     benchmark_name=benchmark_name,
                     server_ip=server_ip, command_port=COMMAND_PORT,
                     data_port=DATA_PORT,
                     length=FLAGS.netperf_test_length,
                     confidence=confidence, verbosity=verbosity)
  stdout, _ = vm.RemoteCommand(netperf_cmd,
                               timeout=2 * FLAGS.netperf_test_length *
                               (FLAGS.netperf_max_iter or 1))

  fp = io.StringIO(stdout)
  # "-o" flag above specifies CSV output, but there is one extra header line:
  banner = next(fp)
  assert banner.startswith('MIGRATED'), stdout
  r = csv.DictReader(fp)
  row = next(r)
  logging.info('Netperf Results: %s', row)
  assert 'Throughput' in row, row

  value = float(row['Throughput'])
  unit = {'Trans/s': TRANSACTIONS_PER_SECOND,
          '10^6bits/s': MBPS}[row['Throughput Units']]
  if unit == MBPS:
    metric = '%s_Throughput' % benchmark_name
  else:
    metric = '%s_Transaction_Rate' % benchmark_name

  meta_keys = [('Confidence Iterations Run', 'confidence_iter'),
               ('Throughput Confidence Width (%)', 'confidence_width_percent')]
  metadata = {meta_key: row[np_key] for np_key, meta_key in meta_keys}
  metadata.update(netperf_test_length=FLAGS.netperf_test_length,
                  max_iter=FLAGS.netperf_max_iter or 1)

  samples = [sample.Sample(metric, value, unit, metadata)]

  # No tail latency for throughput.
  if unit == MBPS:
    return samples

  if FLAGS.netperf_enable_histograms:
    # Generate a sample containing the entire histogram of
    # latencies.
    hist = netperf.ParseHistogram(stdout)
    hist_metadata = {'histogram': json.dumps(hist)}
    hist_metadata.update(metadata)
    samples.append(sample.Sample(
        '%s_Latency_Histogram' % benchmark_name, 0, 'us', hist_metadata))

  for metric_key, metric_name in [
      ('50th Percentile Latency Microseconds', 'p50'),
      ('90th Percentile Latency Microseconds', 'p90'),
      ('99th Percentile Latency Microseconds', 'p99'),
      ('Minimum Latency Microseconds', 'min'),
      ('Maximum Latency Microseconds', 'max'),
      ('Stddev Latency Microseconds', 'stddev')]:
    samples.append(
        sample.Sample('%s_Latency_%s' % (benchmark_name, metric_name),
                      float(row[metric_key]), 'us', metadata))
  return samples

def Run(benchmark_spec):
  """Run netperf TCP_RR on the target vm.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.

  Returns:
    A list of sample.Sample objects.
  """
  vms = benchmark_spec.vms
  client_vm = vms[0]
  server_vm = vms[1]
  logging.info('netperf running on %s', client_vm)
  results = []
  metadata = {'ip_type': 'external'}
  for vm_specifier, vm in ('receiving', server_vm), ('sending', client_vm):
    metadata['{0}_zone'.format(vm_specifier)] = vm.zone
    for k, v in vm.GetMachineTypeDict().iteritems():
      metadata['{0}_{1}'.format(vm_specifier, k)] = v

  for netperf_benchmark in FLAGS.netperf_benchmarks:

    if vm_util.ShouldRunOnExternalIpAddress():
      external_ip_results = RunNetperf(client_vm, netperf_benchmark,
                                       server_vm.ip_address)
      for external_ip_result in external_ip_results:
        external_ip_result.metadata.update(metadata)
      results.extend(external_ip_results)

    if vm_util.ShouldRunOnInternalIpAddress(client_vm, server_vm):
      internal_ip_results = RunNetperf(client_vm, netperf_benchmark,
                                       server_vm.internal_ip)
      for internal_ip_result in internal_ip_results:
        internal_ip_result.metadata.update(metadata)
        internal_ip_result.metadata['ip_type'] = 'internal'
      results.extend(internal_ip_results)

  return results

def Cleanup(benchmark_spec):
  """Cleanup netperf on the target vm (by uninstalling).

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.
  """
  vms = benchmark_spec.vms
  vms[1].RemoteCommand('sudo pkill netserver')
<commit_msg>Initial commit of multithreaded netperf. Handles single thread case correctly. Still need to aggregate data for multithreaded case<commit_after># Copyright 2014 PerfKitBenchmarker Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Runs plain netperf in a few modes.

docs:
http://www.netperf.org/svn/netperf2/tags/netperf-2.4.5/doc/netperf.html#TCP_005fRR
manpage: http://manpages.ubuntu.com/manpages/maverick/man1/netperf.1.html

Runs TCP_RR, TCP_CRR, and TCP_STREAM benchmarks from netperf across two
machines.
"""

import csv
import io
import json
import logging
import threading

from perfkitbenchmarker import configs
from perfkitbenchmarker import flags
from perfkitbenchmarker import sample
from perfkitbenchmarker import vm_util
from perfkitbenchmarker.linux_packages import netperf

flags.DEFINE_integer('netperf_max_iter', None,
                     'Maximum number of iterations to run during '
                     'confidence interval estimation. If unset, '
                     'a single iteration will be run.',
                     lower_bound=3, upper_bound=30)

flags.DEFINE_integer('netperf_test_length', 60,
                     'netperf test length, in seconds',
                     lower_bound=1)
flags.DEFINE_bool('netperf_enable_histograms', True,
                  'Determines whether latency histograms are '
                  'collected/reported. Only for *RR benchmarks')
flags.DEFINE_integer('netperf_num_streams', 1,
                     'Number of netperf processes to run.')

ALL_BENCHMARKS = ['TCP_RR', 'TCP_CRR', 'TCP_STREAM', 'UDP_RR']
flags.DEFINE_list('netperf_benchmarks', ALL_BENCHMARKS,
                  'The netperf benchmark(s) to run.')
flags.RegisterValidator(
    'netperf_benchmarks',
    lambda benchmarks: benchmarks and set(benchmarks).issubset(ALL_BENCHMARKS))

FLAGS = flags.FLAGS

BENCHMARK_NAME = 'netperf'
BENCHMARK_CONFIG = """
netperf:
  description: Run TCP_RR, TCP_CRR, UDP_RR and TCP_STREAM
  vm_groups:
    vm_1:
      vm_spec: *default_single_core
    vm_2:
      vm_spec: *default_single_core
"""

MBPS = 'Mbits/sec'
TRANSACTIONS_PER_SECOND = 'transactions_per_second'

COMMAND_PORT = 20000
DATA_PORT = 20001

def GetConfig(user_config):
  return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME)

def PrepareNetperf(vm):
  """Installs netperf on a single vm."""
  vm.Install('netperf')

def Prepare(benchmark_spec):
  """Install netperf on the target vm.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.
  """
  vms = benchmark_spec.vms
  vms = vms[:2]
  vm_util.RunThreaded(PrepareNetperf, vms)

  if vm_util.ShouldRunOnExternalIpAddress():
    vms[1].AllowPort(COMMAND_PORT)
    vms[1].AllowPort(DATA_PORT)

  vms[1].RemoteCommand('%s -p %s' %
                       (netperf.NETSERVER_PATH, COMMAND_PORT))

def _ParseNetperfOutput(stdout, metadata, benchmark_name):
  """Parses the stdout of a single netperf process.

  Args:
    stdout: the stdout of the netperf process
    metadata: metadata for any sample.Sample objects we create

  Returns:
    A tuple containing (throughput_sample, latency_samples, latency_histogram)
  """
  # Don't modify the metadata dict that was passed in
  metadata = metadata.copy()

  fp = io.StringIO(stdout)
  # "-o" flag above specifies CSV output, but there is one extra header line:
  banner = next(fp)
  assert banner.startswith('MIGRATED'), stdout
  r = csv.DictReader(fp)
  results = next(r)
  logging.info('Netperf Results: %s', results)
  assert 'Throughput' in results

  # Create the throughput sample
  throughput = float(results['Throughput'])
  unit = {'Trans/s': TRANSACTIONS_PER_SECOND, # *_RR
          '10^6bits/s': MBPS}[results['Throughput Units']] # TCP_STREAM
  if unit == MBPS:
    metric = '%s_Throughput' % benchmark_name
  else:
    metric = '%s_Transaction_Rate' % benchmark_name
  meta_keys = [('Confidence Iterations Run', 'confidence_iter'),
               ('Throughput Confidence Width (%)', 'confidence_width_percent')]
  metadata.update({meta_key: results[np_key] for np_key, meta_key in meta_keys})
  throughput_sample = sample.Sample(metric, throughput, unit, metadata)

  # No tail latency for throughput.
  if unit == MBPS:
    return (throughput_sample, [], None)

  hist = None
  latency_samples = []
  if FLAGS.netperf_enable_histograms:
    # Parse the latency histogram. {latency: response_latency} where latency is
    # the latency in microseconds with only 1 significant figure.
    latency_hist = netperf.ParseHistogram(stdout)
    hist_metadata = {'histogram': json.dumps(latency_hist)}
    hist_metadata.update(metadata)
    latency_samples.append(sample.Sample(
        '%s_Latency_Histogram' % benchmark_name, 0, 'us', hist_metadata))

  for metric_key, metric_name in [
      ('50th Percentile Latency Microseconds', 'p50'),
      ('90th Percentile Latency Microseconds', 'p90'),
      ('99th Percentile Latency Microseconds', 'p99'),
      ('Minimum Latency Microseconds', 'min'),
      ('Maximum Latency Microseconds', 'max'),
      ('Stddev Latency Microseconds', 'stddev')]:
    latency_samples.append(
        sample.Sample('%s_Latency_%s' % (benchmark_name, metric_name),
                      float(results[metric_key]), 'us', metadata))

  return (throughput_sample, latency_samples, latency_hist)

def RunNetperf(vm, benchmark_name, server_ip, num_streams):
  """Spawns netperf on a remote VM, parses results.

  Args:
    vm: The VM that the netperf TCP_RR benchmark will be run upon.
    benchmark_name: The netperf benchmark to run, see the documentation.
    server_ip: A machine that is running netserver.
    num_streams: The number of netperf client threads to run.

  Returns:
    A sample.Sample object with the result.
  """
  # Flags:
  # -o specifies keys to include in CSV output.
  # -j keeps additional latency numbers
  # -v sets the verbosity level so that netperf will print out histograms
  # -I specifies the confidence % and width - here 99% confidence that the true
  #    value is within +/- 2.5% of the reported value
  # -i specifies the maximum and minimum number of iterations.
  confidence = ('-I 99,5 -i {0},3'.format(FLAGS.netperf_max_iter)
                if FLAGS.netperf_max_iter else '')
  verbosity = '-v2 ' if FLAGS.netperf_enable_histograms or num_streams > 1 \
                     else ''
  netperf_cmd = ('{netperf_path} -p {command_port} -j {verbosity}'
                 '-t {benchmark_name} -H {server_ip} -l {length} {confidence} '
                 ' -- '
                 '-P {data_port} '
                 '-o THROUGHPUT,THROUGHPUT_UNITS,P50_LATENCY,P90_LATENCY,'
                 'P99_LATENCY,STDDEV_LATENCY,'
                 'MIN_LATENCY,MAX_LATENCY,'
                 'CONFIDENCE_ITERATION,THROUGHPUT_CONFID').format(
                     netperf_path=netperf.NETPERF_PATH,
                     benchmark_name=benchmark_name,
                     server_ip=server_ip, command_port=COMMAND_PORT,
                     data_port=DATA_PORT,
                     length=FLAGS.netperf_test_length,
                     confidence=confidence, verbosity=verbosity)

  # Run all of the netperf processes and collect their stdout
  # TODO: Record start times of netperf processes on the remote machine
  stdouts = [None for _ in range(num_streams)]
  def NetperfThread(i):
    stdout, _ = vm.RemoteCommand(netperf_cmd,
                                 timeout=2 * FLAGS.netperf_test_length *
                                 (FLAGS.netperf_max_iter or 1))
    stdouts[i] = stdout

  threads = [threading.Thread(target=NetperfThread, args=(i,))
             for i in range(num_streams)]
  for thread in threads:
    thread.start()
  for thread in threads:
    thread.join()

  metadata = {'netperf_test_length': FLAGS.netperf_test_length,
              'max_iter': FLAGS.netperf_max_iter or 1}

  parsed_output = [_ParseNetperfOutput(stdout, metadata, benchmark_name) for stdout in stdouts]

  if len(parsed_output) == 1:
    # Only 1 netperf thread
    (throughput_sample, latency_samples, histogram) = parsed_output[0]
    return [throughput_sample] + latency_samples
  else:
    # Multiple netperf threads
    pass

def Run(benchmark_spec):
  """Run netperf TCP_RR on the target vm.

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.

  Returns:
    A list of sample.Sample objects.
  """
  vms = benchmark_spec.vms
  client_vm = vms[0]
  server_vm = vms[1]
  logging.info('netperf running on %s', client_vm)
  results = []
  metadata = {'ip_type': 'external'}
  for vm_specifier, vm in ('receiving', server_vm), ('sending', client_vm):
    metadata['{0}_zone'.format(vm_specifier)] = vm.zone
    for k, v in vm.GetMachineTypeDict().iteritems():
      metadata['{0}_{1}'.format(vm_specifier, k)] = v

  num_streams = FLAGS.netperf_num_streams
  assert(num_streams >= 1)

  for netperf_benchmark in FLAGS.netperf_benchmarks:

    if vm_util.ShouldRunOnExternalIpAddress():
      external_ip_results = RunNetperf(client_vm, netperf_benchmark,
                                       server_vm.ip_address, num_streams)
      for external_ip_result in external_ip_results:
        external_ip_result.metadata.update(metadata)
      results.extend(external_ip_results)

    if vm_util.ShouldRunOnInternalIpAddress(client_vm, server_vm):
      internal_ip_results = RunNetperf(client_vm, netperf_benchmark,
                                       server_vm.internal_ip, num_streams)
      for internal_ip_result in internal_ip_results:
        internal_ip_result.metadata.update(metadata)
        internal_ip_result.metadata['ip_type'] = 'internal'
      results.extend(internal_ip_results)

  return results

def Cleanup(benchmark_spec):
  """Cleanup netperf on the target vm (by uninstalling).

  Args:
    benchmark_spec: The benchmark specification. Contains all data that is
        required to run the benchmark.
  """
  vms = benchmark_spec.vms
  vms[1].RemoteCommand('sudo pkill netserver')
<|endoftext|>


<commit_before>from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
from django.conf import settings

from whats_fresh_api.models import *
from django.contrib.gis.db import models

import os
import time
import sys
import datetime
import json

class ProductViewTestCase(TestCase):

    fixtures = ['whats_fresh_api/tests/testdata/test_fixtures.json']

    def setup(self):
        self.expected_json = {
            "error": {
                "error_status": false,
                "error_name": null,
                "error_text": null,
                "error_level": null
            },
            "1": {
                "model": "whats_fresh_api.Product",
                "pk": "2",
                    "fields": {
                    "name": "Starfish Voyager",
                    "variety": "Tuna",
                    "alt_name": "The Stargazer",
                    "description": "This is one sweet fish!",
                    "origin": "The Delta Quadrant",
                    "season": "Season 1",
                    "available": true,
                    "market_price": "$33.31",
                    "link": "http://www.amazon.com/Star-Trek-Voyager-Complete-Seventh/dp/B00062IDCO/",
                    "image_id": 1,
                    "story_id": 1,
                    "created": "2014-08-08 23:27:05.568395+00:00",
                    "modified": "2014-08-08 23:27:05.568395+00:00"
                }
            },
            "2": {
                "model": "whats_fresh_api.Product",
                "pk": "1",
                "fields": {
                    "name": "Ezri Dax",
                    "variety": "Freshwater Eel",
                    "alt_name": "Jadzia",
                    "description": "That's not actually an eel, it's a symbiote.",
                    "origin": "Trill",
                    "season": "Season 7",
                    "available": true,
                    "market_price": "$32.64 per season",
                    "link": "http://www.amazon.com/Star-Trek-Deep-Space-Nine/dp/B00008KA57/",
                    "image_id": 1,
                    "story_id": 2,
                    "created": "2014-08-08 23:27:05.568395+00:00",
                    "modified": "2014-08-08 23:27:05.568395+00:00"
                }
            }
        }

    def test_url_endpoint(self):
        url = reverse('products-list')
        self.assertEqual(url, '/products')

    def test_json_equals(self):
        c = Client()
        response = c.get(reverse('products-list')).content
        parsed_answer = json.loads(response)
        expected_answer = json.loads(self.expected_json)
        self.assertTrue(parsed_answer == expected_answer)
<commit_msg>Fixed typos and changed image from an id to a url<commit_after>from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
from django.conf import settings

from whats_fresh_api.models import *
from django.contrib.gis.db import models

import os
import time
import sys
import datetime
import json

class ProductViewTestCase(TestCase):

    fixtures = ['whats_fresh_api/tests/testdata/test_fixtures.json']

    def setUp(self):
        self.expected_json = '{
            "error": {
                "error_status": false,
                "error_name": null,
                "error_text": null,
                "error_level": null
            },
            "1": {
                "name": "Starfish Voyager",
                "variety": "Tuna",
                "alt_name": "The Stargazer",
                "description": "This is one sweet fish!",
                "origin": "The Delta Quadrant",
                "season": "Season 1",
                "available": true,
                "market_price": "$33.31",
                "link": "http://www.amazon.com/Star-Trek-Voyager-Complete-Seventh/dp/B00062IDCO/",
                "image": "/media/dog.jpg",
                "story_id": 1,
                "created": "2014-08-08 23:27:05.568395+00:00",
                "modified": "2014-08-08 23:27:05.568395+00:00"
                }
            },
            "2": {
                "name": "Ezri Dax",
                "variety": "Freshwater Eel",
                "alt_name": "Jadzia",
                "description": "That's not actually an eel, it's a symbiote.",
                "origin": "Trill",
                "season": "Season 7",
                "available": true,
                "market_price": "$32.64 per season",
                "link": "http://www.amazon.com/Star-Trek-Deep-Space-Nine/dp/B00008KA57/",
                "image_id": "/media/cat.jpg",
                "story_id": 2,
                "created": "2014-08-08 23:27:05.568395+00:00",
                "modified": "2014-08-08 23:27:05.568395+00:00"
                }
            }
        }'

    def test_url_endpoint(self):
        url = reverse('products-list')
        self.assertEqual(url, '/products')

    def test_json_equals(self):
        c = Client()
        response = c.get(reverse('products-list')).content
        parsed_answer = json.loads(response)
        expected_answer = json.loads(self.expected_json)
        self.assertTrue(parsed_answer == expected_answer)
<|endoftext|>