まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2018/04

恥ずかしながら、今回紹介する Linux の sc コマンドの存在を全くしりませんでした。。


sc(spreadsheet calculator) コマンドは UNIX/Linux のコンソールから使える表計算アプリケーションです。X Window で動作する GUI アプリではなく、あくまで CUI のコンソール上で動く表計算アプリケーションです。オッサン的には DOS 時代の Lotus 1-2-3 を彷彿とさせる画面に胸騒ぎを禁じえません:
2018042900


インストール方法はソースからビルドするものも含めて数通りありますが、Debian ベースの Ubuntu や Raspberry Pi であれば、apt-get コマンドを使って普通に導入できます:
$ sudo apt-get install sc

このコマンドを実行するだけで導入できます。私のラズパイで実行した時の様子がこちら↓です。コマンド実行前のライブラリの導入状況も含めた環境にも依存するとは思いますが、私の環境ではアプリケーション全体で 369 キロバイト( 0.37 メガバイト)しか使いませんでした。超軽量アプリです:
2018042901


インストール後、プロンプトで "sc" を実行すると起動します:
2018042902
 (↑ DOS 版 Lotus 1-2-3 を知っていると、この時点で感動の涙モノ)


起動後の使い方は '?' キーを入力することで参照できますが、ざっと調べた限りでカーソル移動を含めた主なものはこちらです。いわゆる vi エディタのコマンドに近い操作体系になっているので、vi 派であればすぐに使えるようになると思っています:

操作キー解説
カーソル移動(上下左右)方向キーまたは kjhlvi キーバインド
数値入力カーソル位置で = を入力してから数値を入力
文字入力カーソル位置で > を入力してから文字を入力
値の削除カーソル位置で x を入力vi キーバインド
足し算・引き算などカーソル位置で = を入力してからセルを指定して A0 + A1 のような式を入力
終了q


機能的に Excel と比較するようなレベルのものではありませんが、むかーし 1-2-3 の製品開発に関わっていた自分的にはノスタルジックを越えた何かを感じるツールです。

IBM Watson の比較的新しい API の1つである Watson Data Kit を使ってみました。ちなみにこのブログエントリを書いている 2018/Apr/28 の時点ではこの API はベータ版扱いとなっており、無料で利用できますが、ライトプランから利用することはできないようです。

この API は収集・解析済みのデータにアクセスすることができる API です。具体的にはある地点(緯度&経度)と調査半径(マイル)を指定すると、そのエリア内に含まれる観光名所やアトラクションをまとめて教えてくれる、というものです。特別に明記されているわけではないのですが、使っている限りでの印象として、現時点ではどうやら USA のデータ中心に提供されているようです。

使ってみるには IBM Cloud のダッシュボードから、「アプリケーション・サービス」カテゴリ内にある "Watson Data Kits" を選びます。 「Watson」カテゴリではなく「アプリケーション・サービス」カテゴリ内にあることに注意が必要です:
2018042800


そしてプランを選択してサービスを作成します。現時点では利用制限のない Free plan (無料)のみが提供されているようです:
2018042802


Watson Data Kit サービス作成後、サービス資格情報メニューから資格情報を(必要であれば作成した上で)参照します。この中の apikey と書かれている値と、instance-id と書かれている値が後で必要になります:
2018042803


これらの値を使って、以下のような Node.js のアプリケーションを作ってみました。(apikey) 部分と (instance-id) 部分を上記で取得した値に書き換えると動くようになる、と思います。また以下の例では米国ニューヨーク辺りの位置(北緯 40.73061 度、西経 73.935242 度)と、そこからの半径 5 マイル内のアトラクション( /attractions)を検索し、名称(name)と説明(description)を表示しています:

var request = require( 'request' );

var settings = {
  apikey: '(apikey)',
  instance_id: '(instance id)'
};

// NY
var lat = 40.73061;
var lng = -73.935242;

