Posted by virantha on Mon 04 November 2013

Creating new notes and attaching files using Evernote's Python API

Documents to Evernote

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:

  1. Authenticate to Evernote using a developer token (Oauth2 is a topic for another day)
  2. Check if a notebook exists; if not, it will create it for you.
  3. Create a new note in the notebook
  4. Attach a PDF file to that note (including calculating the necessary MD5 hash)
  5. 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.

1   Imports

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
view raw _imports.py hosted with ❤ by GitHub

2   Authentication

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
view raw _auth.py hosted with ❤ by GitHub

The important thing is to keep the EvernoteClient object around in self.client, as this will proved the authenticated access to the note stores.

3   Handle notebooks

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
view raw _notebook.py hosted with ❤ by GitHub

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.

4   Create the new note with attachment

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 = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
note.content += '<en-note>My first PDF upload<br/>'
# 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 = '<en-media type="application/pdf" hash="%s"/>' % md5hash
note.content += link
note.content += '</en-note>'
# 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
view raw _note.py hosted with ❤ by GitHub

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

5   Uploading the 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)
view raw _upload.py hosted with ❤ by GitHub

6   Complete example

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 = '<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">'
note.content += '<en-note>My first PDF upload<br/>'
# 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 = '<en-media type="application/pdf" hash="%s"/>' % md5hash
note.content += link
note.content += '</en-note>'
# 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')

© Virantha Ekanayake. Built using Pelican. Modified svbhack theme, based on theme by Carey Metcalfe