Archive for the 'NSMutableDictionary' Category

2017/05/15 感情推定(極性判定) v2

apitoreのREST API「感情推定(極性判定) v2」を呼び出すAppleScriptです。

400文字以下のテキストを解析して、極性(感情)を判定します。ただし、文章が短すぎると判定しづらくなる傾向があるようです。これまでの感情推定APIではpositive/negativeとして判定する傾向があったため、このv2 APIではneutralとして判定する幅を広めにとってもらいました。

本Scriptをテストするためには、apitoreにサインアップしてAccess Tokenを取得し、Script末尾の伏字部分にコピー&ペーストしてください(掲載リストをそのまま実行してもエラーになります)。

AppleScript名:感情推定(極性判定) v2
– Created 2017-05-15 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4639

set aString to "このePubの考え方にそぐわない文章要素がいろいろある。代表的なものは「表」だ。データを掲載しておくのに、「表」は便利な存在だ。書く方からしても、いちいち長々とした文章で説明しなくていいし、読む方からしても整理された情報を読めるわけで、とくにお年寄りに好まれる。ところが、この「表」がePubの出版物にそぐわない。表の幅は変わってほしくないし、文字サイズが大きくなって数ページにわたって表示されるといったことは好ましくない。"
set a1Res to getSentimentFromString(aString) of me
–>  "positive"(なのか?)

on getSentimentFromString(aString)
  set reqURLStr to "https://api.apitore.com/api/39/sentiment-v2/predict"
  
set accessToken to retAccessToken() of me
  
set aRec to {access_token:accessToken, |text|:aString}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESCode to (responseCode of aRes) as integer
  
if aRESCode is not equal to 200 then return false
  
  
set aRESTres to ((json of aRes)’s valueForKeyPath:"predict.sentiment") as string
  
return aRESTres
end getSentimentFromString

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return "xxXXXxxX-XxXx-XXXX-xXXX-XXxXXxxXxxXx" –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2017/05/06 Bing Web Search APIでWebキーワード検索

MicrosoftのBing Web Search APIを呼び出してWebのキーワード検索を行うAppleScriptです。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に1,000回まで無料で使用できます(1秒間に7回までという制限もあります)。

アカウント登録を行い、Bing Web SearchをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

個人的にBingは検索エンジンとして常用していません。Google検索にくらべて見つかる情報量が少ないと実感していることがその理由です。そのため、Bingの検索エンジンを呼び出せてもあまりメリットを見出せないところ。あくまで実験の一環です。

AppleScript名:Bing Web Search APIでWebキーワード検索
– Created 2017-05-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4634

set aStr to “戦場の絆”

set reqURLStr to “https://api.cognitive.microsoft.com/bing/v5.0/search” –Microsoft Project Oxford
set myAPIkey to retAPIkey() of me –API Primary Key

set aRec to {q:aStr, |count|:“5″, |offset|:“0″, mkt:“ja-JP”}
set bURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPI(bURL, myAPIkey) of me
set aRESTres to json of aRes
set aRESCode to responseCode of aRes
set aRESHeader to responseHeader of aRes

return (webPages of aRESTres)

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestGETAPI(aURL, anAPIkey)
  –Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:anAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
if dRes = missing value then
    set resCode to -1
    
set resHeaders to {}
  else
    set resCode to (dRes’s statusCode()) as integer
    
–Get Response Header
    
set resHeaders to (dRes’s allHeaderFields()) as record
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPI

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “XxXxXXxXxXxXXxXXxXXXxXXXxxxxXxxX” –API Key 1
end retAPIkey

★Click Here to Open This Script 

2017/05/04 Analyze Imageで画像認識

MicrosoftのCognitive ServicesのREST APIのひとつ、Computer Vision APIの「Analyze Image」を呼び出して画像認識するAppleScriptです。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に5,000回まで無料で使用できます(1分間に20回までという制限もあります)。

アカウント登録を行い、Analyze ImageをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

指定した画像のカテゴリ(Categories)、タグ情報(Tags)、詳細内容(Description)の情報を取得しています。

精度については、実際にテスト画像と得られた認識データを見比べていただくしかありません。カタツムリとか誕生日ケーキとか夕日や花火、ワインボトルなど(!)、驚くほど正確に認識されています。夕日の手前にあるのは飛行機ではないのですが、そのぐらいの誤差はあります(イルカを「犬」と判定されたりもしました)。

ためしに、この結果(Description)をそのままGoogle Tlanstlate APIに渡して日本語訳させ画像のFinderコメント欄に入れる実験などもやってみたところ、面白い結果が得られました(実用性はまだまだですが、言いたいことはわかる、というレベル。個人的には英語のままのほうがわかりやすいかも)。

こうしたサービスを利用して、写真.app(Photos.app)内の写真にすべてタグ付けして、タグをもとに風景の写真だけを新規アルバムに入れて分類するといった使い方はできそうです(すでにやってるし)。そして、AppleのアプリケーションとMicrosoftやGoogleのWebサービスを連携させるといった使い方はAppleが提供するわけがないので、こういう用途こそAppleScriptで勝手に連携させるべきだと思います。

r0010704_resized.png
{{metadata:{width:2592, |format|:”Jpeg”, height:1944}, tags:{{|name|:”fireworks”, confidence:0.998994648457}, {|name|:”outdoor object”, confidence:0.998748064041}}, categories:{{|name|:”dark_fireworks”, score:0.99609375}}, |description|:{tags:{”fireworks”, “object”}, captions:{{|text|:“a close up of fireworks”, confidence:0.233880494749}}}, requestId:”ce5e5c15-f68f-4647-9694-5fb8ede9cd31″}}

r0015213_resized.png
{{metadata:{width:2592, |format|:”Jpeg”, height:1944}, tags:{{|name|:”ground”, confidence:0.999917149544}, {|name|:”animal”, confidence:0.993541717529}, {|name|:”invertebrate”, hint:”animal”, confidence:0.952201843262}, {|name|:”outdoor”, confidence:0.887740492821}, {|name|:”mollusk”, hint:”animal”, confidence:0.814713895321}, {|name|:”snail”, hint:”animal”, confidence:0.649717211723}}, categories:{{|name|:”abstract_texture”, score:0.6015625}}, |description|:{tags:{”animal”, “outdoor”, “shellfish”, “snail”, “piece”, “laying”, “food”, “sitting”, “top”, “surface”, “lying”, “banana”, “close”, “fruit”, “bird”, “beach”, “plate”, “board”, “street”}, captions:{{|text|:“a snail on the ground”, confidence:0.480042590526}}}, requestId:”b290dc83-0e33-41b0-8a39-76c067bcffb8″}}

r0015918_resized.png
{{metadata:{width:2048, |format|:”Jpeg”, height:1536}, tags:{{|name|:”grass”, confidence:0.99923813343}, {|name|:”outdoor”, confidence:0.997150361538}, {|name|:”ground”, confidence:0.972813665867}, {|name|:”standing”, confidence:0.93672734499}, {|name|:”animal”, confidence:0.917161226273}, {|name|:”bird”, confidence:0.824012756348}}, categories:{{|name|:”animal_horse”, score:0.98046875}}, |description|:{tags:{”grass”, “outdoor”, “standing”, “bird”, “animal”, “water”, “field”, “white”, “small”, “walking”, “front”, “sitting”, “parrot”, “brown”, “large”, “grassy”, “green”, “body”, “dirt”, “red”, “river”}, captions:{{|text|:“a bird that is standing in the grass”, confidence:0.859258793309}}}, requestId:”bc5b8778-cf96-4d3c-8ba9-be22b38c36dd”}}

img_2502_resized.png
{{metadata:{width:3264, |format|:”Jpeg”, height:2448}, tags:{{|name|:”sky”, confidence:0.999066531658}, {|name|:”outdoor”, confidence:0.99786490202}, {|name|:”sunset”, confidence:0.880310297012}, {|name|:”distance”, confidence:0.229086950421}}, categories:{{|name|:”outdoor_”, score:0.0078125}, {|name|:”outdoor_waterside”, score:0.5234375}}, |description|:{tags:{”outdoor”, “sunset”, “airplane”, “plane”, “sitting”, “building”, “runway”, “large”, “front”, “water”, “pier”, “top”, “sun”, “track”, “orange”, “light”, “road”, “bridge”, “standing”, “train”, “man”, “jet”, “white”, “city”, “riding”, “red”, “flying”, “bird”, “blue”, “night”, “river”, “tower”, “tarmac”}, captions:{{|text|:“a plane that is in front of a sunset”, confidence:0.493561860724}}}, requestId:”49705b7d-99cb-4234-9af6-b30a32f02ad5″}}

img_0313_resized.png
{{metadata:{width:4032, |format|:”Jpeg”, height:3024}, tags:{{|name|:”indoor”, confidence:0.929384291172}, {|name|:”candle”, confidence:0.877458691597}, {|name|:”birthday”, confidence:0.817751348019}, {|name|:”lit”, confidence:0.53784763813}}, categories:{{|name|:”others_”, score:0.01171875}}, |description|:{tags:{”table”, “indoor”, “cake”, “birthday”, “food”, “sitting”, “lit”, “plate”, “front”, “small”, “top”, “bowl”, “fruit”, “filled”, “chocolate”, “holding”, “man”, “woman”}, captions:{{|text|:“a birthday cake with lit candles”, confidence:0.8700279282}}}, requestId:”62b58a19-2d3a-4f9a-bd54-5aa4460d8385″}}

img_2187_resized.png
{{metadata:{width:3264, format:”Jpeg”, height:2448}, tags:{{name:”bottle”, confidence:0.999316096306}, {name:”table”, confidence:0.987863183022}, {name:”indoor”, confidence:0.968602716923}, {name:”wine”, confidence:0.928377449512}, {name:”alcohol”, confidence:0.846504926682}, {name:”food”, confidence:0.785823404789}, {name:”glass”, confidence:0.782037138939}, {name:”beverage”, confidence:0.776733756065}}, categories:{{name:”drink_”, score:0.90234375}}, description:{tags:{”bottle”, “table”, “indoor”, “wine”, “sitting”, “alcohol”, “food”, “glass”, “beverage”, “cup”, “banana”, “empty”, “top”, “sandwich”, “counter”, “beer”, “phone”, “orange”, “wooden”, “computer”, “laying”}, captions:{{text:“a bottle of wine”, confidence:0.858091829408}}}, requestId:”d2db3a05-fcb0-43bb-8191-89707904918a”}}

r0020016_resized.png
{{metadata:{width:1280, format:”Jpeg”, height:960}, tags:{{name:”sky”, confidence:0.999883055687}, {name:”outdoor”, confidence:0.997511506081}, {name:”person”, confidence:0.910886585712}, {name:”toy”, confidence:0.878364562988}}, categories:{{name:”others_”, score:0.00390625}, {name:”outdoor_”, score:0.01171875}}, description:{tags:{”outdoor”, “person”, “toy”, “thing”, “man”, “white”, “grass”, “standing”, “people”, “field”, “riding”, “jumping”, “doing”, “air”, “holding”, “group”, “trick”, “board”, “statue”, “dog”, “player”, “ramp”}, captions:{{text:“a statue of a man jumping in the air”, confidence:0.153422169506}}}, requestId:”ec5dc466-76c5-47df-b858-2e418ce891a8″}}

r0016164_resized.png
{{metadata:{width:2048, format:”Jpeg”, height:1536}, tags:{{name:”water”, confidence:0.999519586563}, {name:”animal”, confidence:0.993490874767}, {name:”outdoor”, confidence:0.992024421692}, {name:”aquatic mammal”, hint:”animal”, confidence:0.978799343109}, {name:”mammal”, hint:”animal”, confidence:0.914148926735}, {name:”ocean”, confidence:0.804374277592}, {name:”whale”, hint:”animal”, confidence:0.533892810345}, {name:”wave”, confidence:0.527211129665}, {name:”dolphin”, hint:”animal”, confidence:0.340564578772}}, categories:{{name:”outdoor_”, score:0.00390625}}, description:{tags:{”water”, “animal”, “outdoor”, “mammal”, “ocean”, “wave”, “laying”, “brown”, “riding”, “surfing”, “top”, “body”, “lying”, “board”, “large”, “beach”, “floating”, “swimming”, “standing”, “dog”, “young”, “white”, “man”}, captions:{{text:“a dog swimming in the ocean”, confidence:0.604725984086}}}, requestId:”161b6c0f-f3fd-4e06-b4be-d5baa9516e53″}}

AppleScript名:Analyze Image APIで画像認識
– Created 2017-05-03 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4625

set imgFilePath to POSIX path of (choose file of type {“public.jpeg”} with prompt “Select an Image to Analyze”)
set aRes to recogImage(imgFilePath) of me

on recogImage(imgFilePath)
  set reqURLStr to “https://api.projectoxford.ai/vision/v1.0/analyze”
  
set myAPIkey to retAPIkey() of me –API Key 1
  
  
set aRec to {visualFeatures:“Categories,Tags,Description”, details:“Celebrities”, language:“en”}
  
set bURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestPOSTAPIAndParseResultsWithImage(bURL, imgFilePath, myAPIkey) of me
  
set aRESTres to json of aRes
  
set aRESCode to responseCode of aRes
  
set aRESHeader to responseHeader of aRes
  
  
return (aRESTres as list)
end recogImage

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestPOSTAPIAndParseResultsWithImage(aURL, imgFilePath, myAPIkey)
  –Get Image Contents from file
  
set imgPathStr to current application’s NSString’s stringWithString:imgFilePath
  
set imgData to current application’s NSData’s dataWithContentsOfFile:imgPathStr
  
set postBody to current application’s NSMutableData’s |data|()
  
postBody’s appendData:imgData
  
  
–Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setHTTPBody:postBody
  
