Skip to content

Commit b105e9d

Browse files
committed
Python Document Visibility Example
1 parent cf2351d commit b105e9d

6 files changed

Lines changed: 344 additions & 0 deletions

File tree

app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
app.register_blueprint(esignature_views.eg036)
113113
app.register_blueprint(esignature_views.eg037)
114114
app.register_blueprint(esignature_views.eg038)
115+
app.register_blueprint(esignature_views.eg040)
115116
if "DYNO" in os.environ: # On Heroku?
116117
import logging
117118

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import base64
2+
from os import path
3+
from typing import List
4+
5+
from docusign_esign import (
6+
EnvelopesApi,
7+
Document,
8+
Signer,
9+
CarbonCopy,
10+
EnvelopeDefinition,
11+
SignHere,
12+
Tabs,
13+
Recipients,
14+
)
15+
from flask import session, request
16+
17+
from ...consts import demo_docs_path, pattern
18+
from ...docusign import create_api_client
19+
20+
21+
class Eg040DocumentVisibility:
22+
@staticmethod
23+
def get_args():
24+
"""Get request and session arguments"""
25+
# More data validation would be a good idea here
26+
# Strip anything other than characters listed
27+
# 1. Parse request arguments
28+
signer1_email = pattern.sub("", request.form.get("signer1_email"))
29+
signer1_name = pattern.sub("", request.form.get("signer1_name"))
30+
signer2_email = pattern.sub("", request.form.get("signer2_email"))
31+
signer2_name = pattern.sub("", request.form.get("signer2_name"))
32+
cc_email = pattern.sub("", request.form.get("cc_email"))
33+
cc_name = pattern.sub("", request.form.get("cc_name"))
34+
envelope_args = {
35+
"signer1_email": signer1_email,
36+
"signer1_name": signer1_name,
37+
"signer2_email": signer2_email,
38+
"signer2_name": signer2_name,
39+
"cc_email": cc_email,
40+
"cc_name": cc_name,
41+
"status": "sent",
42+
}
43+
args = {
44+
"account_id": session["ds_account_id"],
45+
"base_path": session["ds_base_path"],
46+
"access_token": session["ds_access_token"],
47+
"envelope_args": envelope_args
48+
}
49+
return args
50+
51+
# Step 3 start
52+
@classmethod
53+
def worker(cls, args, doc_docx_path, doc_pdf_path):
54+
"""
55+
1. Create the envelope request object
56+
2. Send the envelope
57+
"""
58+
envelope_args = args["envelope_args"]
59+
# 1. Create the envelope request object
60+
envelope_definition = cls.make_envelope(envelope_args, doc_docx_path, doc_pdf_path)
61+
62+
# 2. call Envelopes::create API method
63+
# Exceptions will be caught by the calling function
64+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
65+
66+
envelope_api = EnvelopesApi(api_client)
67+
results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
68+
69+
envelope_id = results.envelope_id
70+
71+
return {"envelope_id": envelope_id}
72+
# Step 3 end
73+
74+
# Step 2 start
75+
@classmethod
76+
def make_envelope(cls, args, doc_docx_path, doc_pdf_path):
77+
"""
78+
Creates envelope
79+
args -- parameters for the envelope:
80+
signer_email, signer_name, signer_client_id
81+
returns an envelope definition
82+
"""
83+
env = EnvelopeDefinition(
84+
email_subject = "Please sign this document set",
85+
)
86+
87+
doc1_b64 = base64.b64encode(bytes(cls.create_document1(args), "utf-8")).decode("ascii")
88+
# read files 2 and 3 from a local directory
89+
# The reads could raise an exception if the file is not available!
90+
with open(path.join(demo_docs_path, doc_docx_path), "rb") as file:
91+
doc2_docx_bytes = file.read()
92+
doc2_b64 = base64.b64encode(doc2_docx_bytes).decode("ascii")
93+
with open(path.join(demo_docs_path, doc_pdf_path), "rb") as file:
94+
doc3_pdf_bytes = file.read()
95+
doc3_b64 = base64.b64encode(doc3_pdf_bytes).decode("ascii")
96+
97+
98+
# Create the document models
99+
document1 = Document( # create the DocuSign document object
100+
document_base64=doc1_b64,
101+
name="Order acknowledgement", # can be different from actual file name
102+
file_extension="html", # many different document types are accepted
103+
document_id="1", # a label used to reference the doc
104+
)
105+
document2 = Document( \
106+
document_base64=doc2_b64,
107+
name="Battle Plan",
108+
file_extension="docx",
109+
document_id="2"
110+
)
111+
document3 = Document(
112+
document_base64=doc3_b64,
113+
name="Lorem Ipsum",
114+
file_extension="pdf",
115+
document_id="3"
116+
)
117+
118+
# The order in the docs array determines the order in the envelope
119+
env.documents = [document1, document2, document3]
120+
121+
# Create the signer recipient model
122+
signer1 = Signer(
123+
# The signer1
124+
email=args["signer1_email"],
125+
name=args["signer1_name"],
126+
excluded_documents = ["2","3"], # Sets which documents are excluded for this signer
127+
recipient_id="1",
128+
routing_order="1",
129+
role_name="Signer1"
130+
)
131+
132+
signer2 = Signer(
133+
# The signer2
134+
email=args["signer2_email"],
135+
name=args["signer2_name"],
136+
excluded_documents = ["1"],
137+
recipient_id="2",
138+
routing_order="1",
139+
role_name="Signer2"
140+
)
141+
142+
cc = CarbonCopy(
143+
email=args["cc_email"],
144+
name=args["cc_name"],
145+
recipient_id="3",
146+
routing_order="2"
147+
)
148+
149+
# Create signHere fields (also known as tabs) on the documents.
150+
sign_here1 = SignHere(
151+
document_id="1",
152+
page_number="1",
153+
name="Signer1",
154+
x_position="200",
155+
y_position="200"
156+
)
157+
sign_here2 = SignHere(
158+
document_id="2",
159+
page_number="1",
160+
name="Signer2",
161+
recipient_id="2",
162+
x_position="300",
163+
y_position="200"
164+
)
165+
166+
sign_here3 = SignHere(
167+
document_id="3",
168+
page_number="1",
169+
name="Signer2",
170+
recipient_id="2",
171+
x_position="300",
172+
y_position="200"
173+
)
174+
175+
# Add the tabs model (including the sign_here tabs) to the signer
176+
# The Tabs object wants arrays of the different field/tab types
177+
signer1.tabs = Tabs(
178+
sign_here_tabs=[sign_here1, ],
179+
)
180+
signer2.tabs = Tabs(sign_here_tabs=[sign_here2, sign_here3])
181+
182+
# Add the recipients to the envelope object
183+
recipients = Recipients(signers=[signer1, signer2], carbon_copies=[cc])
184+
env.recipients = recipients
185+
186+
# Request that the envelope be sent by setting |status| to "sent".
187+
# To request that the envelope be created as a draft, set to "created"
188+
env.status = args["status"]
189+
190+
return env
191+
192+
@classmethod
193+
def create_document1(cls, args):
194+
""" Creates document 1 -- an html document"""
195+
196+
return f"""
197+
<!DOCTYPE html>
198+
<html>
199+
<head>
200+
<meta charset="UTF-8">
201+
</head>
202+
<body style="font-family:sans-serif;margin-left:2em;">
203+
<h1 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
204+
color: darkblue;margin-bottom: 0;">World Wide Corp</h1>
205+
<h2 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
206+
margin-top: 0px;margin-bottom: 3.5em;font-size: 1em;
207+
color: darkblue;">Order Processing Division</h2>
208+
<h4>Ordered by {args["signer1_name"]}</h4>
209+
<p style="margin-top:0em; margin-bottom:0em;">Email: {args["signer1_email"]}</p>
210+
<p style="margin-top:0em; margin-bottom:0em;">Copy to: {args["cc_name"]}, {args["cc_email"]}</p>
211+
<p style="margin-top:3em;">
212+
Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie.
213+
Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée.
214+
Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice.
215+
Donut jujubes oat cake jelly-o.
216+
Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.
217+
</p>
218+
<!-- Note the anchor tag for the signature field is in white. -->
219+
<h3 style="margin-top:3em;">Agreed: <span style="color:white;">**signature_1**/</span></h3>
220+
</body>
221+
</html>
222+
"""

