Evernote API & Python 3.x, #3
In Part 2 of this tutorial we looked at the method getNoteByGuid. This method calls getAllResourceData of our class EvernoteStore(). It has the argument resources which is returned by the function getNote of the module NoteStore.
def getAllResourceData(self, resources):
allResourceData = []
if resources != None:
for i in resources:
fileName = i.attributes.fileName
try:
if fileName.find('.') < 0: fileName = fileName+'.'+i.mime.split('/')[1]
except: fileName = i.guid
allResourceData.append([{'enMediaHash': i.data.bodyHash.hex(), 'fileName': fileName, 'body': i.data.body, 'size': i.data.size, 'mime': i.mime, 'width': i.width, 'height': i.height}])
return allResourceData
resources is a list. Each list item i describes a resource (e.g. an image). The filename is stored in i.attributes.fileName. Sometimes the extension is missing in fileName. The extension is stored in i.mime (e.g. 'image/jpeg'). Sometimes we must take the guid of a resource as it's fileName.
Each resource has the field data.body (the content of the resource e.g. the binary representation of an image) and it's hash value (i.data.bodyHash.hex()). This hash value can be used to find a resource in the content of a note. Below is an example of an image reference in the note content.
<en-media hash="791054554cb482de6b29c89e09d53956" style="display: block !important; height: auto !important; max-width: 100%;" type="image/jpeg"/>
The return value of the method above, allResourceData is a list. Each list item is a dict which completely describes an image resource.
Now we are able to access a notebook by it's name and a note by it's title and we can retrieve all data of this note. What, if we want to find all notes since a specific date or all notes with some tags? As mentioned earlier this can be achieved with Evernotes powerful search grammar. To find all notes which were updated during the last 24 hours, the search string would be 'updated:day'. To find all notes with the tags 'Wine' and 'Dine' the search string would be 'tag:Wine tag:Dine' and if we want to exclude all notes with the tag 'Italy' the search string would be '-tag:Italy'. Of course we can combine everything to one search string: 'updated:day tag:Wine tag:Dine -tag:Italy'. The NoteFilter (remember our method getNoteByTitle in Part 2) would become:
search = 'updated:day tag:Wine tag:Dine -tag:Italy'
filter = NoteFilter()
filter.timeZone = self.timeZone
filter.words = search
filter.order = NoteSortOrder.UPDATED
filter.notebookGuid = self.noteBookGuid
filter.inactive = inactive
If we want to periodically check if there were changes in a notebook we use Polling. From the documentation: "Each Evernote account has a variable named updateCount that makes it easy for you to tell whether the account has been changed since the last time you looked.". We get the updateCount with the function getSyncState() of the module NoteStore. This function also returns the currentTime together with some other data. We store the updateCount and the currentTime in a text file. Whenever we do our polling, we read from this text file: getLastUpdate(). Then we call getSyncState().
lastUpdateCount, lastUpdateTime = self.getLastUpdate()
result = self.noteStore.getSyncState()
newUpdateCount = result.updateCount
newUpdateTime = result.currentTime
If newUpdateCount > lastUpdateCount we know, that something has changed in our Evernote user account. We then check if a relevant note in a specific notebook has changed. Example: A relevant note is a note with a tag 'published' from the notebook 'Food'. Therefor we have to define a filter with the notebookGuid of the Food notebook and an update after lastUpdateTime and the tag 'published'. The search string will be: 'updated:20200704T150000Z tag:published'.
And here is the complete code for this tutorial:
from evernote.edam.type.ttypes import NoteSortOrder
from evernote.edam.notestore.ttypes import NoteFilter, NotesMetadataResultSpec
from evernote.api.client import EvernoteClient
from datetime import datetime as dt
import os
class EvernoteStore():
def __init__(self, sandbox=False):
if sandbox == True: authToken='your authToken for the development server'
elif sandbox == False: authToken='your authToken for the production server'
self.authToken = authToken
self.sandbox = sandbox
self.china = False
self.noteBookName = None
self.noteBookGuid = None
self.client = EvernoteClient(token=self.authToken, sandbox=self.sandbox, china=self.china)
self.userStore = self.client.get_user_store()
self.user = self.userStore.getUser().username # e.g. jacquesbopp
self.noteStore = self.client.get_note_store()
self.noteBookList = None
self.setNoteBooksList()
self.noteBookTags = None
self.timeZone = 'Europe/Zurich'
self.logDirectory = None
def setNoteBooksList(self):
notebooks = self.noteStore.listNotebooks()
self.noteBookList = {'name': [],'guid':[]}
for n in notebooks:
self.noteBookList['name'].append(n.name)
self.noteBookList['guid'].append(n.guid)
return None
def getNoteBooksList(self):
return self.noteBookList
def setNotebook(self, noteBookName):
self.noteBookName = noteBookName
self.setNoteBookGuid()
return None
def setNoteBookGuid(self):
self.noteBookGuid = self.noteBookList['guid'][self.noteBookList['name'].index(self.noteBookName)]
return None
def getNoteBookTags(self):
tags = self.noteStore.listTagsByNotebook(self.authToken, self.noteBookGuid)
self.noteBookTags = {'name': [],'guid':[],'parentGuid':[]}
for t in tags:
self.noteBookTags['name'].append(t.name)
self.noteBookTags['guid'].append(t.guid)
self.noteBookTags['parentGuid'].append(t.parentGuid)
return self.noteBookTags
def getNoteByTitle(self, title):
search = 'intitle:'+title
# filter
filter = NoteFilter()
filter.timeZone = self.timeZone
filter.words = search
filter.order = NoteSortOrder.UPDATED
filter.notebookGuid = self.noteBookGuid
# resultspec
resultSpec = NotesMetadataResultSpec()
notes = self.noteStore.findNotesMetadata(self.authToken, filter, 0, 250, resultSpec)
if len(notes.notes) > 0:
note = self.getNoteByGuid(notes.notes[0].guid)
else: note = {'Result': 'Error'}
return note
def getNoteByGuid(self, guid):
noteE = self.getNoteData(guid)
note = {}
note['Result'] = 'success:'
note['Title'] = noteE.title
note['Guid'] = noteE.guid
note['Notebook'] = self.noteBookList['name'][self.noteBookList['guid'].index(noteE.notebookGuid)]
note['Created'] = enDateTimeToPythonDateTime(noteE.created)
note['Updated'] = enDateTimeToPythonDateTime(noteE.updated)
note['Deleted'] = enDateTimeToPythonDateTime(noteE.deleted)
note['Content'] = self.noteStore.getNoteContent(self.authToken, noteE.guid)
note['TagsGuid'] = noteE.tagGuids
note['TagsName'] = self.noteStore.getNoteTagNames(self.authToken, noteE.guid)
note['Resources'] = self.getAllResourceData(noteE.resources)
return note
def getNoteData(self, guid):
# getNote is depreciated but still used because getNoteWithResultSpec does not work
note = self.noteStore.getNote(self.authToken, guid, True, True, True, True)
return note
def getAllResourceData(self, resources):
allResourceData = []
if resources != None:
for i in resources:
fileName = i.attributes.fileName
try:
if fileName.find('.') < 0: fileName = fileName+'.'+i.mime.split('/')[1]
except: fileName = i.guid
allResourceData.append([{'enMediaHash': i.data.bodyHash.hex(), 'fileName': fileName, 'body': i.data.body, 'size': i.data.size, 'mime': i.mime, 'width': i.width, 'height': i.height}])
return allResourceData
def getNotesBySearchString(self, search):
notesGuid = []
notes = []
# filter
filter = NoteFilter()
filter.timeZone = self.timeZone
filter.words = search
filter.order = NoteSortOrder.UPDATED
filter.notebookGuid = self.noteBookGuid
filter.inactive = False
# resultspec
resultSpec = NotesMetadataResultSpec()
# resultSpec.includeTitle = True
# resultSpec.includeContentLength = True
# resultSpec.includeResourcesData = True
# resultSpec.includeCreated = True
# resultSpec.includeUpdated = True
# resultSpec.includeDeleted = True
# resultSpec.includeTagGuids = True
# resultSpec.includeAttributes = True
# noteList
offset = 0
maxNotes =250
while True:
notes = self.noteStore.findNotesMetadata(self.authToken, filter, offset, maxNotes, resultSpec)
for i in notes.notes: notesGuid.append(i.guid)
if len(notesGuid) < notes.totalNotes: offset = offset+maxNotes
else: break
notes = []
for i in notesGuid: notes.append(store.getNoteByGuid(i))
return notes
def getNotesSinceLastCheck(self, search):
lastUpdateCount, lastUpdateTime = self.getLastUpdate() # your log file from last check with 1 line: 45396::1598513999428 (delimiter='::', 45396=updateCount, 1598513999428=time in seconds of last check)
result = self.noteStore.getSyncState()
newUpdateCount = result.updateCount
newUpdateTime = result.currentTime
if newUpdateCount > lastUpdateCount:
search = 'updated:'+enDateTimeToPythonDateTime(lastUpdateTime).strftime('%Y%m%dT%H%M%S')+' '+search
notes = self.getNotesBySearchString(search)
return notes
def setLogDirectory(self, logDirectory):
self.logDirectory = logDirectory
return None
def getLastUpdate(self):
fileName = self.logDirectory+'evernoteLastSyncState.txt'
if os.path.isfile(fileName):
with open(fileName) as f: content = f.read()
content = content.split('::')
lastUpdateCount = int(content[0])
lastUpdateTime = int(content[1])
else:
lastUpdateCount = 0
lastUpdateTime = 0
return lastUpdateCount, lastUpdateTime
def enDateTimeToPythonDateTime(enTime):
try: date = dt.fromtimestamp(enTime/1000)
except: date = None
return date
if __name__ == '__main__':
store = EvernoteStore()
notebooks = store.getNoteBooksList()
store.setNotebook('your notebook')
tags = store.getNoteBookTags()
note = store.getNoteByTitle('your note title')
notes = store.getNoteBySearchString('updated:day tag:Wine tag:Dine -tag:Italy')
store.setLogDirectory('your log directory')
notes = store.getNotesSinceLastCheck('tag:published')
I hope this little tutorial was helpful. For any question, comments you can contact me: JacquesBopp