var radius = 5;  //. 半径 5 マイル

//. アクセストークン取得
var options = {
  url: 'https://iam.bluemix.net/identity/token',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  form: { grant_type: 'urn:ibm:params:oauth:grant-type:apikey', apikey: settings.apikey },
  json: true
};
request( options, ( err, res, body ) => {
  if( err ){
    console.log( err );
  }else{
    var access_token = body.access_token; //. アクセストークン値

    //. アクセストークンを使って、/attractions API を実行する
    var access_headers = {
      'Authorization': 'Bearer ' + access_token,
      'Instance-ID': settings.instance_id
    };
    var access_options = {
      method: 'GET',
      hostname: 'wck-2.us-south.containers.mybluemix.net',
      port: null,
      uri: settings.apiurl + '/travel/v1/attractions?location=' + lat + ',' + lng + '&radius_miles=' + radius,
      headers: access_headers
    };
    request( access_options, ( err1, res1, body1 ) => {
      if( err1 ){
        console.log( err1 );
      }else{
        body1 = JSON.parse( body1 );
        body1.results.forEach( function( attraction ){
          console.log( attraction.name );
          console.log( ' ' + attraction.description );
        });
      }
    });
  }
});


上記を実行するためにまず apikey 値を使ってアクセストークンを取得し、取得したアクセストークンと instance-id 値を使って /attractions API を実行しています。この辺りの詳しい情報はチュートリアルを参照ください:

https://console.bluemix.net/docs/services/watson-data-kits/index.html#getting-started-tutorial


これを実行するとこんな感じになりました。青字部分が名称(name)で、黒地が説明(description)です:
$ node attractions.js
Calvary Cemetery
 Calvary Cemetery is a Roman Catholic cemetery in Maspeth and Woodside, Queens, in New York City, New York, United States. With about 3 million burials, it has the largest number of interments of any cemetery in the United States; it is also one of the oldest cemeteries in the United States. It covers 365 acres and is owned by the Roman Catholic Archdiocese of New York and managed by the Trustees of St. Patrick's Cathedral.
Calvary Cemetery is divided into four sections, spread across the neighborhoods of Maspeth and Woodside. The oldest, First Calvary, is also called "Old Calvary." The Second, Third and Fourth sections are all considered part of "New Calvary."
* First Calvary Cemetery is located between the Long Island Expressway and Review Avenue. The cemetery's offices are located here, at 49?02 Laurel Hill Boulevard.
* Second Calvary Cemetery is located on the west side of 58th Street between Queens Boulevard and the Brooklyn?Queens Expressway.
* Third Calvary Cemetery is located on the west side of 58th Street between the Long Island Expressway and the Brooklyn?Queens Expressway.
* Fourth Calvary Cemetery is located on the west side of 58th Street between the Long Island Expressway and 55th Avenue.
McCarren Park
 McCarren Park is a public park in Brooklyn, New York City. It is located in Greenpoint, Brooklyn and is bordered by Nassau Avenue, Bayard Street, Lorimer Street and North 12th Street. It is operated by the New York City Department of Parks and Recreation. Opened in 1906 and originally named Greenpoint Park, the park was renamed McCarren Park in 1909 after State Senator Patrick H. McCarren who began work as a cooper at Williamsburg sugar refineries and eventually became the Democratic boss of Brooklyn. The park is a popular destination for recreational softball, volleyball, soccer, handball, and other games. It is also used for sunbathing and dog-walking. In late 2004, the park's track was resurfaced and has been a popular destination for running enthusiasts.
