2008年12月25日木曜日

IIS + MySql


http://forums.microsoft.com/TechNet-JA/ShowPost.aspx?PostID=2113684&SiteID=36


PHP5でMySQLを有効にする(Windows)

1. PHP設定ファイル(PHP.ini)の「;extension=php_mysql.dll」のコメントを解除。
2. PHP設定ファイル(PHP.ini)のextension_dirにphp_mysql.dllを追加。(例:extension_dir = "c:\php\ext\")
3. PHPインストール先(例:c:\php)にあるlibmysql.dllをWindowsディレクトリ(例:c:\windows)にコピー。
4. IIS7を再起動。


http://forum.joomla.jp/download.html


2008年12月18日木曜日

SOQL Questions

Q. Does anyone know an easy way to simulate a SELECT DISTINCT operation in SOQL?
Also, how do you check for NULL in a WHERE clause. I know IS NULL does not work?

A. SELECT Id FROM CASE WHERE Id <> null
or
SELECT Id FROM CASE WHERE Id = null

don't think there's anything like distinct though

http://community.salesforce.com/sforce/board/message?board.id=NET_development&message.id=2406&query.id=8066#M2406




Q. Join
Accessing Query Results from a Relationship Query from the Partner WSDL with Axis for Java

http://wiki.apexdevnet.com/index.php/Accessing_Query_Results_from_a_Relationship_Query_from_the_Partner_WSDL_with_Axis_for_Java







Q. Update many rows

sql = "select id from Product2 where ProductCode like '%.jp%' and Family <> 'blog' "
query_result = client.query( sql )

if ( query_result['records'].__len__() < 1 ):
self.response.out.write('no product found with : %s ' % sql )
else :
products = query_result['records']
for product in products:
product['Family'] = 'blog'
results = client.update( product )

self.response.out.write( results )
if results[0]['success'] :
self.response.out.write( 'updated. ' )
else:
self.response.out.write( results[0]['errors'] )
self.response.out.write( 'Error. ' )

2008年12月17日水曜日

Cannot build indexes that are in state ERROR.

Uploading index definitions.
Error 400: --- begin server output ---
Cannot build indexes that are in state ERROR.

To vacuum and rebuild your indexes:
1. Create a backup of your index.yaml specification.
2. Determine the indexes in state ERROR from your admin console: http://appengine.google.com/datastore/indexes?&app_id=blog-editor
3. Remove the definitions of the indexes in ERROR from your index.yaml file.
4. Run "appcfg.py vacuum_indexes your_app_dir/"
5. Wait until the ERROR indexes no longer appear in your admin console.
6. Replace the modified version of your index.yaml file with the original.
7. Run "appcfg.py update_indexes your_app_dir/"
--- end server output ---
Your app was updated, but there was an error updating your indexes. Please retry later with appcfg.py update_indexes.




C:\google>appcfg.py vacuum_indexes blog-editor/
Fetching index definitions diff.
This index is no longer defined in your index.yaml file.

ancestor: true
kind: BlogUrlYm
properties: []


Are you sure you want to delete this index? (N/y/a): y
This index is no longer defined in your index.yaml file.

ancestor: true
kind: Blog
properties: []


Are you sure you want to delete this index? (N/y/a): y
This index is no longer defined in your index.yaml file.

ancestor: true
kind: Stock
properties: []


Are you sure you want to delete this index? (N/y/a): y
This index is no longer defined in your index.yaml file.

ancestor: true
kind: Img
properties: []


Are you sure you want to delete this index? (N/y/a): y
This index is no longer defined in your index.yaml file.

kind: Blog
properties:
- direction: desc
name: ym


Are you sure you want to delete this index? (N/y/a): y
This index is no longer defined in your index.yaml file.

ancestor: true
kind: Timeline
properties: []


Are you sure you want to delete this index? (N/y/a): y
Deleting selected index definitions.

C:\google>appcfg.py update_indexes blog-editor/
Uploading index definitions.

2008年12月16日火曜日

ASSETS / 納入商品 Force.com

Account - Assset - Product2

ASSETS / 納入商品
とは?
会社が販売する品目が商品であるのに対し、
納入商品とは、販売品のシリアル番号、購入日、その他の情報なども含め、
顧客が購入した特定の商品を表します。
組織における納入商品の使用方法によっては、顧客が所有する競合商品や
自社商品のバージョンを表すこともあります。

組織で商品を有効にした場合、その組織では納入商品が自動的に有効になります。
ただし、ユーザが納入商品を使用するには、まず、
システム管理者が自身の組織をカスタマイズする必要があります。

取引先、取引先責任者、および商品ページレイアウトに納入商品関連リストを追加します。








2008年12月11日木曜日

cloud to cloud

http://cloud-cloud.appspot.com/


Error: Method Not Allowed

The request method GETis inappropriate for the URL /login.


local の SDK では問題なく動作していた
Force.com for Google App Engine 
も実際に cloud にupload してみると、動作せず。
app.yaml  の secure: always で hppts にしているためかなのかと
思ったが、 どうやら static 静的な html から post している部分が
原因であるようで、 template の html を利用するようにしたところ
cloud でも無事動作した。


2008年12月10日水曜日

Force.com for Google App Engine 日本語

日本語を含んだ contact を登録しようとしたところ、
正しく登録できなかったが、

static/create_contract.py に #!-*- coding:utf-8 -*- を追加し
static/create_contract_input.html に以下を追加することで
<meta equiv="Content-Type" content="text/html; charset=utf-8">
登録することができた。







Account Lookup Sample

Enter your credentials:

Username:
Password:

Enter an account name:

     

Found 1 accounts:

Blog-Editor [View Info] 

Account Info:

Name: Blog-Editor 
ID: 0018000000MwqAqAAJ 
Phone: 123456789 
Web Site: 





Create Contact Sample

Enter your credentials:

Username:
Password:

Enter contact info:

First Name:Phone:
Last Name:Email:






Force.com for Google App Engine



Salesforce.com announces Force.com for App Engine

セールスフォース、「Google App Engine」と連携を可能に
クラウド・コンピューティング分野での提携を拡大

(2008年12月09日)

http://www.computerworld.jp/topics/cloud/129329.html


http://googleappengine.blogspot.com/2008/12/salesforcecom-announces-forcecom-for.html
http://developer.force.com/appengine




1.  JOIN DFC





2. Download and Install

http://code.google.com/p/force-app-engine/downloads/list




3. Start Application

c:\google> dev_appserver.py force-app-engine
Force.com for Google AppEngine

Overview

This toolkit enables developers of Google AppEngine to make calls to the Force.com web services API. The form of the toolkit is a python module code-named "BeatBox". Within this module is the main PythonClient class that has methods that coorespond to each web service api call.

Below are links to the samples that come with this toolkit. For more information, visit the Force.com Toolkit for Google AppEngine page at the Developer Force website.

Samples:

  • Simple Login - Illustrates a simple login to Force.com from a Google AppEngine page
  • Account Lookup - Illustrates querying data from Force.com
  • Create Contact - Illustrates inserting data into Force.com
Testing:
  • Unit Test - Test the basics of the library, including query, create, update and delete from Force.com using the SOAP API


Simple Demo Sample

Enter your Force.com credentials:

Username:
Password:


Simple Login Sample

Login Failed

Your login failed. Please check your username and password as well as your IP range restrictions. Error details:

Error Code: LOGIN_MUST_USE_SECURITY_TOKEN 
Error Text: LOGIN_MUST_USE_SECURITY_TOKEN: ユーザ名、パスワード、セキュリティトークンが無効か、ユーザがロックされています。新しい場所からログインしていますか? 貴社の信頼済みネットワーク外からデスクトップクライアントまたは API 経由で Salesforce へアクセスする場合、パスワードにセキュリティトークンを追加してログインする必要があります。新しいセキュリティトークンは、Salesforce (http://www.salesforce.com/jp/) にログインし、[設定] | [私の個人情報] | [セキュリティトークンのリセット] をクリックして入手します。





Simple Login Sample

Login Succeeded

Your login was successful. Below are your session details:

UserId: 005800xxxxxxxx
Server URL: https://na6-api.salesforce.com/services/Soap/u/14.0/511700D800000xxxx
Session Id: 511700D80000000M5IC!ARoAQAnStyOYcjTGD70Ct3cWG2UFx3NdKvA


2008年12月8日月曜日

image drag and drop and tipmage



Query UI Draggablesで簡単ドラッグドロップ


src="/javascript/jquery/jquery-1.2.6.min.js"
src="/javascript/Tipmage-1.0.js"

src="/javascript/jquery/jquery.dimensions.js"
src="/javascript/jquery/ui.mouse.js"
src="/javascript/jquery/ui.draggable.js"
src="/javascript/jquery/ui.draggable.ext.js"





http://www.museum-in-cloud.com/index.php?id=1001&option=tipmage&Itemid=1000
http://urlencode.net/result.cgi

2008年12月2日火曜日

urlfetch 403 Error




GAE automatically adds a Referer header and Referer is said to be one of the headers you can't change. That means you can't post to Twitter from GAE.





2008年11月27日木曜日

Picasa Web Albums Data API

Picasa Web Albums Data API

http://code.google.com/apis/picasaweb/developers_guide_protocol.html
アルバムIDの取得

url = "http://picasaweb.google.com/data/feed/api/user/UserName"
result = urlfetch.fetch(url)
dom = minidom.parseString(result.content)
NS  = 'http://search.yahoo.com/mrss/'
a   = range(0,len(dom.getElementsByTagName('title')) )
for x in a:
  title = dom.getElementsByTagName('title')[x].firstChild.data
  id = dom.getElementsByTagName('id')[x].firstChild.data

2008年11月15日土曜日

ReferenceProperty failed to be resolved

ReferenceProperty を利用した場合、データの削除は注意



Traceback (most recent call last):
File "/base/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 499, in __call__
handler.get(*groups)
File "/base/data/home/apps/blog-reader/1.329303600108062391/imgurl.py", line 226, in get
ImgList = imgurl_get_cache(site,server,title_lang)
File "/base/data/home/apps/blog-reader/1.329303600108062391/imgurl.py", line 308, in imgurl_get_cache
rssItems += ""
File "/base/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 2421, in __get__
raise Error('ReferenceProperty failed to be resolved')
Error: ReferenceProperty failed to be resolved

2008年11月12日水曜日

it's not found as __builtin__.instancemethod

なぜか memcache でエラーが発生するようになっている。
要観察


File "/base/data/home/apps/blog-editor/1.1280/hw_blog.py", line 1073, in get_blog_menu
if not memcache.add("blogurls", blogurls, 60*60*24*30):
File "/base/python_lib/versions/1/google/appengine/api/memcache/__init__.py", line 557, in add
return self._set_with_policy(MemcacheSetRequest.ADD, key, value, time=time)
File "/base/python_lib/versions/1/google/appengine/api/memcache/__init__.py", line 602, in _set_with_policy
stored_value, flags = _validate_encode_value(value, self._do_pickle)
File "/base/python_lib/versions/1/google/appengine/api/memcache/__init__.py", line 170, in _validate_encode_value
stored_value = do_pickle(value)
File "/base/python_lib/versions/1/google/appengine/api/memcache/__init__.py", line 274, in DoPickle
self._pickler_instance.dump(value)
File "/base/python_dist/lib/python2.5/pickle.py", line 218, in dump
self.save(obj)
File "/base/python_dist/lib/python2.5/pickle.py", line 325, in save
self.save_reduce(obj=obj, *rv)
File "/base/python_dist/lib/python2.5/pickle.py", line 413, in save_reduce
save(state)
File "/base/python_dist/lib/python2.5/pickle.py", line 280, in save
f(self, obj) # Call unbound method with explicit self
File "/base/python_dist/lib/python2.5/pickle.py", line 643, in save_dict
self._batch_setitems(obj.iteritems())
File "/base/python_dist/lib/python2.5/pickle.py", line 675, in _batch_setitems
save(v)
File "/base/python_dist/lib/python2.5/pickle.py", line 325, in save
self.save_reduce(obj=obj, *rv)
File "/base/python_dist/lib/python2.5/pickle.py", line 390, in save_reduce
save(cls)
File "/base/python_dist/lib/python2.5/pickle.py", line 280, in save
f(self, obj) # Call unbound method with explicit self
File "/base/python_dist/lib/python2.5/pickle.py", line 742, in save_global
(obj, module, name))
PicklingError: Can't pickle : it's not found as __builtin__.instancemethod

--
回復

Our memcache maintenance was completed at 10:52 PST (GMT-8). During
this period, although calls to the API did in fact return False and
None as expected, memcache API response latency increased
significantly, causing requests with heavy memcache dependence to time
out after 10 seconds due to the request timer, covered in our
documentation here: http://code.google.com/appengine/docs/python/requestsandcgi.html

We're now implementing a fix for this issue, and in the future the
memcache API will return immediately in the event of an outage. We
apologize for any inconvenince this may have caused.

Pete Koomen, App Engine Team

On Nov 12, 6:05 pm, App Engine Team

2008年11月5日水曜日

datastore_errors.BadArgumentError:_app must not be empty.

以前 SDK で動作していた script が動かなくなった。
以下の一行の追加が必要


When I upgraded to 1.1.3 from 1.1.2, my App Engine unit tests (built 
using the scheme described in another post [1]) started failing with 
the following stack trace: 

google.appengine.api.datastore_errors.BadArgumentError: _app must not be empty.

2008年10月27日月曜日

Thumbnail Generation




http://www.zubrag.com/scripts/thumbnail-generator.php

http://localhost/test/webthumb.php?url=http://www.yahoo.co.jp
Thumbnail Generation Error. Thumbnail not created.

http://www.zubrag.com/forum/index.php/topic,33.0.html

C:\xampp\htdocs\test>IECapt.exe http://www.yahoo.com test.png

C:\xampp\htdocs\thumbnail
IECapt.exe
image.class.php
webthumb.php

//$thumbnails_folder = 'Z:/home/localhost/www/thumb/cache/';
$thumbnails_folder = 'C:/work/';


---


Pythonでクリップボードの画像をファイルに保存する
http://d.hatena.ne.jp/himazublog/20080629/1214703203
Python2.5 win32  ( pywin32-212.win32-py2.5.exe   )
http://sourceforge.net/project/showfiles.php?group_id=78018&package_id=79063&release_id=616849

RecIE(IEの操作を記録するツール)とPythonを連携

PILで文字列を画像
 from PIL import Image, ImageDraw, ImageFont
 font = ImageFont.truetype('/usr/local/grass5/fonts/ipagp.ttf',25, encoding='unic')
 image = Image.new('RGBA', (100, 100)) 
 draw = ImageDraw.Draw(image) 
 draw.text((0, 0), u'テスト', font=font, fill='#000000')
 image.save('hoge.png', 'PNG')
http://humming.via-kitchen.com/2007/10/14/string-2-image-using-pil/

PIL Tutorial: How to Create a Button Generator
http://nadiana.com/pil-tutorial-how-create-button-generator

2008年10月13日月曜日

Cooliris と GAE 連携

Cooliris の 3D Wall がすごい。


http://www.cooliris.com/
http://developer.cooliris.com/

crossdomain.xml の設定が必要

http://groups.google.com/group/google-appengine/browse_thread/thread/f064730ead93eedc/4876f0b321b580d0?show_docid=4876f0b321b580d0&fwc=1

ブラウザのキャッシュをクリアしないと、crossdomain エラーが止まらなかった。


ローカルでのテスト
swf ファイルの保存
http://www.browsertools.net/Flash-Saving-Plugin/index.html

ローカルファイルシステムの SWF および信頼されているローカル SWF ファイルのみがローカルリソースにアクセスできます

C:\WINDOWS\system32\Macromed\Flash\FlashPlayerTrust\AcrobatConnect.cfg
に swf の位置を追加
C:\xampp\htdocs\rss\cooliris.swf

2008年10月4日土曜日

Primary Key と Key_name ~MySQLのデータをGAEへ

データの入ったMySQLテーブルの内容を Google App Engine の Cloud に転送することができた。

図のData Viewer の画面であると新たに追加した Model が List に表示されるまでに少しタイムラグがあるため、作業中の動作確認は Logs の画面を監視して行なった。



今回、30個程度のテーブルを対象に作業を行なったが、RDBMS のキー項目とGoogle App Engine の Key_name の対応は以下のようになった。
Key_name は設定したが、parent は設定しなかった。


col = ""
bid = ""
cid = ""
content_id = ""
session_id = ""
menuid = ""
client_id = ""
time = ""
for r in rr:
if r.column_name == 'id' or r.column_name == 'cid':
key_name = 'id' + self.request.get(r.column_name)
j = clazz(key_name = key_name)

if r.column_name == 'cid' : cid = self.request.get(r.column_name)
if r.column_name == 'content_id': content_id = self.request.get(r.column_name)
if r.column_name == 'bid' : bid = self.request.get(r.column_name)
if r.column_name == 'time' : time = self.request.get(r.column_name)
if r.column_name == 'session_id': session_id = self.request.get(r.column_name)
if r.column_name == 'menuid' : menuid = self.request.get(r.column_name)
if r.column_name == 'client_id' : client_id = self.request.get(r.column_name)

if menuid and client_id:
key_name = 'id' + menuid + client_id
j = clazz(key_name = key_name)

if session_id and time:
key_name = 'id' + time + session_id
j = clazz(key_name = key_name)
if content_id:
key_name = 'id' + content_id
j = clazz(key_name = key_name)
if cid and bid:
key_name = 'id' + bid + cid
j = clazz(key_name = key_name)

2008年10月3日金曜日

トランザクションとデータ確認2 ~MySQLのデータをGAEへ

322 レコードのデータを 5 件づつ分けて POST したはずが、件数が合わない。

MySQL からColud へのデータの POST に較べれは、Cloud の一部のキーとなる項目データを取得して、 MySQLに格納するようにするのは工数は少なそうなので、Download 側も作成することに。


Python(Cloud , Google App Engine )
件数とキー項目(key_name)の表示

if self.request.get('cmd') == "columns":
rr = db.GqlQuery("select * from Information_schema_columns")
for r in rr:
self.response.out.write( r.key().name()+"<br>")
self.response.out.write( "count(*) = " + str(rr.count() ))

PHP(MySQL)
Cloud にアクセスし、リストされたデータを取得し、MySQL用のinsert文のSQL を作成し、表示(行末の余計なデータは手動削除)
$req =& new HTTP_Request("http://xxxx/sign?cmd=columns");
if (!PEAR::isError($req->sendRequest())) {
$imgList = $req->getResponseBody();
}
$dd = explode("<br>", $imgList) ;

$sql = "insert into `gae_columns` ( `column` ) values " ;
foreach ($dd as $d) {
$sql_values .= "( '".$d."' )," ;
}


MySQL に gae_column というテーブルを作成し、これに Cloud からの一覧データを挿入。
・MySQL に取得後のデータの確認用SQL
- 一覧

SELECT concat(table_name,'.',column_name) FROM
information_schema.columns i ,
joomlaj.gae_columns g
where i.table_schema ='joomlaj'
and concat('joomlaj_',table_name,'.',column_name) = g.column

- 不足分のリスト

SELECT concat(table_name,'.',column_name)
FROM information_schema.columns i
where i.table_schema ='joomlaj'
and concat('joomlaj_',table_name,'.',column_name) not in
(select g.column from joomlaj.gae_columns g)

やはりこうした作業は RDBMS が格段に優れる。

トランザクションとデータ確認1 ~MySQLのデータをGAEへ

PHP(MySQL) 側で PEAR の HTTP/Request.php を利用し、概略は以下のような流れで1レコードづつ Loop させて POST する。

5回程度のLoop とした。SQL の offset を追加しながら、間隔をあけて実行。
SELECT * FROM information_schema.columns where table_schema ='joomlaj' order by table_name, ordinal_position limit 0,5

SELECT * FROM information_schema.columns where table_schema ='joomlaj' order by table_name, ordinal_position limit 5,5


 require_once "HTTP/Request.php";

$req =& new HTTP_Request($post_url);
$req->setMethod(HTTP_REQUEST_METHOD_POST);

 ・・・

$database->setQuery( $sql2 );
$rows = @$database->loadAssocList();

foreach($fields[$table] as $field=>$type) { ?>
eval($get_fld_value);
$req->addPostData(strtolower($field), $value);


$request .= '&table='.$table ;
$request .= '&table_name='.$table_name ;
$req->addPostData('table', $table);
$req->addPostData('table_name', $table_name);

if (!PEAR::isError($req->sendRequest())) {
$response1 = $req->getResponseBody();
} else {
$response1 = "";
}



POST されたものを受ける、Python(Google App Engine の Cloud )側での概要はこちら

*1 PHP の fsockopen ではGAEのSDKでは動作しても cloud への POST はうまくかないようなので注意

2008年10月2日木曜日

Dynamic Datastore MySQLのデータをGAEへ

information_schema スキーマのcolumns テーブルについては以下のように決め打ちで対応した。
Datastore 側の key_name にはスキーマ、テーブル、カラム名を結合したものを設定した。

key = information_schema_columns.table_schema + "_"
key += information_schema_columns.table_name+ "."
key += information_schema_columns.column_name

Information_schema_columns.get_or_insert(key,
table_catalog = self.request.get('table_catalog'),
table_schema = self.request.get('table_schema'),
table_name = self.request.get('table_name'),
column_name = self.request.get('column_name'),
ordinal_position = ordinal_position,
column_default = column_default,
is_nullable = self.request.get('is_nullable'),
data_type = self.request.get('data_type'),
character_maximum_length = character_maximum_length,
character_octet_length = character_octet_length,
numeric_precision = numeric_precision,
numeric_scale = numeric_scale,
character_set_name = self.request.get('character_set_name'),
collation_name = self.request.get('collation_name'),
column_type = self.request.get('column_type'),
column_key = self.request.get('column_key') ,
extra = self.request.get('extra'),
privileges = self.request.get('privileges'),
column_comment = self.request.get('column_comment'))

けれども、この情報を利用してデータを転送する汎用的なものを作成しなければならない。
RDBMSには動的SQLという仕組みがあるが、 Datastore ではどのように対応したらいいのであろうか。

Pythonでリフレクションする」*1 などと参考にいろいろと試行錯誤、ようやくそれらしいものができた。
苦労したのはまず、

1.  動的に Class を作成するには ?

clazz = globals()[cls.capitalize()]
obj = clazz()

 
2.  動的に作成できた Class の Attribute を動的に作成するには ?

setattr


3. get_or_insert() でなくて、単純に put() するときの key_name の指定方法 ?

4. key_name にはなにを設定するか ?

Joomla! では id あるいは cid 列を設定しているようなので、まずはこれらを利用することに。


def post_mysql_data(self,cls):
clazz = globals()[cls.capitalize()]
obj = clazz()
table = cls
rr = db.GqlQuery("select * from Information_schema_columns where table_name = :1", table )

col = ""
for r in rr:
if r.column_name == 'id' or r.column_name == 'cid':
column_value = self.request.get(r.column_name)
key_name = 'id' + column_value
j = clazz(key_name = key_name)

if j:
for r in rr:
column_value = self.request.get(r.column_name)
if r.data_type == 'varchar' or r.data_type == 'text' or r.data_type == 'longtext' :
if column_value : setattr(j,r.column_name,column_value)
if r.data_type == 'bigint' or r.data_type == 'tinyint' or r.data_type == 'int' :
if column_value : setattr(j,r.column_name,int(column_value))
if r.data_type == 'datetime':
if column_value :
if column_value == '0000-00-00 00:00:00':column_value = '1901-01-01 00:00:00'
column_value = datetime.datetime.strptime(column_value,'%Y-%m-%d %H:%M:%S')
setattr(j,r.column_name,column_value)
col = j.put()


*1  shisashiの開発日記 http://shisashi.blogspot.com/

information.schema.columns MySQLのデータをGAEへ

拡張機能のライブラリ Joomla! Extensions Directory の
Administration - Database Management の中の
を使うと、 Joomla! というCMSの Administration 画面で phpMyAdmin ライクの操作を行なうことができる。
テーブル構造から class の定義の変換についてはオリジナルの components を作成してみたが、以降はこの Extention Components をベースにすることにした。

PHP 側から MySQL のデータを Google App Engine の Cloud に POST して、それを Python側で受けて datastore に put()  するフローとなる。

事前に各model の定義は作成しておいたとしても、datastore に put する際に column ごとにデータの型が intger なのか text なのか、わかっている必要がある。

Cloud 側のデータの型は数字もすべてテキストとし保存してしまってもよいのかもしれないが、いずれにしろまず、列情報を持つ MySQL のテーブルを Cloud に転送する第1ターゲットとすることにした。


SELECT * FROM information_schema.columns where table_schema ='joomlaj' order by table_name, ordinal_position limit 0,10

MySQLのデータをGAEへ

テンプレートの良し悪しで画面の感じがまったくかわってしまう。
PHP で書かれている Joomla とういうCMSのテンプレートの質が高い。また拡張ライブラリが充実している。

GAE にこれらのテンプレートを応用しようとした場合、 やはり Joomla 側のキーとなるいくつかのテーブルと同様のものを Cloud に作成する必要がある。

Joomla! の components の作成手順を参考にまずテーブルに対応する class の定義を作成するようにした。 以下がまず作成した対応一覧。

if ($data_type == "bigint" ) { $datastore_type = " = db.IntegerProperty()" ;}
if ($data_type == "tinyint") { $datastore_type = " = db.IntegerProperty()" ;}
if ($data_type == "int" ) { $datastore_type = " = db.IntegerProperty()" ;}
if ($data_type == "varchar") { $datastore_type = " = db.StringProperty()" ; }
if ($data_type == "text") { $datastore_type = " = db.TextProperty()" ; }
if ($data_type == "longtext"){ $datastore_type = " = db.TextProperty()" ; }
if ($data_type == "datetime"){ $datastore_type = " = db.DateTimeProperty()" ; }
views/auto/tmpl/defaul.php



2008年10月1日水曜日

fsock でcloud にPOSTしても値が届かない

php の fsockopen にて データを POST する、
local の Google App Engine の SDK 環境では動作しても、本番環境の Cloud では
501 エラーなども発生。

チェックが厳しいためか。
http://code.google.com/p/gears/wiki/ContentRangePostProposal


結局 PEAR を利用したところ、無事 cloud に対してでも POST できるようになった。
http://pear.php.net/manual/ja/package.http.http-request.headers.php

2008年9月22日月曜日

Joomla

・ 管理メニューで Top Menu が表示されない
->   モジュール管理 にて Top Menu を有効にする

- インストール時に サンプル を選択しないと、 jos_menu_types  に seed data が登録されないことが原因
joomlaj\installation_\sql\mysql\sample_data_sql
INSERT INTO `#__menu_types` VALUES
(2, 'usermenu', 'User Menu', 'A Menu for logged in Users'),
(3, 'topmenu', 'Top Menu', 'Top level navigation'),
(4, 'othermenu', 'Resources', 'Additional links'),
(5, 'ExamplePages', 'Example Pages', 'Example Pages'),
(6, 'keyconcepts', 'Key Concepts', 'This describes some critical information for new Users.');


・ 最終更新 の日付の日の部分が %d のまま表示される。
/language/ja-JP/ja-JP.ini の以下を変更(結局スペースを追加)
DATE_FORMAT_LC2=%Y年  %B %d日(%A) %H:%M
#DATE_FORMAT_LC2=%Y年%B%d日(%A) %H:%M


無料Joomla!テンプレー          http://www.joomlashack.jp/content/view/4/9/
Joomla ジャパン                    http://www.joomla.jp/
Joomla Template Tutorial    http://dev.joomla.org/content/view/1136/79/


Joomla! 1.5 変更部分
・ メニューの構築手順
  外部ファイルの取り込みがエクステンションに一元化
・ テンプレートではtemplateDetails.xml の役割
  モジュールポディションの設定、テンプレートパラメータの設定が可能
  テンプレート側にポディション

・ 特定のメニューから呼び出されたページに対し、テンプレートを指定することができる
  セクションと、その配下にカテゴリーの2層構造でしかコンテンツを管理することができない
  --->  メニューと組み合わせが必要
  Assign 対応していない template もある。



・ Joomla! 1.5.3 でタイトルタグにサイト名を表示させるための変更

・  機能拡張     http://extensions.joomla.org/
    Joomla! 1.5 Template Tutorials Project


・ Zend Framework の動作


・phpフレームワーク・オブジェクト指向入門 http://phpc2.seesaa.net/
PHP5の新機能(クラス関係) http://www.atmarkit.co.jp/fcoding/articles/php5/02/php502a.html
・引数の値渡しと参照渡し http://ww7.tiki.ne.jp/~inabah/php/004_002.htm

Home Developer Manual 11. API Reference [REVIEW] database->loadAssocList

Tutorial:Developing a Model-View-Controller Component - Part 1

2008年9月19日金曜日

Uploading index definitions Error


何日かぶりに appcfg.py update xxxx/ で更新しようとしたところ以下のエラーで先に進まなくなってしまった。
タイミングとして新しいイメージファイルをいくも追加したので、それが原因かと思ったが、そうではないらしい。

Uploading index definitions.
Error 400: --- begin server output ---
Building a composite index failed: ApplicationError: 3
--- end server output ---

Issue 287:Explosion with >100 indexes


To vacuum your indexes: 
1. Create a backup of your index.yaml specification
2. Remove the definitions of the indexes in Error from your index.yaml file
3. Run appcfg.py vacuum_indexes your_app_dir/
4. Replace the modified version of your index.yaml file with the original
5. Run appcfg.py update your_app_dir/


を参考に まず index.yaml をクリアしてから vacuum すると、

This index is no longer defined in your index.yaml file.

ancestor: true
kind: Timeline
properties: []

Are you sure you want to delete this index? (N/y/a): y

ときかれるので、N として、いかにも不要そうな複合インデックスをいくつか削除して
appcfg.py vacuum_indexes your_app_dir/

にて今度は Y にて削除した。
いくつも削除したため、ダッシュボードでみると index の delete のタスクが走りだした。このタスク中に appcfg.py update xxxx/ すると
Server Error (500) A server error has occured.
となる。しばらく、時間をおいたところ無事 update できたが、その直後から今度はindex 作成のタスクがいくつも走りだした。




http://code.google.com/appengine/docs/datastore/queriesandindexes.html#Big_Entities_and_Exploding_Indexes

To handle "Error" indexes, first remove them from your index.yaml file and run appcfg.py vacuum_indexes. Then, either reformulate the index definition and corresponding queries or remove the entities that are causing the index to "explode." Finally, add the index back to index.yaml and run appcfg.py update_indexes.

2008年9月13日土曜日

Google Visualization API その2


Visualization APIを用いたプログラミング 
を参考に SpreadSheet からの Timeline の描画を行なってみた


Chart API だとすぐにぶつかるデータ数の壁をあっさりクリアしてしまった。
おそるべき JavaScript 。


・メモ
- spreadsheet は Share Anyone can this document WITHOUT LOGGING IN
 にしておくこと
- packages:["annotatedtimeline"]
 にSpreadSheet から 123.4 のように少数点付きの値を
 データとして与える場合 123.*100/100 のように一度 乗算して除算しないとなぜかうまくいか
 なかった。 ( javascript を理解していない? ためか  これには苦労した)
-  query.setQuery("select C, J from `Sheet 1` order by C");
 Sheet 1 を Sheet 2 に変えても、先頭のシートしか参照できない。 要調査

2008年9月12日金曜日

Google Visualization API


Google Visualization API
Gallery

Google Finance で使用されているものが登録されていた
さっそく Google App Engine と組み合わせ、使用したところ動作した
けれども、どうもデータの表示範囲のスクロールがなぜかうまくいかなかった。
原因はデータのラインの本数で、折れ線グラフが1本だけだと、下部分の
スクロール機能が動作しない仕様らしい。




2008年9月8日月曜日

Google Chart API で日本語

日本語を使用するには urllib.quote() 
u' 日本語'  はNG

import urllib

chart_ttile = '日本語'

      url2 = '<img src="http://chart.apis.google.com/chart?'
      url2+= 'chs=600x200'
      url2+= '&chtt=' + urllib.quote(chart_title)


Memo

2008年9月7日日曜日

Timeout Error

GAE で今まで動作していたアプリが動作しなくなった。

TemplateSyntaxError: Caught an exception while rendering:

テンプレートが重いのが問題かと 表示内容をけづっていったところ
動作したり、しなかったり。

  File "/base/python_lib/versions/1/google/appengine/api/datastore.py", line 1627, in _ToDatastoreError
raise errors[err.application_error](err.error_detail)
Timeout
同様の現象の報告もあり、様子をみることに。

http://groups.google.com/group/google-appengine/browse_thread/thread/53e7e0a34f3bec89/37b2a5a033ee0ac0?hl=en&lnk=gst&q=timeout+error#37b2a5a033ee0ac0

つい先日も  login: admin  の設定の URL ではログインできなかったが、翌日は復活していた。

2008年9月5日金曜日

Property XXX must be a str or unicode instance, not a tuple

val = "test",

aa = Img( p1 = val )
aa.put()

不要なカンマがある場合などでもこのエラーとなる
BadValueError: Property p1 must be a str or unicode instance, not a tuple

2008年9月3日水曜日

トランザクション エラー Nested transactions are not supported.

1. get_or_insert  自体がトランザクションなのでこれを含めたトランザクションを
  作成しようとするとエラーとなる。

File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py",
   line 1386, in RunInTransaction'Nested transactions are not supported.')
BadRequestError: Nested transactions are not supported.


2.動作確認


First up is batch writes. You can now include entities in different entity groups in a single db.put() or db.delete() call. Entity modifications are only atomic within each entity group, but a single call that spans entity groups will be more efficient than a call for each group, which was required before.


以下は SDK だと Cannot operate on different entity groups in a transaction:
となるが Cloud では動作した。

class Dummy2(db.Model):
d1 = db.StringProperty()

class Dummy(db.Model):
d1 = db.StringProperty()

def tran():
d2 = Dummy2(d1 = "aaab")
d1 = Dummy(d1 = "aaab")

d1.put()
d2.put()

tran()


3. その他

   トランザクションの中で検索は実行できない。
  Can't query inside a transaction

2008年9月1日月曜日

YouTube API

http://code.google.com/apis/youtube/articles/youtube_api_appengine.html
に従い進めた。

Developer Key の取得の際は自分でどのように利用するか簡単に記述。
(どこかから値を持ってきて登録するのではない)

けれども、検索するだけであれば
self.developer_key = 'ADD YOUR DEVELOPER KEY HERE'
この部分は不要だった。

チュートリアルのSourceは以下にあるが、これらを特にダウンロードするまでの必要はなかった。
http://code.google.com/p/hello-youtube/source/browse/#svn/trunk/03_hello_youtube_search_query
http://code.google.com/p/hello-youtube/source/browse/#svn/trunk/04_hello_user_input

gdata 関連の設定は既に spredsheet などにアクセスするために導入してあったので
素直に検索を行なうことができた。

Youtube は検索言語の設定により、検索結果がかなり異なるので
http://code.google.com/apis/youtube/reference.html#Query_parameter_definitions
を参考に lr は追加しておいた。

query.vq = search_term
query.max_results = '5'
query.lr = 'ja'

* ja 以外の設定にして "japanese" で検索するとひどい結果になるとの指摘を最近みかけたので。

また、以下の値は検索キーによっては値が得られないことがあるため分岐が必要となる。
rating.average 
 if entry.rating:

2008年8月31日日曜日

NormalizeAndTypeCheck エラー

reference などの key は .key() が必要

○ login.session.key()
× login.session

忘れた場合のエラー

keys, multiple = datastore.NormalizeAndTypeCheckKeys(keys)
File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 117, in NormalizeAndTypeCheckK
ys
keys, multiple = NormalizeAndTypeCheck(keys, (basestring, Entity, Key))
File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 96, in NormalizeAndTypeCheck
(types, arg, typename(arg)))
adArgumentError: Expected an instance or sequence of (, , ); received (a Session).

