2012年3月8日木曜日

iOS5.1 purges SQLite file

iOS Data Storage Guidelines

addSkipBackupAttributeToItemAtURL
https://developer.apple.com/library/ios/#qa/qa1719/_index.html
  https://developer.apple.com/icloud/documentation/data/

 1. Apple が想定する iCloud のバックアップ対象は <application_home>/Document 以下のフォルダである。

  1.1. 従って、このエリアに一時ファイルを置いアプリは審査の際に reject される。

2. <application_home>/Library/Caches が一時エリア

  2.1  SQLite のファイルは <application_home>/Library/WebKit 以下に作成されていたが iOS5.1 からは <application_home>/Library/Cache に変更された。

  2.2  SQLite は Apple は恒久的なストレージとは考えていないので、バックアップの対象としない。

Examples of files you should put in the Caches directory include database cache files and downloadable content, http://groups.google.com/group/phonegap/browse_thread/thread/4490b6a80f6c0e7c?pli=1 
I hope I am wrong, but after installing iOS5.1 beta2, I see that the app data of my phonegap app are now stored in the Library/Cache folder : See this screenshot my app data in iExplorer :http://cl.ly/1p3L1U2W1J3y3I1T1r3y (before it was in Library/WebKit folder).  

3. Apple が指定しる一時エリアは <application_home>/tmp

4. Use the "do not back up" attribute for specifying files that should remain on device, even in low storage situations.

Apple は かまわないでよ!属性 を用意したけれども、これでいままで SQLite のストレージが置かれていた <application_home>/Library/WebKit を指定したとしても、もうすでにそのファイルはすべて iOS Update の際に purge されてしまっている。
<application_home>/Library/Caches 以下を かまわないでよ!属性 に指定できるかもしれないが、できたとしてもあまり信用しないほうがいい。
* 属性指定後、現在 2012年10月 までのところ削除されていない。

Use this attribute with data that can be recreated but needs to persist even in low storage situations for proper functioning of your app or because customers expect it to be available during offline use.

This attribute works on marked files regardless of what directory they are in, including the Documents directory.

These files will not be purged and will not be included in the user's iCloud or iTunes backup.

Because these files do use on-device storage space, your app is responsible for monitoring and purging these files periodically.

2012年3月5日月曜日

dev_appserver. 'module' object has no attribute 'HTTPSHandler'

Windows Server 2008 64bit版で   ActivePython 2.5 64bit だと Google AppEngine のlocal server が起動Errorとなった。 

C:\>dev_appserver.py xxxx
'module' object has no attribute 'HTTPSHandler'

ActivePython 2.5 32bit の場合

WARNING urlfetch_stub.py:111] No ssl package found. urlfetch will not be able to validate SSL certificates.

となるが起動できる



ちなみに

File "", line 1, in bind socket.error: (10013, 'Permission denied')


このエラーは port の問題なので port を 8080 以外のものに変更することで 対応。


2011年12月11日日曜日

GAE SDK1.6 Python2.7

GAEがSDK1.6でPython2.7に対応したが既存のアプリを2.7にはDatastoreを利用している場合、移行できない。また local server は2.7に対応していない。  

 

既存のアプリケーションで Datastore を一切使用していない場合 app.yaml を変更して

runtime: python27
# runtime: python
api_version: 1
threadsafe: true  #マルチスレッドで動かす

- url: .*
# script: main.py
   script: main.app
python2.7 に移行することができるのかもしれないが、Datastoreを使用していた場合 Deploy の際、以下のエラーとなる。
Error 400: --- begin server output ---
The 'python27' runtime is only supported for apps using the High Replication Datastore.
 

Local Serverは2.7対応していないがコマンドラインからの実行もあるのでベースは2.7に変更することにした。  

インストールされているバージョンを確認 

$ port select --list python
Available versions for python:
 none
 python25 (active)
 python25-apple
 python26-apple
 python27
 

2.7 に変更 

$sudo port select python python27
Password:
Selecting 'python27' for 'python' succeeded. 'python27' is now active.
 

参考: macports python_select command not found
 

 

GoogleAppEngineLauncherは2.5固定
(python2.7に対応していない。Preferenceを2.7に変更しても2.5に戻される)

 

*** Running dev_appserver with the following flags:
    --admin_console_server= --port=8080
Python command: /opt/local/bin/python2.5
WARNING  2011-12-11 05:39:58,528 urlfetch_stub.py:111] No ssl package found. urlfetch will not be able to validate SSL certificates.
WARNING  2011-12-11 05:39:58,828 rdbms_mysqldb.py:90] The rdbms API is not available because the MySQLdb library could not be loaded.
INFO     2011-12-11 05:39:59,444 dev_appserver_multiprocess.py:637] Running application dev~snsimglink on port 8080: http://localhost:8080
WARNING  2011-12-11 05:40:10,308 py_zipimport.py:139] Can't open zipfile /opt/local/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/setuptools-0.6c11-py2.5.egg-info: IOError: [Errno 13] file not accessible: '/opt/local/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/site-packages/setuptools-0.6c11-py2.5.egg-info'
INFO     2011-12-11 05:40:11,033 dev_appserver.py:2753] "GET / HTTP/1.1" 200 -
 

 

