2008年6月29日日曜日

don't add support for other languages の気持

はじめは PHP でなくて Python か、と思っていましたが、以下の表題に同意するといいいますか、
なとなく気持ちがわかるような気がしています。

Please don't add support for other languages.
http://groups.google.com/group/google-appengine/t/93d7fff1f3cfec95?hl=en

Google App Engine Helper for Django を使おうとしてみた

1. Google App Engine Helper for Django を使う
http://mars.shehas.net/~tmatsuo/misc/appengine_helper_for_django-ja.html
を参考に
http://code.google.com/p/google-app-engine-django/
から appengine_helper_for_django-r30.zip を download

2. これをテスト用の helloworld フォルダ以下に展開しようかとも、思ったが、そういうことはしてはいけない。( app.yaml ファイルなどもある)

C:\google\appengine_helper_for_django に展開


3. UNIX の場合 ln -s /path/to/google_appengine .google_appengine

appengine_helper_for_django は GAE の SDK にアクセスする。
Windows でも ln -s  できたはずなのでやり方を確認したが fsutil はファイル単位だけのサポートでフォルダには対応していないので、ろいろツールは作成されているようですが、
どうも Windows のシンボリックリンクのようなものは信用できない。
結局、物理的に C:\Program Files\Google\google_appengine を copy。

4. 起動 ( 拡張子 .py が c:python25\python.exe に関連づけされている)

C:\google\appengine_helper_for_django>manage.py runserver
WARNING:root:Loading the SDK from the 'google_appengine' subdirectory is now deprecated!
WARNING:root:Please move the SDK to a subdirectory named '.google_appengine' instead.
WARNING:root:See README for further details.
WARNING:root:Could not read datastore data from c:\docume~1\xxx\locals~1\temp\django_google-app-engine-django.datastore
WARNING:root:Could not read datastore data from c:\docume~1\xxx\locals~1\temp\django_google-app-engine-django.datastore.history
WARNING:root:Could not initialize images API; you are likely missing the Python "PIL" module. ImportError: No module named PIL
INFO:root:Server: appengine.google.com
INFO:root:Checking for updates to the SDK.
INFO:root:The SDK is up to date.
WARNING:root:Could not read datastore data from c:\docume~1\xxx\locals~1\temp\django_google-app-engine-django.datastore
WARNING:root:Could not read datastore data from c:\docume~1\xxx\locals~1\temp\django_google-app-engine-django.datastore.history
WARNING:root:Could not initialize images API; you are likely missing the Python "PIL" module. ImportError: No module named PIL
INFO:root:Running application google-app-engine-django on port 8080: http://localhost:8080


5. 起動されたようなのでアクセスしてみる http://localhost:8080/
Welcome to Django
It worked!
Congratulations on your first Django-powered page.

Of course, you haven't actually done any work yet. Here's what to do next:

* If you plan to use a database, edit the DATABASE_* settings in settings/settings.py.
* Start your first app by running python settings/manage.py startapp [appname].

You're seeing this message because you have DEBUG = True in your Django settings file and you haven't configured any URLs. Get to work!





6. manage.py startapp polls を実行、コンソールにあいかわらず警告がでるが、以下が作成される。
C:\google\appengine_helper_for_django\polls
polls/
__init__.py
models.py
views.py

7. モデルの作成
polls/models.py

from django.db import models

# Create your models here.
from appengine_django.models import BaseModel
from google.appengine.ext import db

class Poll(BaseModel):
question = db.StringProperty()
pub_date = db.DateTimeProperty('date published')

class Choice(BaseModel):
poll = db.ReferenceProperty(Poll)
choice = db.StringProperty()
votes = db.IntegerProperty()


8. settings.py の INSTALLED_APPS にpolls を追加


INSTALLED_APPS = (
'appengine_django',
'django.contrib.auth',
'polls',
# 'django.contrib.contenttypes',
# 'django.contrib.sessions',
# 'django.contrib.sites',
)


9. manage.py shell

>>> class Poll(BaseModel):
... question = db.StringProperty()
... pub_date = db.DateTimeProperty('date published')
...
Traceback (most recent call last):
File "", line 1, in
File "C:\google\appengine_helper_for_django\appengine_django\models.py", line 109,in __new__
new_class._meta = ModelOptions(new_class)
File "C:\google\appengine_helper_for_django\appengine_django\models.py", line 49, in __init__
model_module = sys.modules[cls.__module__]
KeyError: '__console__'
>>>