2008年8月26日火曜日

mixi openid

Google App Engine上でmixi OpenIDを使ってユーザ認証をするサンプルコード - yanbe.diff - subtech  
を参考にテストしたところ
local:8080 だと動作するが Cloud (GAE に upload すると)だと以下のエラーとなる。
---
Traceback (most recent call last):
File "/base/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 499, in __call__
handler.get(*groups)
File "/base/data/home/apps/blog-editor/1.937/mixi_login.py", line 28, in get
request = consumer.begin('https://mixi.jp')
File "/base/data/home/apps/blog-editor/1.937/openid/consumer/consumer.py", line 354, in begin
'No usable OpenID services found for %s' % (user_url,), None)
DiscoveryFailure: No usable OpenID services found for https://mixi.jp
----

以下、参考

http://d.hatena.ne.jp/mitsugi-bb/20080823/1219592219#c


http://www.socialpreneur.info/ja/blog/784

http://ms76.jp/2008/08/22/mixi_openid_for_wordpress/


http://insilico.jognote.com/blog/2008/08/13/curl-%E3%81%AB-https-%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B/

---
おそらくこれが原因
Issue 407 http://code.google.com/p/googleappengine/issues/detail?id=407 :
webapp.RequestHandler.redirect missing location header for long urls

2008年8月12日火曜日

