def open(self, recipient_id, subject, message):
        '''
        Opens a new conversation thread.
        @param recipient:str ID of the recipient
        @param subject:str Subject of the thread
        @param message:str Message
        '''
        if is_empty_or_none(recipient_id):
            raise ValueError("Invalid recipient ID")

        if is_empty_or_none(subject):
            raise ValueError("Invalid subject")

        if is_empty_or_none(message):
            raise ValueError("Invalid message")

        data = {"recipient":recipient_id, "subject":subject, "message":message}
        request = Request(self.__seller.client, method="POST",
                          uri=self.get_uri(), data=data)

        response = request.get_response()
        if response.get_status_code() == 201:
            thread_id = extract_id_from_uri(response["Location"])
            thread = self[thread_id]
            thread.sync(response.data, response["Etag"])
            return thread
    def __init__(self, client=None, method="GET", uri=None, data=None,
                 content_type="application/x-www-form-urlencoded"):
        '''
        Initializes a new instance of the Request class.
        @param method:str HTTP method
        @param content_type:str MIME type of the data to carry to the server.
        '''
        if is_empty_or_none(uri):
            raise ValueError("Invalid URI.")

        if (is_empty_or_none(method)
            or method.lower() not in HTTP_METHODS):
            raise ValueError("Invalid HTTP method.")

        if (is_empty_or_none(content_type)
            or content_type not in CONTENT_TYPES):
            raise ValueError("Invalid content type value.")

        if not data and method.lower() in ["post", "put", "patch"]:
            raise ValueError("Data is not expected to be None.")

        self.__content_type = content_type
        self.data = data
        self.uri = uri
        self.method = method
        self.headers = {}
        if client:
            client.sign_request(self)
    def _create_text_node(self, root, name, value, cdata=False):
        """
        Creates and adds a text node
        @param root:Element Root element
        @param name:str Tag name
        @param value:object Text value
        @param cdata:bool A value indicating whether to use CDATA or not.
        @return:Node
        """
        if is_empty_or_none(value):
            return

        if isinstance(value, date):
            value = date_to_string(value)

        if isinstance(value, datetime):
            value = datetime_to_string(value)

        if isinstance(value, Decimal):
            value = "0" if not value else str(value)

        tag = root.ownerDocument.createElement(name)
        if cdata:
            tag.appendChild(root.ownerDocument.createCDATASection(str(value)))
        else:
            tag.appendChild(root.ownerDocument.createTextNode(str(value)))

        return root.appendChild(tag)
    def to_xml(self, tag_name="buyer"):
        """
        Returns an XMLi representation of the object.
        @param tag_name:str Tag name
        @return: Element
        """
        for n, v in {"name": self.name, "address": self.address}.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        if self.__require_email and is_empty_or_none(self.email):
            raise ValueError("'email' attribute cannot be empty or None.")

        doc = Document()
        root = doc.createElement(tag_name)
        self._create_text_node(root, "name", self.name, True)
        self._create_text_node(root, "email", self.email)
        root.appendChild(self.address.to_xml())
        return root
    def to_xml(self):
        """
        Returns an XMLi representation of the shipping details.
        @return: Element
        """
        for n, v in {"recipient": self.recipient}.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        doc = Document()
        root = doc.createElement("shipping")
        root.appendChild(self.recipient.to_xml("recipient"))
        return root
    def parse(cls, raw):
        '''
        Parses a string into a new instance of the ContentRange class.
        @param raw:str String to parse
        @return: ContentRange
        '''
        if is_empty_or_none(raw):
            return

        match = re.match(cls.REG_EXP, raw)
        if not match or len(match.groupdict()) < 4:
            return

        return cls(*match.groups())
    def to_xml(self):
        """
        Returns a DOM element containing the XML representation of the invoice
        @return:Element
        """
        if not len(self.groups):
            raise Exception("An invoice must at least have one group.")

        for n, v in {
            "name": self.name,
            "currency": self.currency,
            "buyer": self.buyer,
            "status": self.status,
            "date": self.date,
            "due_date": self.due_date,
        }.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        doc = Document()
        root = doc.createElement("invoice")
        root.appendChild(self.buyer.to_xml("buyer"))

        if self.shipping:
            root.appendChild(self.shipping.to_xml())

        self._create_text_node(root, "name", self.name, True)
        self._create_text_node(root, "description", self.description, True)
        self._create_text_node(root, "currency", self.currency)
        self._create_text_node(root, "status", self.status)
        self._create_text_node(root, "date", self.date)
        self._create_text_node(root, "dueDate", self.due_date)
        self._create_text_node(root, "customId", self.custom_id, True)
        self._create_text_node(root, "terms", self.terms, True)
        self._create_text_node(root, "total", self.total)

        body = doc.createElement("body")
        root.appendChild(body)

        groups = doc.createElement("groups")
        body.appendChild(groups)
        for group in self.__groups:
            groups.appendChild(group.to_xml())

        # Adding custom elements
        super(Invoice, self).to_xml(body)
        return root
    def to_xml(self, name="address"):
        """
        Returns a DOM Element containing the XML representation of the
        address.
        @return:Element 
        """
        for n, v in {"address": self.street_address, "city": self.city, "country": self.country}.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        doc = Document()
        root = doc.createElement(name)
        self._create_text_node(root, "streetAddress", self.street_address, True)
        self._create_text_node(root, "city", self.city, True)
        self._create_text_node(root, "zipcode", self.zipcode)
        self._create_text_node(root, "state", self.state, True)
        self._create_text_node(root, "country", self.country)
        return root
    def to_xml(self, name):
        """
        Returns a DOM representation of the line treatment.
        @return: Element
        """
        for n, v in {
            "rate_type": self.rate_type,
            "rate": self.rate,
            "name": self.name,
            "description": self.description,
        }.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        doc = Document()
        root = doc.createElement(name)
        root.setAttribute("type", self.rate_type)
        root.setAttribute("name", self.name)
        root.setAttribute("description", self.description)
        root.setAttribute("base", self.interval) if self.interval else ""
        root.appendChild(doc.createTextNode(str(self.rate)))
        return root
    def send(self, xmli, signature=True):
        """
        Sends an invoice
        @param xmli:str Invoice XML representation.
        @return: InvoiceReport
        """
        xmli = unicode(xmli)
        if is_empty_or_none(xmli):
            raise ValueError("Invalid XMLi")

        """
        XMLdsig: required PyCrypto + lxml
        """
        private_key, public_key = self.email.client.keys
        if signature and private_key and public_key:
            from greendizer import xmldsig

            xmli = xmldsig.sign(xmli, private_key, public_key)

        size = 0
        try:
            from os import sys

            size = sys.getsizeof(xmli)
        except AttributeError:
            import base64  # 2.5 and older...

            size = len(base64.encodestring(xmli))  # 1 ASCII = 1 byte

        if size > MAX_CONTENT_LENGTH:
            raise ValueError("XMLi's size is limited to %skb." % MAX_CONTENT_LENGTH / 1024)

        request = Request(self.email.client, method="POST", data=xmli, uri=self._uri, content_type="application/xml")

        response = request.get_response()
        if response.status_code == 202:  # Accepted
            return InvoiceReport(self.email, extract_id_from_uri(response["Location"]))
    def to_xml(self):
        """
        Returns a DOM representation of the line.
        @return: Element
        """
        for n, v in {"name": self.name, "quantity": self.quantity, "unit_price": self.unit_price}.items():
            if is_empty_or_none(v):
                raise ValueError("'%s' attribute cannot be empty or None." % n)

        doc = Document()
        root = doc.createElement("line")
        self._create_text_node(root, "date", self.date)
        self._create_text_node(root, "name", self.name, True)
        self._create_text_node(root, "description", self.description, True)
        self._create_text_node(root, "quantity", self.quantity)
        self._create_text_node(root, "unitPrice", self.unit_price)
        self._create_text_node(root, "unit", self.unit)
        self._create_text_node(root, "gin", self.gin)
        self._create_text_node(root, "gtin", self.gtin)
        self._create_text_node(root, "sscc", self.sscc)

        if len(self.__discounts):
            discounts = root.ownerDocument.createElement("discounts")
            root.appendChild(discounts)
            for discount in self.__discounts:
                discounts.appendChild(discount.to_xml())

        if len(self.__taxes):
            taxes = root.ownerDocument.createElement("taxes")
            root.appendChild(taxes)
            for tax in self.__taxes:
                taxes.appendChild(tax.to_xml())

        super(Line, self).to_xml(root)

        return root