2009/03/11


解説は...いらんでしょ。
母艦は「dan the shell」。

!変数宣言が必要
ルートメニューとはメインメニュー

ファイルメニューとはメニュー
実行とは、メニュー。実行のテキストは「実行」
区切り1とは、メニュー。区切り1のテキストは「-」
閉じるとは、メニュー。閉じるのテキストは「閉じる」

「実行
区切り1
閉じる」を反復
 「ファイルメニューの追加({それ}を)」をナデシコする

「ファイルメニュー」を反復
 「ルートメニューの追加({それ}を)」をナデシコする

入力メモとはメモ。
入力メモについて
  X=0
    Y=0
    W=母艦のクライアントW
    H=母艦のクライアントHを2で割る
結果メモとはメモ。
結果メモについて
  X=0
    Y=母艦のクライアントHを2で割る
    W=母艦のクライアントW
    H=母艦のクライアントHを2で割る

母艦のサイズ変更した時は~
 入力メモについて
  X=0
    Y=0
    W=母艦のクライアントW
    H=母艦のクライアントHを2で割る
 結果メモについて
  X=0
    Y=母艦のクライアントHを2で割る
    W=母艦のクライアントW
    H=母艦のクライアントHを2で割る

閉じるのクリックした時は~
 「閉じますか?」と二択
  もし、それがはいならば、終わり

実行のクリックした時は~
  URLは「http://api.dan.co.jp/perleval.cgi?c=callback&s=」
  それは入力メモのテキスト
  それをURLエンコード
  「{改行}」を「%0A」に置換
  URLにそれを追加
  URLをHTTPデータ取得して、結果に代入
  結果を「^callback\((.*)\);$」で正規表現マッチ
  結果は抽出文字列[0]
  結果をJSONデコード
  結果はそれ
  結果メモのテキストは結果@「result」

参考文献: 404 Blog Not Found:Ajax - perlを実行するAPI
Posted at by




readline使ってコマンドライン提供、curlで通信、結果をjson-cでパースまで作った。燃え尽きた。
追記
shebangから使えるようにした。
#!/usr/bin/dansh
以下コード // for MSVC: cl -I.. /Tp dansh.cpp curl.lib readline.lib ..\Release\json.lib
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
namespace json {
#include "json.h"
}
#define READLINE_STATIC
#include <readline/readline.h>
#include <curl/curl.h>
#define API_URL "http://api.dan.co.jp/perleval.cgi?c=callback&s="

typedef struct {
    char* data;     // response data from server
    size_t size;    // response size of data
} MEMFILE;

MEMFILE*
memfopen() {
    MEMFILE* mf = (MEMFILE*) malloc(sizeof(MEMFILE));
    mf->data = NULL;
    mf->size = 0;
    return mf;
}

void
memfclose(MEMFILE* mf) {
    if (mf->data) free(mf->data);
    free(mf);
}

size_t
memfwrite(char* ptr, size_t size, size_t nmemb, void* stream) {
    MEMFILE* mf = (MEMFILE*) stream;
    int block = size * nmemb;
    if (!mf->data)
        mf->data = (char*) malloc(block);
    else
        mf->data = (char*) realloc(mf->data, mf->size + block);
    if (mf->data) {
        memcpy(mf->data + mf->size, ptr, block);
        mf->size += block;
    }
    return block;
}

char*
memfstrdup(MEMFILE* mf) {
    char* buf = (char*) malloc(mf->size + 1);
    memcpy(buf, mf->data, mf->size);
    buf[mf->size] = 0;
    return buf;
}

char*
url_encode_alloc(const char* str, int force_encode) {
    const char* hex = "0123456789abcdef";

    char* buf = NULL;
    unsigned char* pbuf = NULL;
    int len = 0;

    if (!str) return NULL;
    len = strlen(str)*3;
    buf = (char*) malloc(len+1);
    memset(buf, 0, len+1);
    pbuf = (unsigned char*)buf;
    while(*str) {
        unsigned char c = (unsigned char)*str;
        if (c == ' ')
            *pbuf++ = '+';
        else if (c & 0x80 || force_encode) {
            *pbuf++ = '%';
            *pbuf++ = hex[c >> 4];
            *pbuf++ = hex[c & 0x0f];
        } else
            *pbuf++ = c;
        str++;
    }
    return buf;
}