HRD


Error 400: --- begin server output ---
The 'python27' runtime is only supported for apps using the High Replication Datastore.
--- end server output ---
If deploy fails you might need to 'rollback' manually.
The "Make Symlinks..." menu option can help with command-line work.
*** appcfg.py has finished with exit code 1 ***

 

 

参考: 【備忘録】 GAEでPython2.5からPython2.7へマイグレーションするには

GAEでのDjangoの使い方は2通りある(と思って)います。

フレームワークとして使う(というか、生Djangoとして使う)

この場合settings.pyが必要になります。
書くべき中身は渡邉さんの引用したURLに書いてある通りですが、元ネタはDjangoの配布物に含まれています
ので、これをコピーして書き換えることになります(多分・・・自信無いですが一応動いた実績あり)。

Djangoのテンプレートエンジンをwrapした、google.appengine.ext.webapp.templateとして使う

GAE/Py2.5でそうしていた場合、GAE/Py2.7に切り替える際、あなたはプログラム上は何も変える必要がありません(※)。
Python2.7環境で上記をimportすると、中身はDjango1.2になっているからです。
やるべきことはDjango0.9.6と1.2の差分であるテンプレートエンジンの挙動の違いを、テンプレート側で
吸収してあげることです(デフォルトエスケープの扱いとか)。
※Python2.7対応はまた別の話です。

2011年12月5日月曜日

ImageMagick (convert)画像分割してLoop処理

ImageMagick ( convert )を利用して画像ファイルを分割
-----------------------------

#! /bin/sh

w0=`identify -format "%w" $1`
h=`identify -format "%h" $1`
echo $w0

let w=w0/12
# let w=w0/4
echo $w
echo $h

convert -crop ${w}x${h} $1 dummy.jpg

current_files_directories=$(ls dummy*)
for temp in ${current_files_directories[@]};do
  echo ${temp}
done 

# eof
-----------------------------

2011年12月3日土曜日

Post facebook photo by Command line

access_token に対応したfacebook application名のアルバムに保存される
公開するためには手動で承認作業が必要。
 

fb_image_upload.sh

#!/bin/bash

if [ "$#" -eq 0 ];then
 echo "Do not get image. "
 else
 echo " Get image. "$1
 /opt/local/bin/python2.5 get_image.py $1
fi

msg="test"
caption="message="$msg""
token=`cat .fb_access_token`
echo $caption
echo $token

echo `curl -F "access_token="$token"" -F "source=@"dummy.jpg"" -F "$caption" https://graph.facebook.com/me/photos`

alubm ID によりアルバムを指定して post することもできるが、この場合も公開には承認作業が必要。
echo `curl -F "access_token="$token"" -F "source=@"dummy.jpg"" -F "$caption" https://graph.facebook.com/10150411507966880/photos`
 

アプリケーションで自動作成されるアルバムには1000枚まで写真が保存できるみたいです。

https://graph.facebook.com/ALBUM_ID/photos - The photo will be published to a specific, existing photo album, represented by the ALBUM_ID. Regular albums have a size limit of 200 photos. Default application albums have a size limit of 1000 photos.

https://developers.facebook.com/docs/reference/api/photo/

 

関連
画像の取得 get_image.py

#!-*- coding:utf-8 -*-
#!/opt/local/bin/python2.5

import sys
import urllib
import httplib
import cStringIO
import ImageFile
from PIL import Image, ImageDraw, ImageFont
import string
import re

url = "http://xxx/xxx.jpg"
file = urllib.urlopen(url)
try:
  size = file.headers.get("content-length")
  print "size:" + size
  im = cStringIO.StringIO(file.read())
  img = Image.open(im)

except:
  print "Error: Url = " + url

img_type = "jpg"
img_file = url
if img_file[-3:] == "PNG" or img_file[-3:] == "png":
  img_type = "png"
  img.save("dummy."+ img_type,"PNG")
elif img_file[-3:] == "GIF" or img_file[-3:] == "gif":
  img_type = "gif"
  img.save("dummy."+ img_type,"GIF")
else:
  img.save("dummy."+ img_type,"JPEG")

Get facebook albums by commad line

ポイント
  1. GAEで利用する python2.5 でなくpython2.6以上を利用
    json や urlparse.parse_qs でエラーとなる
    AttributeError: 'module' object has no attribute 'parse_qs'
  2. 'scope':'read_stream,user_photos'
  3. 1度で20件づつしか取得できないので paging が必要となる
    また、先頭の1行のデータは重複する(?)。
  4. http://127.0.0.1:8080/ を登録
     

  5. token が expire している場合は以下のエラーとなるため
    urllib2.HTTPError: HTTP Error 400: Bad Request
    $ rm .fb_access_token

