2008/04/24


こんなのあるんだ...
freshmeat.net: Project details for pytumblr - ロックスターになりたい
pytumblr is a Python library for the tumblr.com API. freshmeat.net: Project details for pytumblr
Google App Engine そう言えば前に「Windowsのエクスプローラで「送る」からShareOnTumblr」なんてのも作ったなぁ。 pythonで作ってあってlinuxなんかでも動くように作ったはず。

でpytumblrですが、ソース見たら簡単なソースだったのでGoogle App Engineで動くように改造してみました。以下パッチ
--- pytumblr.py.orig    Thu Apr 24 03:15:26 2008
+++ pytumblr.py Thu Apr 24 12:02:32 2008
@@ -1,7 +1,9 @@
 #!/usr/bin/env python
+#!-*- coding:utf-8 -*-
 
-import string, httplib, urllib2, urllib
-from xml.dom import minidom
+import urllib2, urllib
+from google.appengine.api import urlfetch
+from BeautifulSoup import BeautifulSoup
 
 class pytumblr(object):
    """Tumblr API Object.
@@ -75,14 +77,11 @@
 
        data = urllib.urlencode(values)
        headers = {"Content-type": "application/x-www-form-urlencoded"}
-       conn = httplib.HTTPConnection(self.url)
-       conn.follow_all_redirects = True
-       conn.request("POST",'/api/write', data, headers)
-       response = conn.getresponse()
-       if ( int(response.status) == 201):
+       response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+       if ( int(response.status_code) == 201):
            return 'Success'
-       elif( int(response.status) != 201):
-           raise 'Error - Status %s (%s) returned' %(response.status, response.reason)
+       elif( int(response.status_code) != 201):
+           raise 'Error - Status %s (%s) returned' %(response.status_code, 'something wrong')
 
    def auth(self):
        values = {
@@ -92,13 +91,10 @@
            }
        data = urllib.urlencode(values)
        headers = {"Content-type": "application/x-www-form-urlencoded"}
-       conn = httplib.HTTPConnection(self.url)
-       conn.follow_all_redirects = True
-       conn.request("POST", '/api/write', data, headers)
-       response = conn.getresponse()
-       if ( int(response.status) != 200 ):
-           return '< There was a tiny problem: %s (%s) >' %(response.status, response.reason)
-       if ( int(response.status) == 200 ):
+       response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+       if ( int(response.status_code) != 200 ):
+           return '< There was a tiny problem: %s (%s) >' %(response.status_code, 'something wrong')
+       if ( int(response.status_code) == 200 ):
            return '< Authenticated! >'
    
 
@@ -112,25 +108,8 @@
            opts = opts + "?type=" + type
        if ( id != 'None' ):
            opts = opts + "?id=" + id
-       return urllib.urlopen('http://%s.tumblr.com/api/read%s' %(self.user, opts)).read()
+       return urlfetch.fetch('http://%s.tumblr.com/api/read%s' %(self.user, opts)).content
        
    def getblog(self):
-       rxml = minidom.parseString(self.blogread())
-       titles = rxml.getElementsByTagName('regular-title')
-       postid = rxml.getElementsByTagName('post')
-       i = 0
-       n = 0
-       posts = {}
-       while ( i < len(postid)):
-           if ( postid[i].attributes['type'].value == 'regular' ):
-               t = titles [n]
-               t = t.toxml()
-               t = t.replace('<regular-title)', '')
-               t = t.replace('</regular-title)', '')
-               poid = postid[i].attributes["id"].value
-               posts[ poid ] = t
-               n = n + 1
-           if ( postid[i].attributes['type'].value != 'regular' ):
-               pass
-           i = i + 1
-       return posts
+       soap = BeautifulSoup(self.blogread())
+       return soap('post')
思いっきり弄ってますね。。。
minidomの代わりにBeautifulSoupを、urllib2の代わりにurlfetchを使っています。したがってgetblogは自分のregular情報のdictだけを返すのではなくBeautifulSoupを使ってlink、photo、quoteを返す様にしてあります。
実際に動くよって所は以下のサイトで確認して下さい。
pytumblr
このサイトのURLの後ろに http://mattn.appspot.com/tumblr/mattn と言った感じにtumblrアカウント名を付けてみて下さい。
今回のデモにはweb.pyというフレームワークを使用してみました。
以下スクリプトソースです。 #!-*- coding:utf-8 -*-
import web
from pytumblr import pytumblr
from BeautifulSoup import BeautifulSoup

def unescape(str):
  return  BeautifulSoup(str, convertEntities=BeautifulSoup.HTML_ENTITIES).contents[0].encode('utf-8', 'replace')

urls = (
  '/tumblr/(.*)', 'tumblr'
)
render = web.template.render('templates/')

class tumblr:
  def GET(self, name):
    web.header("Content-Type", "text/html; charset=utf-8")
    template_values = { 'name': '', 'posts': [] }
    name = name.replace('/', '')
    if name:
      pt = pytumblr(name, None, None)
      template_values['name'] = name
      for blog in pt.getblog():
        if blog['type'] == 'link':
          template_values['posts'].append({
            'type': 'link',
            'text': unescape(blog('link-text')[0].string),
            'desc': blog('link-description') and unescape(blog('link-description')[0].string) or '',
            'link': unescape(blog('link-url')[0].string),
          })
        if blog['type'] == 'photo':
          template_values['posts'].append({
            'type': 'photo',
            'text': unescape(blog('photo-caption')[0].string),
            'link': unescape(blog('photo-url')[0].string),
          })
        if blog['type'] == 'quote':
          template_values['posts'].append({
            'type': 'quote',
            'text': unescape(blog('quote-text')[0].string),
            'link': unescape(blog('quote-source')[0].string),
          })
    return render.tumblr(template_values)

if __name__ == "__main__":
  web.application(urls, globals()).cgirun()
案外短く書けますね。そしてテンプレートHTML
$def with (res)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/static/css/tumblr.css" type="text/css" />
$if res['name']:
    <title>pytumblr - $res['name']</title>
$else:
    <title>pytumblr</title>
</head>
<body>
$if res['name']:
    <h1>pytumblr - $res['name']</h1>
$else:
    <h1>pytumblr</h1>
    <img src="http://b.hatena.ne.jp/entry/image/http://mattn.appspot.com/tumblr/" title="はてなブックマーク" />
    <div id="content">
$if res['name']:
    <h2>$res['name']'s tumblr</h2>
    $for post in res['posts']:
        $if post['type'] == 'photo':
            <b>PHOTO:</b>$:post['text']<br />
            <blockquote><img src="$post['link']" /></blockquote>
        $elif post['type'] == 'link':
            <b>LINK:</b><a href="$post['link']">$:post['text']</a><br />
            <blockquote class="link">$:post['desc']</blockquote>
        $elif post['type'] == 'quote':
            <b>QUOTE:</b><br />
            <blockquote class="quote">
                $:post['text']<br />
                <cite>$:post['link']</cite>
            </blockquote>
    </div>
    <hr clear="all" />
    <p style="text-align: center">provided by <a href="http://mattn.kaoriya.net">mattn</a>, hosted on google app server.</p>
</body>
</html>
このweb.pyのテンプレートって癖があってpythonインデント方式なのですが、これってpreとかcodeで先頭が入っちゃったら不味いんじゃないかと思ったり...
解決方あるのか、調べてみます。pytumblrで面白いもの作ってみて下さい。

Posted at by




クァーってサービスが出来たみたいです。
Qaa(クァー):Twitterで簡単アンケート!(まだまだ開発中)
Qaa

Qaa(クァー)は、Twitterを利用して簡単にアンケートをとることができるサービスです。

Qaa(クァー)を通じて投稿されたアンケートはYes or No形式で回答する事ができ、その結果はアンケート毎に用意されるページでパイチャートで確認する事ができます。


http://qaa.orig.jp/h#about
twitterを使ったソーシャルアンケートサービスですね。
でもいちいちtwitterやIMから「@qaa qid14 YES」とか入力するの面倒臭いのでMinibufferコマンド作ってみました。
アンケートエントリページに行って、「q y」もしくは「q n」で「YES」もしくは「NO」の回答をtwitter経由で投稿します。
qaa_yes_or_no.user.js
よろしければどうぞ。
※一応、firefox3/greasemonkeyからBasic認証のダイアログが出ない件は対応してあります。
Posted at by



2008/04/21


なるべくdictぽく扱えるように作ってみました。
WedataオブジェクトのコンストラクタにAPIKEYを掘り込んで操作します。
api = Wedata('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
databases = api.databases()
for database in databases:
  print database
  for key in database.keys():
    print " %s=%s" % (key, database[key])
  print
print
また、AutoPagerizeのデータベースであれば以下の様にdataプロパティからの属性参照の様に書く事も出来ます。
database = api.database('AutoPagerize')
print "%s : %s" % (database.name, database.description)
items = database.items()
for item in database.items():
  print " %s" % item.data.pageElement
  print " %s" % item.data.insertBefore
  print
Database.create_databaseの戻り値にはデータベース名(キー名)、Item.create_itemの戻り値にはアイテムIDが戻ります。このIDを使ってDatabase.delete_datebaseおよびItem.dalete_itemを呼び出す事が出来ます。
dbid = api.create_database('my_example_database', 'my_example_database', ['name', 'description'], ['value', 'xpath'], False)
api.delete_database(dbid)
今のところGoogle App Engineには対応していません。Google App Engine上での用途を見付けたら対応するかもしれません。
いつものようにコードはcodereposに置いてあります。
/lang/python/wedata
Posted at by