spreadsheet からの GAE への upload

GAE SDK付属のdemo/guestbook.py をベースに spreadsheet.google.com のデータを
GAE Cloud の datastore に upload する script を作成
参考ファイルはこちら guestbook.py
今回程度のレコードサイズのデータの場合、read は 150 レコード、 put (write)は 20~30レコード程度毎の処理としました。

利用方法

  1. まず、spreadsheet のデータを読み込みます。

    • 今回は対象は DOW の株価データで 960 レコード程度です。
    • SDK 環境では一度にすべての spreadsheet のデータを読み込むことができましたが、
      cloud では 100 行程度 ( 200行はエラー ) ごとに複数のシートにデータを分割
    • "read" とし、シートの key と シート番号を指定
    •  1 sheet 分のデータを Memcache に読み込みます。


  2. 読み込んだデータを GAE Cloud の datastore に書き込みます。

    • "put" を指定
    • 100 行一度に書き込もうとするとエラーになりますので、今回の場合、20行程度ごとに
      区切り、 offset をかけながら、何度かに分けて書き込み処理を行ないました。


  3. offset は自動的に加算するようにしましたので、[Sign Guest]を繰り返しクリック

  4. 1 sheet 分のデータの upload が完了したら Memcache をクリア
  5. 次のシート番号を指定し、同様の作業を繰り返します。



