def assign_to_portfolio(self, orderID=0, loanIDs=[], returnJson=False):
        """
        Assign an order to a the portfolio named in the investing dictionary.
        If returnJson is True, this method will return the JSON returned from the server (this is primarily for unit testing)
        Otherwise it returns the name of the portfolio the order was assigned to or False
        """

        # Assign to portfolio
        resText = ''
        try:
            if not self.settings['portfolio']:
                return True

            if len(loanIDs) != 0 and orderID != 0:

                # Data
                orderIDs = [orderID]*len(loanIDs)  # 1 order ID per record
                postData = {
                    'loan_id': loanIDs,
                    'record_id': loanIDs,
                    'order_id': orderIDs
                }
                paramData = {
                    'method': 'addToLCPortfolio',
                    'lcportfolio_name': self.settings['portfolio']
                }

                # New portfolio
                folioList = self.get_portfolio_list()
                if self.settings['portfolio'] not in folioList:
                    paramData['method'] = 'createLCPortfolio'

                # Send
                response = util.post_url('/data/portfolioManagement', params=paramData, data=postData)
                resText = response.text
                resJson = response.json()

                if returnJson is True:
                    return resJson

                # Failed if the response is not 200 or JSON result is not success
                if response.status_code != 200 or resJson['result'] != 'success':
                    self.logger.error('Could not assign order #{0} to portfolio \'{1}: Server responded with {2}\''.format(str(orderID), self.settings['portfolio'], response.text))

                # Success
                else:

                    # Assigned to another portfolio, for some reason, raise warning
                    if 'portfolioName' in resJson and resJson['portfolioName'] != self.settings['portfolio']:
                        self.logger.warning('Added order #{0} to portfolio "{1}" - NOT - "{2}", and I don\'t know why'.format(str(orderID), resJson['portfolioName'], self.settings['portfolio']))
                    # Assigned to the correct portfolio
                    else:
                        self.logger.info('Added order #{0} to portfolio "{1}"'.format(str(orderID), self.settings['portfolio']))

                    return resJson['portfolioName']

        except Exception as e:
            self.logger.error('Could not assign order #{0} to portfolio \'{1}\': {2} -- {3}'.format(orderID, self.settings['portfolio'], str(e), resText))

        return False
    def browse_notes(self):
        """
        Sends the filters to the Browse Notes API and returns a JSON of the notes found
        """
        try:

            # Get all investment options
            filters = util.get_filter_json(self.settings['filters'])
            print 'Filter!\n', filters
            if filters is False:
                filters = 'default'
            payload = {
                'method': 'search',
                'filter': filters
                #'filter': '[{"m_id":39,"m_metadata":{"m_controlValues":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}],"m_type":"MVAL","m_rep":"CHKBOX","m_label":"Term (36 - 60 month)","id":39,"m_onHoverHelp":"Select the loan maturities you are interested to invest in","m_className":"classname","m_defaultValue":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}]},"m_value":[{"value":"Year3","label":"36-month","sqlValue":null,"valueIndex":0},{"value":"Year5","label":"60-month","sqlValue":null,"valueIndex":1}],"m_visible":false,"m_position":0},{"m_id":38,"m_metadata":{"m_controlValues":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}],"m_type":"SVAL","m_rep":"CHKBOX","m_label":"Exclude Loans already invested in","id":38,"m_onHoverHelp":"Use this filter to exclude loans from a borrower that you have already invested in.","m_className":"classname","m_defaultValue":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}]},"m_value":[{"value":true,"label":"Exclude loans invested in","sqlValue":null,"valueIndex":0}],"m_visible":false,"m_position":0},{"m_id":10,"m_metadata":{"m_controlValues":[{"value":"All","label":"All","sqlValue":null,"valueIndex":0},{"value":"D","label":"<span class=\\"grades d-loan-grade\\">D</span> 18.76%","sqlValue":null,"valueIndex":1},{"value":"A","label":"<span class=\\"grades a-loan-grade\\">A</span> 7.41%","sqlValue":null,"valueIndex":2},{"value":"E","label":"<span class=\\"grades e-loan-grade\\">E</span> 21.49%","sqlValue":null,"valueIndex":3},{"value":"B","label":"<span class=\\"grades b-loan-grade\\">B</span> 12.12%","sqlValue":null,"valueIndex":4},{"value":"F","label":"<span class=\\"grades f-loan-grade\\">F</span> 23.49%","sqlValue":null,"valueIndex":5},{"value":"C","label":"<span class=\\"grades c-loan-grade\\">C</span> 15.80%","sqlValue":null,"valueIndex":6},{"value":"G","label":"<span class=\\"grades g-loan-grade\\">G</span> 24.84%","sqlValue":null,"valueIndex":7}],"m_type":"MVAL","m_rep":"CHKBOX","m_label":"Interest Rate","id":10,"m_onHoverHelp":"Specify the interest rate ranges of the notes  you are willing to invest in.","m_className":"short","m_defaultValue":[{"value":"All","label":"All","sqlValue":null,"valueIndex":0}]},"m_value":[{"value":"A","label":"<span class=\\"grades a-loan-grade\\">A</span> 7.41%","sqlValue":null,"valueIndex":2},{"value":"B","label":"<span class=\\"grades b-loan-grade\\">B</span> 12.12%","sqlValue":null,"valueIndex":4},{"value":"C","label":"<span class=\\"grades c-loan-grade\\">C</span> 15.80%","sqlValue":null,"valueIndex":6}],"m_visible":false,"m_position":0},{"m_id":37,"m_metadata":{"m_controlValues":null,"m_type":"SVAL","m_rep":"TEXTBOX","m_label":"Keyword","id":37,"m_onHoverHelp":"Type any keyword","m_className":"classname","m_defaultValue":[]},"m_value":null,"m_visible":false,"m_position":0}]'
            }
            response = util.post_url('/browse/browseNotesAj.action', data=payload)
            jsonRes = response.json()
            return jsonRes

        except Exception as e:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_tb(exc_traceback, limit=10, file=sys.stdout)
            self.logger.error(str(e))
            return False
    def get_investment_option(self, cash):
        """
        When investing, lending club provides a list of investment portfolio options, all with different
        diversification of loan classes which come out to an average percent return.

        This method returns an investment option that best matches your available cash and min/max
        percentage (defined in investing) desired. If there are multiple options between min & max,
        the one closest to max will be chosen.
        """
        try:
            maxPercent = self.settings['maxPercent']
            minPercent = self.settings['minPercent']

            # Get all investment options
            filters = util.get_filter_json(self.settings['filters'])
            if filters is False:
                filters = 'default'
            payload = {
                'amount': cash,
                'max_per_note': 0,
                'filter': filters
            }
            response = util.post_url('/portfolio/lendingMatchOptionsV2.action', data=payload)
            json = response.json()

            if json['result'] == 'success' and 'lmOptions' in json:
                options = json['lmOptions']
                lastOption = False

                # Loop through all the investment options
                i = 0
                for option in options:
                    option['optIndex'] = i

                    # A perfect match
                    if option['percentage'] == maxPercent:
                        return option

                    # Over the max
                    elif option['percentage'] > maxPercent:
                        break

                    # Over the minimum
                    elif option['percentage'] >= minPercent:
                        lastOption = option

                    i += 1

                # If the perfect match wasn't found, return the last
                # option that was under the maximum percent
                return lastOption
            else:
                self.logger.error('Could not get investment portfolio options! Server responded with: {0}'.format(response.text))
                return False

        except Exception as e:
            self.logger.error(str(e))

        return False
    def place_order(self, strutToken, cash, investmentOption):
        """
        Place the order and get the order number, loan ID from the resulting HTML -- then assign to a portfolio
        The cash parameter is the amount of money invest in this order
        The investmentOption parameter is the investment portfolio returned by get_investment_option()
        """

        orderID = 0
        loanIDs = []

        # Process order confirmation page
        try:
            payload = {}
            if strutToken:
                payload['struts.token.name'] = 'struts.token'
                payload['struts.token'] = strutToken
            response = util.post_url('/portfolio/orderConfirmed.action', data=payload)

            # Process HTML
            html = response.text
            soup = BeautifulSoup(html)

            # Order num
            orderField = soup.find(id='order_id')
            if orderField:
                orderID = int(orderField['value'])

            # Load ID
            loanTags = soup.find_all('td', {'class': 'loan_id'})
            for tag in loanTags:
                loanIDs.append(int(tag.text))

            # Print status message
            if orderID == 0:
                self.logger.error('An investment order was submitted, but a confirmation could not be determined')
            else:
                self.logger.info('Order #{0} was successfully submitted for ${1} at {2}%'.format(orderID, cash, investmentOption['percentage']))

            # Print order summary
            orderSummary = self.get_option_summary(investmentOption)
            self.logger.info(orderSummary)

        except Exception as e:
            self.logger.error('Could not get your order number or loan ID from the order confirmation. Err Message: {0}'.format(str(e)))

        return (orderID, loanIDs)
    def authenticate(self):
        """
        Attempt to authenticate the user with the email/pass in the investing dictionary.
        Returns True/False
        """

        payload = {
            'login_email': self.settings.auth['email'],
            'login_password': self.settings.auth['pass']
        }
        response = util.post_url('/account/login.action', data=payload, useCookies=False)

        if (response.status_code == 200 or response.status_code == 302) and 'LC_FIRSTNAME' in response.cookies:
            self.authed = True
            util.cookies = response.cookies
            return True

        self.logger.error('Authentication returned {0}. Cookies: {1}'.format(response.status_code, str(response.cookies.keys())))
        return False