Thursday, November 27, 2014

Python Knowledge

I learned a few things from an article linked to on HackerNews: Top 10 Python idioms I wish I'd learned earlier

There was also a lot of great information in the comments thread for the post. This included a link to the youtube video below:

Tuesday, November 25, 2014

Google API's

I've been exporting info from my iRacing Stats application to html to do weekly and end of season updates on my iRacing blog. This process had been fairly manual; I'd manually upload the graph images to blogger, create a new blog post and paste in the exported html and edit the img tags to point to the uploaded images. Its not that it was too painful, but since it was a repetitive task I wanted to see if I could automate it.

So I dived into the Google API doco and pretty quickly worked out how to get my python application to post a new blog update. Getting the images uploaded was more painful, since blogger actually uses picasa to store images and the picasa API is terrible. I ended up using Google Drive to store the images, which means I needed to handle a few more steps than just uploading; namely changing the permissions to public viewing, uploading to a folder and retrieving the public url.

I've got it all working now, including prompting the user for only their blog's URL from which it app pulls the BlogID (rather than having to have the user go off and find it). All in all I'm quite proud of getting it all working. I just hope Google don't go changing their APIs in the near future.

I'm sure the knowledge I've gained here will be useful in many other projects.

Here's a bunch of links which I found helpful:

  • Authorized Google API access from Python
  • google-api-python-client
  • oauth2client

    Here's a bit of code:

    from oauth2client import file, client, tools
    from apiclient.discovery import build
    from apiclient.http import MediaFileUpload
    from httplib2 import Http
    
    def blogger_post(outfile):
     try:
      html_file = open(outfile)
      html_lines = html_file.read()
      chop_start = html_lines.find('')
      chop_end = html_lines.find('')
      html_lines = html_lines[chop_start+6:chop_end]
      
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = 'https://www.googleapis.com/auth/blogger'
      store = file.Storage('storage_blogger.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)
         
      service = build('blogger', 'v3', creds.authorize(Http()))
      body = {
       "kind": "blogger#post",
       "id": cfg.config['Blogger']['blogid'],
       "title": os.path.basename(os.path.splitext(outfile)[0]),
       "content":html_lines
       }
    
      request = service.posts().insert(blogId=cfg.config['Blogger']['blogid'], isDraft=True, body=body)
      response = request.execute()
      return response['url']
     except:
      return "Failed"
    
    def blogger_img_upload(filename):
     try:
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = ('https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file')
      store = file.Storage('storage_drive.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)    
    
      service = build('drive', 'v2', creds.authorize(Http()))
      
      q = "title = 'iRacing Stats Graphs' and mimeType = 'application/vnd.google-apps.folder'"
    
      request = service.files().list(q=q)
      response = request.execute()
      
      if len(response['items']) == 0:
       body = {
        "title": "iRacing Stats Graphs",
        "mimeType": "application/vnd.google-apps.folder"
        }
    
       request = service.files().insert(body=body)
       response = request.execute()
       folderId = response['id']
      else:
       folderId = response['items'][0]['id']
     
      pub.sendMessage('Uploading', graph=os.path.basename(os.path.splitext(filename)[0]))
      body = {
       "title": os.path.basename(os.path.splitext(filename)[0]),
       }
      body['parents'] = [{'id': folderId}]
      media_body = MediaFileUpload(filename)
    
      request = service.files().insert(body=body, media_body=media_body)
      response = request.execute()
      fileId = response['id']
     
      body = {
       "type": "anyone",
       "role": "reader"
       }
      response = service.permissions().insert(fileId=fileId, body=body).execute()
      response = service.files().get(fileId=fileId).execute()
      return response['webContentLink'].split('&')[0]
     except:
      print("Upload of %s to blogger failed" % os.path.basename(os.path.splitext(filename)[0]))
      return "Failed"
    
    def blogger_config(url):
     try:
      CLIENT_SECRET = 'client_secrets.json'
      SCOPE = 'https://www.googleapis.com/auth/blogger'
      store = file.Storage('storage_blogger.json')
      creds = store.get()
      
      if not creds or creds.invalid:
       flow = client.flow_from_clientsecrets(CLIENT_SECRET, SCOPE)
       creds = tools.run(flow, store)
         
      service = build('blogger', 'v3', creds.authorize(Http()))
      response = service.blogs().getByUrl(url=url).execute()
      cfg.write_blogid(response['id'])
      return True
     except:
      pub.sendMessage('Alert', msg="Unable to find BlogID of: %s" % url, title="Blogger Config Failed")
      return False
    
  • Monday, November 24, 2014

    PhotoFrame PC Update

    The Digital PhotoFrame PC located in my kitchen sees near daily use; in the morning while I make coffee I trigger playback of the Australian Broadcasting Commision's 90 second news headlines and NPR's 5 minute news update. While I'm preparing dinner I'll often use it to stream jazz from KJZZ.org. Occasionally I'll use it to display a recipe to follow along with.

    All in all there isn't much reason that I'd want to change the setup, it currently functions exactly as I want. However, it is running Windows XP which is no longer support by Microsoft. I don't like the idea of having an unsecurable machine running on the network so I've started planning on switching it to run Lubuntu Linux.

    Obviously this would require that I recreate all the AutoIT scripts which currently trigger all of the above things. I'm very confident that I could easily do this via Python based scripts. I've also started looking for one to one replacements for the handful of other features I make use of:

  • Variety to handle background image display and rotation.
  • Conky or LXDE screenlet for weather and clock.