2008年8月11日月曜日

Memcache API

http://code.google.com/appengine/docs/memcache/

日本語訳 http://d.hatena.ne.jp/technohippy/20080717#1216393318



memcache.add( key, value, time=xx, min_compress_len=0)
time 設定した有効時間(単位秒)以後は再度、検索処理を行なう
Optional expiration time, either relative number of seconds from current time (up to 1 month), or an absolute Unix epoch time. By default, items never expire, though items may be evicted due to memory pressure. Float values will be rounded up to the nearest whole second.   



2008年8月4日月曜日

Debug デバッグ

quick and dirty: きれいではなけれども簡単な方法(開発環境用)
* Cloud ではログにエラー出力されるの危険
import sys
print >>sys.stderr, "xxxxxxx"


正しくは
import logging
logging.debug("xxxxxxx")

def main():
# Set the logging level in the main function
# See the section on Requests and App Caching for information on how
# App Engine reuses your request handlers when you specify a main function
logging
.getLogger().setLevel(logging.DEBUG)
application
= webapp.WSGIApplication([('/', MainPage),
('/sign', Guestbook)],
debug
=True)
webapp
.util.run_wsgi_app(application)

if __name__ = '__main__':
main
()

参考
http://code.google.com/appengine/articles/logging.html
http://code.google.com/appengine/docs/python/logging.html
http://groups.google.com/group/google-appengine/browse_thread/thread/a67752ac402bb21e/345e203a5bdd0750?lnk=gst&q=debug+#345e203a5bdd0750
・Django Middleware で Traceback をコンソールに出力する
http://yamashita.dyndns.org/blog/django-middleware-traceback/