void do_dan(const char* line) {
    char* source = url_encode_alloc(line, TRUE);
    char* url = (char*) malloc(strlen(API_URL) + strlen(source) + 1);
    strcpy(url, API_URL);
    strcat(url, source);

    MEMFILE* mf = memfopen();
    CURL* curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, memfwrite);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, mf);
    CURLcode res = curl_easy_perform(curl);
    free(url);
    if (res == CURLE_OK) {
        char* data = memfstrdup(mf);
        memfclose(mf);

        // remove callback function
        char* ptr;
        ptr = strrchr(data, ')');
        if (ptr) *ptr = 0;
        ptr = strchr(data, '(');
        if (ptr) *ptr++ = 0;
        else ptr = data;

        json::json_object *obj = json::json_tokener_parse(ptr);
        if (!is_error(obj)) {
            json::json_object *result = json::json_object_object_get(obj, "result");
            if (!is_error(result)) {
                printf("%s\n", json::json_object_to_json_string(result));
                json_object_put(result);
            }
        }
        free(data);
    }
    curl_easy_cleanup(curl);
}

int main(int argc, char **argv)
{
    char* line = NULL;

    //json::mc_set_debug(1);
    if (argc == 2) {
        FILE* fp = fopen(argv[1], "rb");
        if (!fp) {
            perror("can't open file");
            exit(-1);
        }
        char buf[BUFSIZ];
        while (fgets(buf, sizeof(buf), fp)) {
            if (!line) {
                if (strncmp(buf, "#!", 2))
                    line = strdup(buf);
            } else {
                line = (char*) realloc(line, strlen(line) + strlen(buf) + 1);
                strcat(line, buf);
            }
        }
        fclose(fp);
        do_dan(line);
        free(line);
    }
    if (!isatty(fileno(stdin))) {
        char buf[BUFSIZ];
        while (fgets(buf, sizeof(buf), stdin)) {
            if (!line) {
                if (strncmp(buf, "#!", 2))
                    line = strdup(buf);
            } else {
                line = (char*) realloc(line, strlen(line) + strlen(buf) + 1);
                strcat(line, buf);
            }
        }
        do_dan(line);
        free(line);
    } else {
        while (line = readline("dan> ")) {
            do_dan(line);
            free(line);
        }
    }
    return 0;
}

参考文献: 404 Blog Not Found:Ajax - perlを実行するAPI
Posted at by



2009/03/05


結果から言うと実に使いにくい!
PyAWS - A Python wrapper for Amazon Web Service

PyAWS is a Python wrapper for the latest Amazon Web Service. It is designed to pave the way for Python developers to interactivate AWS. This project is forked from the code base of pyamazon. The Amazone E-Commerce Services is supported.

http://pyaws.sourceforge.net/
配列なら配列で、存在したり存在しない場合があるプロパティならばそれ用のアクセサを作ってほしい...
Djangoテンプレートで配列かどうかを判断してループで回してってのはつらいです。
あと、Django Template Engineの最新版でないと {% for key, value in values %}
こういう書き方が出来ないので、結局明示的な名前の付いたdictに作り変える必要があった。まぁこれはpyawsのせいではないけれど。
aws.py
#!-*- coding:utf-8 -*-
import os
import cgi
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
import logging
import yaml
from pyaws import ecs

def to_dict_array(array):
  ret = []
  for item in array:
    ret.append({'key': item.keys()[0], 'value':item.values()[0] })
  return ret

class MainPage(webapp.RequestHandler):
  config = yaml.safe_load(open(os.path.join(os.path.dirname(__file__), 'aws.yaml'), 'r'))
  def get(self):
    kind = self.request.get('kind')
    keyword = self.request.get('keyword')
    template_values = {}
    template_values = {
      'asoid'    : self.config['asoid'],
      'kinds'    : to_dict_array(self.config['kinds']),
      'kind'     : kind,
      'keyword'  : keyword,
      'books'    : [],
    }
    if keyword:
      asoid = self.config['asoid']
      devkey = self.config['devkey']
      kinds = self.config['kinds']
      ecs.setLicenseKey(devkey)
      ecs.setLocale(self.config['locale'])
      books = ecs.ItemSearch(keyword, SearchIndex=kind, ResponseGroup='Medium,Offers')
      count = 0
      max = 5
      for book in books:
        if 'Author' in book.__dict__ and not isinstance(book.Author, list):
          book.Author = [book.Author]
        if 'Offer' in book.Offers.__dict__:
          if isinstance(book.Offers.Offer, list):
            book.Availability = book.Offers.Offer[0].OfferListing.Availability
          elif 'Availability' in book.Offers.Offer.OfferListing.__dict__:
            book.Availability = book.Offers.Offer.OfferListing.Availability
        template_values['books'].append(book)
        count += 1
        if count > max: break
    path = os.path.join(os.path.dirname(__file__), 'aws.html')
    self.response.out.write(template.render(path, template_values))