Django をみてみないと。 テンプレート機能だけしか使っていない。
以前、 An example of using Django on top of App Engine はみたのだけれども。

2008年6月27日金曜日

Key.from_path( kind, id ), key_name is not unique in Kind

http://code.google.com/appengine/docs/datastore/keyclass.html#Key_from_path

ある kind のデータの id (or key_name)がわかっているとき
 greeting = db.get(db.Key.from_path('Greeting', int(id)))
のような使い方ができるが、さらに、その親の条件をつけることができる。

k = Key.from_path('User', 'Boris', 'Address', 9876)
User:親の kind 名  Boris はこの kind の key_name(=id)


親が異なれば、key_name は同じものをいくつも登録することができる。
つまり、 kind のなかで key_name は必ずしも Unique ではない。

ちなみに key_name は小文字

NewSection = Section(parent=band, Title="Net", URL="News21", Type="News",key_name="k2")
NewSection.put()


"where ANCESTOR IS :1", oya_key

http://googleappengine.blogspot.com/2008/04/posted-by-ken-ashcraft-software.html

  • Avoid large entity groups. Any two entities that share a common ancestor belong to the same entity groups. All writes to an entity group are sequential, so large entity groups can bog down popular apps quickly if there are a lot of writes to that group. Instead, use small, localized groups in your design.

#490 のようなbugはあるようだけれども

NewBand = Band(Name="Test", URL="Test", Description="A test description")
NewBand.put()

NewSection = Section(parent=NewBand, Title="News", URL="News", Type="News")
NewSection.put()


子のput ()の際に parent を設定しておけば、ancector is に親キーを指定して、検索する
ことができる。

oya_key = db.GqlQuery("select * from Band")[0].key()
ko = db.GqlQuery("select * from Section where ANCESTOR IS :1", oya_key )



http://groups.google.com/group/google-appengine/browse_thread/thread/ad5175429f2f61f2
App works locally, but fails when uploaded
x = Section.all().ancestor(self).filter("URL = ", SectionURL).get

Property Names

name Property は alias ということ。

from google.appengine.ext import db

class MyModel(db.Model):
obj_key = db.StringProperty(name="key11")
content = db.StringProperty()

r = db.GqlQuery("select * from MyModel")
for rr in r:
print rr.content , rr.obj_key, key11
---
test None test



Transactions

Docs > Datastore API > Transactions

from google.appengine.ext import db

class Accumulator(db.Model):
 counter = db.IntegerProperty()
 a = Accumulator()
 a.counter = 0
 a.put()

def increment_counter(key, amount):
 obj = db.get(key)
 obj.counter += amount
 obj.put()

q = db.GqlQuery("SELECT * FROM Accumulator")
acc = q.get()
db.run_in_transaction(increment_counter,acc.key(), 5)
r = db.GqlQuery("select * from Accumulator" )

for rr in r:
 print rr.counter
----
5

2008年6月26日木曜日

Too Many Versions (403) The application already has the maximum number of versions.

2008-06-26 23:13:09,546 ERROR appcfg.py:1128 An unexpected error occurred. Aborting.
Rolling back the update.
Error 403: --- begin server output ---

Too Many Versions (403)
The application already has the maximum number of versions.
--- end server output ---


http://groups.google.com/group/google-appengine/browse_thread/thread/5bafb18014ca366a
issue として登録されているが
http://code.google.com/p/googleappengine/issues/detail?id=212&q=too%20many%20version&colspec=ID%20Type%20Status%20Priority%20Stars%20Owner%20Summary

application-id を変更するのは危険なので、しばらく静観。

http://groups.google.com/group/google-appengine/browse_thread/thread/24870ee8ecdfa34d/116c890c63876919#116c890c63876919


Fixed, i have to manually delete all the other version by dashboard,
than it started to work again fine...



一時はこの application-id はあきらめなければ
ならないのかとも思いましたが。

 バージョン情報を削除して復活です。

2008年6月25日水曜日

ascending single-property indexes are not necessary


Uploading index definitions.
Error 400: --- begin server output ---
Creating a composite index failed: ascending single-property indexes are not necessary
--- end server output ---