2008年7月23日水曜日

UnicodeEncodeError と webapp.RequestHandler

class MainPage(webapp.RequestHandler):
 の中で
str.decode("utf-8",'ignore')
 とすると
UnicodeEncodeError
 となるようなのですが、
webapp.RequestHandler を介さない別のところで処理している場合、
エラーは発生していない。

UnicodeEncodeError はまだよく理解できていないところがありますが。

メモ

UnicodeEncodeError: 'ascii' codec can't encode characters in position 422-424
: ordinal not in range(128)

Unicode 文字列をバイト列に符号化 (encode) するとき, range(128) つまり 0 から 127 までの文字コードしか扱えない 'ascii' エンコーディングが使われ,日本語文字に対して例外 UnicodeEncodeError が起こったもの
http://www.okisoft.co.jp/esc/cygwin-15a.html

webapp を調べたよ (前編) - Google App Engine
http://d.hatena.ne.jp/hamatsu1974/20080422/1208802967

2008年7月17日木曜日

DeadlineExceededError

DeadlineExceededError のエラー対応を検討

対応例
http://stage.vambenepe.com/archives/category/implementation

結果 google/appengine/runtime/apiproxy.py を参考に

from google.appengine import runtime
from google.appengine.runtime import apiproxy_errors
from google3.apphosting.runtime import _apphosting_runtime___python__apiproxy

