Beispiel #1
0
    def documents(self):
        """ Retrieve the :class:`AgreementDocuments <pyEchosign.classes.documents.AgreementDocument>` associated with
        this agreement. If the files have not already been retrieved, this will result in an additional request to
        the API.

        Returns: A list of :class:`AgreementDocument <pyEchosign.classes.documents.AgreementDocument>`

        """
        # If _documents is None, no (successful) API call has been made to retrieve them
        if self._documents is None:
            url = self.account.api_access_point + 'agreements/{}/documents'.format(
                self.echosign_id)
            r = requests.get(url,
                             headers=get_headers(self.account.access_token))
            # Raise Exception if there was an error
            check_error(r)
            try:
                data = r.json()
            except ValueError:
                raise ApiError(
                    'Unexpected response from Echosign API: Status {} - {}'.
                    format(r.status_code, r.content))
            else:
                self._documents = []

                # Take both sections of documents from the response and turn into AgreementDocuments
                documents = self._document_data_to_document(
                    data.get('documents', []))
                supporting_documents = self._document_data_to_document(
                    data.get('supportingDocuments', []))
                self._documents = documents + supporting_documents

        return self._documents
Beispiel #2
0
 def delete(self):
     """ Deletes the LibraryDocument from Echosign. It will not be visible on the Manage page. """
     url = self.account.api_access_point + 'libraryDocuments/{}'.format(
         self.echosign_id)
     headers = get_headers(self.account.access_token)
     r = requests.delete(url, headers=headers)
     check_error(r)
Beispiel #3
0
    def audit_trail_file(self):
        # type: () -> BytesIO
        """ The PDF file of the audit trail."""
        endpoint = '{}agreements/{}/auditTrail'.format(
            self.account.api_access_point, self.echosign_id)

        response = requests.get(endpoint,
                                headers=get_headers(self.account.access_token))
        check_error(response)

        return BytesIO(response.content)
Beispiel #4
0
    def combined_document(self):
        # type: () -> BytesIO
        """ The PDF file containing all documents within this agreement."""
        endpoint = '{}agreements/{}/combinedDocument'.format(
            self.account.api_access_point, self.echosign_id)

        response = requests.get(endpoint,
                                headers=get_headers(self.account.access_token))
        check_error(response)

        return BytesIO(response.content)
Beispiel #5
0
    def get_library_documents(self):
        """ Gets all Library Documents for the EchosignAccount

        Returns: A list of :class:`Agreement <pyEchosign.classes.library_document.LibraryDocument>` objects
        """
        url = self.api_access_point + 'libraryDocuments'
        headers = get_headers(self.access_token)
        r = requests.get(url, headers=headers)
        response_data = r.json()

        check_error(r)

        return LibraryDocument.json_to_agreements(self, response_data)
Beispiel #6
0
    def get_form_data(self):
        """ Retrieves the form data for this agreement as CSV.

        Returns: StringIO

        """
        url = '{}agreements/{}/formData'.format(self.account.api_access_point,
                                                self.echosign_id)

        r = requests.get(url, headers=self.account.headers())

        check_error(r)

        return StringIO(r.text)
Beispiel #7
0
    def retrieve_complete_document(self):
        """ Retrieves the remaining data for the LibraryDocument, such as locale, status, and security options. """
        url = self.account.api_access_point + 'libraryDocuments/{}'.format(
            self.echosign_id)
        headers = get_headers(self.account.access_token)
        r = requests.get(url, headers=headers)

        check_error(r)

        response_data = r.json()
        self._locale = response_data.get('locale')
        self._status = response_data.get('status')
        self._security_options = response_data.get('securityOptions')
        self.fully_retrieved = True
Beispiel #8
0
    def send_reminder(self, comment=''):
        """ Send a reminder for an agreement to the participants.

        Args:
            comment: An optional comment that will be sent with the reminder

        """
        url = self.account.api_access_point + 'reminders'
        payload = dict(agreementId=self.echosign_id, comment=comment)

        r = requests.post(url,
                          data=json.dumps(payload),
                          headers=self.account.headers())

        check_error(r)
Beispiel #9
0
    def __init__(self, account, file_name, file, mime_type=None):
        # type: (EchosignAccount, str, Union[IOBase, FileIO, BytesIO], str) -> None
        self.file_name = file_name
        self.file = file
        self.mime_type = mime_type

        self.document_id = None
        self.expiration_date = None

        # With file data provided, make request to Echosign API for transient document
        url = account.api_access_point + 'transientDocuments'

        # Create post_data
        file_tuple = (file_name, file)
        # Only add the mime type if provided
        if mime_type is not None:
            file_tuple = file_tuple + (mime_type, )

        files = dict(File=file_tuple)
        r = requests.post(url,
                          headers=get_headers(account.access_token,
                                              content_type=None),
                          files=files)

        if response_success(r):
            log.debug('Request to create document {} successful.'.format(
                self.file_name))
            response_data = r.json()
            self.document_id = response_data.get('transientDocumentId', None)
            # If there was no document ID, something went wrong
            if self.document_id is None:
                log.error(
                    'Did not receive a transientDocumentId from Echosign. Received: {}'
                    .format(r.content))
                raise ApiError(
                    'Did not receive a Transient Document ID from Echosign')
            else:
                today = arrow.now()
                # Document will expire in 7 days from creation
                self.expiration_date = today.replace(days=+7).datetime
        else:
            try:
                log.error(
                    'Error encountered creating document {}. Received message: {}'
                    .format(self.file_name, r.content))
            finally:
                check_error(r)