2008年6月24日火曜日

no matching index found

SDK 環境では徐々にデータを追加していったためか、あまり発生しなかったエラー

Cloud には bulkupload したためか、no matching index found エラーが多発する。
index.yaml に指定の内容を追加して update して対応。

2008年6月23日月曜日

DeadlineExceededError

削除は手動で画面を Reload して繰り返し実行すればいいが

r = db.GqlQuery("select * from Stock limit 100")
for rr in r:
 rr.delete()
後方一致 ( like *key_word ) の場合、offset で移動させた。
r = db.GqlQuery("select * from Model limit 100 offset " +  str(offset) )
start = int(offset) + 100
self.response.out.write('<a href="/xxx?offset=%s">del</a>' % str(start) )
for rr in r:
 if rr.trackback_url.find('</td></tr>') <>-1:
 ・・・

プログラムで LOOP させて offset を自動処理しては DeadlineExceededError はさけられず、
意味がない。

このあたり how should I clean up inside the datastore?
http://groups.google.com/group/google-appengine/browse_thread/thread/da854121e242755a?hl=en
でも話題になっているので、そのうち trauncate table のようなコマンドは用意されるか。
制限事項が多く、少々うんざり気味。

でも、RDBMS でも Undo 領域を必要としない truncate table ができるまでは Oracle などよく delete では「Rollbackセグメントが足りません」といってデータを思うように削除させてくれず、仕方がないので条件指定して少しづつ削除したこともあった。
Rollback などできなくていいから、まとめて削除させて欲しいと思いつつ。

untrusted app XXX cannot access app helloworld's data

BadRequestError: untrusted app XXX cannot access app helloworld's data


Reference Property の値( =Key ) をbulkupload
・・・
('stock', datastore_types.Key),
・・・
しようとして発生。

仮に upload できたとしても、意味がないのでコメントアウト。

max execution time

put にしろ fetch にしろ1回の処理に制限がある。

bulkload は 100 件
fetch は 1,000 件
delete も 100 件程度


> Is there any limit of execution time of scripts? If there is, how long
> is this max execution time for a function?

A few seconds, eight or ten seconds at the most.

> Is there any way to set the max_execution_time or functions like
> set_time_limit() in php?
I don't think so.

> If I want my script to execut in a long time to finish its job, maybe
> 20 mininuts, just looks like a service, is there any way in App
> Engine ?

Nope. Google App Engine is only designed for applications that can
respond to requests in under 300 ms or so.


http://groups.google.com/group/google-appengine/browse_thread/thread/08548c43d692af63?hl=en

2008年6月22日日曜日

fetch() returns a maximum of 1000 results

SDK 環境ではこの
1000件以上のデータがあっても、1000件までしか、検索結果を得ることができない

などという制限はなかったので、
実際にようやく bulkloader でデータを 投入してみてはじめて気がついた。
SELECT * FROM model limit 100 offset 1000
のようなことをすれば、1000 の先のデータにアクセスできるのか? とも思ったが、そんなことはできなかった。

http://code.google.com/appengine/docs/datastore/gqlqueryclass.html

  • The query has performance characteristics that correspond linearly with the offset amount plus the limit.

Note: fetch() returns a maximum of 1000 results. If more than 1000 entities match the query, and either no limit is specified or a limit larger than 1000 is used, only the first 1000 results are returned by fetch().



Google I/O session videos posted with slides
Working with Google App Engine Models

 Store counts を推奨するようなことが書かれて
いたので、ちょっと気になってはいたがけれども。
1000 件以内に収まる条件で管理しないと
どこまでデータがあるのかさえわからなくなるとは。

2008年6月20日金曜日

Importing UTF-8 Data with Bulkloader

Importing UTF-8 Data
http://groups.google.com/group/google-appengine/browse_thread/thread/d4cf3013483220b5
を参考にいろいろ試したが、以下のパターンで UTF-8 が登録できたが、
str とタイプが決まってしまて、 datastore_types.Text にできなかったので
500 バイト以上の UTF-8 データについては別途検討。

myloader.py

#from google.appengine.ext import bulkload
import bulkload
・・・
class PersonLoader(bulkload.Loader):
def __init__(self):
# Our 'Person' entity contains a name string and an email
bulkload.Loader.__init__(self, 'Person',
[
#      ('name', str),
      ('name', lambda x: unicode(x,'utf-8')),
('email', datastore_types.Email),
])