これらを import して対応

except runtime.DeadlineExceededError:

File "/base/python_lib/versions/1/google/appengine/runtime/apiproxy.py", line 161, in Wait
rpc_completed = _apphosting_runtime___python__apiproxy.Wait(self)
DeadlineExceededError
だけでなく CancelleError というものもある。
File "/base/python_lib/versions/1/google/appengine/runtime/apiproxy.py", line 189, in CheckSuccess
raise self.exception
CancelledError: The API call datastore_v3.Count() was explicitly cancelled.

2008年7月8日火曜日

Key Limitations: 500 bytes

key_name を設定すると、その分 key が長くなる。
parent を設定すると、親の key が自分の key の先頭に付く。
parent が parent を持っていると、孫の key は長くなる。
限界が 500 byte

従って、当然、 親を設定し、さらにこれに親が設定されていれば
ancestor is  により祖先を検索条件に指定することができる。
b = db.GqlQuery("select * from Blog where ancestor is :1 ", granpa.key())


Tools for storing data: Keys

* Key corresponds to the Bigtable row for an Entity
* Bigtable accessible as a distributed hashtable
* Get() by Key: Very fast! No scanning, just copying data


* Limitations:
o Only one ID or key_name per Entity
o Cannot change ID or key_name later
o 500 bytes