Events on the baseball fields of McCarren Park include members of the punk and indie communities gathering to participate in league-controlled kickball tournaments. For several years, the baseball fields have hosted tournament play for the Hasidim; weekend afternoons provide T-ball and softball games for organized area youth groups; Latino families and friends often utilize the fields to play soccer and volleyball into the late hours of the night. Since June 2003, McCarren Park has hosted SummerScreen in McCarren Park, and The Renegade Craft Fair, a DIY event. The fair attracts artists and creative types, featuring a wide range of merchandise such as reconstructed clothing, comic books, tote bags and other handmade goods.
Pulaski Bridge
 The Pulaski Bridge in New York City connects Long Island City in Queens to Greenpoint in Brooklyn over Newtown Creek. It was named after Polish military commander and American Revolutionary War fighter Kazimierz Pu?aski because of the large Polish-American population in Greenpoint. It connects 11th Street in Queens to McGuinness Boulevard (formerly Oakland Street) in Brooklyn.
5 Pointz
 5 Pointz: The Institute of Higher Burnin' or 5Pointz Aerosol Art Center, Inc., mainly referred to as simply 5 Pointz or 5Pointz, was an American mural space at 45?46 Davis Street in Long Island City, Queens, New York City. When it opened in 1892, it housed the Neptune Meter factory, which built water meters.
   :
   :

位置情報サービスに対して、その周辺のアトラクションやキーワードとなる付加情報を加えることができるようになる、面白い API だと感じました。



業務の忙しさを言いわけに、IBM Cloud の新しい情報に疎くなっていました。で「なんか面白そうな API ないかなあ」とカタログを眺めていたら・・・

「金融」カテゴリに、、ん??
2018041901


こっ、これはっ!? もしやシグナイト!?
2018041902


おー、やっぱりシグナイトだ。世界中の株や為替を始めとする色々なマーケット情報を提供するシグナイトの API が IBM Cloud のサードパーティ API としても登録されていたのでした(知りませんでした、何か)。ただこの API はここからそのまま使えるわけではなく、別途アカウント登録が必要なようで、↓ひとつ進んだこのページの "Xignite" と書かれたリンクからアカウント登録に進みます:
2018041903


すると、この↓ページにジャンプします。ほうほう、シグナイト API は本来有料なのですが、IBM Bluemix(Cloud) ユーザー向けに7日間のトライアルが提供されている模様です:
https://market-data.xignite.com/IBM_Marketplace.html

2018041900


で、ここから名前やメールアドレスを指定して登録し、アドレスに届いたメールの内容を使って申請していきます。途中、以下のような画面になり、こんかいのトライアルで利用する API を1つ指定します(どうやらトライアルで利用できるのは一つだけのようで複数指定はできないようでした)。迷いましたが、個人的にも取引経験のある FX 向けの XigniteGlobalCurrencies を選択しました:
42


更に先に進み、アカウントのパスワードを指定してアクティベートします:
33


アクティベートが完了すると登録したアドレスに以下のようなメールが届きます。ここから "token" と書かれたリンクをクリックして、API 用のトークンを確認できます:
18


再びシグナイトのページに移動し、以下の赤枠部分に API 用のトークンが表示されています。API 実行時にこのトークン文字列が必要になります:
52


次にどんな API が提供されているのか調べてみます。シグナイトの API カタログページから今回選択した API(上記例の場合は XigniteGlobalCurrencies)を探し、その "API List" と書かれたボタンをクリックします:
26


すると指定した種類(XigniteGlobalCurrencies)の API 一覧が表示されます。ここでどんな API が存在して、どうすると実行できるのか、実際の実行結果がどんなフォーマットになるのか、といった情報を確認したり、実際に実行したりできます:
2018041904


試しにひとつ使ってみます。左側の API 一覧からリアルタイムレートを参照する GetRealTimeRate を選びます。すると画面右側が GetRealTimeRate 用に切り替わり、ここから各種パラメータを指定して実際に実行することができます(注 実行できるのはログイン時のみ)。以下の例では Request タブで
 Symbol: USDJPY(米ドル円レート)
 Result Format: JSON(結果を JSON で取得)