def main():
  application = webapp.WSGIApplication([('/pyaws/', MainPage)], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()
aws.html
<html>
<head>
<title>AWS商品検索</title>
<style tyle="text/css"><!--
body {
    font-family: 'メイリオ', 'Osaka'
}
#content {
    margin-left: 50px;
}
#error {
    color: red;
}
.awsxom {
    background: #eeeeee;
    padding: 0.5em;
}
--></style>
</head>
<body>
    <h1>AWS商品検索</h1><img src="http://b.hatena.ne.jp/entry/image/http://mattn.appspot.com/pyaws/" title="はてなブックマーク" />
    <div id="content">
        <p align="right"><a href="/">目次</a></p>
        <form method="get">
            <label for="kind">種類</label>
            <select id="kind" name="kind" value="{{ kind }}">
                {% for item in kinds %}<option value="{{ item.key }}"{% ifequal item.key kind %} selected="selected"{% endifequal %}>{{ item.value }}</option>
                {% endfor %}
            </select>
            <label for="keyword">キーワード</label><input id="keyword" name="keyword" type="text" value="{{ keyword|escape }}" />
            <input type="submit" />
        </form>
        <hr />
        {% for book in books %}
        <div class="awsxom">
            <a href="http://www.amazon.co.jp/exec/obidos/ASIN/{{ book.ASIN }}/ref=nosim/{{ asoid }}">
                <img src="{{ book.SmallImage.URL }}" align="left" hspace="5" border="0" alt="{{ book.Title }}" class="image" />
                <strong>{{ book.Title }}</strong></a><br />
            {{ book.Author|join:", " }}<br />
            {{ book.Manufacturer}} / {{ book.ListPrice.FormattedPrice }} ({% if book.PublicationDate %}{{ book.PublicationDate }}{% else %}{{ book.ReleaseDate }}{% endif %})<br />
            &nbsp;<br />
            発送可能時間:{{ book.Availability }}<br />
            <br clear="all" />
        </div><br />
        {% endfor %}
    </div>
</body>
</html>
あと、設定用のyaml
aws.yaml
asoid : xxxxxxxxx
devkey : 1XXXXXXXXXXXXXXXXXXX
locale: jp
kinds:
 - Blended: Blended すべての商品
 - Books: 本
 - Classical: クラシック音楽
 - DVD: DVD
 - Electronics: エレクトロニクス
 - ForeignBooks: 洋書
 - Hobbies: ホビー
 - Kitchen: ホーム&キッチン
 - Music: 音楽
 - MusicTracks: 曲名
 - Software: ソフトウェア
 - SportingGoods: スポーツ
 - Toys: おもちゃ
 - VHS: VHSビデオ
 - Video: DVD&ビデオ
 - VideoGames: ゲーム
 - HealthPersonalCare: ヘルス&ビューティー
これ、AuthorとかAvailabilityなんかのアクセサが最初から決まった型で扱えればコードは2/3くらいになりそう。
いっそBeautifulSoupとかで作った方が作り手側としては納得が行くのかも。
動いてる物は以下
AWS商品検索
追記
パッチを付けるのを忘れてました。
pyaws.diff
--- pyaws/ecs.py.orig   Mon Apr 09 07:38:57 2007
+++ pyaws/ecs.py    Wed May 07 18:12:21 2008
@@ -19,7 +19,8 @@
 
 
 import os, urllib, string, inspect
-from xml.dom import minidom
+from google.appengine.api import urlfetch
+import pxdom
 
 __author__ = "Kun Xi < kunxi@kunxi.org >"
 __version__ = "0.2.0"
@@ -164,10 +165,7 @@
    """Send the query url and return the DOM
    
    Exception is raised if there is errors"""
-   u = urllib.FancyURLopener(HTTP_PROXY)
-   usock = u.open(url)
-   dom = minidom.parse(usock)
-   usock.close()
+   dom = pxdom.parseString(urlfetch.fetch(url).content)
 
    errors = dom.getElementsByTagName('Error')
    if errors:
@@ -282,7 +280,7 @@
    if(plugins == None):
        plugins = {}
 
-   childElements = [e for e in element.childNodes if isinstance(e, minidom.Element)]
+   childElements = [e for e in element.childNodes if isinstance(e, pxdom.Element)]
 
    if childElements:
        for child in childElements:
@@ -291,7 +289,7 @@
                if type(getattr(rc, key)) <> type([]):
                    setattr(rc, key, [getattr(rc, key)])
                setattr(rc, key, getattr(rc, key) + [unmarshal(child, plugins)])
-           elif isinstance(child, minidom.Element):
+           elif isinstance(child, pxdom.Element):
                if plugins.has_key('isPivoted') and plugins['isPivoted'](child.tagName):
                        unmarshal(child, plugins, rc)
                elif plugins.has_key('isBypassed') and plugins['isBypassed'](child.tagName):
@@ -303,7 +301,7 @@
                else:
                    setattr(rc, key, unmarshal(child, plugins))
    else:
-       rc = "".join([e.data for e in element.childNodes if isinstance(e, minidom.Text)])
+       rc = "".join([e.data for e in element.childNodes if isinstance(e, pxdom.Text)])
    return rc
 
 
pxdomはDOM Level 3をpythonで実装しているすばらしいライブラリです。ココから取得して下さい。
Posted at by