2008年7月7日月曜日

Building Scalable Web Applications/Building a Blog


from google.appengine.ext import db

class BlogIndex(db.Model):
max_index = db.IntegerProperty(required=True,default=0)

class BlogEntry(db.Model):
index = db.IntegerProperty(required=True)
title = db.StringProperty(required=True)
body = db.TextProperty(required=True)


def post_entry(blogname, title, body):
def txn():
blog_index = BlogIndex.get_by_key_name(blogname)
if blog_index is None:
blog_index = BlogIndex(key_name=blogname)
new_index = blog_index.max_index
blog_index.max_index += 1
blog_index.put()
new_entry = BlogEntry(key_name=blogname + str(new_index),parent=blog_index, index=new_index,title=title, body=body)
new_entry.put()
db.run_in_transaction(txn)

def get_entry(blogname, index):
entry = BlogEntry.get_by_key_name(blogname + str(index),parent=Key.from_path('BlogIndex',blogname))
return entry


def get_entries(start_index):
extra = None
if start_index is None:
entries = BlogEntry.gql(
'ORDER BY index DESC').fetch(POSTS_PER_PAGE + 1)
else:
start_index = int(start_index)
entries = BlogEntry.gql(
'WHERE index <= :1 ORDER BY index DESC', start_index).fetch(POSTS_PER_PAGE + 1) if len(entries) > POSTS_PER_PAGE:
extra = entries[-1]
entries = entries[:POSTS_PER_PAGE]
return entries, extra