を指定しました。するとその下の URL が動的に切り替わり、この条件で API を実行する際のエンドポイント URL を示してくれます。下図ではモザイクをかけていますが、この URL にはトークンが含まれているので、そのままコピペ等で実際に確認することも可能な URL になっています。最後に下の "View Result" ボタンを押して実行します:
2018041905


成功すると実行結果が下部に JSON フォーマットで表示されます:
2018041906


実際の中身はこんな感じ、ほぼリアルタイムに値が取得できています(このタイミングで1ドルを買うと 107.374 円、売ると 107.368 円で、仲値が 107.371 円、という結果でした):
{
 "Outcome": "Success",
 "Message": null,
 "Identity": "Request",
 "Delay": 0.0167211,
 "BaseCurrency": "USD",
 "QuoteCurrency": "JPY",
 "Symbol": "USDJPY",
 "Date": "04/19/2018",
 "Time": "9:18:18 AM",
 "QuoteType": "Spot",
 "Bid": 107.368,
 "Mid": 107.371,
 "Ask": 107.374,
 "Spread": 0.006,
 "Text": "1 United States dollar = 107.371 Japanese yen",
 "Source": "SIX Financial Information"
}

トライアルだと7日間限定とはいえ、こんな便利な API が IBM Cloud から利用できるようになっていたんですねー。パラメータを変えて実行したり、他の API もここから同様に試すことができそうです。


なお、トライアルではない正式版(?)の API の価格については Flexible Pricing Model が適用されるようで、価格そのものはウェブからは確認できませんでした。興味ある方はこちらから問い合わせる必要がありそうです:
https://www.xignite.com/Pricing

 

 は IBM CloudantApache CouchDB と API 互換のある NoSQL データベースです。JavaScript で操作することができます。npm を使ってサーバーサイドで動かすこともできますが、ブラウザから JavaScript ライブラリをロードして、個々のブラウザ内で使うことも可能です。

特にこれをブラウザから使う場合、マスターデータはクラウドの IBM Cloudant で保持しつつ、必要なデータをユーザーのブラウザと同期して、ほぼすべての処理をローカルブラウザ内で完結させる(=時間のかかるDBアクセスをローカルDBだけを対象に行えばよくなるので、アプリケーションとしての安定性やパフォーマンス向上も期待できる)、ということが可能になります。とても強力な同期機能を持ったデータベースエンジンと言えます。
2018040900


この「同期」を具体的にどうやるか、という内容が今回のブログエントリです。


今回の説明では、サーバー側の DB を IBM Cloudant で用意することにします。IBM Cloud のライトアカウントを作成すると容量 1GB まで使えるライトプランの DBaaS が無料で用意できます。

で、この Cloudant DB をブラウザに同期・・・するわけですが、IBM Cloudant を使う場合はその前に1つ設定が必要です。標準状態の IBM Cloudant はクロスオリジンからのアクセスを許可していません。そのため、標準設定のままウェブブラウザから同期をとろうとするとクロスオリジンアクセスになってしまい、エラーとなってしまいます。したがってクロスオリジンアクセスを許可するよう、設定を変更する必要があります。詳しくはこちらにも記載していますが、curl コマンドと IBM Cloudant の API を使って IBM Cloudant の CORS アクセスを有効にするための設定を行います:
$ curl -i -u 'db_username:db_password' -XPUT 'https://db_username.cloudant.com/_api/v2/user/config/cors' -H 'Content-type: application/json' -d '{"enable_cors":true,"allow_credentials":true,"origins":["*"]}'

↑db_username, db_password は IBM Cloudant のインスタンスにアクセスするためのユーザー名およびパスワードです。


次にブラウザ内の JavaScript で DB の同期を行います。普通に「データベースそのものの同期をとる」のであれば話は単純で、以下のようなコードを記述するだけです:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

remoteDB.sync( localDB, {
live: true, retry: true });
: :

