2011年12月3日土曜日

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 システム管理者


















2010年1月6日水曜日

2009年12月13日日曜日

Task Queue, Transaction

Scheduled Tasks With Cron for Pythonhttp://code.google.com/intl/en/appengine/docs/python/config/cron.html
  • Task Queue戦記 - スティルハウスの書庫
  • TaskQueueをローカルでデバッグする方法 - ひがやすを blog
  • Offline Processing on App Engine: a Look Aheadを見たメモ - スティルハウスの書庫

    タスク Task -- http://code.google.com/intl/ja/appengine/docs/python/taskqueue/overview.html



    やはり追加テーブルとのトランザクションはできない
    けれども、子孫となるテーブルにすればできないこともない

    key_name ="http://pages.google.com/edit/kwin786/Impressionnism01_s.jpg-2"
    r = db.get(db.Key.from_path('Imgmap',key_name))
    db.run_in_transaction(update_imgmap,r.key())

    def update_imgmap(key):
    r = db.get(key)
    r.regimgmapcontenturl = "1"
    s = ImgmapContentUrl(
        key_name=r.content_url,
    #   parent=key,
       content_url = r.content_url)
    s.put()
    r.put()

    Traceback (most recent call last): File "C:\Program Files\Google\google_appengine\google\appengine\ext\webapp\__init__.py", line 507, in __call__ handler.get(*groups) File "C:\google\museum-in-cloud\cron.py", line 70, in get db.run_in_transaction(update_imgmap,r.key()) File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 1904, in RunInTransaction DEFAULT_TRANSACTION_RETRIES, function, *args, **kwargs) File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 2001, in RunInTransactionCustomRetries result = function(*args, **kwargs) File "C:\google\museum-in-cloud\cron.py", line 148, in update_imgmap s.put() File "C:\Program Files\Google\google_appengine\google\appengine\ext\db\__init__.py", line 797, in put return datastore.Put(self._entity) File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 198, in Put tx = _MaybeSetupTransaction(req, keys) File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 2090, in _MaybeSetupTransaction raise _DifferentEntityGroupError(expected_group, group) File "C:\Program Files\Google\google_appengine\google\appengine\api\datastore.py", line 2122, in _DifferentEntityGroupError b.kind(), id_or_name(b))) BadRequestError: Cannot operate on different entity groups in a transaction: (kind=u'Imgmap', name=u'http://pages.google.com/edit/kwin786/Impressionnism01_s.jpg-2') and (kind=u'ImgmapContentUrl', name=u'http://pages.google.com/edit/kwin786/Impressionnism01_s.jpg').

  • 2009年9月18日金曜日

    Twitter

    EmbedはQueyを複数指定しするとLoadingのままとなって、NGのようです。











    2009年8月4日火曜日

    Index 復活


    ようやく復活

    properties: [] 

    このインデックスは削除しないように注意

    diff は一切表示なし。
    C:\google>appcfg.py vacuum_indexes blog-editor
    Fetching index definitions diff.

    C:\google>

    エラーログの内容を index.yaml に素直に追加するのが一番
    - kind: Img
    properties:
    - name: open_mode
    - name: category
    direction: desc
    - name: modified
    direction: desc

    2009年8月1日土曜日

    2009年7月31日金曜日

    Google App Engine : Index Error

    どうやらまた同じようなミスをしてしまった

    http://groups.google.co.jp/group/google-app-engine-japan/msg/2cc3dbed1cbb8ccf


    このパターンだと思われ、1日待ってみる必要がある。
    index.yaml の該当部分を削除して vacume_indexes しても Status が Error の
    ままで Deleting にならず、削除が開始されない。 結果、 Error が消えないのでどうしようもない。

    ローカル: 2009年7月21日(火) 午前11:52
    件名: Re: can not force delete a index with error!

    I wasn't able to delete the "Error" status index for a day, the next
    day when I tried vacuum_indexes it got deleted without any problem.

    The fist day I tried many times, but the index status was "Error" it
    didn't change to "Deleting...". It was kind of stuck at the error
    status.



    関連

    Google App Engineで、index.yamlに記述したインデックスが正しく生成されないときの対処法
    http://akisute.com/2009/05/google-app-engineindexyaml.html

    GAE(google app engine) インデックスのトラブルの顛末
    http://osima.jp/blog/gae-index-in-trouble.html

    2009年5月29日金曜日

    Blogger API / max_resuts



    X max-results
    ○ max_results


    ex. この値をセットしないと25程度しか reults が得られない

    start_time ='2009-05-01'
    end_time ='2009-05-31'
    query = service.Query()
    query.feed = '/feeds/' + blogID + '/posts/default'
    query.max_results = "100"

    query.published_min = start_time
    query.published_max = end_time
    feed = gd_service.Get(query.ToUri())



    2009年2月5日木曜日

    Blogger Data API

    PHP による処理

    Zend Google Data APIs client library をダウンロードし展開 

    c:\php\php.ini の include_path に追加 

    include_path = ".;c:\php\ZendGdata-1.7.3\library"



    コマンドラインより実行 

    C:\php\ZendGdata-1.7.3\demos\Zend\Gdata>c:\php\php Blogger.php -- --user=xxx --pass=xxxx 


    Note: This sample may Create, Read, Update and Delete data stored in the account provided. Please exit now if you provi
    ded an account which contains important data.

    0 Tech memo
    1 web2000

    public function promptForBlogID()// 以下の処理で prompt を停止可
    {
    $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/default/blogs');
    $feed = $this->gdClient->getFeed($query);
    $this->printFeed($feed);
    //$input = getInput("\nSelection");
    $input = "0";//0 Tech memo に固定

    Selection: 0
    Creating a post.
    Creating a draft post.
    Updating the previous post and publishing it.
    The new title of the post is: Hello, world, it is.
    The new body of the post is: There we go.
    Adding a comment to the previous post.
    Added new comment: I am so glad this is public now.
    Adding another comment.~
    Added new comment: This is a spammy comment.~
    Deleting the previous comment.~
    Printing all posts.
    0 Hello, world, it is.
    1 Hello, world!
    2 Mambo / Install Maniax2008
    3 Joomla! / Install Maniax2008
    ・・・~
    23 multipart/form-data~
    24 it's not found as __builtin__.instancemethod
    Printing posts between 2007-01-01 and 2007-03-01.
    Deleting the post titled: Hello, world, it is.

    Blogger.php を元に
    Blogger2.php を作成し転送処理を実行

    2009年1月30日金曜日

    Blogger Data API Developer's Guide: PHP Hello, world!

    I am on the intarweb!


    http://code.google.com/intl/ja/apis/blogger/docs/1.0/developers_guide_php.html

    c:\php\php.ini
    include_path = ".;c:\php\ZendGdata-1.7.3\library"
    C:\php\ZendGdata-1.7.3\demos\Zend\Gdata>c:\php\php Blogger.php -- --user=xxx --pass=xxxx
    Note: This sample may Create, Read, Update and Delete data stored in the account provided.  Please exit now if you provi
    ded an account which contains important data.

    0 Tech memo
    1 web2000

    Selection: 0
    Creating a post.
    Creating a draft post.
    Updating the previous post and publishing it.
    The new title of the post is: Hello, world, it is.
    The new body of the post is: There we go.
    Adding a comment to the previous post.
    Added new comment: I am so glad this is public now.
    Adding another comment.
    Added new comment: This is a spammy comment.
    Deleting the previous comment.
    Printing all posts.
    0 Hello, world, it is.
    1 Hello, world!
    2 Mambo / Install Maniax2008
    3 Joomla! / Install Maniax2008
    ・・・
    23 multipart/form-data
    24 it's not found as __builtin__.instancemethod
    Printing posts between 2007-01-01 and 2007-03-01.
    Deleting the post titled: Hello, world, it is.

    C:\php\ZendGdata-1.7.3\demos\Zend\Gdata>
     


    blogger new posts, setting the scheduled post date and categories
    http://groups.google.com/group/bloggerDev/browse_thread/thread/a1eee09632f7eb9e?pli=1

    public function createPost($title, $content, $isDraft=False)
    {
    // We're using the magic factory method to create a Zend_Gdata_Entry.
    // http://framework.zend.com/manual/en/zend.gdata.html#zend.gdata.introdduction.magicfactory
    $entry = $this->gdClient->newEntry();
    $entry->title = $this->gdClient->newTitle(trim($title));
    $entry->content = $this->gdClient->newContent(trim($content));
    $entry->content->setType('text');
    $entry->category = array(
    $this->gdClient->newCategory('this is a label', 'http://www.blogger.com/atom/ns#'),
    $this->gdClient->newCategory('this is another label', 'http://www.blogger.com/atom/ns#'));
    $uri = "http://www.blogger.com/feeds/" . $this->blogID . "/posts/default";

    1 日に多数の投稿を行う場合は、確認用の文字の入力を求められます。 確認用の文字は、24 時間が経過すると自動的に削除されます 
    このためのエラー
    Fatal error: Uncaught exception 'Zend_Gdata_App_HttpException' with message 'Expected response code 200, got 400 Blog has exceeded rate limit or otherwise requires word verification for new posts' in C:\php\ZendGdata-1.7.3\library\Ze nd\Gdata\App.php:644

    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


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

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