I managed to piece together how to attach a new PDF file to an Evernote
note using their Python API, so I thought it might be useful to have a
post that has all of this information together in one place. I've put
together a complete example that will:
Authenticate to Evernote using a developer token (Oauth2 is a topic
for another day)
Check if a notebook exists; if not, it will create it for you.
Create a new note in the notebook
Attach a PDF file to that note (including calculating the necessary
MD5 hash)
Upload the new note
The complete file is shown at the end of this post, and I'll go through
each function separately in the next sections.
Here's the complete set of imports that took me a while to track down,
even from Evernote's own examples:
import os
import hashlib
import sys
from evernote.api.client import EvernoteClient
import evernote.edam.type.ttypes as Types
import evernote.edam.userstore.constants as UserStoreConstants
from evernote.edam.error.ttypes import EDAMUserException
from evernote.edam.error.ttypes import EDAMSystemException
from evernote.edam.error.ttypes import EDAMNotFoundException
from evernote.edam.error.ttypes import EDAMErrorCode
And here's how to do the authentication using a developer token (Go to
the following places to get a token: Sandbox evernote server or Production Evernote server
def _connect_to_evernote(self, dev_token):
user = None
try:
self.client = EvernoteClient(token=dev_token)
self.user_store = self.client.get_user_store()
user = self.user_store.getUser()
except EDAMUserException as e:
err = e.errorCode
print("Error attempting to authenticate to Evernote: %s - %s" % (EDAMErrorCode._VALUES_TO_NAMES[err], e.parameter))
return False
except EDAMSystemException as e:
err = e.errorCode
print("Error attempting to authenticate to Evernote: %s - %s" % (EDAMErrorCode._VALUES_TO_NAMES[err], e.message))
sys.exit(-1)
if user:
print("Authenticated to evernote as user %s" % user.username)
return True
else:
return False
The important thing is to keep the EvernoteClient object around in
self.client , as this will proved the authenticated access to the
note stores.
The next step is to check whether the required notebook is available,
or if we need to make it. See the _check_and_make_notebook function.
def _get_notebooks(self):
note_store = self.client.get_note_store()
notebooks = note_store.listNotebooks()
return {n.name:n for n in notebooks}
def _create_notebook(self, notebook):
note_store = self.client.get_note_store()
return note_store.createNotebook(notebook)
def _update_notebook(self, notebook):
note_store = self.client.get_note_store()
note_store.updateNotebook(notebook)
return
def _check_and_make_notebook(self, notebook_name, stack=None):
notebooks = self._get_notebooks()
if notebook_name in notebooks:
# Existing notebook, so just update the stack if needed
notebook = notebooks[notebook_name]
if stack:
notebook.stack = stack
self._update_notebook(notebook)
return notebook
else:
# Need to create a new notebook
notebook = Types.Notebook()
notebook.name = notebook_name
if stack:
notebook.stack = stack
notebook = self._create_notebook(notebook)
return notebook
We use the get_note_store API call to get all the
notebooks, and return a dict with the notebook name mapping to the
notebook, in function _get_notebooks . Then, if the desired
notebook is present, we update the stack (in Evernote, a notebook can be
in a collection called a "stack" of notebooks) and return the notebook
pointer. If not, we create a new notebook using the Types.Notebook()
call, and store it using the createNotebook API call in the
note_store .
Next is the real meat of this example, where we create the note with
the attachment:
def _create_evernote_note(self, notebook, filename):
# Create the new note
note = Types.Note()
note.title = os.path.basename(filename)
note.notebookGuid = notebook.guid
note.content = ''
note.content += 'My first PDF upload '
# Calculate the md5 hash of the pdf
md5 = hashlib.md5()
with open(filename,'rb') as f:
pdf_bytes = f.read()
md5.update(pdf_bytes)
md5hash = md5.hexdigest()
# Create the Data type for evernote that goes into a resource
pdf_data = Types.Data()
pdf_data.bodyHash = md5hash
pdf_data.size = len(pdf_bytes)
pdf_data.body = pdf_bytes
# Add a link in the evernote boy for this content
link = ' ' % md5hash
note.content += link
note.content += ' '
# Create a resource for the note that contains the pdf
pdf_resource = Types.Resource()
pdf_resource.data = pdf_data
pdf_resource.mime = "application/pdf"
# Create a resource list to hold the pdf resource
resource_list = []
resource_list.append(pdf_resource)
# Set the note's resource list
note.resources = resource_list
return note
We create a new note using Types.Note() , and set its containing
notebook using the GUID of the notebook. We then start setting the
contents of the note using the Evernote markup language. All the text
and attachment links must be inside the "en-note" tag. The content is
then built up as follows:
Read in the PDF file to attach
Calculate the MD5 hash
Create a new Data container for Evernote and store the hash, size,
and data from the file
Create a link to this file to insert into the content of the note
Create a Resource type to hold the PDF Data , and put it into
a Resource list
Append the resource list to the note
Return this newly formed note
The final step is to upload the note:
def upload_to_notebook(self, filename, notebookname):
# Check if the evernote notebook exists
print ("Checking for notebook named %s" % notebookname)
notebook = self._check_and_make_notebook(notebookname, "my_stack")
print("Uploading %s to %s" % (filename, notebookname))
note = self._create_evernote_note(notebook, filename)
# Store the note in evernote
note_store = self.client.get_note_store()
note = note_store.createNote(note)
import os
import hashlib
import sys
from evernote.api.client import EvernoteClient
import evernote.edam.type.ttypes as Types
import evernote.edam.userstore.constants as UserStoreConstants
from evernote.edam.error.ttypes import EDAMUserException
from evernote.edam.error.ttypes import EDAMSystemException
from evernote.edam.error.ttypes import EDAMNotFoundException
from evernote.edam.error.ttypes import EDAMErrorCode
class EvernoteUpload(object):
def __init__(self, dev_token):
self._connect_to_evernote(dev_token)
def _connect_to_evernote(self, dev_token):
user = None
try:
self.client = EvernoteClient(token=dev_token)
self.user_store = self.client.get_user_store()
user = self.user_store.getUser()
except EDAMUserException as e:
err = e.errorCode
print("Error attempting to authenticate to Evernote: %s - %s" % (EDAMErrorCode._VALUES_TO_NAMES[err], e.parameter))
return False
except EDAMSystemException as e:
err = e.errorCode
print("Error attempting to authenticate to Evernote: %s - %s" % (EDAMErrorCode._VALUES_TO_NAMES[err], e.message))
sys.exit(-1)
if user:
print("Authenticated to evernote as user %s" % user.username)
return True
else:
return False
def _get_notebooks(self):
note_store = self.client.get_note_store()
notebooks = note_store.listNotebooks()
return {n.name:n for n in notebooks}
def _create_notebook(self, notebook):
note_store = self.client.get_note_store()
return note_store.createNotebook(notebook)
def _update_notebook(self, notebook):
note_store = self.client.get_note_store()
note_store.updateNotebook(notebook)
return
def _check_and_make_notebook(self, notebook_name, stack=None):
notebooks = self._get_notebooks()
if notebook_name in notebooks:
# Existing notebook, so just update the stack if needed
notebook = notebooks[notebook_name]
if stack:
notebook.stack = stack
self._update_notebook(notebook)
return notebook
else:
# Need to create a new notebook
notebook = Types.Notebook()
notebook.name = notebook_name
if stack:
notebook.stack = stack
notebook = self._create_notebook(notebook)
return notebook
def _create_evernote_note(self, notebook, filename):
# Create the new note
note = Types.Note()
note.title = os.path.basename(filename)
note.notebookGuid = notebook.guid
note.content = ''
note.content += 'My first PDF upload '
# Calculate the md5 hash of the pdf
md5 = hashlib.md5()
with open(filename,'rb') as f:
pdf_bytes = f.read()
md5.update(pdf_bytes)
md5hash = md5.hexdigest()
# Create the Data type for evernote that goes into a resource
pdf_data = Types.Data()
pdf_data.bodyHash = md5hash
pdf_data.size = len(pdf_bytes)
pdf_data.body = pdf_bytes
# Add a link in the evernote boy for this content
link = ' ' % md5hash
note.content += link
note.content += ' '
# Create a resource for the note that contains the pdf
pdf_resource = Types.Resource()
pdf_resource.data = pdf_data
pdf_resource.mime = "application/pdf"
# Create a resource list to hold the pdf resource
resource_list = []
resource_list.append(pdf_resource)
# Set the note's resource list
note.resources = resource_list
return note
def upload_to_notebook(self, filename, notebookname):
# Check if the evernote notebook exists
print ("Checking for notebook named %s" % notebookname)
notebook = self._check_and_make_notebook(notebookname, "my_stack")
print("Uploading %s to %s" % (filename, notebookname))
note = self._create_evernote_note(notebook, filename)
# Store the note in evernote
note_store = self.client.get_note_store()
note = note_store.createNote(note)
if __name__ == '__main__':
dev_token = "YOUR_DEV_TOKEN"
p = EvernoteUpload(dev_token)
p.upload_to_notebook('test_sherlock.pdf', 'boom')