bulkload をフォルダごとProjectフォルダにコピーして、__init__.py を加工。
bulkload/__init__.py
・・・
buffer = StringIO.StringIO(data.encode('utf-8'))
・・・


('name', lambda x: unicode(x,'utf-8')) をやめて、以下を追加すると
日本語がきれいに抜けて、英数字だけになる。
#     val =  unicode(val, errors='ignore')
entity[name] = converter(val)

bulkload/__init__.py

c:/Program Files/Google/google_appengine/google/appengine/ext/bulkload/
__init__.py


"""A mix-in handler for bulk loading data into an application.

For complete documentation, see the Tools and Libraries section of the
documentation.

To use this in your app, first write a script, e.g. bulkload.py, that
instantiates a Loader for each entity kind you want to import and call
bulkload.main(instance). For example:

person = bulkload.Loader(
'Person',
[('name', str),
('email', datastore_types.Email),
('birthdate', lambda x: datetime.datetime.fromtimestamp(float(x))),
])

if __name__ == '__main__':
bulkload.main(person)

See the Loader class for more information. Then, add a handler for it in your
app.yaml, e.g.:

urlmap:
- regex: /load
handler:
type: 1
path: bulkload.py
requires_login: true
admin_only: true

Finally, deploy your app and run bulkload_client.py. For example, to load the
file people.csv into a dev_appserver running on your local machine:

./bulkload_client.py --filename people.csv --kind Person --cookie ... \
--url http://localhost:8080/load

The kind parameter is used to look up the Loader instance that will be used.
The bulkload handler should usually be admin_only, so that non-admins can't use
the shell to modify your app's data. The bulkload client uses the cookie
parameter to piggyback its HTTP requests on your login session. A GET request
to the URL specified for your bulkload script will give you a cookie parameter
you can use (/load in the example above). If your bulkload handler is not
admin_only, you may omit the cookie parameter.

If you want to do extra processing before the entities are stored, you can
subclass Loader and override HandleEntity. HandleEntity is called once with
each entity that is imported from the CSV data. You can return one or more
entities from HandleEntity to be stored in its place, or None if nothing
should be stored.

For example, this loads calendar events and stores them as
datastore_entities.Event entities. It also populates their author field with a
reference to the corresponding datastore_entites.Contact entity. If no Contact
entity exists yet for the given author, it creates one and stores it first.

class EventLoader(bulkload.Loader):
def __init__(self):
EventLoader.__init__(self, 'Event',
[('title', str),
('creator', str),
('where', str),
('startTime', lambda x:
datetime.datetime.fromtimestamp(float(x))),
])

def HandleEntity(self, entity):
event = datastore_entities.Event(entity.title)
event.update(entity)

creator = event['creator']
if creator:
contact = datastore.Query('Contact', {'title': creator}).Get(1)
if not contact:
contact = [datastore_entities.Contact(creator)]
datastore.Put(contact[0])
event['author'] = contact[0].key()

return event

if __name__ == '__main__':
bulkload.main(EventLoader())
"""

---
bulkload.Loader.__init__(self, 'Greeting',
[
('author', datastore_types.users.User ),
('content', str ),
('curl1', str ),
('cmapinfo', datastore_types.Text),
('date', lambda x: datetime.datetime.strptime(x,'%Y-%m-%d %H:%M:%S')),
('dateJST', lambda x: datetime.datetime.strptime(x,'%Y-%m-%d %H:%M:%S')),
])

--

C:\google\bulkload>c:\google\bulkload\bulkload_client.py --filename greeting_test.csv --kind Greeting --url http://xxxx.appspot.com/load02

2008年6月19日木曜日

model.properties()


kind =(Greeting(),Blog() )

for k in kind:

print "DROP TABLE IF EXISTS `" + k.kind() +"`;"
print "Create table `" + k.kind() + "` ("
print " `key` char(36) not null,"
print " `id` int,"
print " `key_name` char(12),"
p = k.properties()
for pp in p:
attr = str(p[pp]).split('google.appengine.ext.db.')[1]
attr = attr.split(' object')[0]
sql_attr = attr
if attr == "IntegerProperty" : sql_attr = "int"
if attr == "StringProperty" : sql_attr = "varchar(500)"
if attr == "ReferenceProperty" : sql_attr = "char(36)"
if attr == "DateTimeProperty" : sql_attr = "datetime"
if attr == "UserProperty" : sql_attr = "varchar(128)"
if attr == "TextProperty" : sql_attr = "text"
print "`" + pp + "`" + " " + sql_attr + ", #" + attr
print ") # remove last `,` !!!"

