service.py

#

Copyright (C) 2010 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Service details and instances for the Blogger service.

#
from __future__ import with_statement

__author__ = 'tom.h.miller@gmail.com (Tom Miller)'
import atom
import gdata
import gdata.blogger
import gdata.blogger.service
import logging
import os
from googlecl import safe_encode
import googlecl.base
import googlecl.service
from googlecl.blogger import SECTION_HEADER


LOG = logging.getLogger(googlecl.blogger.LOGGER_NAME)
#

Command-line-friendly service for the Blogger API.

class BloggerServiceCL(gdata.blogger.service.BloggerService,
                       googlecl.service.BaseServiceCL):
#

Some of this is based off gdata/samples/blogger/BloggerExampleV1.py

#

Constructor.

  def __init__(self, config):
#
    gdata.blogger.service.BloggerService.__init__(self, account_type='GOOGLE')
    googlecl.service.BaseServiceCL.__init__(self, SECTION_HEADER, config)
#

Uploads content.

  def _upload_content(self, post_title, content, blog_id=None, is_draft=False):
#

Keyword arguments: blog_title: Title of the blog to post to. title: Title to give the post. content: String to get posted. This may be contents from a file, but NOT the path itself! is_draft: If this content is a draft post or not. (Default False)

Returns: Entry of post. (Returns same results as self.AddPost())

    entry = gdata.blogger.BlogPostEntry()
    entry.title = atom.Title(title_type='xhtml', text=post_title)
    entry.content = atom.Content(content_type='html', text=content)
    if is_draft:
      control = atom.Control()
      control.draft = atom.Draft(text='yes')
      entry.control = control
    return self.AddPost(entry, blog_id)
#

Return the blog ID of the blog that matches blog_title.

  def _get_blog_id(self, blog_title=None, user_id='default'):
#

Keyword arguments: blog_title: Name or title of the blog. user_id: Profile ID of blog's owner as seen in the profile view URL. Default 'default' for the authenticated user.

Returns: Blog ID (blog_entry.GetSelfLink().href.split('/')[-1]) if a blog is found matching the user and blog_title. None otherwise.

    blog_entry = self.GetSingleEntry('/feeds/' + user_id + '/blogs', blog_title)
    if blog_entry:
      return blog_entry.GetSelfLink().href.split('/')[-1]
    else:
      if blog_title is not None:
        LOG.error('Did not find a blog with title matching %s', blog_title)
      else:
        LOG.error('No blogs found!')
      return None
#

Check that the token being used is valid.

  def is_token_valid(self, test_uri='/feeds/default/blogs'):
#
    return googlecl.service.BaseServiceCL.IsTokenValid(self, test_uri)

  IsTokenValid = is_token_valid
#

Get entries for posts that match a title.

  def get_posts(self, blog_title=None, post_titles=None, user_id='default'):
#

Keyword arguments: blog_title: Name or title of the blog the post is in. (Default None) post_titles: string or list Titles that the posts should have. Default None, for all posts user_id: Profile ID of blog's owner as seen in the profile view URL. (Default 'default' for authenticated user)

Returns: List of posts that match parameters, or [] if none do.

    blog_id = self._get_blog_id(blog_title, user_id)
    if blog_id:
      uri = '/feeds/' + blog_id + '/posts/default'
      return self.GetEntries(uri, post_titles)
    else:
      return []

  GetPosts = get_posts
#

Add or remove labels on a list of posts.

  def label_posts(self, post_entries, tags):
#

Keyword arguments: post_entries: List of post entry objects. tags: String representation of tags in a comma separated list. For how tags are generated from the string, see googlecl.base.generate_tag_sets().

    scheme = 'http://www.blogger.com/atom/ns#'
    remove_set, add_set, replace_tags = googlecl.base.generate_tag_sets(tags)
    successes = []
    for post in post_entries:
#

No point removing tags if we're replacing all of them.

      if remove_set and not replace_tags:
#

Keep categories if they meet one of two criteria: 1) Are of a different scheme than the one we're looking at, or 2) Are of the same scheme, but the term is in the 'remove' set

        post.category = [c for c in post.category \
                          if c.scheme != scheme or \
                          (c.scheme == scheme and c.term not in remove_set)]

      if replace_tags:
#

Remove categories that match the scheme we are updating.

        post.category = [c for c in post.category if c.scheme != scheme]
      if add_set:
        new_tags = [atom.Category(term=tag, scheme=scheme) for tag in add_set]
        post.category.extend(new_tags)
      updated_post = self.UpdatePost(post)
      if updated_post:
        successes.append(updated_post)
    return successes

  LabelPosts = label_posts
#

Uploads posts.

  def upload_posts(self, content_list, blog_title=None, post_title=None,
                   is_draft=False):
#

Args: content_list: List of filenames or content to upload. blog_title: Name of the blog to upload to. post_title: Name to give the post(s). is_draft: Set True to upload as private draft(s), False to make upload(s) public.

Returns: List of entries of successful posts.

    max_size = 500000
    entry_list = []
    blog_id = self._get_blog_id(blog_title)
    if not blog_id:
      return []
    for content_string in content_list:
      if os.path.exists(content_string):
        with open(content_string, 'r') as content_file:
          content = content_file.read(max_size)
          if content_file.read(1):
            LOG.warning('Only read first %s bytes of file %s' %
                        (max_size, content_string))
        if not post_title:
          title = os.path.basename(content_string).split('.')[0]
      else:
        if not post_title:
          title = 'New post'
        content = content_string
      try:
        entry = self._upload_content(post_title or title,
                                     content,
                                     blog_id=blog_id,
                                     is_draft=is_draft)
      except self.request_error, err:
        LOG.error(safe_encode('Failed to post: ' + unicode(err)))
      else:
        entry_list.append(entry)
        if entry.control and entry.control.draft.text == 'yes':
          html_link = _build_draft_html(entry)
        else:
          html_link = entry.GetHtmlLink().href
        LOG.info('Post created: %s', html_link)
    return entry_list

  UploadPosts = upload_posts


SERVICE_CLASS = BloggerServiceCL
#
def _build_draft_html(entry):
  template = 'http://www.blogger.com/post-edit.g?blogID=%s&postID=%s'
  return template % (entry.GetBlogId(), entry.GetPostId())