Building Scalable Web Applications から

Disk Seek を意識している。
Oracle などで Create sequence するように Counter 専用のModel を作成して、
increment("xxxx")するようにしたほうがよいのか。


Writes are expensive!
  • Datastore is transactional: writes require disk access
    • Disk access means disk seeks


  • Rule of thumb: 10ms for a disk seek
  • Simple math:
    • 1s / 10ms = 100 seeks/sec maximum


  • Depends on:
    • The size and shape of your data
    • Doing work in batches (batch puts and gets)

Reads are cheap!
  • Reads do not need to be transactional, just consistent


  • Data is read from disk once, then it's easily cached
  • All subsequent reads come straight from memory


  • Rule of thumb: 250usec for 1MB of data from memory
  • Simple math:
    • 1s / 250usec = 4GB/sec maximum
    • For a 1MB entity, that's 4000 fetches/sec


Tools for storing data: Entities
  • Fundamental storage type in App Engine
  • Set of property name/value pairs
  • Most properties indexed and efficient to query
  • Other large properties not indexed (Blobs, Text)


  • Think of it as an object store, not relational
    • Kinds are like classes
    • Entities are like object instances
  • Relationship between Entities using Keys
    • Reference properties
    • One to many, many to many





Tools for storing data: Entity groups 2

Hierarchical

  • Each Entity may have a parent
  • A "root" node defines an Entity group
  • Hierarchy of child Entities can go many levels deep
    • Watch out! Serialized writes for all children of the root


Datastore scales wide

  • Each Entity group has serialized writes
  • No limit to the number of Entity groups to use in parallel
  • Think of it as many independent hierarchies of data







class CounterConfig(db.Model):
 name = db.StringProperty(required=True)
 num_shards = db.IntegerProperty(required=True,default=1)

class Counter(db.Model):
 name = db.StringProperty(required=True)
 count = db.IntegerProperty(required=True,default=0)

def get_count(name):
 total = 0
 for counter in Counter.gql('WHERE name = :1', name):
  total += counter.count
 return total

def increment(name):
 config = CounterConfig.get_or_insert(name,name=name)
 def txn():
  index = random.randint(0, config.num_shards - 1)
  shard_name = name + str(index)
  counter = Counter.get_by_key_name(shard_name)
  if counter is None:
   counter = Counter(key_name=shard_name, name=name)
  counter.count += 1
  counter.put()
 db.run_in_transaction(txn)

increment("test")
print get_count("test")


def get_count(name):
 total = memcache.get(name)
 if total is None:
 total = 0
 for counter in Counter.gql('WHERE name = :1', name):
  total += counter.count
  memcache.add(name, str(total), 60)
 return total

def increment(name):
 config = CounterConfig.get_or_insert(name,name=name)
 def txn():
  index = random.randint(0, config.num_shards - 1)
  shard_name = name + str(index)
  counter = Counter.get_by_key_name(shard_name)
  if counter is None:
   counter = Counter(key_name=shard_name,name=name)
  counter.count += 1
  counter.put()
 db.run_in_transaction(txn)
 memcache.incr(name)















http://sites.google.com/site/io/building-scalable-web-applications-with-google-app-engine

Swift UI チュートリアル Loading watchOS が終わらない?

Loading watchOS が終わらない? ディスク容量の残量が少ないので不要なシュミレーターを削除したとこころ watchOSのものが全部なくなってしまっていた。 WatchOS を削除して再度インストールしても復活せず。 Create a new simulator で ...