aRequest’s setValue:“application/octet-stream” forHTTPHeaderField:“Content-Type”
  
aRequest’s setValue:myAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
if dRes = missing value then
    set resCode to -1
    
set resHeaders to {}
  else
    set resCode to (dRes’s statusCode()) as integer
    
–Get Response Header
    
set resHeaders to (dRes’s allHeaderFields()) as record
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResultsWithImage

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString())
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “xXXxXXXXxXXxXXXxXXXXXXXXxXXXXXXx” –API Key 1
end retAPIkey

★Click Here to Open This Script 

2017/04/30 Computer Vision APIで文字認識(OCR)

MicrosoftのCognitive ServicesのREST APIのひとつ、Computer Vision APIを呼び出すAppleScriptです。

MicrosoftのAPIは割とシンプルでオンラインドキュメントも読みやすいため、書くのに3分ぐらいしかかかっていません(既存の別のAPI用のものをend pointとAPI keyとパラメータを書き換えただけ)。

Microsoft Cognitive Servicesのサイトで、開発者アカウント登録を行えば、本APIについては1か月に5,000回まで無料で使用できます(1分間に20回までという制限もあります)。アカウント登録を行い、OCR ServiceをEnableに設定して、本AppleScriptの末尾のretAPIKey()ハンドラ内にAPI Key 1を入力してください(API Keyを記入したScriptを決してそのまま第三者に配布しないでください。あくまでテストと確認用です)。

cognitive_key.png
▲ここの「Key 1」

Vision APIでは、対応画像ファイル形式はJPEG、PNG、GIF、BMP(!)。ファイル容量は4MB以下、50×50ピクセル以上の大きさである必要があります。

img_0320_resized.png

こんな「戦場の絆」のリプレイIDをゲームセンターで撮影して、このIDをもとにYouTubeのリプレイムービーのURLを検索することになるわけですが、このリプレイIDのOCRにComputer Vision APIを使ってみました。

この用途(ランダムに近い英数字の固定桁の組み合わせ、画面上のイメージの認識)については、かなり使える印象です。認識用に使用した画像は、iPhone 7で撮影して写真.appにiCloud経由で転送された写真をそのまま利用しており、ファイルサイズは2.6MB、画像の大きさは4032×3024ピクセルありました。このぐらいだと、認識のためにMicrosoftのクラウドにアップロードするのに少々待たされます。

{boundingBox:”929,1171,746,134″, text:”fy57nt2f“}
{boundingBox:”920,1438,770,135″, text:”3kt5y72v“}

この用途においては、とても誤認識が少ないと感じています。本来取り出したい目的のデータが日本語(ja)ではないので、言語を英数字(en)に指定して呼び出しています。

縦書きの日本語の印刷物の認識だと、もう少し結果が異なってくると思います。一応、この画面についても当初は日本語(ja)で認識していましたが、書体の問題もあり(ちょっと太すぎ?)日本語の文字については誤認識されまくっていました。

AppleScript名:Computer Vision APIで文字認識(OCR)
– Created 2017-04-30 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4622

set imgFilePath to POSIX path of (choose file of type {“public.image”} with prompt “Select an Image to OCR”)

set reqURLStr to “https://api.projectoxford.ai/vision/v1.0/ocr” –Microsoft Cognitive Services OCR endpoint
set myAPIkey to retAPIkey() of me –API Primary Key

–Request parameters
set aRec to {|language|:“en”, detectOrientation:“true”}
set bURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestPOSTAPIAndParseResultsWithImage(bURL, myAPIkey, imgFilePath) of me
set aRESTres to json of aRes

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false
set aRESHeader to responseHeader of aRes

return ((regions of aRESTres) as list)
–>  {{language:”en”, regions:{{boundingBox:”728,366,1497,2042″, lines:{{boundingBox:”1655,366,186,28″, words:{{boundingBox:”1655,366,186,28″, text:”pea-a-r”}}}, {boundingBox:”1223,962,467,89″, words:{{boundingBox:”1223,962,467,89″, text:”UäLCOD”}}}, {boundingBox:”753,1134,922,171″, words:{{boundingBox:”753,1134,64,156″, text:”1″}, {boundingBox:”929,1171,746,134″, text:”fy57nt2f”}}}, {boundingBox:”1761,1167,458,64″, words:{{boundingBox:”1761,1175,80,53″, text:”20″}, {boundingBox:”1862,1169,169,62″, text:”17/0″}, {boundingBox:”2045,1167,174,60″, text:”4/26″}}}, {boundingBox:”2005,1231,215,52″, words:{{boundingBox:”2005,1231,215,52″, text:”17:55″}}}, {boundingBox:”742,1403,948,170″, words:{{boundingBox:”742,1403,86,160″, text:”2″}, {boundingBox:”920,1438,770,135″, text:”3kt5y72v”}}}, {boundingBox:”1765,1432,459,65″, words:{{boundingBox:”1765,1441,81,53″, text:”20″}, {boundingBox:”1866,1434,170,63″, text:”17/0″}, {boundingBox:”2050,1432,174,61″, text:”4/26″}}}, {boundingBox:”2010,1495,215,54″, words:{{boundingBox:”2010,1495,215,54″, text:”17:47″}}}, {boundingBox:”736,1672,88,176″, words:{{boundingBox:”736,1672,88,176″, text:”3″}}}, {boundingBox:”728,2240,96,168″, words:{{boundingBox:”728,2240,96,168″, text:”5″}}}}}, {boundingBox:”2817,1217,186,320″, lines:{{boundingBox:”2817,1217,175,53″, words:{{boundingBox:”2817,1217,175,53″, text:”6vs6″}}}, {boundingBox:”2826,1483,177,54″, words:{{boundingBox:”2826,1483,177,54″, text:”6vs6″}}}}}}, textAngle:0.0, orientation:”Up”}}

–POST methodのREST APIを画像をアップロードしつつ呼ぶ
on callRestPOSTAPIAndParseResultsWithImage(aURL, anAPIkey, imgFilePath)
  
  
–Get Image Contents from file
  
set imgPathStr to current application’s NSString’s stringWithString:imgFilePath
  
set imgData to current application’s NSData’s dataWithContentsOfFile:imgPathStr
  
set postBody to current application’s NSMutableData’s |data|()
  
postBody’s appendData:imgData
  
  
–Request
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setHTTPBody:postBody
  
aRequest’s setValue:“application/octet-stream” forHTTPHeaderField:“Content-Type”
  
aRequest’s setValue:anAPIkey forHTTPHeaderField:“Ocp-Apim-Subscription-Key”
  
  
–CALL REST API
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
  
–Parse Results
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to (dRes’s statusCode()) as integer
  
  
–Get Response Header
  
set resHeaders to (dRes’s allHeaderFields()) as record
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResultsWithImage

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) –as text
  
  
return aURL
end retURLwithParams

on retAPIkey()
  return “xXXXXxXxXxXXXXxxxXXxXXXxxXxxXxxx” –API Primary Key
end retAPIkey

★Click Here to Open This Script 

2017/04/27 テックサイト ごちゃまぜフィードを呼び出す

apitoreREST API「テックサイト ごちゃまぜフィード」を呼び出すAppleScriptです。

apitore側で、以下のテック系Blogを自動巡回して(30分に一度)更新情報を蓄積、新しい順番にソートしたものがREST API経由で提供されています。

巡回サイトは、ライフハッカー、メディア・パブ、スラッシュドット、ギズモード、ゆかしメディア、TechWave、TechDoll.jp、TechCrunch、Reuters、Google Japan Blog、GIGAZINE、Engadget、CNET、All About(オールアバウト) [新着記事]、男子ハック、Techable、ReadWrite Japan、MdN Design Interactive、WIRED.jp、アンドロイドアプリが見つかる!スマホ情報ならオクトバ、ITmedia 総合、ITmedia トップストーリー

となっています(記事執筆時)。

apitoreでは、この「テックサイト ごちゃまぜフィード」のほか、「旅行系」「ニュース」「ブログ 」「デザイン系」などの情報源のフィード系REST APIを提供しています。

本Scriptをテストするためには、apitoreにサインアップしてAccess Tokenを取得して、Script末尾の伏字部分にコピー&ペーストしてください(掲載リストをそのまま実行してもエラーになります)。

Webクローラーを自前で作って定期実行しなくても、Web API経由でクローリングした結果がもらえるというのは、とてもメリットが大きいと思います。AppleScriptではGUIアプリケーションの内部機能からOS内部のCocoa API、shell script、そしてWeb上のREST APIまで直接呼べるので、こんな「なんでもアリ」な環境はなかなかないでしょう。

AppleScript名:テックサイト ごちゃまぜフィード
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4616

set allFeeds to {}

repeat with i from 1 to 100
  set {aRes, maxNum} to getAFeeds(i) of me
  
  
if aRes = false then exit repeat
  
set maxNum to maxNum as number
  
set allFeeds to allFeeds & aRes
  
if (maxNum div 10) i then
    exit repeat
  end if
end repeat