まず CDN を指定して PouchDB のライブラリをロードします。そして IBM Cloudant のユーザー名(db_username)とパスワード(db_password)を指定して、'testdb' という名前のデータベースインスタンスを remoteDB 変数に代入します。これで IBM Cloudant 上のリモートデータベースを remoteDB から操作できるようになりました。同時にローカルブラウザ内に(同じ名前の)'testdb' という名前のデータベースインスタンスを作って localDB 変数に代入しています(こちらは作成時点では空です)。 この2つのリモート/ローカルデータベースを sync() 関数で同期するように指定しています。これによってこれら2つのデータベースインスタンス変数の内容は自動同期され、一方(例えばブラウザ内の localDB)に変化が起こるともう一方(サーバーの remoteDB)にその変化内容が勝手に反映される、という仕組みが実現できます。

ちなみに sync() 実行時の live:true オプションはリアルタイム同期の指定で、retry:true オプションは一度接続が切れた後に自動的にリトライして接続が戻った時に同期も復活させるための指定です。ここまでは超簡単です。


さて、本来やりたかったのはデータベースをまるごと同期するのではなく、データベースの一部だけを同期する、というものです。上記例だと remoteDB はサーバー側のものなので、全部で数ギガバイトになったりそれ以上になったりすることも想定しないといけないのですが、localDB はブラウザ内で作るものなのであまり大きくなっては困ります。そこで(一般的には部分同期とか Partial Sync とか呼ばれる方法で)特定条件を満たす一部の文書だけを対象にローカルに同期し、ローカルでの変更・追加・削除といった処理をサーバー側のマスターに同期し直す方法を紹介します。

PouchDB にも部分同期機能は存在しています。ただそこで「この条件を満たす文書だけ」を指定する方法がかなり限られていて、現時点では文書 ID を配列で指定する方法しかないように見えます(このフィールドがこの値で・・・みたいなクエリーではできないっぽい)。具体的にはこんな感じ:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

var doc_ids = [ 'xxx', 'yyy', 'zzz' ];  //. 同期対象文書の _id 値配列

remoteDB.sync( localDB, {
doc_ids: doc_ids,
live: true, retry: true }); : :

上記例では 'xxx', 'yyy', 'zzz' という3つの文書ID(Cloudant 内だと文書の _id の値)を指定して、sync() 関数を doc_ids パラメータで配列指定しています。これだけで localDB には指定された3文書だけが同期され、削除を含めた変更があるとリモートにも即時に反映されるようになります。

したがって実装する場合はページロード時の最初に IBM Cloudant に対してクエリー API を実行して同期の対象となる文書(の ID 配列)をまとめて取得し、その ID 配列を指定して PouchDB と部分同期する、という流れになると思っています。この方法なら最初のロード時のネットワーク接続は必須になりますが、どのみちページをロードするにもネットワークは必須だし、同期をとった後はネットワークが切れても平気、というシステム構成が可能になります。


なお、一点だけ注意が必要なことがあります。この方法で部分同期した後に localDB に新たに文書を追加した場合です。部分同期の条件は doc_ids に指定された文書ID配列だったので、ここに含まれない新しい文書を追加してもサーバー側には同期されません。その場合は新たに doc_ids を指定しなおして(新しい文書の ID を追加して)改めて sync() 関数を実行する必要があります:
  :
  :
<html>
<head>
<script src="https://cdn.jsdelivr.net/pouchdb/5.4.5/pouchdb.min.js"></script>
<script>
var localDB = new PouchDB( 'testdb' );
var remoteDB = new PouchDB( 'https://db_username:db_password@db_username.cloudant.com/testdb' );

var doc_ids = [ 'xxx', 'yyy', 'zzz' ];