app/eSignature/views/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@
3535
from .eg036_delayed_routing import eg036
3636
from .eg037_sms_delivery import eg037
3737
from .eg038_responsive_signing import eg038
38+
from .eg040_document_visibility import eg040
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Example 040: Document Visibility"""
2+
3+
from os import path
4+
5+
from docusign_esign.client.api_exception import ApiException
6+
from flask import render_template, redirect, Blueprint
7+
8+
from ..examples.eg040_document_visibility import Eg040DocumentVisibility
9+
from ...docusign import authenticate
10+
from ...ds_config import DS_CONFIG
11+
from ...error_handlers import process_error
12+
13+
eg = "eg040" # reference (and url) for this example
14+
eg040 = Blueprint("eg040", __name__)
15+
16+
17+
@eg040.route("/eg040", methods=["POST"])
18+
@authenticate(eg=eg)
19+
def embedded_signing():
20+
"""
21+
1. Get required arguments
22+
2. Call the worker method
23+
3. Redirect the user to the embedded signing
24+
"""
25+
try:
26+
# 1. Get required arguments
27+
args = Eg040DocumentVisibility.get_args()
28+
# 2. Call the worker method
29+
results = Eg040DocumentVisibility.worker(args, DS_CONFIG["doc_docx"], DS_CONFIG["doc_pdf"])
30+
except ApiException as err:
31+
return process_error(err)
32+
33+
# 3. Render success response with envelopeId
34+
return render_template(
35+
"example_done.html",
36+
title="Envelope sent",
37+
h1="Envelope sent",
38+
message=f"The envelope has been created and sent!<br/>Envelope ID {results['envelope_id']}."
39+
)
40+
41+
@eg040.route("/eg040", methods=["GET"])
42+
@authenticate(eg=eg)
43+
def get_view():
44+
"""responds with the form for the example"""
45+
return render_template(
46+
"eg040_document_visibility.html",
47+
title="Document Visibility",
48+
source_file= "eg040_document_visibility.py",
49+
source_url=DS_CONFIG["github_example_url"] + "eg040_document_visibility.py",
50+
documentation=DS_CONFIG["documentation"] + eg,
51+
show_doc=DS_CONFIG["documentation"],
52+
signer1_name=DS_CONFIG["signer_name"],
53+
signer1_email=DS_CONFIG["signer_email"]
54+
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<!-- extend base layout --> {% extends "base.html" %} {% block content %}
2+
3+
<h4>Set document visibility for envelope recipients</h4>
4+
<p>Demonstrates how to set document visibility for envelope recipients.</p>
5+
6+
{% if show_doc %}
7+
<p><a target='_blank' href='{{ documentation | safe }}'>Documentation</a> about this example.</p>
8+
{% endif %}
9+
10+
<p>API method used:
11+
<a target ='_blank' href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>.
12+
</p>
13+
14+
<p>
15+
View source file <a target="_blank" href="{{ source_url | safe }}">{{ source_file }}</a> on GitHub.
16+
</p>
17+
18+
<form class="eg" action="" method="post" data-busy="form">
19+
<div class="form-group">
20+
<label for="signer1_email">Signer1 Email</label>
21+
<input type="email" class="form-control" id="signer1_email" name="signer1_email"
22+
aria-describedby="emailHelp" placeholder="pat@example.com" required
23+
value="{{ signer1_email }}" />
24+
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
25+
</div>
26+
<div class="form-group">
27+
<label for="signer1_name">Signer1 Name</label>
28+
<input type="text" class="form-control" id="signer1_name" placeholder="Pat Johnson" name="signer1_name"
29+
value="{{ signer1_name }}" required />
30+
</div>
31+
<div class="form-group">
32+
<label for="signer2_email">Signer2 Email</label>
33+
<input type="text" class="form-control" id="signer2_email" placeholder="bob@mail.com" name="signer2_email"
34+
value="{{ signer2_email }}" required />
35+
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
36+
</div>
37+
<div class="form-group">
38+
<label for="signer2_name">Signer2 Name</label>
39+
<input type="text" class="form-control" id="signer2_name" placeholder="Bob Smith" name="signer2_name"
40+
value="{{ signer2_name }}" required />
41+
</div>
42+
<div class="form-group">
43+
<label for="cc_email">CC Email</label>
44+
<input type="email" class="form-control" id="cc_email" name="cc_email"
45+
aria-describedby="emailHelp" placeholder="pat@example.com" required />
46+
<small id="emailHelp" class="form-text text-muted">The email for the cc recipient must be different from the signer's email.</small>
47+
</div>
48+
<div class="form-group">
49+
<label for="cc_name">CC Name</label>
50+
<input type="text" class="form-control" id="cc_name" placeholder="Pat Johnson" name="cc_name"
51+
required />
52+
</div>
53+
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
54+
<button type="submit" class="btn btn-docu">Submit</button>
55+
</form>
56+
57+
{% endblock %}

app/templates/home.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,15 @@ <h4 id="example038">38. <a href="eg038">Create a signable HTML document</a></h4>
375375
<a target='_blank'
376376
href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopeviews/createrecipient/">EnvelopeViews::createRecipient</a>.
377377
</p>
378+
<h2>Document Visibility</h2>
379+
380+
<h4 id="example040"><a href="eg040">Set document visibility for envelope recipients</a></h4>
381+
<p>
382+
Demonstrates how to set document visibility for envelope recipients.
383+
<p>
384+
API methods used:
385+
<a target="_blank" href="https://developers.docusign.com/docs/esign-rest-api/reference/envelopes/envelopes/create/">Envelopes::create</a>.
386+
</p>
378387

379388
</div>
380389

0 commit comments

Comments
 (0)