return allFeeds
–>  
(*
{{author:”Ittousai”, sourceTitle:”Engadget Japanese RSS Feed”, title:” 速報:サムスンGalaxy S8発表イベント UNPACKED 2017 (ライブ更新ページ)
“, link:”http://japanese.engadget.com/2017/03/29/galaxy-s8-unpacked-2017/”, pubDate:1.49079606E+12, description:”

“, sourceLink:”http://japanese.engadget.com/rss.xml”},
*)

on getAFeeds(aNum)
  set reqURLStr to “https://api.apitore.com/api/35/feeds/tech”
  
set accessToken to retAccessToken() —Access Token
  
set aRec to {access_token:accessToken, page:(aNum as string)}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
  
set aRESCode to (responseCode of aRes) as integer
  
if aRESCode is not equal to 200 then return {false, false}
  
  
set aRESTres to (json of aRes) as record
  
  
set sentiRes to entries of aRESTres
  
set allNum to num of aRESTres
  
log allNum
  
  
return {sentiRes, allNum}
end getAFeeds

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXXXxxxX-xxxx-XXXx-xxxX-XxxxXxXxxXXx” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2017/03/31 簡単ツイート収集_search

Apitoreの「簡単ツイート収集」REST APIを呼び出して、指定キーワードによるTweet内容の検索を行うAppleScriptです。

実行前にApitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

また、Twitterのサービスとの連携をオンにするために、Apitoreのマイページから「Twitterと連携する」をクリックし、ApitoreをTwitterとアプリ連携してください。

AppleScript名:簡単ツイート収集_search
– Created 2017-03-29 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4572

set aQuery to “#07Itakura”
set aResList to twitterSearchByQuery(aQuery) of me
–> {tweets:{{sentiment:missing value, favoritedCount:409, source:”Echofon“, retweetCount:392, userId:371790917, userName:”機動戦士ガンダム第07板倉小隊”, userScreenName:”07itakura”, text:”【お知らせ】本日放送した板倉小隊特番のアーカイブを明日26日の昼12時から板倉小隊のHPにて1ヵ月限定で配信決定!見逃してしまった方、もう一度見たいという方、ぜひお楽しみに! https://t.co/Q9q6VhiNGU #07itakura”, statusId:8.45606316664021E+17, sentimentScore:missing value, retweeted:false, favorited:false, createdAt:1.490443231E+12, userProfileImageURL:”http://pbs.twimg.com/profile_images/2578804231/hmzlhl1aaxaen8nf8i5b_normal.png”},….

on twitterSearchByQuery(aQuery)
  set reqURLStr to “https://api.apitore.com/api/23/twitter/search”
  
set accessToken to retAccessToken() —Access Token
  
set aRec to {access_token:accessToken, q:aQuery, sinceId:“-1″, maxId:“-1″, iter:“1″}
  
set aURL to retURLwithParams(reqURLStr, aRec) of me
  
  
set aRes to callRestGETAPIAndParseResults(aURL) of me
  
set aRESCode to (responseCode of aRes) as integer
  
set aRESTres to (json of aRes) as record
  
  
return aRESTres
end twitterSearchByQuery

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXXXxxxX-xxxx-XXXx-xxxX-XxxxXxXxxXXx” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2017/01/13 デコードしたQRコードのメールデータの各フィールドを取り出す v2.2

メールアドレス、Subject、Bodyの各フィールドが同梱されたQRコードをデコードした文字列を、docomo形式のフィールド形式と見立てて、各フィールドを取り出すAppleScriptです。

test_qr.png
–> “MATMSG:TO:hiyoko@piyocast.com;SUB:たいとる;BODY:ほんぶん;;”

こんな(↑)QRコードを読み取って、文字列にデコードできるわけですが、携帯電話用のQRコードはもともと、

名刺データ(氏名、住所など)交換、Webブックマーク(名前とURL)読み取り、メール文面生成などを目的として作られたため(フリー形式もありますけれども)、単にデータをデコードしただけではなく、これらのデータ形式であるかどうかのチェックが必要です。

これらのデータ形式だった場合には、それぞれフィールド値を取り出す必要があるわけですが、ここではメール文面の生成のみ対象にしています。

さて、各フィールドを解釈しようとしても、日本には主に3つの携帯電話キャリアがあり(docomo、au、softbank)、それぞれ微妙にこのフィールド定義形式が異なります。

調査したところ、docomo形式でないと読み取れないiOSアプリがあったため、docomo形式をデコードすることにします(AppStoreでも5本ぐらいのiOSアプリしかフィールド検出できませんでした)。海外産のiOSアプリでQRコードのこれらのデータ形式に対応しているケースではdocomo形式にのみ対応しているようでした(それでもあまたの未対応iOSアプリの1億倍えらい)。

先日、国内外のマニアさんによって寄ってたかって改良されまくった「&と=で区切られたテキストをrecordに(NSScanner版)」をもとに若干の手直しを行い、このためのサブルーチンに作り変えてみました。

Cocoaを呼び出すことで、AppleScript単体で(他のアプリケーションの力を借りずに)QRコードの作成や認識はできていますが、実際には携帯電話側でQRコードを読み取り、その内容をメールでMac側に転送し、Mac側で評価するような運用を想定しています。本Scriptはそのための「準備段階」のものです。

だいたいは、スマホでQRコードをスキャンして、その情報をメールで送ってくるので、Mac側で処理することを考えると、本文にデータが入ってくることだけを見ていればいいはずです。なので、スマホから来たデータ処理のためにはこんなたいそうなルーチンは必要ありません。

一応、Mac側でQRコードをデコードしたあとの「解釈」を行えるようにしておくことは無駄にはならないでしょう。

AppleScript名:デコードしたQRコードのメールデータの各フィールドを取り出す v2.2
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
– Modified 2017-01-12 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4387

set aStr to “こんにちは、ぴよまるです

MATMSG:TO:hiyoko@piyocast.com;SUB:たいとる;BODY:ほんぶん;;

Takaaki Naganoya
XXX-XXXX-XXXX

iPhoneから送信”

set aDict to (parseStrByParamlabelAndTail(aStr, “MATMSG:”, “:”, “;”) of me)

set eMailAddrs to (aDict’s valueForKey:“TO”) as string
–>  ”hiyoko@piyocast.com”
set aSubject to (aDict’s valueForKey:“SUB”) as string
–>  ”たいとる”
set aBody to (aDict’s valueForKey:“BODY”) as string
–>  ”ほんぶん”

on parseStrByParamlabelAndTail(aParamStr, aDataHeader, aParamLabel, aParamTail)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
—Skip over the data header
  
set {theResult, theKey} to theScanner’s scanUpToString:aDataHeader intoString:(reference)
  
if theResult as boolean = false then return false –Error: Data header is not present
  
  
theScanner’s scanString:aDataHeader intoString:(missing value)
  
  
repeat until (theScanner’s isAtEnd as boolean)
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:aParamLabel intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:aParamLabel intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:aParamTail intoString:(reference)
    
if theValue is missing value then set theValue to “”
    
    
– skip over separator
    
theScanner’s scanString:aParamTail intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
  
  
return aDict
end parseStrByParamlabelAndTail

★Click Here to Open This Script 

2017/01/03 NSMapTableのじっけん

どうしてもNSMapTableを作って呼び出す必要のある部品があって、仕方なくNSMapTableについて調査。「NSMutableDictionaryとほぼ同じだがメモリー上の管理方式が異なる」という以上の説明がなく、なかなか強敵です。

しかも、NSDictionary/NSMutableDictionaryから変換するメソッドなどもないため、なかなか作るのもめんどくさいと来ています。

NSMapTable自体を操作するメソッドも少ないので、AppleScript側にとってあまりメリットはなさそうですが、とりあえずNSDictionary/NSMutableDictionaryとの間で相互変換できるようにしてみました。

AppleScript名:NSMapTableのじっけん
– Created 2017-01-02 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–AppleScriptではStrongでもWeakでも関係ない???
set aMapTable to current application’s NSMapTable’s mapTableWithKeyOptions:(current application’s NSMapTableStrongMemory) valueOptions:(current application’s NSMapTableStrongMemory)

–set aMapTable to current application’s NSMapTable’s mapTableWithKeyOptions:(current application’s NSMapTableWeakMemory) valueOptions:(current application’s NSMapTableWeakMemory)

aMapTable’s setObject:“1″ forKey:“foo1″
aMapTable’s setObject:“2″ forKey:“foo2″
aMapTable’s |count|()
–> 2

aMapTable’s objectEnumerator()’s allObjects() as list –objects
–>  {”2″, “1″}

aMapTable’s keyEnumerator()’s allObjects() as list –keys
–>  {”foo2″, “foo1″}

aMapTable’s dictionaryRepresentation() as record –NSMapTable to NSDictionary to record
–>  {foo:”1″, foo2:”2″}

–以下はあまり関係ない?
–aMapTable’s keyPointerFunctions()
–>  (NSConcretePointerFunctions)

–aMapTable’s valuePointerFunctions()
–>  (NSConcretePointerFunctions)

★Click Here to Open This Script 

record –> NSMutableDictionary –> NSMapTable と変換するサブルーチンも作ってみました。

AppleScript名:NSMapTableのじっけん(NSDictionary to NSMapTable)
– Created 2017-01-02 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set aRec to {foo1:1, foo2:2, foo3:3, foo4:4, foo5:5}
set aDict to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
set aMap to convertDictToMap(aDict) of me
(*
–>  (NSConcreteMapTable) NSMapTable {
[1] foo2 -> 2
[5] foo4 -> 4
[7] foo5 -> 5
[11] foo3 -> 3
[14] foo1 -> 1
}
*)


–Verify
set bDict to aMap’s dictionaryRepresentation() as record
–>  {foo1:1, foo4:4, foo2:2, foo5:5, foo3:3}

on convertDictToMap(aDict)
  set aMapTable to current application’s NSMapTable’s mapTableWithKeyOptions:(current application’s NSMapTableStrongMemory) valueOptions:(current application’s NSMapTableStrongMemory)
  
  set keyList to aDict’s allKeys() as list
  
set valList to aDict’s allValues() as list
  
set aLen to length of keyList
  
  repeat with i from 1 to aLen
    set aKey to contents of item i of keyList
    
set aVal to contents of item i of valList
    (
aMapTable’s setObject:aVal forKey:aKey)
  end repeat
  
  return aMapTable
end convertDictToMap

★Click Here to Open This Script 

recordから直接NSMapTableに変換するものも作っておきました。

AppleScript名:NSMapTableのじっけん(record to NSMapTable)
– Created 2017-01-02 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set aRec to {foo1:1, foo2:2, foo3:3, foo4:4, foo5:5}
set aMap to convertRecToMap(aRec) of me
(*
–>  (NSConcreteMapTable) NSMapTable {
[1] foo2 -> 2
[5] foo4 -> 4
[7] foo5 -> 5
[11] foo3 -> 3
[14] foo1 -> 1
}
*)

–Verify
set bDict to aMap’s dictionaryRepresentation() as record
–>  {foo1:1, foo4:4, foo2:2, foo5:5, foo3:3}

on convertRecToMap(aRec as record)
  set aDict to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aMapTable to current application’s NSMapTable’s mapTableWithKeyOptions:(current application’s NSMapTableStrongMemory) valueOptions:(current application’s NSMapTableStrongMemory)
  
  
set keyList to aDict’s allKeys() as list
  
set valList to aDict’s allValues() as list
  
set aLen to length of keyList
  
  
repeat with i from 1 to aLen
    set aKey to contents of item i of keyList
    
set aVal to contents of item i of valList
    (
aMapTable’s setObject:aVal forKey:aKey)
  end repeat
  
  
return aMapTable
end convertRecToMap

★Click Here to Open This Script 

2016/12/14 &と=で区切られたテキストをrecordに 改

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

「&と=で区切られたテキストをrecordに(NSScanner版)」をedama2さんがさらに改良したものです。

「先日掲載されたNSScannerのスクリプト、自分でも試してみてとても便利だったんですが、値なしの時にエラーになるので修正してみました。

あとリファレンス本に「repeat until」はあんまり使わないと書いてあったので使ってみました。(edama2さん)」

なるほど。たしかにラベルだけで値が存在しないケースに対応できるとよさそうです。

AppleScript名:&と=で区切られたテキストをrecordに 改
– Created 2016-12-12 by Shane Stanley
– Modified 2016-12-14 by edama2
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4358

set aParamStr to "access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399&name="
set aDict to (parseStrByAmpAndEqual(aParamStr) of me)
–>  {expires:"86399", |name|:"", access_token:"XXxxx(XXxXXXXXxxXxxXXx))"}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:"=" intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:"=" intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:"&" intoString:(reference)
    
if theValue is missing value then set theValue to "" –>追加
    
    
– skip over separator
    
theScanner’s scanString:"&" intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
  
  
return aDict as record
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2016/12/12 &と=で区切られたテキストをrecordに(NSScanner版)

「aParam=1234&bParam=2345」のように、URLのパラメータ部のような文字列を「&」と「=」で区切ってrecordとして出力するAppleScriptです。

Shane Stanleyから「NSScannerを使った実装例」を教えてもらいました。「高速ではないけど、NSSCannerは便利なクラスなので慣れておいたほうがいいよ」という実例です。

Cocoaには、AppleScriptの処理系にはまったく存在していない強力な機能があり、使うととても便利で、プログラムを短く書くことができ、圧倒的に高速処理が可能です。

 リスト項目のユニーク化:NSSet
 リスト項目のユニーク化と出現回数カウント:NSCountedSet
 リスト項目のユニーク化とソート:NSMutableIndexSet
 リスト項目のしぼりこみ:NSPredicate

など、もはやプログラムを書く上で「ないと困る!」というレベルの道具になっています。ファイルパスの加工が強力なNSStringもないと困るレベルのものです。

NSScannerについて、Shaneのプログラムをそのまま掲載するとこんな感じですが、これは「巨大なプログラムを書き慣れた人が書く」タイプのプログラムであり、個別に理解を深めようとすると・・・つまり、「教材」として提示する際には若干の清書が必要になります。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner Orig)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

自分が動作内容を確認するためには、こんな感じに

(1)意味を取りやすいように空行を追加
(2)プログラムの動きを理解しやすいように、さまざまな変数のログ表示を行う
(3)プログラムのInputとOutputがわかりやすいように、サンプルデータの処理結果を掲載

のように書き換えます。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 2)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
log (theScanner’s scanLocation())
  
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
log (theScanner’s scanLocation())
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
    
log (theScanner’s scanLocation())
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

そして、最終的に確認用のコードを削除して(クリーニングして)、こういうリストを掲載しています。

AppleScript名:&と=で区切られたテキストをrecordに(NSScanner 1)
– Created 2016-12-12 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4356

set aParamStr to “access_token=XXxxx(XXxXXXXXxxXxxXXx))&expires=86399″
set aDict to (parseStrByAmpAndEqual(aParamStr) of me) as record
–>  {expires:”86399″, access_token:”XXxxx(XXxXXXXXxxXxxXXx))”}

on parseStrByAmpAndEqual(aParamStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set aDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
repeat
    – terminate check, return the result (aDict) to caller
    
set {theResult, theKey} to theScanner’s scanUpToString:“=” intoString:(reference)
    
if theResult as boolean is false then return aDict
    
    
– skip over separator
    
theScanner’s scanString:“=” intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:“&” intoString:(reference)
    
    
– skip over separator
    
theScanner’s scanString:“&” intoString:(missing value)
    
    
aDict’s setObject:theValue forKey:theKey
  end repeat
end parseStrByAmpAndEqual

★Click Here to Open This Script 

2016/12/05 IPIFYで自分のグローバルIPアドレスを取得

ipfy.orgを呼び出して、グローバルIPアドレスを取得するAppleScriptです。

以前に掲載した「自分の(インターネットアクセス時の)IPアドレスを調べる」と同じ働きをするものです。

同じ結果が得られることを確認していますが、こちらの方が高速にアドレスを取得できています。

AppleScript名:IPIFYで自分のグローバルIPアドレスを取得
– Created 2016-12-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4343

set reqURLStr to “https://api.ipify.org”
set aRec to {|format|:“json”}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me
set aRESCode to (responseCode of aRes) as integer
if aRESCode is not equal to 200 then error “Server Error”

set aRESHeader to responseHeader of aRes
set aRESTres to |ip| of ((json of aRes) as record)

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
aRequest’s setHTTPMethod:“GET”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

2016/11/30 Livedoorお天気WebサービスAPIで指定地点の天気予報を取得する

livedoorのお天気WebサービスAPIを呼び出して、指定地点コードで指定した地点の天気予報を取得するAppleScriptです。

本サービスを呼び出すのに、開発者登録を行う必要はありません。

例として東京都の東京地方のコード(130010)を指定しています。

現在の緯度・軽度情報を取得して、その情報から住所コードを取得する(逆住所ジオコーディング)と、このようなサービスを利用して「現在位置の天気予報を取得する」という処理ができます。

AppleScript名:Livedoorお天気WebサービスAPIで指定地点の天気予報を取得する
– Created 2016-10-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://weather.livedoor.com/weather_hacks/webservice
–http://piyocast.com/as/archives/4337

set reqURLStr to “http://weather.livedoor.com/forecast/webservice/json/v1″

set aRec to {city:“130010″}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then error “Server Error”

set aRESHeader to responseHeader of aRes

set aRESTres to (json of aRes) as record
return aRESTres
(*
{location:{prefecture:”東京都”, city:”東京”, area:”関東”}, publicTime:”2016-11-30T17:00:00+0900″, title:”東京都 東京 の天気”, copyright:{provider:{{link:”http://tenki.jp/”, |name|:”日本気象協会”}}, title:”(C) LINE Corporation”, image:{height:26, title:”livedoor 天気情報”, width:118, |url|:”http://weather.livedoor.com/img/cmn/livedoor.gif”, link:”http://weather.livedoor.com/”}, link:”http://weather.livedoor.com/”}, link:”http://weather.livedoor.com/area/forecast/130010″, forecasts:{{|date|:”2016-11-30″, dateLabel:”今日”, temperature:{min:missing value, max:missing value}, image:{title:”曇り”, width:50, |url|:”http://weather.livedoor.com/img/icon/8.gif”, height:31}, telop:”曇り”}, {|date|:”2016-12-01″, dateLabel:”明日”, temperature:{min:{celsius:”6″, fahrenheit:”42.8″}, max:{celsius:”14″, fahrenheit:”57.2″}}, image:{title:”曇時々雨”, width:50, |url|:”http://weather.livedoor.com/img/icon/10.gif”, height:31}, telop:”曇時々雨”}, {|date|:”2016-12-02″, dateLabel:”明後日”, temperature:{min:missing value, max:missing value}, image:{title:”晴時々曇”, width:50, |url|:”http://weather.livedoor.com/img/icon/2.gif”, height:31}, telop:”晴時々曇”}}, |description|:{|text|:” 高気圧が三陸沖を東へ移動しています。一方、黄海付近は気圧の谷となっ
ています。

【関東甲信地方】
関東甲信地方は、気圧の谷や湿った空気の影響で曇りとなっています。

30日は、曇りで、気圧の谷や湿った空気の影響で、夜には雨の降る所が
あるでしょう。

12月1日は、気圧の谷の影響により、曇りで、朝にかけて雨の所が多い
見込みです。伊豆諸島では朝にかけて雷を伴う所があるでしょう。

関東近海では、30日から12月1日にかけて波が高いでしょう。船舶は
高波に注意してください。

【東京地方】
30日は、曇りで、夜遅くは雨の降る所があるでしょう。
12月1日は、曇りで、明け方まで雨となる見込みです。”, publicTime:”2016-11-30T16:34:00+0900″}, pinpointLocations:{{link:”http://weather.livedoor.com/area/forecast/1310100″, |name|:”千代田区”}, {link:”http://weather.livedoor.com/area/forecast/1310200″, |name|:”中央区”}, {link:”http://weather.livedoor.com/area/forecast/1310300″, |name|:”港区”}, {link:”http://weather.livedoor.com/area/forecast/1310400″, |name|:”新宿区”}, {link:”http://weather.livedoor.com/area/forecast/1310500″, |name|:”文京区”}, {link:”http://weather.livedoor.com/area/forecast/1310600″, |name|:”台東区”}, {link:”http://weather.livedoor.com/area/forecast/1310700″, |name|:”墨田区”}, {link:”http://weather.livedoor.com/area/forecast/1310800″, |name|:”江東区”}, {link:”http://weather.livedoor.com/area/forecast/1310900″, |name|:”品川区”}, {link:”http://weather.livedoor.com/area/forecast/1311000″, |name|:”目黒区”}, {link:”http://weather.livedoor.com/area/forecast/1311100″, |name|:”大田区”}, {link:”http://weather.livedoor.com/area/forecast/1311200″, |name|:”世田谷区”}, {link:”http://weather.livedoor.com/area/forecast/1311300″, |name|:”渋谷区”}, {link:”http://weather.livedoor.com/area/forecast/1311400″, |name|:”中野区”}, {link:”http://weather.livedoor.com/area/forecast/1311500″, |name|:”杉並区”}, {link:”http://weather.livedoor.com/area/forecast/1311600″, |name|:”豊島区”}, {link:”http://weather.livedoor.com/area/forecast/1311700″, |name|:”北区”}, {link:”http://weather.livedoor.com/area/forecast/1311800″, |name|:”荒川区”}, {link:”http://weather.livedoor.com/area/forecast/1311900″, |name|:”板橋区”}, {link:”http://weather.livedoor.com/area/forecast/1312000″, |name|:”練馬区”}, {link:”http://weather.livedoor.com/area/forecast/1312100″, |name|:”足立区”}, {link:”http://weather.livedoor.com/area/forecast/1312200″, |name|:”葛飾区”}, {link:”http://weather.livedoor.com/area/forecast/1312300″, |name|:”江戸川区”}, {link:”http://weather.livedoor.com/area/forecast/1320100″, |name|:”八王子市”}, {link:”http://weather.livedoor.com/area/forecast/1320200″, |name|:”立川市”}, {link:”http://weather.livedoor.com/area/forecast/1320300″, |name|:”武蔵野市”}, {link:”http://weather.livedoor.com/area/forecast/1320400″, |name|:”三鷹市”}, {link:”http://weather.livedoor.com/area/forecast/1320500″, |name|:”青梅市”}, {link:”http://weather.livedoor.com/area/forecast/1320600″, |name|:”府中市”}, {link:”http://weather.livedoor.com/area/forecast/1320700″, |name|:”昭島市”}, {link:”http://weather.livedoor.com/area/forecast/1320800″, |name|:”調布市”}, {link:”http://weather.livedoor.com/area/forecast/1320900″, |name|:”町田市”}, {link:”http://weather.livedoor.com/area/forecast/1321000″, |name|:”小金井市”}, {link:”http://weather.livedoor.com/area/forecast/1321100″, |name|:”小平市”}, {link:”http://weather.livedoor.com/area/forecast/1321200″, |name|:”日野市”}, {link:”http://weather.livedoor.com/area/forecast/1321300″, |name|:”東村山市”}, {link:”http://weather.livedoor.com/area/forecast/1321400″, |name|:”国分寺市”}, {link:”http://weather.livedoor.com/area/forecast/1321500″, |name|:”国立市”}, {link:”http://weather.livedoor.com/area/forecast/1321800″, |name|:”福生市”}, {link:”http://weather.livedoor.com/area/forecast/1321900″, |name|:”狛江市”}, {link:”http://weather.livedoor.com/area/forecast/1322000″, |name|:”東大和市”}, {link:”http://weather.livedoor.com/area/forecast/1322100″, |name|:”清瀬市”}, {link:”http://weather.livedoor.com/area/forecast/1322200″, |name|:”東久留米市”}, {link:”http://weather.livedoor.com/area/forecast/1322300″, |name|:”武蔵村山市”}, {link:”http://weather.livedoor.com/area/forecast/1322400″, |name|:”多摩市”}, {link:”http://weather.livedoor.com/area/forecast/1322500″, |name|:”稲城市”}, {link:”http://weather.livedoor.com/area/forecast/1322700″, |name|:”羽村市”}, {link:”http://weather.livedoor.com/area/forecast/1322800″, |name|:”あきる野市”}, {link:”http://weather.livedoor.com/area/forecast/1322900″, |name|:”西東京市”}, {link:”http://weather.livedoor.com/area/forecast/1330300″, |name|:”瑞穂町”}, {link:”http://weather.livedoor.com/area/forecast/1330500″, |name|:”日の出町”}, {link:”http://weather.livedoor.com/area/forecast/1330700″, |name|:”檜原村”}, {link:”http://weather.livedoor.com/area/forecast/1330800″, |name|:”奥多摩町”}}}
*)

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/30 Yahoo! 形態素解析APIで日本語テキストを解釈

Yahoo!の形態素解析APIで、日本語テキストを形態素解析するAppleScriptです。

Yahoo!に開発者登録(無料)して、アプリケーションIDを取得し、リスト中のretAccessKey()ハンドラにアプリケーションIDを記入すると実行可能です。

単語ごとに「品詞」「よみがな」などを取得できます。辞書が充実しているためか、自分の名前も正しく単語として認識されました。

形態素解析エンジンはローカルに置いて、辞書をカスタマイズするべきだと思っていますが、Yahoo!のAPI(が備えている辞書)だとそれなりに使える感じがします。

Yahoo!のテキスト解析系APIはひととおり試してみましたが、

 校正支援API:漢字の誤変換は指摘してくれるが、助詞の間違いなどは指摘してくれない
 キーフレーズ抽出API:使えるかどうか評価が難しい
 かな漢字変換API:呼んで使えるが、使い道が難しい

この形態素解析APIが一番実用度が高そうな感じがします。

AppleScript名:Yahoo! 形態素解析APIで日本語テキストを解釈
– Created 2016-11-25 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

–http://developer.yahoo.co.jp/webapi/jlp/ma/v1/parse.html

–日本語形態素解析Web APIは、24時間以内で1つのアプリケーションIDにつき50000件のリクエストが上限となっています。また、1リクエストの最大サイズを100KBに制限 しています。

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set japaneseText to “私の名前は長野谷です。”

set reqURLStr to “http://jlp.yahooapis.jp/MAService/V1/parse”

set aKey to retAccessKey() of me

set aRec to {|key|:aKey, sentence:japaneseText, results:“ma”, page:“1″, output:“xml”, appid:aKey}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseXMLResults(aURL) of me

set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false

set aRESHeader to responseHeader of aRes
set aXMLres to (xml of aRes)

set parsedList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.surface.contents”) as list
–>  {”私”, “の”, “名前”, “は”, “長野谷”, “です”, “。”}

set yomiganaList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.reading.contents”) as list
–>  {”わたし”, “の”, “なまえ”, “は”, “ながのや”, “です”, “。”}

set kindList to (aXMLres’s valueForKeyPath:“ResultSet.ma_result.word_list.word.pos.contents”) as list
–>  {”名詞”, “助詞”, “名詞”, “助詞”, “名詞”, “助動詞”, “特殊”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseXMLResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set aXmlRec to my makeRecordWithXML:resStr
  
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {xml:aXmlRec, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseXMLResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessKey()
  return “xxXxxxXxXXxxxXXXXXXXXXXXXXXxXXXxxxXXxXXxxXXxxxXXXxxXXXX-” –Yahoo! API Key
end retAccessKey

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

——–XMLParse Lib

on makeRecordWithXML:xmlString
  set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
theNSXMLParser’s setDelegate:me
  
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) –as record
  else
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  set parentDict to my dictStack’s lastObject()
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
theArray’s addObject:childDict
  else
    parentDict’s setObject:childDict forKey:elementName
  end if
  
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/17 ぐるなびの「レストラン検索API」でレストランを現在位置から3km以内でキーワード検索する

ぐるなびの「レストラン検索API」で、レストランを現在位置(CoreLocation経由で取得)から3km以内でキーワードを指定して検索するAppleScriptです。

実行のためには、ぐるなびAPIの「新規アカウント発行」より申請を行い、アクセスキーを取得してください(申請無料)。取得したアクセスキーをプログラム中のretAccessKey()ハンドラ中に記述しておいてください。また、現在位置取得のためにWiFiをオンにして実行する必要があります。初回実行時になかなか位置取得機能がイネーブルにならずに焦りました。

ぐるなびでは、「レストラン検索API」「多言語版レストラン検索API」「応援口コミAPI」「エリアマスタ取得API」「都道府県マスタAPI」「エリアLマスタAPI」「エリアMマスタAPI」「エリアSマスタAPI」「大業態マスタ取得API」「小業態マスタ取得API」などのAPIを公開しているため、さまざまなアプリケーション上のデータと組み合わせて有効に利用できると思います。

下世話なところでは、Webサイトから検索すると無駄なデータが多い割に一度に検索できるデータ件数が少ないので、(Webブラウザではなく)スクリプトエディタ上でしぼりこみ検索を行っていたりします。

プログラムの難易度は高くないのですが、ふだんライブラリ化している部品を掲載のために1リスト中に展開しています。内容が素朴な割にプログラムが長くなってしまっていますが、AppleScriptObjCの宿命といったところでしょうか。

AppleScript名:ぐるなびの「レストラン検索API」でレストランを現在位置から3km以内でキーワード検索する
– Created 2016-10-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use framework “CoreLocation”
–use curLocLib : script “curLocationLib”
–http://api.gnavi.co.jp/api/manual/restsearch/

–http://piyocast.com/as/archives/4320

–from curLocationLib
property locationManager : missing value
property curLatitude : 0
property curLongitude : 0
–from curLocationLib

set reqURLStr to “http://api.gnavi.co.jp/RestSearchAPI/20150630/”

–現在地の緯度、経度情報を取得する
set {aLat, aLong} to getCurrentLocation() of me
set aKey to retAccessKey() of me

–指定位置から3km以内の、キーワード”とんかつ”でヒットするレストランをピックアップ
set aRec to {keyid:aKey, |format|:“json”, latitude:aLat as string, longitude:aLong as string, range:“5″, freeword:“とんかつ”}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me
set aRESCode to responseCode of aRes
if aRESCode is not equal to 200 then return false
set aRESHeader to responseHeader of aRes
set aRESTres to (json of aRes)
–>  {@attributes:{api_version:”20150630″}, total_hit_count:”24″, page_offset:”1″, rest:{{name_kana:”アサノ”, address:”〒179-0074 東京都練馬区春日町6-12-5 “, party:{}, credit_card:{}, url:”http://r.gnavi.co.jp/nhefra550000/?ak=Q%2BN8L78dCT2RgZUYqtECBTRS3%2BEtZ0qY0cmsoQMxWqU%3D”, code:{category_name_l:{”和食”, {@attributes:{order:”1″}}}, areaname:”関東”, areacode:”AREA110″, prefname:”東京都”, prefcode:”PREF13″, areacode_s:”AREAS2225″, areaname_s:”光が丘”, category_code_s:{”RSFST01005″, {@attributes:{order:”1″}}}, category_code_l:{”RSFST01000″, {@attributes:{order:”1″}}}, category_name_s:{”とんかつ(トンカツ)”, {@attributes:{order:”1″}}}}, tel:”03-3999-4099″, parking_lots:{}, lunch:{}, fax:{}, @attributes:{order:”0″}, latitude:”35.747856″, category:”とんかつ”, url_mobile:”http://mobile.gnavi.co.jp/shop/5421186/?ak=Q%2BN8L78dCT2RgZUYqtECBTRS3%2BEtZ0qY0cmsoQMxWqU%3D”, name:”あさの “, holiday:{}, flags:{mobile_site:”1″, mobile_coupon:”0″, pc_coupon:”0″}, id:”5421186″, coupon_url:{mobile:{}, pc:{}}, access:{walk:”徒歩7″, line:”都営大江戸線(放射部)”, station_exit:”A3口”, note:{}, station:”練馬春日町駅”}, tel_sub:{}, longitude:”139.637081″, pr:{pr_long:{}, pr_short:{}}, budget:{}, e_money:{}, opentime:{}, update_date:”2015-10-14 14:14:02″, image_url:{shop_image1:{}, shop_image2:{}, qrcode:”http://r.gnst.jp/tool/qr/?id=5421186&q=6″}}, ….

set aNameRes to (aRESTres’s valueForKeyPath:“rest.name”) as list
–>  {”あさの “, “CoCo壱番屋 西武中村橋駅前通店”, “とんかつ かつ富士 “, “華屋与兵衛 豊島園店”, “華屋与兵衛 春日町店”, “とんかつ和幸 西武練馬駅店 “, “とんかつ和幸 IMA光が丘店 “, “松乃家 練馬店”, “とん陣 “, “よしだ “}

set aAddressRes to (aRESTres’s valueForKeyPath:“rest.address”) as list
–>  {”〒179-0074 東京都練馬区春日町6-12-5 “, “〒176-0023 東京都練馬区中村北3-22-10 友伸ビル1F”, “〒176-0021 東京都練馬区貫井3-14-5 “, “〒179-0085 東京都練馬区早宮4-39″, “〒179-0074 東京都練馬区春日町2-7-18″, “〒176-0001 東京都練馬区練馬1-3-5 西武池袋線練馬駅下 2F”, “〒179-0072 東京都練馬区光が丘5-1-1 光が丘IMA内 3F”, “〒176-0012 東京都練馬区豊玉北5-19-12″, “〒176-0012 東京都練馬区豊玉北5-18-7 “, “〒179-0073 東京都練馬区田柄1-4-24 “}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessKey()
  return “xxXxXXxxXxxxXxxXXxXXxXXxXxXXxxXX” –ぐるなびAPI アカウントのアクセスキー
end retAccessKey

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

——————————————————–
— curLocationLib

on getCurrentLocation()
  
  
set locationManager to current application’s CLLocationManager’s alloc()’s init()
  
  
set locE to locationManager’s locationServicesEnabled()
  
if (locE as boolean) = true then
    locationManager’s setDelegate:me
    
locationManager’s setDesiredAccuracy:(current application’s kCLLocationAccuracyNearestTenMeters)
    
locationManager’s setDistanceFilter:500
    
locationManager’s startUpdatingLocation()
  else
    return false –error in init
  end if
  
  
set hitF to false
  
repeat 3000 times
    if {curLatitude, curLongitude} is not equal to {0, 0} then
      set hitF to true
      
exit repeat
    end if
    
delay 0.01
  end repeat
  
  
if hitF = false then return false
  
return {curLatitude, curLongitude}
  
end getCurrentLocation

on locationManager:manager didUpdateLocations:locations
  –Listing 1-3 Processing an incoming location event
  
set location to (locations’s lastObject())
  
set eventDate to (location’s timestamp())
  
set howRecent to (eventDate’s timeIntervalSinceNow())
  
  
–Calc ABS
  
set howRecent to howRecent as real
  
set howRecent to absNum(howRecent)
  
  
if howRecent < 15.0 then
    set alt to location’s altitude
    
set aSpeed to location’s speed
    
set aCourse to location’s course –North:0, East:90, South:180, West:270
    
set theDescription to location’s |description|()
    
set anNSScanner to current application’s NSScanner’s scannerWithString:theDescription
    
anNSScanner’s setCharactersToBeSkipped:(current application’s NSCharacterSet’s characterSetWithCharactersInString:“< ,")
    
set {theResult, aLat} to anNSScanner’s scanDouble:(reference)
    
set {theResult, aLng} to anNSScanner’s scanDouble:(reference)
    
    
copy {aLat, aLng} to {my curLatitude, my curLongitude}
  else
    locationManager’s stopUpdatingLocation()
  end if
end locationManager:didUpdateLocations:

on locationManager:anCLLocationManager didFailWithError:anNSError
  display dialog (anNSError’s localizedDescription() as text)
end locationManager:didFailWithError:

on absNum(aNum)
  if aNum > 0 then
    return aNum
  else
    return (aNum * -1)
  end if
end absNum

★Click Here to Open This Script 

2016/11/06 XMLをrecordにv2

XMLをrecordに変換するAppleScriptです。

以前にAppleScript-Users ML上で流れていたXML→record変換のAppleScriptでしたが、動作確認を行ってもうまく動かず、そのまま放置状態になっていました。

見直してみたところ、「NSMutableDictionary’s dictionary()」というカラのmutable dictionaryを作成する部分が、うまくAppleScriptの処理系に認識されていなかったようでした。少し書き直してみました。

AppleScript名:XMLをrecordにv2
–2015 Shane Stanley & Alex Zavatone
– Modified 2016-11-06 by Takaaki Naganoya
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4306

property dictStack : missing value – stack to hold array of dictionaries
property textInProgress : “” – string to collect text as it is found
property anError : missing value – if we get an error, store it here

set xmlString to “< ?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>

Saga
Nor
én
Malm
ö
Martin
Rohde
K
øbenhavn

set xmlRes to my makeRecordWithXML:xmlString
–> {|character|:{firstName:{|contents|:”Saga”}, lastName:{|contents|:”Norén”}, city:{|contents|:”Malmö“}, partner:{firstName:{|contents|:”Martin”}, lastName:{|contents|:”Rohde”}, city:{|contents|:”København”}, attributes:{approach:”dogged”}}}}

on makeRecordWithXML:xmlString
  – set up properties
  
set my dictStack to current application’s NSMutableArray’s array() – empty mutable array
  
set anEmpty to current application’s NSMutableDictionary’s |dictionary|()
  (
my dictStack)’s addObject:anEmpty – add empty mutable dictionary
  
set my textInProgress to current application’s NSMutableString’s |string|() – empty mutable string
  
  
– convert XML from string to data
  
set anNSString to current application’s NSString’s stringWithString:xmlString
  
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
– initialize an XML parser with the data
  
set theNSXMLParser to current application’s NSXMLParser’s alloc()’s initWithData:theData
  
  
– set this script to be the parser’s delegate
  
theNSXMLParser’s setDelegate:me
  
  
– tell it to parse the XML
  
set theResult to theNSXMLParser’s parse()
  
if theResult then – went OK, get first item on stack
    return ((my dictStack)’s firstObject()) as record
  else – error, so return error
    error (my anError’s localizedDescription() as text)
  end if
end makeRecordWithXML:

– this is an XML parser delegate method. Called when new element found
on parser:anNSXMLParser didStartElement:elementName namespaceURI:aString qualifiedName:qName attributes:aRecord
  – store reference to last item on the stack
  
set parentDict to my dictStack’s lastObject()
  
  
– make new child
  
set childDict to current application’s NSMutableDictionary’s |dictionary|()
  
  
– if there are attributes, add them as a record with key “attributes”
  
if aRecord’s |count|() > 0 then
    childDict’s setValue:aRecord forKey:“attributes”
  end if
  
  
– see if there’s already an item for this key
  
set existingValue to parentDict’s objectForKey:elementName
  
  
if existingValue is not missing value then
    – there is, so if it’s an array, store it…
    
if (existingValue’s isKindOfClass:(current application’s NSMutableArray)) as boolean then
      set theArray to existingValue
    else
      – otherwise create an array and add it
      
set theArray to current application’s NSMutableArray’s arrayWithObject:existingValue
      
parentDict’s setObject:theArray forKey:elementName
    end if
    
    
– then add the new dictionary to the array
    
theArray’s addObject:childDict
  else
    – add new dictionary directly to the parent
    
parentDict’s setObject:childDict forKey:elementName
  end if
  
  
– also add the new dictionary to the end of the stack
  (
my dictStack)’s addObject:childDict
end parser:didStartElement:namespaceURI:qualifiedName:attributes:

– this is an XML parser delegate method. Called at the end of an element
on parser:anNSXMLParser didEndElement:elementName namespaceURI:aString qualifiedName:qName
  – if any text has been stored, add it as a record with key “contents”
  
if my textInProgress’s |length|() > 0 then
    set dictInProgress to my dictStack’s lastObject()
    
dictInProgress’s setObject:textInProgress forKey:“contents”
    
    
– reset textInProgress property for next element
    
set my textInProgress to current application’s NSMutableString’s |string|()
  end if
  
  
– remove last item from the stack
  
my dictStack’s removeLastObject()
end parser:didEndElement:namespaceURI:qualifiedName:

– this is an XML parser delegate method. Called when string is found. May be called repeatedly
on parser:anNSXMLParser foundCharacters:aString
  – only append string if it’s not solely made of space characters (which should be, but aren’t, caught by another delegate method)
  
if (aString’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()))’s |length|() > 0 then
    (my textInProgress)’s appendString:aString
  end if
end parser:foundCharacters:

– this is an XML parser delegate method. Called when there’s an error
on parser:anNSXMLParser parseErrorOccurred:anNSError
  set my anError to anNSError
end parser:parseErrorOccurred:

★Click Here to Open This Script 

2016/11/04 Wikipedia APIで記事の問い合わせを行う

WikipediaのAPIを呼び出して、指定記事の内容を取得するAppleScriptです。

自分で書いた記事の本文を取得して、本文中のURLを抽出し、それらが継続して存在しているかどうかを自動でチェックする、といった用途に使えると思います。

AppleScript名:Wikipedia APIで記事の問い合わせを行う
– Created 2016-11-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–https://www.mediawiki.org/wiki/API:Main_page/ja
–http://piyocast.com/as/archives/4302

–set reqURLStr to "https://en.wikipedia.org/w/api.php"–English Version
set reqURLStr to "https://jp.wikipedia.org/w/api.php" –Japanese Version

set aRec to {action:"query", titles:"AppleScript", |prop|:"revisions", rvprop:"content", |format|:"json"}
–set aRec to {action:"query", titles:"AppleScript|Mac OS X|Objective-C", |prop|:"revisions", rvprop:"content", |format|:"json"}
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–> {query:{pages:{2954:{pageid:2954, title:"AppleScript", revisions:{{contentformat:"text/x-wiki", *:"{{Infobox プログラミング言語|名前 = AppleScript ……., contentmodel:"wikitext"}}, ns:0}}}, batchcomplete:""}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/02 connpassイベントサーチAPIで検索を行う

connpassイベントサーチAPIで検索を行うAppleScriptです。

エンジニアをつなぐIT勉強会支援プラットフォームであるconnpassのサイトでは、イベントサーチAPIを用意しており、

  event_id(イベントID)
  keyword(キーワード (AND))
  keyword_or(キーワード (OR))
  ym(イベント開催年月)
  ymd(イベント開催年月日)
  nickname(参加者のニックネーム)
  owner_nickname(管理者のニックネーム)
  series_id(グループID)

などの検索キーをもとにイベントを検索できます。あー、自分の登録したイベントが見つかってよかったー、という確認です。

# 先日、connpassイベントサーチAPIがhttps経由でのアクセスに変更されたため、本リストも修正しておきました

AppleScript名:connpassイベントサーチAPIで検索を行うv2
– Created 2016-10-29 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–http://connpass.com/about/api/
–http://piyocast.com/as/archives/4300

set reqURLStr to "https://connpass.com/api/v1/event/"

set aRec to {keyword:"AppleScript", ym:"201611"} –サーチクエリーと対象月
set aURL to retURLwithParams(reqURLStr, aRec) of me
set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>
(*
{results_available:1, results_start:1, |events|:{{place:"マイ・スペース MS&BB 池袋西武横店 1号室", event_url:"http://ashole.connpass.com/event/44103/", accepted:2, title:"AppleScript本フィードバック会", limit:7, event_type:"participation", owner_id:64136, ended_at:"2016-11-26T20:30:00+09:00", updated_at:"2016-11-01T09:22:15+09:00", lon:"139.711383200000", waiting:0, event_id:44103, hash_tag:"AppleScript,Mac,macOS,Mac OS X", owner_nickname:"Piyomaru", lat:"35.726486900000", started_at:"2016-11-26T18:30:00+09:00", owner_display_name:"Piyomaru", catch:"「AppleScript最新リファレンス」「AppleScript最新10大技術」についての解説", series:{|url|:"http://ashole.connpass.com/", |id|:3041, title:"AppleScriptの穴"}, address:"〒171-0022 東京都豊島区南池袋1-16-20(ぬかりやビル2階)", |description|:"<p>macOS標準装備で、GUIアプリケーションを操作できるマクロ言語「AppleScript」、その20年以上の歴史をまとめ、最新情報を盛り込んだ電子書籍「AppleScript最新リファレンス」「AppleScript 最新10大技術」を発行いたしました。</p>\n<p>・電子書籍オンライン販売URL\n<a href=\"https://piyomarusoft.booth.pm\" rel=\"nofollow\">https://piyomarusoft.booth.pm</a></p>\n<p>これらの本について、分からない点やもっと知りたい点について、筆者本人と直接お話できる場を設けました。</p>\n<p>参加資格は、AppleScriptを実際に使っている方、興味を持っている方で、筆者の書籍を実際に購入した、あるいは購入しようと考えている方です。事前に内容を読んであることが望ましいです。</p>\n<p>筆者Blog「AppleScriptの穴」\n<a href=\"http://piyocast.com/as/\" rel=\"nofollow\">http://piyocast.com/as/</a></p>"}}, results_returned:1}
*)

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/11/01 日本語形態素解析【新語対応】

Apitoreの「日本語形態素解析【新語対応】」REST APIを呼び出して、指定の日本語の形態素解析を行うAppleScriptです。使用している形態素解析エンジンはJavaベースの「Kuromoji」とのこと。

IPADICを辞書に使うバージョンと、IPADIC NEologdを辞書に使うバージョンの2つのAPIがあり、それぞれ同じ文章で形態素解析を行っています。NEologdのほうは「きゃりーぱみゅぱみゅ」などの新しめの固有名詞が登録されており、単語として認識します。

その一方で、やはり人名などの固有名詞(例:自分のなまえ)が正しく単語として認識されないと困るので、形態素解析についてはローカルの日本語入力IMやアドレスブックに登録してある人名との連携は欠かせないと思うものであります(IMの学習は「間違っている」ケースもあるので難しいところですが)。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

AppleScript名:日本語形態素解析【新語対応】_ipadic
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4299

set reqURLStr to “https://api.apitore.com/api/7/kuromoji-ipadic/tokenize”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:“私の名前はきゃりーぱみゅぱみゅです。”}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {startTime:”1477627346754″, tokens:{{partOfSpeechLevel1:”名詞”, baseForm:”私”, pronunciation:”ワタシ”, position:0, partOfSpeechLevel3:”一般”, reading:”ワタシ”, surface:”私”, known:true, allFeatures:”名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ”, conjugationType:”*”, partOfSpeechLevel2:”代名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “代名詞”, “一般”, “*”, “*”, “*”, “私”, “ワタシ”, “ワタシ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”の”, pronunciation:”ノ”, position:1, partOfSpeechLevel3:”*”, reading:”ノ”, surface:”の”, known:true, allFeatures:”助詞,連体化,*,*,*,*,の,ノ,ノ”, conjugationType:”*”, partOfSpeechLevel2:”連体化”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “連体化”, “*”, “*”, “*”, “*”, “の”, “ノ”, “ノ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”名前”, pronunciation:”ナマエ”, position:2, partOfSpeechLevel3:”*”, reading:”ナマエ”, surface:”名前”, known:true, allFeatures:”名詞,一般,*,*,*,*,名前,ナマエ,ナマエ”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “名前”, “ナマエ”, “ナマエ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”動詞”, baseForm:”はく”, pronunciation:”ハキャ”, position:4, partOfSpeechLevel3:”*”, reading:”ハキャ”, surface:”はきゃ”, known:true, allFeatures:”動詞,自立,*,*,五段・カ行イ音便,仮定縮約1,はく,ハキャ,ハキャ”, conjugationType:”五段・カ行イ音便”, partOfSpeechLevel2:”自立”, conjugationForm:”仮定縮約1”, allFeaturesArray:{”動詞”, “自立”, “*”, “*”, “五段・カ行イ音便”, “仮定縮約1”, “はく”, “ハキャ”, “ハキャ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助動詞”, baseForm:”り”, pronunciation:”リ”, position:7, partOfSpeechLevel3:”*”, reading:”リ”, surface:”り”, known:true, allFeatures:”助動詞,*,*,*,文語・リ,基本形,り,リ,リ”, conjugationType:”文語・リ”, partOfSpeechLevel2:”*”, conjugationForm:”基本形”, allFeaturesArray:{”助動詞”, “*”, “*”, “*”, “文語・リ”, “基本形”, “り”, “リ”, “リ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”*”, pronunciation:”*”, position:8, partOfSpeechLevel3:”一般”, reading:”*”, surface:”ー”, known:false, allFeatures:”名詞,固有名詞,一般,*,*,*,*,*,*”, conjugationType:”*”, partOfSpeechLevel2:”固有名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “固有名詞”, “一般”, “*”, “*”, “*”, “*”, “*”, “*”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”*”, pronunciation:”*”, position:9, partOfSpeechLevel3:”*”, reading:”*”, surface:”ぱみゅぱみゅです”, known:false, allFeatures:”名詞,一般,*,*,*,*,*,*,*”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “*”, “*”, “*”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”記号”, baseForm:”。”, pronunciation:”。”, position:17, partOfSpeechLevel3:”*”, reading:”。”, surface:”。”, known:true, allFeatures:”記号,句点,*,*,*,*,。,。,。”, conjugationType:”*”, partOfSpeechLevel2:”句点”, conjugationForm:”*”, allFeaturesArray:{”記号”, “句点”, “*”, “*”, “*”, “*”, “。”, “。”, “。”}, partOfSpeechLevel4:”*”}}, endTime:”1477627346755″, log:”", processTime:”1″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json;charset=UTF-8″, Access-Control-Allow-Origin:”*”, Pragma:”no-cache”, X-XSS-Protection:”1; mode=block, 1; mode=block”, Server:”nginx/1.10.1″, Transfer-Encoding:”Identity”, Expires:”0″, Cache-Control:”no-cache, no-store, max-age=0, must-revalidate”, Date:”Thu, 27 Oct 2016 03:19:17 GMT”, Strict-Transport-Security:”max-age=31536000; includeSubDomains;”, Connection:”keep-alive”, X-Content-Type-Options:”nosniff, nosniff”, X-Frame-Options:”DENY, SAMEORIGIN”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

AppleScript名:日本語形態素解析【新語対応】_ipadic_neologd
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4299

set reqURLStr to “https://api.apitore.com/api/7/kuromoji-ipadic-neologd/tokenize”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:“私の名前はきゃりーぱみゅぱみゅです。”}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {startTime:”1477998343747″, tokens:{{partOfSpeechLevel1:”名詞”, baseForm:”私”, pronunciation:”ワタシ”, position:0, partOfSpeechLevel3:”一般”, reading:”ワタシ”, surface:”私”, known:true, allFeatures:”名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ”, conjugationType:”*”, partOfSpeechLevel2:”代名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “代名詞”, “一般”, “*”, “*”, “*”, “私”, “ワタシ”, “ワタシ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”の”, pronunciation:”ノ”, position:1, partOfSpeechLevel3:”*”, reading:”ノ”, surface:”の”, known:true, allFeatures:”助詞,連体化,*,*,*,*,の,ノ,ノ”, conjugationType:”*”, partOfSpeechLevel2:”連体化”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “連体化”, “*”, “*”, “*”, “*”, “の”, “ノ”, “ノ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”名前”, pronunciation:”ナマエ”, position:2, partOfSpeechLevel3:”*”, reading:”ナマエ”, surface:”名前”, known:true, allFeatures:”名詞,一般,*,*,*,*,名前,ナマエ,ナマエ”, conjugationType:”*”, partOfSpeechLevel2:”一般”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “一般”, “*”, “*”, “*”, “*”, “名前”, “ナマエ”, “ナマエ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助詞”, baseForm:”は”, pronunciation:”ワ”, position:4, partOfSpeechLevel3:”*”, reading:”ハ”, surface:”は”, known:true, allFeatures:”助詞,係助詞,*,*,*,*,は,ハ,ワ”, conjugationType:”*”, partOfSpeechLevel2:”係助詞”, conjugationForm:”*”, allFeaturesArray:{”助詞”, “係助詞”, “*”, “*”, “*”, “*”, “は”, “ハ”, “ワ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”名詞”, baseForm:”きゃりーぱみゅぱみゅ”, pronunciation:”キャリーパミュパミュ”, position:5, partOfSpeechLevel3:”一般”, reading:”キャリーパミュパミュ”, surface:”きゃりーぱみゅぱみゅ”, known:true, allFeatures:”名詞,固有名詞,一般,*,*,*,きゃりーぱみゅぱみゅ,キャリーパミュパミュ,キャリーパミュパミュ”, conjugationType:”*”, partOfSpeechLevel2:”固有名詞”, conjugationForm:”*”, allFeaturesArray:{”名詞”, “固有名詞”, “一般”, “*”, “*”, “*”, “きゃりーぱみゅぱみゅ”, “キャリーパミュパミュ”, “キャリーパミュパミュ”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”助動詞”, baseForm:”です”, pronunciation:”デス”, position:15, partOfSpeechLevel3:”*”, reading:”デス”, surface:”です”, known:true, allFeatures:”助動詞,*,*,*,特殊・デス,基本形,です,デス,デス”, conjugationType:”特殊・デス”, partOfSpeechLevel2:”*”, conjugationForm:”基本形”, allFeaturesArray:{”助動詞”, “*”, “*”, “*”, “特殊・デス”, “基本形”, “です”, “デス”, “デス”}, partOfSpeechLevel4:”*”}, {partOfSpeechLevel1:”記号”, baseForm:”。”, pronunciation:”。”, position:17, partOfSpeechLevel3:”*”, reading:”。”, surface:”。”, known:true, allFeatures:”記号,句点,*,*,*,*,。,。,。”, conjugationType:”*”, partOfSpeechLevel2:”句点”, conjugationForm:”*”, allFeaturesArray:{”記号”, “句点”, “*”, “*”, “*”, “*”, “。”, “。”, “。”}, partOfSpeechLevel4:”*”}}, endTime:”1477998343748″, |log|:”", processTime:”1″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json;charset=UTF-8″, Access-Control-Allow-Origin:”*”, Pragma:”no-cache”, X-XSS-Protection:”1; mode=block, 1; mode=block”, Server:”nginx/1.10.1″, Transfer-Encoding:”Identity”, Expires:”0″, Cache-Control:”no-cache, no-store, max-age=0, must-revalidate”, Date:”Thu, 27 Oct 2016 03:19:17 GMT”, Strict-Transport-Security:”max-age=31536000; includeSubDomains;”, Connection:”keep-alive”, X-Content-Type-Options:”nosniff, nosniff”, X-Frame-Options:”DENY, SAMEORIGIN”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

★Click Here to Open This Script 

2016/10/29 同義語辞書【新語対応】

Apitoreの「同義語辞書【新語対応】」APIを呼び出して、指定の日本語の単語の同義語(っぽいもの)を返すAppleScriptです。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

一般的なシソーラス的な動作を行うわけではなく、固有名詞に特化しているとのこと。そこで、「ガンダム」を渡してみたところ、

{input:”ガンダム”, endTime:”1477742279389″, processTime:”1019″, log:”Success.”, distances:{{word:”ζガンダム”, distance:0.819100677967}, {word:”rx-78″, distance:0.798426866531}, {word:”ζζガンダム”, distance:0.794555306435}, {word:”ザク”, distance:0.783695220947}, {word:”zガンダム”, distance:0.776543617249}, {word:”フルアーマー”, distance:0.757733106613}, {word:”gアーマー”, distance:0.75621265173}, {word:”νガンダム”, distance:0.754522383213}, {word:”ガンキャノン”, distance:0.752012193203}, {word:”モビルスーツ”, distance:0.744036734104}}, startTime:”1477742278370″, num:”10″}

やや、ゴミ(ζガンダム、ζζガンダム)が入っているようです。また、「グフ・カスタム」を入力してみると、

{input:”グフ・カスタム”, endTime:”1477742501687″, processTime:”934″, log:”Success.”, distances:{{word:”sideral”, distance:0.922877013683}, {word:”北海道道104号網走端野線”, distance:0.922159075737}, {word:”北海道道966号十勝岳温泉美瑛線”, distance:0.921318411827}, {word:”kuttner”, distance:0.916471898556}, {word:”北海道道102号網走川湯線”, distance:0.915675938129}, {word:”北海道道42号八雲北檜山線”, distance:0.915441274643}, {word:”三浦隆”, distance:0.914838135242}, {word:”桧山泰浩”, distance:0.914380848408}, {word:”北海道道120号美深中頓別線”, distance:0.914329528809}, {word:”knipex”, distance:0.913379848003}}, startTime:”1477742500753″, num:”10″}

と、関係ない言葉が返ってくるようです。Wikipediaに項目が単独で存在しているような単語と、その関連語では本サービス上での扱いが異なるように感じられます。

AppleScript名:同義語辞書【新語対応】
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4294

set reqURLStr to “https://api.apitore.com/api/9/word2vec-neologd-jawiki/distance”
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |word|:“ガンダム”, num:“10″}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {input:”ガンダム”, endTime:”1477742279389″, processTime:”1019″, log:”Success.”, distances:{{word:”ζガンダム”, distance:0.819100677967}, {word:”rx-78″, distance:0.798426866531}, {word:”ζζガンダム”, distance:0.794555306435}, {word:”ザク”, distance:0.783695220947}, {word:”zガンダム”, distance:0.776543617249}, {word:”フルアーマー”, distance:0.757733106613}, {word:”gアーマー”, distance:0.75621265173}, {word:”νガンダム”, distance:0.754522383213}, {word:”ガンキャノン”, distance:0.752012193203}, {word:”モビルスーツ”, distance:0.744036734104}}, startTime:”1477742278370″, num:”10″}

–>  {input:”グフ・カスタム”, endTime:”1477742501687″, processTime:”934″, log:”Success.”, distances:{{word:”sideral”, distance:0.922877013683}, {word:”北海道道104号網走端野線”, distance:0.922159075737}, {word:”北海道道966号十勝岳温泉美瑛線”, distance:0.921318411827}, {word:”kuttner”, distance:0.916471898556}, {word:”北海道道102号網走川湯線”, distance:0.915675938129}, {word:”北海道道42号八雲北檜山線”, distance:0.915441274643}, {word:”三浦隆”, distance:0.914838135242}, {word:”桧山泰浩”, distance:0.914380848408}, {word:”北海道道120号美深中頓別線”, distance:0.914329528809}, {word:”knipex”, distance:0.913379848003}}, startTime:”1477742500753″, num:”10″}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“GET”
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
set bLen to length of aValList
  
if aLen is not equal to bLen then return false
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

on retAccessToken()
  return “XXxXXXxx-XXxX-XXXx-XXxX-XXxxXxXXXxXX” –API Tore Access Token
end retAccessToken

on urlencodeStr(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set aString to (aString’s stringByAddingPercentEncodingWithAllowedCharacters:(current application’s NSCharacterSet’s URLQueryAllowedCharacterSet())) as text
  
return aString
end urlencodeStr

★Click Here to Open This Script 

2016/10/27 感情推定【極性判定】

Apitore「感情推定」APIを呼び出して、指定の日本語の文章がPositive(肯定的)かNegative(否定的)かを判定するAppleScriptです。

実行前にapitoreにユーザー登録を行い(無料)、Web上でアクセストークンを取得。そのトークンをretAccessToken()内で返すように書いておく必要があります(掲載のリストのまま実行すると、エラーになります。かならずアクセストークンを取得してください)。

用途は割といろいろあって、ちょっと考えただけでも、

「メールの文面をチェックして、日本語であれば(NSLinguisticTaggerを使って判定)感情推定APIを呼び出し、Negativeと判定されたらメールの一覧に赤く色をつける」

とかいうAppleScriptはかんたんに書けますし、便利だと思います(色付きメールを読みたくなくなる、とかいう心理的なフィードバックが発生することについてはさておき)。

また、

Safariの閲覧履歴を取得して、各URLにアクセスして本文のみ取得。読んだ内容がPositveかNegativeかの内訳をグラフで表示」

とかいうのも面白そうです。

テストしてみたところ、たまたまサーバー側で問題を起こしていた最中のようで、サンプルプログラム中に記述してある「かならずPositive(肯定的)と判定される例文」がNegative(否定的)と出てきて焦りましたが、じきに安定することでしょう。

AppleScript名:感情推定【極性判定】
– Created 2016-10-27 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4289

set aText to "おめでとう!カブスが4勝2敗でドジャースを下し、2016年ナ・リーグの覇者に!次は水曜日(10月26日)に行われるワールドシリーズでア・リーグの覇者インディアンスと対決!"

set reqURLStr to "https://api.apitore.com/api/11/sentiment/predict"
set accessToken to retAccessToken() —Access Token
set aRec to {access_token:accessToken, |text|:aText}
set aURL to retURLwithParams(reqURLStr, aRec) of me

set aRes to callRestGETAPIAndParseResults(aURL) of me

set aRESTres to (json of aRes) as record
return aRESTres
–>  {endTime:"1477617363491", processTime:"44", text:"おめでとう!カブスが4勝2敗でドジャースを下し、2016年ナ・リーグの覇者に!次は水曜日(10月26日)に行われるワールドシリーズでア・リーグの覇者インディアンスと対決!", log:"Success.", predict:{score:0.520746946335, sentiment:"positive"}, startTime:"1477617363447", sentimens:{{score:0.520746946335, sentiment:"positive"}, {score:0.479253053665, sentiment:"negative"}}}

–set aRESCode to (responseCode of aRes) as integer
–>  200

–set aRESHeader to (responseHeader of aRes) as record
–>  {Content-Type:"application/json;charset=UTF-8", Access-Control-Allow-Origin:"*", Pragma:"no-cache", X-XSS-Protection:"1; mode=block, 1; mode=block", Server:"nginx/1.10.1", Transfer-Encoding:"Identity", Expires:"0", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Date:"Thu, 27 Oct 2016 03:22:11 GMT", Strict-Transport-Security:"max-age=31536000; includeSubDomains;", Connection:"keep-alive", X-Content-Type-Options:"nosniff, nosniff", X-Frame-Options:"DENY, SAMEORIGIN"}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:"GET"
  
aRequest’s setCachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData)
  
aRequest’s setHTTPShouldHandleCookies:false
  
aRequest’s setTimeoutInterval:60
  
aRequest’s setValue:"application/json" forHTTPHeaderField:"Accept"
  
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
  
set bRes to contents of (first item of resList)
  
set resStr to current application’s NSString’s alloc()’s initWithData:bRes encoding:(current application’s NSUTF8StringEncoding)
  
  
set jsonString to current application’s NSString’s stringWithString:resStr
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to current application’s NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
  
–Get Response Code & Header
  
set dRes to contents of second item of resList
  
if dRes is not equal to missing value then
    set resCode to (dRes’s statusCode()) as number
    
set resHeaders to (dRes’s allHeaderFields()) as record
  else
    set resCode to 0
    
set resHeaders to {}
  end if
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

on retAccessToken()
  return "xxxXxxXx-XXXX-XXXX-XxXX-XXxXXxxXXxxx" –API Tore Access Token
end retAccessToken

on retURLwithParams(aBaseURL, aRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
  
set aKeyList to (aDic’s allKeys()) as list
  
set aValList to (aDic’s allValues()) as list
  
set aLen to length of aKeyList
  
  
set qList to {}
  
repeat with i from 1 to aLen
    set aName to contents of item i of aKeyList
    
set aVal to contents of item i of aValList
    
set the end of qList to (current application’s NSURLQueryItem’s queryItemWithName:aName value:aVal)
  end repeat
  
  
set aComp to current application’s NSURLComponents’s alloc()’s initWithString:aBaseURL
  
aComp’s setQueryItems:qList
  
set aURL to (aComp’s |URL|()’s absoluteString()) as text
  
  
return aURL
end retURLwithParams

★Click Here to Open This Script 

2016/06/28 recordのlistでKeynoteに表を作成する

recordのlistでKeynote v6.6.2のドキュメント上に表を作成するAppleScriptです。

今回作成した本の各バージョンのPDFの情報を収集するAppleScriptを作成し、さあこのデータをどうしようと思ったときに、「じゃあ、まとめたデータをKeynoteの書類上に表で作成すればいいじゃない」ということになり、AppleScriptで自動生成するようにしてみました。

これまでに、意外とAppleScriptで収集したデータをKeynoteにまとめるのは手作業で行っていたりで、なかなか大変でした。発表資料でも、仕様書でも、これが省けるのは(自分的に)省力化になります。

Keynoteでオープン中のドキュメントにページ(slide)を新規追加し、指定データから表を作成します。表の作成は、作成時に一気にすべてのセルのデータを指定することも可能なはずなので、まだまだスピードアップは可能なはずです。

table1.png

table2_resized.png

tabletograph_resized.png

AppleScript名:recordのlistでKeynoteに表を作成する
– Created 2016-06-28 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

property aFontSize : 12

set aList to {{pathStr:“book1_0.1.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:222, countChars:127978, fileSize:“12027660″}, {pathStr:“book1_0.2.pdf”, creationDate:“2016年6月17日金曜日”, pageCount:230, countChars:129506, fileSize:“11109818″}, {pathStr:“book1_0.210.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147119, fileSize:“22832000″}, {pathStr:“book1_0.211.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:254, countChars:147123, fileSize:“22831931″}, {pathStr:“book1_0.212.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134856, fileSize:“22273252″}, {pathStr:“book1_0.213.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134845, fileSize:“22271667″}, {pathStr:“book1_0.214.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:241, countChars:134850, fileSize:“22270980″}, {pathStr:“book1_0.220.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:242, countChars:134870, fileSize:“21098301″}, {pathStr:“book1_0.222.pdf”, creationDate:“2016年6月18日土曜日”, pageCount:243, countChars:135694, fileSize:“21146421″}, {pathStr:“book1_0.300.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142787, fileSize:“21427502″}, {pathStr:“book1_0.301.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:251, countChars:142784, fileSize:“21421107″}, {pathStr:“book1_0.302.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:256, countChars:142827, fileSize:“22593201″}, {pathStr:“book1_0.303.pdf”, creationDate:“2016年6月20日月曜日”, pageCount:257, countChars:142845, fileSize:“22616595″}, {pathStr:“book1_0.400.pdf”, creationDate:“2016年6月22日水曜日”, pageCount:281, countChars:162419, fileSize:“22430779″}, {pathStr:“book1_0.500.pdf”, creationDate:“2016年6月23日木曜日”, pageCount:309, countChars:178210, fileSize:“27611566″}, {pathStr:“book1_0.600.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:326, countChars:194751, fileSize:“26820825″}, {pathStr:“book1_0.700.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195943, fileSize:“26408415″}, {pathStr:“book1_0.701.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195926, fileSize:“26406738″}, {pathStr:“book1_0.702.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:310, countChars:195924, fileSize:“26406703″}, {pathStr:“book1_0.703.pdf”, creationDate:“2016年6月24日金曜日”, pageCount:311, countChars:196594, fileSize:“26416223″}, {pathStr:“book1_1.0.pdf”, creationDate:“2016年6月25日土曜日”, pageCount:311, countChars:196594, fileSize:“26075419″}}

set anItem to contents of first item of aList
set aDict to (current application’s NSMutableArray’s arrayWithObject:anItem)’s firstObject()
set aKeyList to aDict’s allKeys() as list
set aKeyCount to length of aKeyList
set aRowCount to length of aList

tell application “Keynote”
  tell document 1
    set aNewSlide to make new slide
    
    
tell aNewSlide
      set aTable to make new table with properties {column count:aKeyCount + 1, row count:aRowCount + 1}
      
tell aTable
        –ヘッダー行を作成(ラベルで埋める)
        
tell row 1
          repeat with i from 2 to aKeyCount + 1
            set aKey to item (i - 1) of aKeyList
            
set value of cell i to aKey
            
set font size to aFontSize
          end repeat
        end tell
        
        
–各行のデータを埋める
        
repeat with ii from 2 to aRowCount + 1
          set aRecRow to item (ii - 1) of aList
          
tell row ii
            repeat with iii from 2 to (aKeyCount + 1)
              tell cell iii
                set aKey to item (iii - 1) of aKeyList
                
set value to retValueForKey(aRecRow, aKey) of me
                
set font size to aFontSize
              end tell
            end repeat
          end tell
        end repeat
        
      end tell
    end tell
    
  end tell
end tell

on retValueForKey(aRec, aLabel)
  set tmpDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
return (tmpDic’s valueForKey:aLabel) as string
end retValueForKey

★Click Here to Open This Script 

2016/06/06 レコードの値をクリア

与えられたレコード(record)型の変数について、ラベルはそのままで値についてクリアを行うAppleScriptです。

たしかに、ありそうで書いていなかったような気がする内容です。単純にヌル(ヌル文字列)でクリアするバージョンと、与えたrecordの各値の型を見てクリア時の初期値を変更するバージョンを作ってみました。

実際のAppleScriptのワークフローの中で使う場合には、AppleScriptアプレット起動後にplistファイルから設定値を読み取り、初期化動作が必要なものについてだけ初期化を行うというような内容になるでしょうか。

そのため、初期化してほしい属性ラベルのリストを渡すか、初期化してはいけない属性ラベルのリストを与えるかをしつつ、このような動作を行うものが「実戦レベルで使える」ルーチンになるはずです。

AppleScript名:レコードの値をクリア
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aRec to {aaa:“111″, bbb:“222″, ccc:“333″}
set aRec to clearRecordValues(aRec, initVal) of me
–>  {aaa:”", bbb:”", ccc:”"}

on clearRecordValues(aRec, initVal)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:aRec
  
repeat with i in (aDic’s allKeys() as list)
    set j to contents of i
    (
aDic’s setValue:initVal forKey:j)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

AppleScript名:レコードの値をクリア v2
– Created 2016-06-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”

set initVal to “”
set aaaaaaaRec to {aaa:“111″, bbb:2.1234, ccc:-1}
set bRec to clearRecordValues(aaaaaaaRec) of me
–>  {aaa:”", bbb:0, ccc:0}

on clearRecordValues(paramRec)
  set aDic to current application’s NSMutableDictionary’s dictionaryWithDictionary:paramRec
  
set keyList to (aDic’s allKeys() as list)
  
set valList to (aDic’s allValues() as list)
  
  
repeat with i from 1 to (length of keyList)
    set aKey to contents of item i of keyList
    
set tmpVal to contents of item i of valList
    
set aClass to class of tmpVal
    
    
if aClass is in {number, integer, real} then
      set initVal to 0
    else if aClass is in {string, text, Unicode text} then
      set initVal to “”
    else
      set initVal to missing value
    end if
    
    (
aDic’s setValue:initVal forKey:aKey)
  end repeat
  
return aDic as record
end clearRecordValues

★Click Here to Open This Script 

2016/04/06 指定のDiskの情報を取得する

diskutilコマンドの結果をparseして、指定のデバイス名のdiskの情報を取得するAppleScriptです。

デバイス名はbsdレイヤーで利用している/dev/xxxxxといった名前であり、マウント中の光学メディア(CD-R、DVD-R、BD-Rなど)のbsdデバイス名を求めるのはちょっと手間がかかります。本来なら、choose folderコマンドで指定したドライブ(に含まれているフォルダ)からbsdデバイス名を求めて処理・・・をしたかったのですが、時間がなかったのでとりあえず決め打ちで。

とりあえず、指定のドライブに入っている光学メディアの種類(Optical Media Type)と、光学ドライブ側がサポートしているメディアの種類(Optical Drive Type)を取得できます。結果がNSDictionaryになっているため、Script Editor上では内容を確認できません。AppleScriptネイティブのrecord型のデータだと属性ラベルの自由度が低いため(属性ラベルに空白文字とか入りませんし)、NSDictionaryのままにしてあります。

AppleScript名:指定のDiskの情報を取得する
– Created 2016-04-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set targDev to “/dev/disk3″ –Blu-ray drive via USB

set aDict to current application’s NSMutableDictionary’s alloc()’s init()
set a to do shell script “diskutil info “ & targDev
set aList to paragraphs of a

repeat with i in aList
  set j to contents of i
  
if j is not equal to “” then
    set jj to parseByDelim(j, “:”) of me
    
    
if (count every item of jj) is equal to 2 then
      copy jj to {j2, j3}
      
      
set jj2 to (current application’s NSString’s stringWithString:j2)
      
set jj3 to (current application’s NSString’s stringWithString:j3)
      
      
set jjj2 to (jj2’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceCharacterSet()))
      
set jjj3 to (jj3’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceCharacterSet()))
      
      (
aDict’s setValue:jjj3 forKey:jjj2)
    end if
    
  end if
end repeat

return aDict
–>  (NSDictionary) {Read-Only Media:”Yes”, OS Can Be Installed:”No”, Volume Name:”Not applicable (no file system)”, OS 9 Drivers:”No”, Virtual:”No”, Device Block Size:”2048 Bytes”, Low Level Format:”Not supported”, Removable Media:”Yes”, Device / Media Name:”MATSHITA BD-MLT UJ240AS”, Optical Media Erasable:”No”, Optical Drive Type:”CD-ROM, CD-R, CD-RW, DVD-ROM, DVD-R, DVD-R DL, DVD-RW, DVD-RAM, DVD+R, DVD+R DL, DVD+RW, BD-ROM, BD-R, BD-RE”, Part of Whole:”disk3″, Device Identifier:”disk3″, Whole:”Yes”, Optical Media Type:”BD-R”, SMART Status:”Not Supported”, Device Location:”External”, Volume Free Space:”Not applicable (no file system)”, Device Node:”/dev/disk3″, Read-Only Volume:”Not applicable (no file system)”, Media Type:”Generic”, Total Size:”0 B (0 Bytes) (exactly 0 512-Byte-Units)”, Media Removal:”Software-Activated”, Mounted:”Not applicable (no file system)”, Content (IOContent):”None”, File System:”None”, Protocol:”USB”}

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

★Click Here to Open This Script 

2015/11/03 QuartzComposerを任意のパラメータで表示

QuartzComposerのCompositionを任意のパラメータでレンダリングして表示するAppleScriptです。

Appleのサンプルコードに、QuartzComposerでグラフ表示するもの(QuartzComposerChart)があり、ScriptとComposerの組み合わせについては以前から注目していました。

qcchart_resized.png

Xcode上のCocoa AppleScriptアプリケーションでは同様にCompositionを呼び出して表示できることは(未確認ながら)、可能と思っていましたが・・・Script Editor上のAppleScriptから呼び出して表示することは、自分の知り得るかぎり誰も実現していませんでした。

chart_as_resized.png
▲本ScriptからQuartzComposerのCompositionをパラメータ指定しつつ表示したところ。データにはとくに意味はありません

もともとのAppleのサンプルプロジェクト「QuartzComposerChart」の中身を分析してAppleScriptに置き換え、Compositionで作られたグラフのパラメータをいろいろいじくってみました。

comp1.png

params.png

Scriptのバンドル内に収めたQTZファイル(Composition)を参照しているため、QTZファイルを含んだAppleScriptバンドルを以下からダウンロードできるようにしておきました。興味のある方はお試しください(掲載リストから「★Click Here to Open This Script」をクリックしてScript Editorに転送しただけでは、グラフは表示されません)。

–> Download script bundle including Composition

AppleScript名:QuartzComoserでグラフ表示てすと
– Created 2015-11-03 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “Quartz”
use framework “AppKit”

set chartData to current application’s NSMutableArray’s alloc()’s init()
chartData’s addObject:(current application’s NSMutableDictionary’s dictionaryWithObjectsAndKeys_(“練馬区”, “label”, 3, “value”, missing value))
chartData’s addObject:(current application’s NSMutableDictionary’s dictionaryWithObjectsAndKeys_(“青梅市”, “label”, 1, “value”, missing value))
chartData’s addObject:(current application’s NSMutableDictionary’s dictionaryWithObjectsAndKeys_(“中野区”, “label”, 2, “value”, missing value))

try
  set aPath to path to resource “Chart.qtz”
on error
  return
end try

set qtPath to current application’s NSString’s stringWithString:(POSIX path of aPath)

set aView to current application’s QCView’s alloc()’s init()
set qtRes to (aView’s loadCompositionFromFile:qtPath)

aView’s setValue:chartData forInputKey:“Data”
aView’s setValue:(current application’s NSNumber’s numberWithFloat:(0.5)) forInputKey:“Scale”
aView’s setValue:(current application’s NSNumber’s numberWithFloat:(0.1)) forInputKey:“Spacing”
aView’s startRendering() –レンダリング開始

set maXFrameRate to aView’s maxRenderingFrameRate()

set aWin to (my makeWinWithView(aView, 800, 800, “Composition Test”))

repeat with i from 1 to 10
  (aView’s setValue:(current application’s NSNumber’s numberWithFloat:(i / 10)) forInputKey:“Scale”)
  
delay 0.1
end repeat

repeat with i from 10 to 1 by -1
  (aView’s setValue:(current application’s NSNumber’s numberWithFloat:(i / 10)) forInputKey:“Scale”)
  
delay 0.1
end repeat

my closeWin:aWin
aView’s stopRendering() –レンダリング停止

–make Window for Input
on makeWinWithView(aView, aWinWidth, aWinHeight, aTitle)
  set aScreen to current application’s NSScreen’s mainScreen()
  
set aFrame to {{0, 0}, {aWinWidth, aWinHeight}}
  
set aBacking to current application’s NSTitledWindowMask –NSBorderlessWindowMask
  
set aDefer to current application’s NSBackingStoreBuffered
  
  
– Window
  
set aWin to current application’s NSWindow’s alloc()
  (
aWin’s initWithContentRect:aFrame styleMask:aBacking backing:aDefer defer:false screen:aScreen)
  
aWin’s setBackgroundColor:(current application’s NSColor’s whiteColor())
  
  
aWin’s setTitle:aTitle
  
aWin’s setDelegate:me
  
aWin’s setDisplaysWhenScreenProfileChanges:true
  
aWin’s setHasShadow:true
  
aWin’s setIgnoresMouseEvents:false
  
aWin’s setLevel:(current application’s NSNormalWindowLevel)
  
aWin’s setOpaque:false
  
aWin’s setReleasedWhenClosed:true
  
aWin’s |center|()
  
aWin’s makeKeyAndOrderFront:(me)
  
  
– Set Custom View
  
aWin’s setContentView:aView
  
  
return aWin
  
end makeWinWithView

–close win
on closeWin:aWindow
  delay 5
  
repeat with n from 10 to 1 by -1
    (aWindow’s setAlphaValue:n / 10)
    
delay 0.02
  end repeat
  
aWindow’s |close|()
end closeWin:

★Click Here to Open This Script 

2015/09/22 クリップボードの内容をRTFとPDFとHTMLで書き出す

Cocoaの機能を用いて、クリップボードの内容をデスクトップにRTFとPDFで書き出すAppleScriptの改訂版です。

前バージョンのAppleScriptでは、スタイル付きテキスト(NSAttributedString)をHTMLに書き出すと、HTML内のタグでは「UTF-8」になっていながらも、HTMLのテキスト自体のエンコーディングがShift JISというたいへん不思議な状態になっていました。

スタイル付きテキスト(NSAttributedString)の取得部分に問題がないことを確認。スタイル付きテキストをPDFやRTFに書き出した場合には問題なし。HTMLの書き出しに固有の根の深い問題があるのかと疑っていましたが・・・難易度でいえば、それほど難しくない話でした。

修正部分はたった1箇所。最後にHTMLをファイルに書き込む際に、UTF-8(NSUTF8StringEncoding)を指定する記述を追加しただけです。

テキストエンコーディングの省略時にはかならず「UTF-8」が指定されるものとばかり思っていましたが、明示的に指定しないとダメということがよくわかりました。

AppleScript名:クリップボードの内容をRTFとPDFとHTMLで書き出す v2
– Created 2015-09-20 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit” – for NSPasteboard, which is the clipboard

–クリップボードの内容をNSAttributedStringに
set anAttr to my getClipboardASStyledText()

–保存先とファイル名を求める
set targFol to POSIX path of (path to desktop)
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text

set aRes to my saveStyledTextAsRTF(aUUID, targFol, anAttr) –RTFで書き出す
set bRes to my saveStyledTextAsPDF(aUUID, targFol, anAttr) –PDFで書き出す
set cRes to my saveStyledTextAsHTML(aUUID, targFol, anAttr) –HTMLで書き出す

– クリップボードの内容をNSAttributedStringとして取り出して返す
on getClipboardASStyledText()
  set theNSPasteboard to current application’s NSPasteboard’s generalPasteboard()
  
set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({current application’s NSAttributedString}) options:(missing value)
  
set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0
  
return theNSAttributedString
end getClipboardASStyledText

–スタイル付きテキストを指定フォルダ(POSIX path)にRTFで書き出し
on saveStyledTextAsRTF(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings to RTF
  
set bstyledLength to aStyledString’s |string|()’s |length|()
  
set bDict to current application’s NSDictionary’s dictionaryWithObject:“NSRTFTextDocumentType” forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set bRTF to aStyledString’s RTFFromRange:(current application’s NSMakeRange(0, bstyledLength)) documentAttributes:bDict
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“rtf”
  
  
return (bRTF’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsRTF

–スタイル付きテキストを指定フォルダ(POSIX path)にPDFで書き出し
on saveStyledTextAsPDF(aFileName, targFol, aStyledString)
  – get page size being used for printing
  
set printInfo to current application’s NSPrintInfo’s sharedPrintInfo()
  
set pageSize to printInfo’s paperSize()
  
set theLeft to printInfo’s leftMargin()
  
set theTop to printInfo’s topMargin()
  
  
– make a text view
  
set theView to current application’s NSTextView’s alloc()’s initWithFrame:{origin:{x:0, y:0}, |size|:pageSize}
  
theView’s setTextContainerInset:{theLeft, theTop}
  
  
– put in the text
  
theView’s textStorage()’s setAttributedString:aStyledString
  
set theData to theView’s dataWithPDFInsideRect:{origin:{x:0, y:0}, |size|:pageSize}
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“pdf”
  
  
return (theData’s writeToFile:thePath atomically:true) as boolean
end saveStyledTextAsPDF

–スタイル付きテキストを指定フォルダ(POSIX path)にHTMLで書き出し
on saveStyledTextAsHTML(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings toHTML
  
set theNSDictionary to current application’s NSMutableDictionary’s dictionaryWithObject:(current application’s NSHTMLTextDocumentType) forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set theNSData to aStyledString’s dataFromRange:{location:0, |length|:aStyledString’s |length|()} documentAttributes:theNSDictionary |error|:(missing value)
  
set aHTML to current application’s NSString’s alloc()’s initWithData:theNSData encoding:(current application’s NSUTF8StringEncoding)
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:“/” withString:“_”
  
set theName to theName’s stringByReplacingOccurrencesOfString:“:” withString:“_”
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:“html”
  
  
return (aHTML’s writeToFile:thePath atomically:true encoding:(current application’s NSUTF8StringEncoding) |error|:(missing value)) as boolean
  
end saveStyledTextAsHTML

★Click Here to Open This Script 

2015/09/19 Notes上の指定のエントリをHTMLとして書き出す

Cocoaの機能を使って、Notes(日本語名:「メモ」)の指定エントリの内容をHTMLとして書き出すAppleScriptです。

ただ、普通にPDFやRTFと同様に出力してみたら・・・文字化けしてしまいました(汗)。

html1.png

UTF-8で出力しているはずなのですが、

html2.png

Safariで文字エンコーディングを「日本語(Shift JIS)」に設定すると・・・

html3.png

html4.png

文字化けしなくなります。

html5.png

保存されたHTMLのソースを見ると、エンコーディングはUTF-8となっているのですが、ソースをUTF-8でオープンしても文字化けしたままです。

AppleScript名:Notes上の指定のエントリをHTMLとして書き出す
– Created 2015-09-18 by Shane Stanley
– Modified 2015-09-19 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set targFol to POSIX path of (path to desktop)

tell application "Notes"
  set theBody to body of note 2
  
set theName to name of note 2 – for file name
end tell

– Notesの本文をスタイル付きテキストに
set anNSString to current application’s NSString’s stringWithString:theBody
set theData to anNSString’s dataUsingEncoding:(current application’s NSUTF16StringEncoding)
set styledString to current application’s NSAttributedString’s alloc()’s initWithHTML:theData documentAttributes:(missing value)

set cRes to my saveStyledTextAsHTML(theName, targFol, styledString)
–> true

–スタイル付きテキストを指定フォルダ(POSIX path)にHTMLで書き出し
on saveStyledTextAsHTML(aFileName, targFol, aStyledString)
  –Convert NSMutableStyledStrings toHTML
  
set theNSDictionary to current application’s NSMutableDictionary’s dictionaryWithObject:(current application’s NSHTMLTextDocumentType) forKey:(current application’s NSDocumentTypeDocumentAttribute)
  
set theNSData to aStyledString’s dataFromRange:{location:0, |length|:aStyledString’s |length|()} documentAttributes:theNSDictionary |error|:(missing value)
  
  
set aHTML to current application’s NSString’s alloc()’s initWithData:theNSData encoding:(current application’s NSUTF8StringEncoding)
  
  
– build path based on title
  
set theName to current application’s NSString’s stringWithString:aFileName
  
set theName to theName’s stringByReplacingOccurrencesOfString:"/" withString:"_"
  
set theName to theName’s stringByReplacingOccurrencesOfString:":" withString:"_"
  
set thePath to current application’s NSString’s stringWithString:targFol
  
set thePath to (thePath’s stringByAppendingPathComponent:theName)’s stringByAppendingPathExtension:"html"
  
  
return (aHTML’s writeToFile:thePath atomically:true) as boolean
  
end saveStyledTextAsHTML

★Click Here to Open This Script 

一応、Notes(メモ)から取得した本文データがHTMLだったので、Styled Textに変換しないでそのまま保存するバージョンも作ってみました。こちらであれば、文字化けは見られませんが・・・Styled Text(NSAttributedString)をHTMLに変換してファイル出力するという機能は、普通に文字化けせずに使えてほしいので、回避策を検討しておきたいところです。

AppleScript名:Notes上の指定のエントリをHTMLとして書き出す v2
– Created 2015-09-18 by Shane Stanley
– Modified 2015-09-19 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

set targFol to POSIX path <