remoteDB.sync( localDB, {
doc_ids: doc_ids, live: true, retry: true }); : : function add( doc ){ //. ドキュメントを追加する処理 localDB.put( doc ).then( function( res ){ //. localDB に文書(doc)追加 doc_ids.push( doc._id ); //. 文書の _id 値を配列に追加 remoteDB.sync( localDB, { //. sync() 再実行 doc_ids: doc_ids, live: true, retry: true }); }).catch( function( err ){ console.log( err ); }); }

例えば上記例では add( doc ) 関数の中で doc に指定されたオブジェクトを localDB に追加する想定で処理を記述していますが、localDB.put() が成功したら doc_ids 配列を更新した上で remoteDB.sync() を再実行して同期条件を変えるようにしています。

現実問題としては追加なのか更新なのか(どちらも put() 関数を使う)、更新だとすると _rev の値も必要になって・・・とか、多少細かい実装が必要になることも事実ですが、一応これだけでローカルDBとその部分同期を使ったアプリの実装はできることになります。

問題は上述したローカルDBに同期したい文書の ID をどうやって調べるかですが、そこはやっぱり一度サーバーにクエリー投げるしかないのかなあ・・・


なお、PouchDB の API Reference はこちらを参照ください:
https://pouchdb.com/api.html


大阪府吹田市が市内の観光名所をテーマにしたデザインマンホール3種を作成し、先日公開されました:
吹田市がデザインマンホール蓋3種類 楽しみながら探して

この3種は「三色彩道の紅葉」、「ガンバ大阪」、そして「大阪モノレール」をテーマにしたもので、設置場所についてはヒントだけが公開されていました。ちょうど万博公園の「太陽の塔」の内部公開を見に行くタイミングと重なったので、この3つを探してきました。そしてそれぞれマンホールマップに投稿したので、以下は結果的に場所のネタバレを含む内容になっています(苦笑)。

















まず1つ目、「三色彩道の紅葉」


これは阪急千里線「北千里」駅からテーマになっている「三色彩道」に向かって歩いた途中の保育園の近くで見つけました。
http://manholemap.juge.me/page.jsp?id=5607739796554497828



2つ目、「ガンバ大阪」、ガンバのマスコットであるガンバボーイと、吹田市のイメージキャラクター「すいたん」がサッカーで共演しているマンホールです:



これは当然パナソニック吹田スタジアム・・・かと思いきや、結構難問でした。結論として最寄り駅は大阪モノレール公園東口駅で、ここから吹田スタジアムに向かって南下し、中国自動車道をくぐった直後の植え込みの中に設置されていました。↑の写真を見ると周囲が植え込みっぽくなっていることがわかると思います。

http://manholemap.juge.me/page.jsp?id=6283324744500361540



そして3つ目、「大阪モノレール」



こちらは大阪モノレール万博記念公園駅から東のららぽーとに向かって歩きます。そしてららぽーとに入るのではなく、万博記念公園に向かう橋も渡らず、ららぽーとの北側にある、モノレールと並行して伸びる側道(自転車の駐輪場? ちょっとわかりにくい)に入ります。そして入ってすぐの所に設置されていました。発見難易度はこれが一番難しかったと思います。

http://manholemap.juge.me/page.jsp?id=1433437503660013763


ちなみに、2番目のガンバマンホールに万博記念公園駅から向かう場合、この3番目のマンホールが設置されている側道をそのまま東に向かって歩き続けることで最短距離で移動できます(ららぽーとに入ったり、ぐるっと廻って吹田スタジアム経由で向かおうとすると手間取ることになります)。



なお、これら3種すべてのマンホールに suitable city という文字が表記されています。この "suitable city" を直訳すると「適度な市」とか「ちょうどいい市」みたいな感じだと思いますが、同時に suita(吹田)という部分が強調されていて、ダジャレマンホールになっています。こんなオチを隠し含めるあたりは大阪の意地なんでしょうかね。。。


吹田を訪ねる予定があり、これらのマンホールを探したいけど、滞在時間の関係などでどうしても効率よく周りたい、という方は参考にしてください。 (^^;


このページのトップヘ