Beispiel #10
0
    def get_agreements(self, query=None):
        # type: (str) -> List[Agreement]
        """ Gets all agreements for the EchosignAccount

        Keyword Args:
            query: (str) A search query to filter results by
        
        Returns: A list of :class:`Agreement <pyEchosign.classes.agreement.Agreement>` objects
        """
        url = self.api_access_point + 'agreements'
        params = dict()

        if query is not None:
            params.update({'query': query})

        r = requests.get(url, headers=get_headers(self.access_token), params=params)
        check_error(r)
        response_body = r.json()
        return Agreement.json_to_agreements(self, response_body)
Beispiel #11
0
    def cancel(self):
        """ Cancels the agreement on Echosign. Agreement will still be visible in the Manage page. """
        url = '{}agreements/{}/status'.format(self.account.api_access_point,
                                              self.echosign_id)
        body = dict(value='CANCEL')
        r = requests.put(url,
                         headers=get_headers(self.account.access_token),
                         data=json.dumps(body))

        if response_success(r):
            log.debug('Request to cancel agreement {} successful.'.format(
                self.echosign_id))

        else:
            try:
                log.error(
                    'Error encountered cancelling agreement {}. Received message: {}'
                    .format(self.echosign_id, r.content))
            finally:
                check_error(r)
Beispiel #12
0
    def delete(self):
        """ Deletes the agreement on Echosign. Agreement will not be visible in the Manage page. 
        
        Note:
            This action requires the 'agreement_retention' scope, which doesn't appear
            to be actually available via OAuth
        """
        url = self.account.api_access_point + 'agreements/' + self.echosign_id

        r = requests.delete(url,
                            headers=get_headers(self.account.access_token))

        if response_success(r):
            log.debug('Request to delete agreement {} successful.'.format(
                self.echosign_id))
        else:
            try:
                log.error(
                    'Error encountered deleting agreement {}. Received message:{}'
                    .format(self.echosign_id, r.content))
            finally:
                check_error(r)
Beispiel #13
0
    def send(self,
             recipients,
             agreement_name=None,
             ccs=None,
             days_until_signing_deadline=0,
             external_id='',
             signature_flow=SignatureFlow.SEQUENTIAL,
             message='',
             merge_fields=None):
        # type: (List[User], str, list, int, str, Agreement.SignatureFlow, str, List[Dict[str, str]]) -> None
        """ Sends this agreement to Echosign for signature

        Args:
            agreement_name: A string for the document name which will appear in the Echosign Manage page, the email
                to recipients, etc. Defaults to the name for the Agreement.
            recipients: A list of :class:`Users <pyEchosign.classes.users.User>`.
                The order which they are provided in the list determines the order in which they sign.
            ccs: (optional) A list of email addresses to be CC'd on the Echosign agreement emails
                (document sent, document fully signed, etc)
            days_until_signing_deadline: (optional) "The number of days that remain before the document expires.
                You cannot sign the document after it expires" Defaults to 0, for no expiration.
            external_id: (optional) "A unique identifier for your transaction...
                You can use the ExternalID to search for your transaction through [the] API"
            signature_flow: (optional) (SignatureFlow): The routing style of this agreement, defaults to Sequential.
            merge_fields: (optional) A list of dictionaries, with each one providing the 'field_name' and
                'default_value' keys. The field name maps to the field on the document, and the default value is
                what will be placed inside.
            message: (optional) A message which will be displayed to recipients of the agreement

        Returns:
            A namedtuple representing the information received back from the API. Contains the following attributes

            `agreement_id`
                *"The unique identifier that can be used to query status and download signed documents"*

            `embedded_code`
                *"Javascript snippet suitable for an embedded page taking a user to a URL"*

            `expiration`
                *"Expiration date for autologin. This is based on the user setting, API_AUTO_LOGIN_LIFETIME"*

            `url`
             *"Standalone URL to direct end users to"*

        Raises:
            ApiError: If the API returns an error, such as a 403. The exact response from the API is provided.

        """
        if agreement_name is None:
            agreement_name = self.name

        if ccs is None:
            ccs = []

        security_options = dict(passwordProtection="NONE",
                                kbaProtection="NONE",
                                webIdentityProtection="NONE",
                                protectOpen=False,
                                internalPassword="",
                                externalPassword="",
                                openPassword="")

        files_data = [{
            'transientDocumentId': file.document_id
        } for file in self.files]

        if merge_fields is None:
            merge_fields = []

        converted_merge_fields = [
            dict(fieldName=field['field_name'],
                 defaultValue=field['default_value']) for field in merge_fields
        ]

        recipients_data = self.__construct_recipient_agreement_request(
            recipients)

        document_creation_info = dict(
            signatureType="ESIGN",
            name=agreement_name,
            callbackInfo="",
            securityOptions=security_options,
            locale="",
            ccs=ccs,
            externalId=external_id,
            signatureFlow=signature_flow,
            fileInfos=files_data,
            mergeFieldInfo=converted_merge_fields,
            recipientSetInfos=recipients_data,
            message=message,
            daysUntilSigningDeadline=days_until_signing_deadline,
        )

        request_data = dict(documentCreationInfo=document_creation_info)
        url = self.account.api_access_point + 'agreements'
        api_response = requests.post(url,
                                     headers=self.account.headers(),
                                     data=json.dumps(request_data))

        if response_success(api_response):
            response = namedtuple(
                'Response',
                ('agreement_id', 'embedded_code', 'expiration', 'url'))

            response_data = api_response.json()
            embedded_code = response_data.get('embeddedCode', None)
            expiration = response_data.get('expiration', None)
            url = response_data.get('url', None)

            response = response(response_data['agreementId'], embedded_code,
                                expiration, url)

            return response

        else:
            check_error(api_response)