参考
https://sites.google.com/site/benersuayen/lab/facebook     

 

fb_albums.py

#!-*- coding:utf-8 -*-
#!/opt/local/bin/python2.7
import os.path
import json
import urllib2
import urllib
import urlparse
import BaseHTTPServer
import webbrowser

APP_ID = 'XXX'
APP_SECRET = 'XXX'
ENDPOINT = 'graph.facebook.com'
REDIRECT_URI = 'http://127.0.0.1:8080/'
ACCESS_TOKEN = None
LOCAL_FILE = '.fb_access_token'

def get_url(path, args=None):
    args = args or {}
    if ACCESS_TOKEN:
        args['access_token'] = ACCESS_TOKEN
    if 'access_token' in args or 'client_secret' in args:
        endpoint = "https://"+ENDPOINT
    else:
        endpoint = "http://"+ENDPOINT
    return endpoint+path+'?'+urllib.urlencode(args)

def get(path, args=None):
    return urllib2.urlopen(get_url(path, args=args)).read()

def do_next(next,last_id):
    count = 0
    for item in json.loads(get(next))['data']:
        if last_id <> item['id'].encode('utf-8'):
            print "%s, %s" % ( item['id'].encode('utf-8') ,item['name'].encode('utf-8') )
            count = count + 1
    print '---'
    last_id = item['id'].encode('utf-8')
    next = json.loads(get(next))['paging']['next']
    next = next.replace("https://graph.facebook.com/me/albums","/me/albums")
    if next and count > 0:
        do_next(next,last_id)

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        global ACCESS_TOKEN
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()

        code = urlparse.parse_qs(urlparse.urlparse(self.path).query).get('code')
        code = code[0] if code else None
        if code is None:
            self.wfile.write("Sorry, authentication failed.")
            sys.exit(1)
        response = get('/oauth/access_token', {'client_id':APP_ID,
                                               'redirect_uri':REDIRECT_URI,
                                               'client_secret':APP_SECRET,
                                               'code':code})
        ACCESS_TOKEN = urlparse.parse_qs(response)['access_token'][0]
        open(LOCAL_FILE,'w').write(ACCESS_TOKEN)
        self.wfile.write("You have successfully logged in to facebook. "
                         "You can close this window now.")

if __name__ == '__main__':
    if not os.path.exists(LOCAL_FILE):
        print "Logging you in to facebook..."
        webbrowser.open(get_url('/oauth/authorize',
                                {'client_id':APP_ID,
                                 'redirect_uri':REDIRECT_URI,
                                 'scope':'read_stream,user_photos'}))

        httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 8080), RequestHandler)
        while ACCESS_TOKEN is None:
            httpd.handle_request()
    else:
        ACCESS_TOKEN = open(LOCAL_FILE).read()
#    for item in json.loads(get('/me/feed'))['data']:
#        if item['type'] == 'status':
#            print item['from']['name'].encode('utf-8')
#            print item['message'].encode('utf-8')
#            if 'comments' in item:
#                for comment in item['comments']['data']:
#                  print comment['from']['name'].encode('utf-8')
#                 print comment['message'].encode('utf-8')
#            print '---'

#   for item in json.loads(get('/me/friends'))['data']:
    for item in json.loads(get('/me/albums'))['data']:
        print "%s, %s" % ( item['id'].encode('utf-8') ,item['name'].encode('utf-8') )
    last_id = item['id'].encode('utf-8')
    print '---'
    next = json.loads(get('/me/albums'))['paging']['next']
    prev = json.loads(get('/me/albums'))['paging']['previous']
#   print  prev
    print  next
    next = next.replace("https://graph.facebook.com/me/albums","/me/albums")
    if next:
        do_next(next,last_id)

2010年1月20日水曜日

Force.com Mail設定

指定の @xxxx.force.com にメールを送信すると Todo あるいはリード(引き合い)として登録される。
























Todo で「すべての未完了」を選択し、登録内容を確認













Todo から必要に応じて

あるいは
リード (取引先などの登録は必要ない)
商談 (取引先の登録が必要)
を作成する

また 製品のサポート対応にはケースを利用する


















ケース画面の製品リストは以下で作成





































設定手順

















システム管理者が組織の [電子メール to Salesforce] を有効化しました。 [電子メール to Salesforce] により、出先からでも、自宅からでも、任意の電子メールアカウントやクライアントから、"電子メール to Salesforce" アドレスに電子メールを BCC すると、送信した電子メールを Salesforce 内のリード、取引先責任者、商談のレコードの活動として電子メールを記録できます。

次のリンクにアクセスして、使用を開始してください。
http://na6.salesforce.com/email/admin/emailToSalesforceUserEdit.apexp

以上、よろしくお願い申し上げます。
xxxxxxx
Salesforce システム管理者


















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

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