Skip to content

Commit c49aff6

Browse files
authored
Merge pull request #80 from docusign/DEVDOCS-8173
Python Document Visibility Example
2 parents cf2351d + cb5ddbd commit c49aff6

9 files changed

Lines changed: 742 additions & 177 deletions

app/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@
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.eg039)
116+
app.register_blueprint(esignature_views.eg040)
117+
118+
115119
if "DYNO" in os.environ: # On Heroku?
116120
import logging
117121

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import base64
2+
from os import path
3+
4+
from docusign_esign import EnvelopesApi, RecipientViewRequest, Document, Signer, EnvelopeDefinition, SignHere, Tabs, \
5+
Recipients, InPersonSigner
6+
from flask import session, url_for, request
7+
8+
from ...consts import authentication_method, demo_docs_path, pattern, signer_client_id
9+
from ...docusign import create_api_client, DSClient
10+
from ...ds_config import DS_CONFIG
11+
12+
13+
class Eg039InPersonSigner:
14+
@staticmethod
15+
def get_args():
16+
"""Get request and session arguments"""
17+
# More data validation would be a good idea here
18+
# Strip anything other than characters listed
19+
# 1. Parse request arguments
20+
signer_name = pattern.sub("", request.form.get("signer_name"))
21+
envelope_args = {
22+
"host_email": session["ds_user_email"],
23+
"host_name": session["ds_user_name"],
24+
"signer_name": signer_name,
25+
"ds_return_url": url_for("ds.ds_return", _external=True),
26+
}
27+
args = {
28+
"account_id": session["ds_account_id"],
29+
"base_path": session["ds_base_path"],
30+
"access_token": session["ds_access_token"],
31+
"envelope_args": envelope_args
32+
}
33+
return args
34+
35+
@classmethod
36+
def worker(cls, args):
37+
"""
38+
1. Create the envelope request object
39+
2. Send the envelope
40+
3. Create the Recipient View request object
41+
4. Obtain the recipient_view_url for the embedded signing
42+
"""
43+
envelope_args = args["envelope_args"]
44+
# 1. Create the envelope request object
45+
envelope_definition = cls.make_envelope(envelope_args)
46+
47+
# 2. call Envelopes::create API method
48+
# Exceptions will be caught by the calling function
49+
api_client = create_api_client(base_path=args["base_path"], access_token=args["access_token"])
50+
51+
envelope_api = EnvelopesApi(api_client)
52+
results = envelope_api.create_envelope(account_id=args["account_id"], envelope_definition=envelope_definition)
53+
54+
envelope_id = results.envelope_id
55+
56+
# 3. Create the Recipient View request object
57+
recipient_view_request = RecipientViewRequest(
58+
authentication_method=authentication_method,
59+
recipient_id="1",
60+
return_url=envelope_args["ds_return_url"],
61+
user_name=envelope_args["host_name"],
62+
email=envelope_args["host_email"]
63+
)
64+
# 4. Obtain the recipient_view_url for the embedded signing session
65+
# Exceptions will be caught by the calling function
66+
results = envelope_api.create_recipient_view(
67+
account_id=args["account_id"],
68+
envelope_id=envelope_id,
69+
recipient_view_request=recipient_view_request
70+
)
71+
72+
return {"envelope_id": envelope_id, "redirect_url": results.url}
73+
74+
@classmethod
75+
def make_envelope(cls, args):
76+
"""
77+
Creates envelope
78+
args -- parameters for the envelope:
79+
signer_email, signer_name, signer_client_id
80+
returns an envelope definition
81+
"""
82+
83+
# document 1 (pdf) has tag /sn1/
84+
#
85+
# The envelope has one recipient.
86+
# recipient 1 - signer
87+
with open(path.join(demo_docs_path, DS_CONFIG["doc_pdf"]), "rb") as file:
88+
content_bytes = file.read()
89+
base64_file_content = base64.b64encode(content_bytes).decode("ascii")
90+
91+
# Create the document model
92+
document = Document( # create the DocuSign document object
93+
document_base64=base64_file_content,
94+
name="Example document", # can be different from actual file name
95+
file_extension="pdf", # many different document types are accepted
96+
document_id=1 # a label used to reference the doc
97+
)
98+
99+
# Create the in person signer recipient model
100+
signer = InPersonSigner(
101+
# The signer
102+
host_name = args["host_name"],
103+
host_email = args["host_email"],
104+
signer_name = args["signer_name"],
105+
recipient_id="1",
106+
routing_order="1",
107+
108+
)
109+
110+
# Create a sign_here tab (field on the document)
111+
sign_here = SignHere(
112+
# DocuSign SignHere field/tab
113+
anchor_string="/sn1/",
114+
anchor_units="pixels",
115+
anchor_y_offset="10",
116+
anchor_x_offset="20"
117+
)
118+
119+
# Add the tabs model (including the sign_here tab) to the signer
120+
# The Tabs object wants arrays of the different field/tab types
121+
InPersonSigner.tabs = Tabs(sign_here_tabs=[sign_here])
122+
123+
# Next, create the top level envelope definition and populate it.
124+
envelope_definition = EnvelopeDefinition(
125+
email_subject="Please host this in-person signing session",
126+
documents=[document],
127+
# The Recipients object wants arrays for each recipient type
128+
recipients=Recipients(in_person_signers=[signer]),
129+
status="sent" # requests that the envelope be created and sent.
130+
)
131+
132+
return envelope_definition
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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+
anchor_string="**signature_1**",
155+
anchor_units="pixels",
156+
anchor_y_offset="10",
157+
anchor_x_offset="20"
158+
)
159+
sign_here2 = SignHere(
160+
document_id="2",
161+
anchor_string="/sn1/",
162+
anchor_units="pixels",
163+
anchor_y_offset="10",
164+
anchor_x_offset="20"
165+
)
166+
167+
# Add the tabs model (including the sign_here tabs) to the signer
168+
# The Tabs object wants arrays of the different field/tab types
169+
signer1.tabs = Tabs(
170+
sign_here_tabs=[sign_here1, ],
171+
)
172+
173+
signer2.tabs = Tabs(sign_here_tabs=[sign_here2, ])
174+
175+
# Add the recipients to the envelope object
176+
recipients = Recipients(signers=[signer1, signer2], carbon_copies=[cc])
177+
env.recipients = recipients
178+
179+
# Request that the envelope be sent by setting |status| to "sent".
180+
# To request that the envelope be created as a draft, set to "created"
181+
env.status = args["status"]
182+
183+
return env
184+
185+
@classmethod
186+
def create_document1(cls, args):
187+
""" Creates document 1 -- an html document"""
188+
189+
return f"""
190+
<!DOCTYPE html>
191+
<html>
192+
<head>
193+
<meta charset="UTF-8">
194+
</head>
195+
<body style="font-family:sans-serif;margin-left:2em;">
196+
<h1 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
197+
color: darkblue;margin-bottom: 0;">World Wide Corp</h1>
198+
<h2 style="font-family: "Trebuchet MS", Helvetica, sans-serif;
199+
margin-top: 0px;margin-bottom: 3.5em;font-size: 1em;
200+
color: darkblue;">Order Processing Division</h2>
201+
<h4>Ordered by {args["signer1_name"]}</h4>
202+
<p style="margin-top:0em; margin-bottom:0em;">Email: {args["signer1_email"]}</p>
203+
<p style="margin-top:0em; margin-bottom:0em;">Copy to: {args["cc_name"]}, {args["cc_email"]}</p>
204+
<p style="margin-top:3em;">
205+
Candy bonbon pastry jujubes lollipop wafer biscuit biscuit. Topping brownie sesame snaps sweet roll pie.
206+
Croissant danish biscuit soufflé caramels jujubes jelly. Dragée danish caramels lemon drops dragée.
207+
Gummi bears cupcake biscuit tiramisu sugar plum pastry. Dragée gummies applicake pudding liquorice.
208+
Donut jujubes oat cake jelly-o.
209+
Dessert bear claw chocolate cake gummies lollipop sugar plum ice cream gummies cheesecake.
210+
</p>
211+
<!-- Note the anchor tag for the signature field is in white. -->
212+
<h3 style="margin-top:3em;">Agreed: <span style="color:white;">**signature_1**/</span></h3>
213+
</body>
214+
</html>
215+
"""

app/eSignature/views/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@
3535
from .eg036_delayed_routing import eg036
3636
from .eg037_sms_delivery import eg037
3737
from .eg038_responsive_signing import eg038
38+
from .eg039_in_person_signer import eg039
39+
from .eg040_document_visibility import eg040

0 commit comments

Comments
 (0)