Local or Cloud

ようやく Cloud に 「Create and Application」できた。
しかし app.yaml の application: の部分を変更して、起動したことで
Local の datastore が初期化されてしまった。
バックアップがあるので、2,3日前の状態には戻せるが...
#!-*- coding:utf-8 -*-
import cgi
import wsgiref.handlers
import os

from google.appengine.ext import webapp
class MainPage(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/html'
self.response.out.write(u'Hello, webapp World! こんにちわ<br />' )
self.response.out.write(os.environ["PATH_INFO"] + "<br />")
self.response.out.write(os.environ["TZ"] + "<br />" )
self.response.out.write(os.environ["SERVER_NAME"] + "<br />")

-----
Hello, webapp World! こんにちわ
/hw0
UTC
localhost

2008年6月18日水曜日

「Google App Engine」の現況および今後の展開

http://codezine.jp/ の 【Google Developer Day 2008】 Google App Engineをおさらいしようより
500MBのストレージ、2GB/日のデータ転送量(500万PV/月 相当とのこと)まで無料
無償サービスについては、今後も継続していく予定
BigTableによる「分散型データストア」
SQLライクなGQLで操作できる。SQLのJOINにあたる機能はサポートしていないため、
少し慣れるのに時間がかかるが、分散環境の恩恵に預かれる。
確かに慣れるのに時間がかかると思う
現在、データベースAPIに含まれていない全文検索については、具体的な対応予定は
立っていない
SSLは今のところサポートしていないが、一部fetch APIなどではSSL接続を行っている
日本では、携帯電話のSMSによる認証関係のバグで試すことができないようだ(6月11日現在)。
これについてGoogleは、おそらく今週から日本でも使えるようになるはずだ
おかげさまでようやく Sign Up することができました。

2008年6月17日火曜日

Datastore Viewer Error

key ="agpoZWxsb3dvcmxkcgsLEgRCbG9nGPckDA"
r = db.get(key)
is OK.

But...
Google\google_appengine\google\appengine\ext\admin\__init__.py", line 542, in input_field
if len(sample_value) > 255 or sample_value.find('\n') >= 0:
TypeError: object of type 'NoneType' has no len()

Odd behaviour in admin interface
http://groups.google.com/group/google-appengine/browse_thread/thread/29cd28daea3c37d3?fwc=1

2008年6月16日月曜日

alter table ...

TextProperty は検索に利用できないのでStringPropertyに変更しようと、
db.Model を修正し、
None で Update
したが、新たに Text データを put したところ、また
TextProperty として表示されてしまった。

それで、
新しい、StringPropertyを追加したが、反映されるタイミングがまだわからない。
すくなくとも新しい Property に No error で put できても、実際には
データどころか、 Property さえ、作成されていなかったりする。

Propertyを追加した場合は、必ず
http://localhost:8080/_ah/admin/datastore/
で確認し、ここで追加部分に値を登録してみておいたほうがよい。

更新処理は時間がかかるので、これが無駄になると、時間の無駄。



class TextProperty()

A long string.
Unlike StringProperty, a TextProperty value can be more than 500 bytes long. However, TextProperty values are not indexed, and cannot be used in filters or sort orders.

2008年6月14日土曜日

Many-to-many JOIN

Many-to-many JOIN のサンプル、Intersection Table にあたるものに Reference Property だけの db.Model を作成することで対応。
back-references を使っているので注意
def books()  などは必須ではないと思う。(当初、かえってこれで混乱してしまった)













Google App Engine: [A Better] Many-to-many JOIN
http://blog.arbingersys.com/2008/04/google-app-engine-better-many-to-many.html

ER-Modeling with Google App Engine (updated)
http://daily.profeth.de/search/label/entity%20relationship%20model

Google App Engine 入門6 検索件数
http://webdba.blogspot.com/2008/04/google-app-engine6.html

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

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