PageRenderTime 141ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/googlecl/blogger/service.py

http://googlecl.googlecode.com/
Python | 214 lines | 134 code | 23 blank | 57 comment | 37 complexity | ea2ed7944e43d18148835ede4253957f MD5 | raw file
  1. # Copyright (C) 2010 Google Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Service details and instances for the Blogger service."""
  15. from __future__ import with_statement
  16. __author__ = 'tom.h.miller@gmail.com (Tom Miller)'
  17. import atom
  18. import gdata
  19. import gdata.blogger
  20. import gdata.blogger.service
  21. import logging
  22. import os
  23. from googlecl import safe_encode
  24. import googlecl.base
  25. import googlecl.service
  26. from googlecl.blogger import SECTION_HEADER
  27. LOG = logging.getLogger(googlecl.blogger.LOGGER_NAME)
  28. class BloggerServiceCL(gdata.blogger.service.BloggerService,
  29. googlecl.service.BaseServiceCL):
  30. """Command-line-friendly service for the Blogger API.
  31. Some of this is based off gdata/samples/blogger/BloggerExampleV1.py
  32. """
  33. def __init__(self, config):
  34. """Constructor."""
  35. gdata.blogger.service.BloggerService.__init__(self, account_type='GOOGLE')
  36. googlecl.service.BaseServiceCL.__init__(self, SECTION_HEADER, config)
  37. def _upload_content(self, post_title, content, blog_id=None, is_draft=False):
  38. """Uploads content.
  39. Keyword arguments:
  40. blog_title: Title of the blog to post to.
  41. title: Title to give the post.
  42. content: String to get posted. This may be contents from a file, but NOT
  43. the path itself!
  44. is_draft: If this content is a draft post or not. (Default False)
  45. Returns:
  46. Entry of post. (Returns same results as self.AddPost())
  47. """
  48. entry = gdata.blogger.BlogPostEntry()
  49. entry.title = atom.Title(title_type='xhtml', text=post_title)
  50. entry.content = atom.Content(content_type='html', text=content)
  51. if is_draft:
  52. control = atom.Control()
  53. control.draft = atom.Draft(text='yes')
  54. entry.control = control
  55. return self.AddPost(entry, blog_id)
  56. def _get_blog_id(self, blog_title=None, user_id='default'):
  57. """Return the blog ID of the blog that matches blog_title.
  58. Keyword arguments:
  59. blog_title: Name or title of the blog.
  60. user_id: Profile ID of blog's owner as seen in the profile view URL.
  61. Default 'default' for the authenticated user.
  62. Returns:
  63. Blog ID (blog_entry.GetSelfLink().href.split('/')[-1]) if a blog is
  64. found matching the user and blog_title. None otherwise.
  65. """
  66. blog_entry = self.GetSingleEntry('/feeds/' + user_id + '/blogs', blog_title)
  67. if blog_entry:
  68. return blog_entry.GetSelfLink().href.split('/')[-1]
  69. else:
  70. if blog_title is not None:
  71. LOG.error('Did not find a blog with title matching %s', blog_title)
  72. else:
  73. LOG.error('No blogs found!')
  74. return None
  75. def is_token_valid(self, test_uri='/feeds/default/blogs'):
  76. """Check that the token being used is valid."""
  77. return googlecl.service.BaseServiceCL.IsTokenValid(self, test_uri)
  78. IsTokenValid = is_token_valid
  79. def get_posts(self, blog_title=None, post_titles=None, user_id='default'):
  80. """Get entries for posts that match a title.
  81. Keyword arguments:
  82. blog_title: Name or title of the blog the post is in. (Default None)
  83. post_titles: string or list Titles that the posts should have.
  84. Default None, for all posts
  85. user_id: Profile ID of blog's owner as seen in the profile view URL.
  86. (Default 'default' for authenticated user)
  87. Returns:
  88. List of posts that match parameters, or [] if none do.
  89. """
  90. blog_id = self._get_blog_id(blog_title, user_id)
  91. if blog_id:
  92. uri = '/feeds/' + blog_id + '/posts/default'
  93. return self.GetEntries(uri, post_titles)
  94. else:
  95. return []
  96. GetPosts = get_posts
  97. def label_posts(self, post_entries, tags):
  98. """Add or remove labels on a list of posts.
  99. Keyword arguments:
  100. post_entries: List of post entry objects.
  101. tags: String representation of tags in a comma separated list.
  102. For how tags are generated from the string,
  103. see googlecl.base.generate_tag_sets().
  104. """
  105. scheme = 'http://www.blogger.com/atom/ns#'
  106. remove_set, add_set, replace_tags = googlecl.base.generate_tag_sets(tags)
  107. successes = []
  108. for post in post_entries:
  109. # No point removing tags if we're replacing all of them.
  110. if remove_set and not replace_tags:
  111. # Keep categories if they meet one of two criteria:
  112. # 1) Are of a different scheme than the one we're looking at, or
  113. # 2) Are of the same scheme, but the term is in the 'remove' set
  114. post.category = [c for c in post.category \
  115. if c.scheme != scheme or \
  116. (c.scheme == scheme and c.term not in remove_set)]
  117. if replace_tags:
  118. # Remove categories that match the scheme we are updating.
  119. post.category = [c for c in post.category if c.scheme != scheme]
  120. if add_set:
  121. new_tags = [atom.Category(term=tag, scheme=scheme) for tag in add_set]
  122. post.category.extend(new_tags)
  123. updated_post = self.UpdatePost(post)
  124. if updated_post:
  125. successes.append(updated_post)
  126. return successes
  127. LabelPosts = label_posts
  128. def upload_posts(self, content_list, blog_title=None, post_title=None,
  129. is_draft=False):
  130. """Uploads posts.
  131. Args:
  132. content_list: List of filenames or content to upload.
  133. blog_title: Name of the blog to upload to.
  134. post_title: Name to give the post(s).
  135. is_draft: Set True to upload as private draft(s), False to make upload(s)
  136. public.
  137. Returns:
  138. List of entries of successful posts.
  139. """
  140. max_size = 500000
  141. entry_list = []
  142. blog_id = self._get_blog_id(blog_title)
  143. if not blog_id:
  144. return []
  145. for content_string in content_list:
  146. if os.path.exists(content_string):
  147. with open(content_string, 'r') as content_file:
  148. content = content_file.read(max_size)
  149. if content_file.read(1):
  150. LOG.warning('Only read first %s bytes of file %s' %
  151. (max_size, content_string))
  152. if not post_title:
  153. title = os.path.basename(content_string).split('.')[0]
  154. else:
  155. if not post_title:
  156. title = 'New post'
  157. content = content_string
  158. try:
  159. entry = self._upload_content(post_title or title,
  160. content,
  161. blog_id=blog_id,
  162. is_draft=is_draft)
  163. except self.request_error, err:
  164. LOG.error(safe_encode('Failed to post: ' + unicode(err)))
  165. else:
  166. entry_list.append(entry)
  167. if entry.control and entry.control.draft.text == 'yes':
  168. html_link = _build_draft_html(entry)
  169. else:
  170. html_link = entry.GetHtmlLink().href
  171. LOG.info('Post created: %s', html_link)
  172. return entry_list
  173. UploadPosts = upload_posts
  174. SERVICE_CLASS = BloggerServiceCL
  175. def _build_draft_html(entry):
  176. template = 'http://www.blogger.com/post-edit.g?blogID=%s&postID=%s'
  177. return template % (entry.GetBlogId(), entry.GetPostId())