Last active
December 15, 2015 04:29
-
-
Save mcsquaredjr/5201619 to your computer and use it in GitHub Desktop.
Revisions
-
mcsquaredjr revised this gist
Mar 21, 2013 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,3 @@ # Script to install greencal and required libraries import tarfile @@ -14,7 +13,7 @@ ############################################################ try: os.mkdir('greencal') json_url = 'https://api.github.com/gists/15a18fa914219c5546f9Zz' gist_json = requests.get(json_url).text gist_info = json.loads(gist_json) files = gist_info['files'] -
mcsquaredjr revised this gist
Mar 20, 2013 . 1 changed file with 125 additions and 469 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,483 +1,139 @@ # Script to install greencal and required libraries import tarfile import shutil import urllib import requests import os import json ############################################################ # INSTALL GREEN_CAL.PY # ############################################################ try: os.mkdir('greencal') json_url = 'https://api.github.com/gists/15a18fa914219c5546f9' gist_json = requests.get(json_url).text gist_info = json.loads(gist_json) files = gist_info['files'] file_info = files.values()[0] filename = file_info['filename'] content = file_info['content'] print 'Installing green_cal.py...' f = open('green_cal.py', 'w+') f.write(content) f.close() shutil.move('green_cal.py', 'greencal') print 'green_cal.py installed.' except Exception, e: print e ############################################################ # INSTALL EVERNOTE SDK # ############################################################ try: os.mkdir('evernote-sdk') print 'Downloading evernote...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/e/evernote/evernote-1.23.2.tar.gz') print 'Installing evernote...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('evernote-1.23.2/lib/evernote', 'evernote-sdk/evernote') shutil.move('evernote-1.23.2/lib/thrift', 'evernote-sdk/thrift') shutil.rmtree('evernote-1.23.2') print 'evernote-1.23.2 installed' print 'Downloading httplib2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/h/httplib2/httplib2-0.7.7.tar.gz') print 'Installing httplib2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('httplib2-0.7.7/python2/httplib2', 'evernote-sdk/httplib2') shutil.rmtree('httplib2-0.7.7') print 'Downloading oauth2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/o/oauth2/oauth2-1.5.211.tar.gz') print 'Installing oauth2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('oauth2-1.5.211/oauth2', 'evernote-sdk/oauth2') shutil.rmtree('oauth2-1.5.211') print 'oauth2-1.5.211 installed' except: pass ############################################################ # INSTALL GOOGLE API # ############################################################ try: os.mkdir('google-api') print 'Downloading google-api-client...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/g/google-api-python-client/google-api-python-client-1.0.tar.gz') print 'Installing google-api-client...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('google-api-python-client-1.0/apiclient', 'google-api/apiclient') shutil.move('google-api-python-client-1.0/oauth2client', 'google-api/oauth2client') shutil.move('google-api-python-client-1.0/uritemplate', 'google-api/uritemplate') shutil.rmtree('google-api-python-client-1.0') print 'google-api-python-client-1.0 installed.' print 'Downloading python-gflags...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/p/python-gflags/python-gflags-2.0.tar.gz') print 'Installing python-gflags...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('python-gflags-2.0/gflags.py', 'google-api/gflags.py') shutil.move('python-gflags-2.0/gflags2man.py', 'google-api/gflags2man.py') shutil.move('python-gflags-2.0/gflags_validators.py', 'google-api/gflags_validators.py') shutil.rmtree('python-gflags-2.0') print 'python-gflags-2.0 instaled.' print 'Downloading httplib2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/h/httplib2/httplib2-0.7.7.tar.gz') print 'Installing httplib2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('httplib2-0.7.7/python2/httplib2', 'google-api/httplib2') shutil.rmtree('httplib2-0.7.7') print 'httplib2 installed.' except: pass ############################################################ # INSTALL PARSEDATETIME # ############################################################ try: os.mkdir('parsedatetime') print 'Downloading parsedatetime...' filename, headers = urllib.urlretrieve('https://github.com/bear/parsedatetime/archive/master.tar.gz') print 'Installing parsedatetime...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('parsedatetime-master/parsedatetime/__init__.py', 'parsedatetime') shutil.move('parsedatetime-master/parsedatetime/parsedatetime.py', 'parsedatetime') shutil.move('parsedatetime-master/parsedatetime/pdt_locales.py', 'parsedatetime') shutil.rmtree('parsedatetime-master') print 'parsedatetime installed.' except: pass import editor editor.reload_files() print 'All done.' -
mcsquaredjr revised this gist
Mar 20, 2013 . 1 changed file with 469 additions and 127 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,141 +1,483 @@ ''' Green Calendar: process Evernote notes, create events, and cross-reference notes with events. ''' __author__ = 'Serge Boyko aka mcsquaredjr' __version__ = '0.3' __date__ = 'Tuesday, March 5th, 2013' __email__ = "serge dot boyko at gmail dot com" import sys # Try to determine if we are in Pythonista try: # to import a Pythonista-specific module from scene import Layer in_pythonista = True del(sys.modules['scene']) except ImportError: in_pythonista = False if in_pythonista: sys.path.append('../') sys.path.append('../evernote-sdk') sys.path.append('../google-api') import evernote.edam.userstore.constants as UserStoreConstants import evernote.edam.type.ttypes as Types import evernote.edam.error.ttypes as Errors from evernote.api.client import EvernoteClient from evernote.edam.notestore import NoteStore import gflags import httplib2 from apiclient.discovery import build from oauth2client.file import Storage from oauth2client.client import OAuth2WebServerFlow from oauth2client.tools import run import datetime import time import parsedatetime as pdt import argparse # Replace with the values obtained from Evernote dev site EN_TOKEN = 'S=s4:U=4e6ce:E=144843b4814:C=13d2c8a1c15:P=1cd:A=en-devtoken:V=2:H=571cd0a766b20553e40416d9f171f960' EN_URL = 'https://www.evernote.com' # Replace with the values obtained from Google GC_CLIENT_ID = '187093465256.apps.googleusercontent.com' GC_CLIENT_SECRET = '7_iClvcZBpyn9rFU63sFKNPT' GC_SCOPE = 'https://www.googleapis.com/auth/calendar' GC_USER_AGENT = 'Green Calendar for Evernote' GC_DEV_KEY = 'AIzaSyDI982IYusCZv2Rf3z5rQ7aGuvtLtcEbOM' ############################################################ # CLASS EVERNOTE_PROCESSOR # ############################################################ class Evernote_Processor(object): '''Represents objects and methods to access, update, and process Evernote notes and their attributes to extract due dates and link with Google Calendar Events. ''' def __init__(self, token): self.token = token self.client = EvernoteClient(token=EN_TOKEN, sandbox=False) self.user_store = self.client.get_user_store() self.note_store = self.client.get_note_store() self.shard_id = self.get_shard_id() def check_version(self): '''Check for version''' version_ok = self.user_store.checkVersion( "Version check", UserStoreConstants.EDAM_VERSION_MAJOR, UserStoreConstants.EDAM_VERSION_MINOR ) if not version_ok: return -1 else: return 0 def _make_note(self, title, content, url): '''Prepare a note to be posted''' note = Types.Note() note.title = title # Set up note attributes attrs = Types.NoteAttributes() attrs.sourceURL = url note.attributes = attrs note.content = '<?xml version="1.0" encoding="UTF-8"?>' note.content += '<!DOCTYPE en-note SYSTEM ' \ '"http://xml.evernote.com/pub/enml2.dtd">' # Wrap content in <en-note> note.content += '<en-note>' note.content += content note.content += '</en-note>' return note def get_shard_id(self): ''' Get the User from user_store and return the user's shard ID ''' try: user = self.user_store.getUser(self.token) except (Errors.EDAMUserException, Errors.EDAMSystemException), e: print "Exception while getting user's shardID:" print type(e), e return None if hasattr(user, 'shardId'): return user.shardId return None def post_note(self, title, note, url): '''Post a note to Evernote''' ver = self.check_version() if ver < 0: print '*** VERSION ERROR: Update client to the latest version.' else: note = self._make_note(title, note, url) try: created_note = self.note_store.createNote(note) except (Errors.EDAMUserException, Errors.EDAMSystemException), e: print "Exception while getting user's shardID:" print type(e), e return None if hasattr(created_note, 'guid'): return created_note.guid return None def get_notebook_guid(self, notebook_name): '''Return notebooks guid given the notebook name''' if notebook_name is not None: notebooks = self.note_store.listNotebooks() # Iterate over notebooks until a given mame is not found # Return None if non-existing name is given matched_notebook = next((notebook for notebook in notebooks \ if notebook.name == notebook_name), None) if matched_notebook is not None: guid = matched_notebook.guid else: guid = None print '*** Warning: notebook {0} was not found.'.format(notebook_name) else: guid = self.note_store.getDefaultNotebook().guid return guid def get_notes(self, notebook_name, words=None, offset=0, max_num=25): '''Get max_num notes from a notebook''' # This is how we construct a filter, we cannot get it from # an instance of the NoteStore filter = NoteStore.NoteFilter() # Evernote search grammar is described here: # http://dev.evernote.com/documentation/cloud/chapters/search_grammar.php guid = self.get_notebook_guid(notebook_name) if guid is not None: filter.notebookGuid = guid if words is not None: # words is an attribute in Python filter.words = words try: notes_list = self.note_store.findNotes(self.token, filter, 0, max_num) except Exception, e: print 'Exception while finding notes: ', e notes_list = None else: notes_list = None # NoteList is not a Python list, but a structure return notes_list def get_note_url(self, guid): ''' Share a single note and return the public URL for the note ''' if not self.shard_id: self.shard_id = self.get_shard_id() if not self.shard_id: raise SystemExit try: share_key = self.note_store.shareNote(self.token, guid) except (EDAMNotFoundException, EDAMSystemException, EDAMUserException), e: print "Error sharing note:" print type(e), e return None return "%s/shard/%s/sh/%s/%s" % \ (EN_URL, self.shard_id, guid, share_key) def get_note_due_date(self, note): '''Extract due date from a note title''' title = note.title ind = title.find('__') ddate = title[ind+2:] # Rerurn note's title (without due date) and due date return (title[:ind].strip(), ddate.strip()) def update_note(self, note, new_title, url): '''Update note title for processed notes''' note.title = new_title attrs = Types.NoteAttributes() attrs.sourceURL = url note.attributes = attrs # Update the note with new_title try: updated_note = self.note_store.updateNote(note) return updated_note.guid except Exception, e: print 'Exception while updating note: ', e return None ############################################################ # CLASS GCAL_EVENT # ############################################################ class GCal_Event(object): '''Access and create events in Google Calendar and link with Evernote notes representing actions with due dates.''' def __init__(self): FLAGS = gflags.FLAGS FLOW = OAuth2WebServerFlow( client_id=GC_CLIENT_ID, client_secret=GC_CLIENT_SECRET, scope=GC_SCOPE, user_agent=GC_USER_AGENT) # We need to disable it for an installed app FLAGS.auth_local_webserver = False # Read credentials if available storage = Storage('calendar.dat') credentials = storage.get() if credentials is None or credentials.invalid == True: credentials = run(FLOW, storage) # Create an httplib2.Http object to handle our HTTP requests # and authorize it with our good Credentials. http = httplib2.Http() http = credentials.authorize(http) # Build a service object for interacting with the API self.service = build(serviceName='calendar', version='v3', http=http, developerKey=GC_DEV_KEY) def get_service(self): '''Get service object''' return self.service def generate_event(self, start, end, tz, location, summary, desc): '''Generate event from provided data''' start_dict = dict() start_dict['dateTime'] = start start_dict['timeZone'] = tz end_dict = dict() end_dict['dateTime'] = end end_dict['timeZone'] = tz evt_dict = dict() evt_dict['kind'] = 'calendar#event' evt_dict['status'] = 'confirmed' evt_dict['summary'] = summary evt_dict['description'] = desc evt_dict['summary'] = summary evt_dict['start'] = start_dict evt_dict['end'] = end_dict evt_dict['location'] = location evt_dict['colorId'] = '2' # Mint! return evt_dict def create_event(self, cal_id, evt): '''Create event in Google calendar, identified by cal_id''' try: events = self.get_service().events() created_evt = events.insert(body=evt, calendarId=cal_id).execute() return created_evt['htmlLink'] except Exception, e: print 'Exception while creating event: ', e return None ############################################################ # CLASS GREEN_CALENDAR # ############################################################ class Green_Calendar(object): '''Controller class''' def __init__(self): self.ep = Evernote_Processor(EN_TOKEN) self.gc = GCal_Event() self.start_time = time.time() def process_due_date(self, due_date_str, duration=15): '''Parse date string and return start date/time in format used by Google Calendar (RFC-3889). ''' c = pdt.Constants() c.BirthdayEpoch = 80 p = pdt.Calendar(c) result = p.parse(due_date_str) # Unpack the tuple and convert to string start = datetime.datetime(*result[0][0:6]) end = start + datetime.timedelta(minutes=duration) # Ignore seconds start_str = start.strftime('%Y-%m-%dT%H:%M:00') end_str = end.strftime('%Y-%m-%dT%H:%M:00') return (start_str, end_str, result[1]) def _setup(self, notebooks, # list of notebooks, use default if None sep, # separator before due date sep_proc, # separator in processed notes max_age, # seach notes not older than max_age cal_id # Google calendar ID ): '''Configure Green Calendar''' self.notebooks = notebooks self.sep = sep self.sep_proc = sep_proc self.max_age = max_age self.cal_id = cal_id def run(self): '''Glue all pieces together, parse dates, cross-link notes and events. ''' # Configure search words = 'intitle:"{0}"'.format(self.sep) # Find all matching notes notes_list = [] if self.notebooks is None: notes = self.ep.get_notes(None, words=words, offset=0, max_num=25) notes_list.append(notes.notes) else: if type(self.notebooks) is list: for notebook in self.notebooks: #print notebook notes = self.ep.get_notes(notebook, words=words, offset=0, max_num=25) if notes is not None: notes_list.append(notes.notes) else: raise TypeError('Notebooks must be a list or a None-type.') # Flatten the note list notes_list = [item for sublist in notes_list for item in sublist] fmt ='{:<5}{:<24}{:<45}{:<5}'.format('No.', 'Due date/time', 'Note title', 'Result') print '\nPROCESSING REPORT' print '-'*len(fmt) print fmt print '-'*len(fmt) # Start processing here for i, note in enumerate(notes_list): title, ddate = self.ep.get_note_due_date(note) (start, end, result) = self.process_due_date(ddate) if result > 0: note_url = self.ep.get_note_url(note.guid) # Create time zone string # TODO: take into account periods when Europe ans USA are # not in sync when switching to the summer/winter time tz = str.format('{0:+06.2f}', float(-time.timezone)/3600) tz = 'UTC' + tz location = 'Evernote' description = note_url + '\n\n--Created by Green Calendar' evt = self.gc.generate_event(start, end, tz, location, title, description) # Construct new title title = title + ' ' + self.sep_proc + ' ' + start # Create event and update the note evt_url = self.gc.create_event(self.cal_id, evt) guid = self.ep.update_note(note, title, evt_url) if (guid is not None) and (evt_url is not None): result = 'OK' else: result = 'Error' # Truncate the title for report title_ = (title[:40] + '...') if len(title) > 40 else title fmt ='{:<5}{:<24}{:<45}{:^4}'.format(i+1, start, title_, result) print fmt else: # We cannot parse the date; no events created result = 'Error' title_ = (title[:40] + '...') if len(title) > 40 else title fmt ='{:<5}{:<24}{:<45}{:<5}'.format(i+1, 'UNDEFINED', title_, result) print fmt print '-'*len(fmt) e_time = time.time() - self.start_time print 'Elapsed time: {0:f}'.format(e_time) print 'The number of processed notes is: {0:d}'.format(len(notes_list)) if __name__ == '__main__': # Change the values below if you want different defaults # You do not need to supply a value for notebooks if you want # to use the default notebook defaults = dict() defaults['max_age'] = 7 defaults['sep'] = '__' defaults['sep_proc'] = '##' defaults['cal_id'] = 'primary' # Create a parser parser = argparse.ArgumentParser(description='parse evernote notes, \ create events in google calendar, and \ cross-reference notes and events', usage='python green_calendar.py -h', prog='Green Calendar', epilog='report bugs to [email protected].') parser.add_argument('--notebooks', '-n', help='whitepace-delimited list of notebooks to \ search within', nargs='+', type=str, metavar='LIST' ) parser.add_argument('--max_age', '-a', help='do not process notes older than M days', type=int, metavar='M', default=defaults['max_age'] ) parser.add_argument('--sep', '-s', help='separator used for non-processed notes \ (default is "__")', type=str, metavar='S', default=defaults['sep'] ) parser.add_argument('--sep_proc', '-p', help='separator used for proceesed notes \ (default is "##")', type=str, metavar='P', default=defaults['sep_proc'] ) parser.add_argument('--cal_id', '-c', help='id of the calendar used for created events', type=str, metavar='ID', default=defaults['cal_id'] ) args = parser.parse_args() # Now run! gc = Green_Calendar() gc._setup(notebooks=args.notebooks, sep=args.sep, sep_proc=args.sep_proc, max_age=args.max_age, cal_id=args.cal_id) gc.run() -
mcsquaredjr revised this gist
Mar 20, 2013 . 1 changed file with 10 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,20 +1,28 @@ # Script to install greencal and required libraries import tarfile import shutil import urllib import requests import os import json ############################################################ # INSTALL GREEN_CAL.PY # ############################################################ try: os.mkdir('greencal') json_url = 'https://api.github.com/gists/15a18fa914219c5546f9' gist_json = requests.get(json_url).text gist_info = json.loads(gist_json) files = gist_info['files'] file_info = files.values()[0] filename = file_info['filename'] content = file_info['content'] print 'Installing green_cal.py...' f = open('green_cal.py', 'w') f.wrire(r.content) @@ -131,4 +139,3 @@ editor.reload_files() print 'All done.' -
mcsquaredjr revised this gist
Mar 20, 2013 . 1 changed file with 1 addition and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,4 @@ # Script to install greencal and required libraries import tarfile @@ -14,7 +13,7 @@ ############################################################ try: os.mkdir('greencal') url = 'https://gist.github.com/mcsquaredjr/15a18fa914219c5546f9' r = request.get(url) print 'Installing green_cal.py...' f = open('green_cal.py', 'w') -
mcsquaredjr created this gist
Mar 20, 2013 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,135 @@ # Script to install greencal and required libraries import tarfile import shutil import urllib import requests import os ############################################################ # INSTALL GREEN_CAL.PY # ############################################################ try: os.mkdir('greencal') url = '' r = request.get(url) print 'Installing green_cal.py...' f = open('green_cal.py', 'w') f.wrire(r.content) f.close() shutil.move('green_cal.py', 'greencal') print 'green_cal.py installed.' except: pass ############################################################ # INSTALL EVERNOTE SDK # ############################################################ try: os.mkdir('evernote-sdk') print 'Downloading evernote...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/e/evernote/evernote-1.23.2.tar.gz') print 'Installing evernote...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('evernote-1.23.2/lib/evernote', 'evernote-sdk/evernote') shutil.move('evernote-1.23.2/lib/thrift', 'evernote-sdk/thrift') shutil.rmtree('evernote-1.23.2') print 'evernote-1.23.2 installed' print 'Downloading httplib2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/h/httplib2/httplib2-0.7.7.tar.gz') print 'Installing httplib2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('httplib2-0.7.7/python2/httplib2', 'evernote-sdk/httplib2') shutil.rmtree('httplib2-0.7.7') print 'Downloading oauth2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/o/oauth2/oauth2-1.5.211.tar.gz') print 'Installing oauth2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('oauth2-1.5.211/oauth2', 'evernote-sdk/oauth2') shutil.rmtree('oauth2-1.5.211') print 'oauth2-1.5.211 installed' except: pass ############################################################ # INSTALL GOOGLE API # ############################################################ try: os.mkdir('google-api') print 'Downloading google-api-client...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/g/google-api-python-client/google-api-python-client-1.0.tar.gz') print 'Installing google-api-client...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('google-api-python-client-1.0/apiclient', 'google-api/apiclient') shutil.move('google-api-python-client-1.0/oauth2client', 'google-api/oauth2client') shutil.move('google-api-python-client-1.0/uritemplate', 'google-api/uritemplate') shutil.rmtree('google-api-python-client-1.0') print 'google-api-python-client-1.0 installed.' print 'Downloading python-gflags...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/p/python-gflags/python-gflags-2.0.tar.gz') print 'Installing python-gflags...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('python-gflags-2.0/gflags.py', 'google-api/gflags.py') shutil.move('python-gflags-2.0/gflags2man.py', 'google-api/gflags2man.py') shutil.move('python-gflags-2.0/gflags_validators.py', 'google-api/gflags_validators.py') shutil.rmtree('python-gflags-2.0') print 'python-gflags-2.0 instaled.' print 'Downloading httplib2...' filename, headers = urllib.urlretrieve('https://pypi.python.org/packages/source/h/httplib2/httplib2-0.7.7.tar.gz') print 'Installing httplib2...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('httplib2-0.7.7/python2/httplib2', 'google-api/httplib2') shutil.rmtree('httplib2-0.7.7') print 'httplib2 installed.' except: pass ############################################################ # INSTALL PARSEDATETIME # ############################################################ try: os.mkdir('parsedatetime') print 'Downloading parsedatetime...' filename, headers = urllib.urlretrieve('https://github.com/bear/parsedatetime/archive/master.tar.gz') print 'Installing parsedatetime...' t = tarfile.open(filename, 'r') t.extractall() t.close() shutil.move('parsedatetime-master/parsedatetime/__init__.py', 'pdt') shutil.move('parsedatetime-master/parsedatetime/parsedatetime.py', 'pdt') shutil.move('parsedatetime-master/parsedatetime/pdt_locales.py', 'pdt') shutil.rmtree('parsedatetime-master') ptint 'parsedatetime installed.' except: pass import editor editor.reload_files() print 'All done.'