Archive for the 'Script Libraries' Category

2017/07/19 SQL LibでmacOS上の各種SQLite DBの情報を確認する

Shane Stanleyの「SQL Lib」を用いてmacOS上のアプリケーションが使用しているSQLite DBの内容を確認してみるAppleScriptです。

このところSQL Libを用いて、いろいろと基礎的な操作を試しています。Shane Stanleyから「基礎的な操作ってどんなんだよ?!」とツッコミが入っていろいろやりとりをしつつ、自分でも試していたら急に全体像がくわしく見えてきました。Google翻訳の精度が上がって、わりと日本語の文章でも微妙なニュアンスが伝わるようで、チョットコワイデス(^ー^;;

SQL Libについては、AppleScriptのレベルで日常的な検索や更新、作成や削除などはひととおりできるようで、少し込み入った操作や取得になると内蔵のFMDBASフレームワークに(Cocoaのオブジェクトを経由して)アクセスすることになるようです。

なので、SQLiteのバージョン取得などもSQL Libに通常のAppleScriptのハンドラ呼び出しで行うのではなく、内蔵のFMDBASフレームワークに問い合わせを行うことになるのだとか(do shell scriptコマンド経由でもいいんですが)。

macOS上のアプリケーションの多くはSQLite DBを利用しており、直接データを読めると便利なケースもありそうです。Notes、Safari、Calendarが有名ですが、ほかにもあるでしょう(探せばきっといっぱいある)。もしも、Mail.appもSQLiteを使っているのであればSQLite経由でデータを取れたほうが便利なケースもありそうです。SpotlightのIndex DBとかも。

AppleScriptなどの言語を使っているユーザーはSQLと相性がいいとか悪いとかいう話をすれば、きっと「関係ない」と思っている人が多いことでしょう。ただ、SQLiteの形式になっている既存のデータであるとか、巨大なデータ配布物に問い合わせを行う場合にはどうしても避けて通れない存在であるため、なるべくフレンドリーなインタフェース経由で操作したいところです。

AppleScriptでも巨大なデータ(10万レコード以上のrecordとかlist)を扱うようになってくると、やはりデータベースの併用を視野に入れる必要が出てきます。FileMaker ProはものすごくAppleScriptにフレンドリーな存在ですが、SQLiteならOS標準装備なうえにFileMaker Proよりもベンチマーク上では倍ぐらいのパフォーマンスが出るようなので、使わない手はないことでしょう。

とりあえず、指定データベース上のテーブル名の取得と、テーブルのスキーマ定義の取得のあたりを(Shaneに聞きつつ)まとめてみました。各アプリケーションが利用しているSQLite DBのスキーマについてはOSのアップデートなどで変更になる場合もありそうですから、呼び出す前にスキーマの照合ぐらいはしておいたほうがよさそうだと思ったもので。

AppleScript名:SQL LibでmacOS上の各種SQLite DBの情報を確認する
– Created 2017-07-18 by Shane Stanley
– Modified 2017-07-19 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use sqlLib : script “SQLite Lib” version “1.0.0″
use BridgePlus : script “BridgePlus”
–http://piyocast.com/as/archives/4731

–Notes
set aDBpath to (POSIX path of (path to library folder from user domain)) & “Containers/com.apple.Notes/Data/Library/Notes/NotesV6.storedata”
set db1Res to retTableNames(aDBpath) of me
–>  {”ZATTACHMENT”, “ZNOTEBODY”, “ZOFFLINEACTION”, “Z_PRIMARYKEY”, “Z_METADATA”, “ZFOLDER”, “Z_MODELCACHE”, “ZACCOUNT”, “ZNOTE”}

–Safari
set bDBpath to (POSIX path of (path to library folder from user domain)) & “Safari/History.db”
set db2Res to retTableNames(bDBpath) of me
–>  {”history_items”, “sqlite_sequence”, “history_visits”, “history_tombstones”, “metadata”, “history_client_versions”}

–Calendar
set cDBpath to (POSIX path of (path to library folder from user domain)) & “Calendars/Calendar Cache”
set db3Res to retTableNames(cDBpath) of me
–> {”ZATTACHMENT”, “ZATTENDEE”, “ZCHANGEREQUESTDEPENDENCY”, “ZCOMMENT”, “ZDIFF”, “ZERROR”, “ZICSELEMENTPROPERTIES”, “ZLOCATION”, “ZMESSAGECONTENTS”, “ZPERSISTENTOPERATION”, “ZRECURRENCEEXCEPTION”, “ZRECURRENCESET”, “ZSEARCHPROPERTY”, “Z_METADATA”, “Z_MODELCACHE”, “ZCALENDARITEM”, “ZNODE”, “ZSUBSCRIPTIONINFO”, “ZALARM”, “ZCALENDARUSERADDRESS”, “ZCHANGEREQUEST”, “ZDEFAULTALARMSET”, “ZMESSAGE”, “ZPUBLICATION”, “ZSHAREE”, “Z_PRIMARYKEY”}

–Get Schema Definition of each Table(Safari Histrory)
repeat with i in db2Res
  set aRes to retDBSChema(bDBpath, i)
  
log aRes
  
–> {{sql:”CREATE TABLE history_client_versions (client_version INTEGER PRIMARY KEY,last_seen REAL NOT NULL)”, rootpage:540, type:”table”, name:”history_client_versions”, tbl_name:”history_client_versions”}, …..
  
end repeat

on retTableNames(aDBpath)
  set theDb to sqlLib’s makeNewDbWith:aDBpath
  
theDb’s openReadOnly()
  
set aRes to theDb’s doQuery:“select name from sqlite_master where type = ’table’;”
  
set bRes to (current application’s SMSForder’s arrayByFlattening:aRes) as list
  
theDb’s |close|()
  
return bRes
end retTableNames

on retDBSChema(aDBpath, aTableName)
  set theDb to sqlLib’s makeNewDbWith:aDBpath
  
theDb’s openReadOnly()
  
  
set x to theDb’s underlyingFMDatabase()’s getSchema()
  
set theResult to current application’s NSMutableArray’s array()
  
  
repeat while x’s next() as boolean
    theResult’s addObject:(x’s resultDictionary())
  end repeat
  
  
theDb’s |close|()
  
return theResult as list
end retDBSChema

★Click Here to Open This Script 

2017/02/23 Myriad Tables v1.0.7がリリースされる

Shane StanleyによるAppleScript Libraries「Myriad Tables Lib」のバージョン1.0.7がリリースされました。AppleScriptから手軽に「表」インタフェースを利用可能にするものです。

table3.png

同ライブラリをサイトからダウンロードし、~/Libraries/Script Librariesフォルダに入れるとAppleScriptから使えるようになります(このフォルダが存在しない場合には作成)。

tables01.png

tables02.png

同ライブラリにはAppleScript用語辞書が用意されており、Script Editorに直接ドラッグ&ドロップではなく、いったんScript Editorの「ライブラリ」ウィンドウに登録し、リスト上の「Myriad Tables Lib」を選択した状態で「用語説明を開く」をクリックすると、用語辞書の内容を確認できます。

tables003.png

tables004_resized.png

一般的に、Excel書類やCSVなどで支給されたデータに対して、どの行のデータを処理するかをセルの選択範囲で明示的にScriptに対して指示することはよくあります。ただ、実行環境にMicrosoft Excelがすべてインストールされているわけでもありません。

Myriad Tables Libがあれば、CSVデータ中の処理範囲を選択したり、簡易的なデータ入力・確認用のGUIをAppleScriptから利用できます。

Myriad Tables Lib 1.0.7の新機能として紹介されているものを、同ライブラリ添付のサンプルコードから紹介してみましょう。

新機能:セルのダブルクリックを「OK」ボタンのクリックと等価とみなす「double click means OK」オプション

table1.png

AppleScript名:double click means OK
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows how to use the ’double click means OK’ parameter
display table with data {“One”, “Two”, “Three”, “Four”, “Five”} with title “Simple table” with prompt “You can double-click an entry rather than selecting and pressing OK” with double click means OK and empty selection allowed

–> {rows selected:{5}, values selected:{”Five”}, values returned:{”One”, “Two”, “Three”, “Four”, “Five”}, button number:1, timed out:false, final position:{978.0, 254.0, 246.0, 278.0}}

★Click Here to Open This Script 

新機能:ダイアログの表示座標を指定する「initial position」オプション

table2.png

AppleScript名:initial position
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows the use of the ’initial position’ parameter to set the dialog’s position
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table appears at the top-left of the screen” with double click means OK and empty selection allowed
modify table theTable initial position {0, 0} with alternate backgrounds
display table theTable

★Click Here to Open This Script 

initial positionでは画面上の表示座標だけでなく、サイズ{表示位置(X), 表示位置(Y), 表示幅(X),表示高さ(Y)}の指定も可能です。

AppleScript名:initial position_2
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows the use of the ’initial position’ parameter to set the dialog’s size and position
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table’s size and position have been set in the script” with double click means OK and empty selection allowed
modify table theTable initial position {100, 10, 400, 400} with alternate backgrounds
display table theTable

★Click Here to Open This Script 

新機能:ダイアログの前回表示位置&サイズを取得する「final position」

AppleScript名:final position
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
use script “Myriad Tables Lib” version “1.0.7″

– This table shows how to retrieve the final position and size of the dialog
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “Move and resize this dialog before clicking OK” with double click means OK and empty selection allowed
modify table theTable with alternate backgrounds
set theResult to display table theTable
set theBounds to final position of theResult

– This table is positioned using the results from the previous one
set theTable to make new table with data {{1.0, 1, 1.11, 1}, {-2.0, 2, 2.2, 2}, {3.11, -3, 3.11, 3}, {4.0, 4, -4.41, 4}, {5.0, 5, 5.55, -5}} column headings {“Reals”, “Integers”, “Reals”, “Integers”} with prompt “This table’s size and position should match those used last time” with double click means OK and empty selection allowed
modify table theTable initial position theBounds with alternate backgrounds
display table theTable

★Click Here to Open This Script 

2016/12/15 PDFしおり用データをNumbersから取得

PDFに「しおり」を作成する元のデータをNumbers上に記述しておくと、作成用のデータを取得・変換するAppleScriptです。構文確認および実行には、Shane Stanleyの「BridgePlus」AppleScript Libraries(フリー)のインストールを必要とします。

また、Numbersで(↓)のような書類を作成して、Numbersでオープンしていることが動作の前提条件です。

numbers_shiori.png

元のプログラムでは直接Script Editor上でレコードとして記述するのが、なかなか大変。また、親項目をタイトル文字列で記述するのも(作業時にミスりそうで)大変だったので、Numbers書類上で記述できるようにしてみたものです。

shiori.png

親項目は番号で記述するようにして、ID自体の連番の生成もAppleScriptから行い、極力作業ミスが発生しないように配慮してみました。

shiori2.png

AppleScript名:しおり用データをNumbersから取得
【コメント】 Book2_index_v2 を前提としています
– Created 2016-12-15 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use BridgePlus : script “BridgePlus”
–http://piyocast.com/as/archives/4363

set aData to getIndexRecListFromNumbers() of me
–> {{|index|:3, title:”広告”, |parent|:”"}, {|index|:4, title:”本書購入特典のご案内”, |parent|:”"},…..

–NumbersのデータからPDFに付けるしおりのデータを取得する
on getIndexRecListFromNumbers()
  tell application “Numbers”
    tell window 1
      set aWinProp to properties
    end tell
    
    
set aDoc to document of aWinProp
    
tell aDoc
      tell active sheet
        tell table 1
          set colNum to column count
          
if colNum is not equal to 4 then error “Illegal Column Numbers”
          
set rowNum to row count
          
set vList to value of every cell
        end tell
      end tell
    end tell
  end tell
  
  
–Transform 1D array to 2D array
  
load framework
  
set tdList to (current application’s SMSForder’s subarraysFrom:(vList) groupedBy:colNum |error|:(missing value)) as list
  
–> {{”ID”, “index”, “title”, “parent”}, {1.0, 3.0, “広告”, missing value}, …..
  
  
–Skip First Row
  
set td2List to rest of tdList –first itemだけスキップする
  
  
set mokujiRecords to {}
  
repeat with i in td2List
    copy i to {anID, anIND, aTITLE, aParent}
    
    
–log {anID, anIND, aTITLE, aParent}
    
if aParent is not equal to missing value then
      set bParent to contents of item 3 of (item aParent of td2List)
    else
      set bParent to “”
    end if
    
    
set tmpRec to {|index|:(contents of anIND) as integer, title:aTITLE, |parent|:bParent}
    
set the end of mokujiRecords to tmpRec
  end repeat
  
  
return mokujiRecords
  
end getIndexRecListFromNumbers

★Click Here to Open This Script 

2016/11/18 ライブラリを使って指定カレンダー(iCloud)の指定日のイベントを削除する

Shane StanleyのAppleScriptライブラリ「Calendar Lib」を利用して、EventKit経由で指定カレンダーの指定日時のイベントを削除するAppleScriptです。

構文確認(コンパイル)および実行のためには、Shane Stanleyの「CalendarLib」を~/Library/Script Librariesフォルダにインストールしておくことが必要です(作成時にはバージョン1.1.2を使用)。

昨日のCalendar.appを操作するScriptがあまりに実用的でなかったため、ライブラリを使ってOSのAPIを直接呼んでみることにしました。あらかじめ、できることはわかっていたものの、ドキュメント中に削除のサンプルが存在しておらず、ライブラリ内を調査してイベントの削除方法をみつけました。

本Script実行時にCalendar.appを起動していると、実行後3秒程度でCalendar.appの画面側でもイベントが削除されたことが確認できました。

Calendar Libではカレンダー種別を cal local/ cal cloud/ cal exchange/ cal subscriptionと明示的に指定できるため、Calendar.appを直接操作するよりも(はるかに)便利です。

AppleScript名:ライブラリを使って指定カレンダー(iCloud)の指定日のイベントを削除する
– Created 2016-11-18 17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.5″
use scripting additions
use framework “Foundation”
use calLib : script “CalendarLib EC”
–http://piyocast.com/as/archives/4322

set sDat to “2016/11/21 0:00:00″
set eDat to “2016/11/22 0:00:00″
set sDatO to date sDat
set eDatO to date eDat

set theStore to fetch store
set theCal to fetch calendar “ぴよまるソフトウェア” cal type cal cloud event store theStore – change to suit

set theEvents to fetch events starting date sDatO ending date eDatO searching cals {theCal} event store theStore

repeat with i in theEvents
  set j to contents of i
  
remove event event j event store theStore without future events
end repeat

★Click Here to Open This Script 

2016/10/25 指定ムービーを指定形式に変換する v2

指定のムービーファイルを指定形式に変換するAppleScriptの改良版です。

一晩たったら、旧石器時代から現代までタイムスリップしたぐらいの改良が加わっています(汗)。野蛮な手段でファイル拡張子からUTIを求めていたのが、フレームワーク呼び出し一発で、、、処理速度も大幅に向上しています。

AppleScript名:指定ムービーを指定形式に変換する v2
– Created 2016-10-24 by Shane Stanley
– Modified 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use scripting additions
use BridgePlus : script “BridgePlus” –Version 1.3.2以降

load framework
–http://piyocast.com/as/archives/4288

set posixPath to POSIX path of (choose file with prompt “Choose a Movie file:”)
my convertMovieAt:posixPath ToType:“AVAssetExportPresetAppleM4A” withExtension:“m4a” deleteOriginal:false

–指定のムービーを、指定の書き出しプリセットで、指定のファイル形式で、オリジナルファイルを削除するかどうか指定しつつ書き出し
on convertMovieAt:(posixPath as string) ToType:(assetTypeStr as string) withExtension:(aExt as string) deleteOriginal:(deleteFlag as boolean)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
  
set aPreset to current application’s NSString’s stringWithString:assetTypeStr
  
  
– set destination to use same path, different extension
  
set destURL to theURL’s URLByDeletingPathExtension()’s URLByAppendingPathExtension:aExt
  
set theAsset to current application’s AVAsset’s assetWithURL:theURL
  
  
– check asset can be converted
  
set allowedPresets to current application’s AVAssetExportSession’s exportPresetsCompatibleWithAsset:theAsset
  
if (allowedPresets’s containsObject:aPreset) as boolean is false then
    error “Can’t export this file as an .” & aExt & ” file.”
  end if
  
  
– set up export session
  
set fileTypeUTIstr to retFileFormatUTI(aExt) of me –ファイル拡張子からFile Type UTIを求める
  
set theSession to current application’s AVAssetExportSession’s exportSessionWithAsset:theAsset presetName:aPreset
  
theSession’s setOutputFileType:fileTypeUTIstr
  
theSession’s setOutputURL:destURL
  
  
– begin export and poll for completion
  
theSession’s exportAsynchronouslyWithCompletionHandler:(missing value)
  
repeat
    set theStatus to theSession’s status() as integer
    
if theStatus < 3 then
      delay 0.2
    else
      exit repeat
    end if
  end repeat
  
  
– throw error if it failed
  
if theStatus = (current application’s AVAssetExportSessionStatusFailed) as integer then
    error (theSession’s |error|()’s localizedDescription() as text)
  end if
  
  
– delete original if required
  
if deleteFlag then
    current application’s NSFileManager’s defaultManager()’s removeItemAtURL:theURL |error|:(missing value)
  end if
end convertMovieAt:ToType:withExtension:deleteOriginal:

on retFileFormatUTI(aExt as string)
  return (current application’s SMSForder’s UTIForExtension:aExt)
end retFileFormatUTI

★Click Here to Open This Script 

2016/10/25 ムービー系の拡張子からFile Format UTIを取得する v2, v3

ムービー系のファイルの拡張子から、File Format UTIを取得するAppleScriptの改良版です。

Shane Stanleyから指摘があって、

(1)com.apple.quicktime-movieと current application’s AVFileTypeQuickTimeMovieは同じものなので変換する必要はないよ(なんとなく、そうじゃないかとは思ってました ^ー^;;)

(2)BridgePlus v1.3.2に拡張子からUTIを求めるメソッドが用意してあるよ(!!!)

というわけで、(1)を反映させたv2、(2)まで反映させたv3を作成してみましたが、v3にいたってはたったの1行。どこかにもっとスマートな解決策が転がっていると思っていましたが、ここまでスマートになるとは(^ー^;;;

一応、試した範囲ではv1もv2もv3も実行結果は同じです。

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v2
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use framework “AppKit”
use scripting additions
–http://piyocast.com/as/archives/4287

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt)
  set theWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set valList to {“com.apple.quicktime-movie”, “public.mpeg-4″, “com.apple.m4v-video”, “com.apple.m4a-audio”, “public.3gpp”, “public.3gpp2″, “com.apple.coreaudio-format”, “com.microsoft.waveform-audio”, “public.aiff-audio”, “public.aifc-audio”, “org.3gpp.adaptive-multi-rate-audio”, “public.mp3″, “public.au-audio”, “public.ac3-audio”}
  
repeat with aUTI in valList
    if (theWorkspace’s filenameExtension:aExt isValidForType:aUTI) as boolean then
      return (current application’s NSString’s stringWithString:aUTI)
    end if
  end repeat
  
error “Invalid Constant String”
end retFileFormatUTI

★Click Here to Open This Script 

AppleScript名:ムービー系の拡張子からFile Format UTIを取得する v3
– Created 2016-10-24 by Takaaki Naganoya
– Modified 2016-10-25 by Shane Stanley
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use framework “AVFoundation”
use scripting additions
use BridgePlus : script “BridgePlus” –Version 1.3.2以降
load framework

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

set aRes to retFileFormatUTI(“mov”) of me
–>  (NSString) “com.apple.quicktime-movie”

set aRes to retFileFormatUTI(“mp4″) of me
–>  (NSString) “public.mpeg-4″

set aRes to retFileFormatUTI(“m4v”) of me
–>  (NSString) “com.apple.m4v-video”

set aRes to retFileFormatUTI(“m4a”) of me
–>  (NSString) “com.apple.m4a-audio”

set aRes to retFileFormatUTI(“3gp”) of me
–>  (NSString) “public.3gpp”

set aRes to retFileFormatUTI(“3gp2″) of me
–>  (NSString) “public.3gpp2″

set aRes to retFileFormatUTI(“caf”) of me
–>  (NSString) “com.apple.coreaudio-format”

set aRes to retFileFormatUTI(“wav”) of me
–>  (NSString) “com.microsoft.waveform-audio”

set aRes to retFileFormatUTI(“aif”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“aifc”) of me
–>  (NSString) “public.aifc-audio”

set aRes to retFileFormatUTI(“amr”) of me
–>  (NSString) “org.3gpp.adaptive-multi-rate-audio”

set aRes to retFileFormatUTI(“mp3″) of me
–>  (NSString) “public.mp3″

set aRes to retFileFormatUTI(“au”) of me
–>  (NSString) “public.au-audio”

set aRes to retFileFormatUTI(“ac3″) of me
–>  (NSString) “public.ac3-audio”

on retFileFormatUTI(aExt as string)
  return (current application’s SMSForder’s UTIForExtension:aExt)
end retFileFormatUTI

★Click Here to Open This Script 

2016/10/07 サウンド入出力を変更

Macのサウンド入出力先を変更するAppleScriptです。自分で作って使っているAppleScript Libraries「soundIO Lib」のインストールを必要とします(入っていない環境では構文確認自体を行えません)。

とくに、システム環境設定を起動したりすることはありません。すぐに実行は終了します。

本Scriptは実際のライブラリ呼び出しコードではなく、動作確認のためのコードなので、実際にはここまでしつこく設定内容の確認を行う必要はありません。ライブラリ側で切り替わったことを検証してtrue/falseを返しています。

書籍購入者で、書籍の感想を送って下さった方にsoundIO Libをプレゼントします。maro@piyocast.comまでご感想をお送りください

AppleScript名:サウンド入出力を変更
– Created 2016-10-07 by Takaaki Naganoya
– 2016 Piyomaru Software
– http://piyocast.com/as/archives/4252
use AppleScript version “2.4″
use scripting additions
use soundIO : script “soundIO Lib” version “1.0″

set targOutputDevice to “Logicool Z600″
set targIntputDevice to “Built-in Microphone”

–出力デバイス一覧に設定対象が入っているかチェック
set aList to soundIO’s getEveryAudioOutputDevice()
if targOutputDevice is not in aList then return false
–> {”Logicool Z600″, “Built-in Output”, “Mobiola Headphone”, “Mobiola Microphone”, “Soundflower (2ch)”, “Soundflower (64ch)”}

–入力デバイス一覧に設定対象が入っているかチェック
set bList to soundIO’s getEveryAudioInputDevice()
if targIntputDevice is not in bList then return false
–>  {”Built-in Microphone”, “Mobiola Headphone”, “Mobiola Microphone”, “Soundflower (2ch)”, “Soundflower (64ch)”}

–入出力デバイスを設定
set i1 to soundIO’s setAudioInuptDevice(targIntputDevice)
set o1 to soundIO’s setAudioOutuptDevice(targOutputDevice)

–サウンド入出力デバイスの変更確認
set i2 to soundIO’s getCurrentAudioInuptDevice()
set o2 to soundIO’s getCurrentAudioOutuptDevice()

set aRes to (i1 = true and o1 = true) and (i2 = targIntputDevice and o2 = targOutputDevice)
if aRes = true then
  tell current application
    display dialog “サウンド入出力を” & targOutputDevice & “に設定しました。”
  end tell
else
  tell current application
    display dialog “サウンド入出力の設定に失敗しました。”
  end tell
end if

★Click Here to Open This Script 

2016/06/18 指定フォルダ以下にあるMacDownとPages書類をソートしてPDFに書き出す

指定フォルダ以下にあるMacDownで記述したMarkdown書類と、Pages書類をすべての階層からピックアップしてファイル名でソートして、すべてデスクトップにPDFで書き出すAppleScriptです。

コンパイル(構文確認)および実行に際しては、Shane StanleyのScript Library「Bridge Plus」をインストールしておく必要があります。また、GUI Scriptingを利用しているため「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でスクリプトエディタ(アプレットとして実行する場合にはアプレットそのもの)を登録して許可しておく必要があります。

「技術書典」に出す電子ブックのフォーマットがギリギリまで決まらず、しかも縦長のスクロールさせるタイプのものにできないかとあがいていたのですが、結局iPadあたりで読むことを考慮するとiPadのリーダーの仕様にしたがう必要があります。

……あれ?(^ー^; 結局、ページめくりは発生するし、一般的な本と同じような体裁になってしまいますよ → フォーマットがPDFになりました。

Markdown書類とPages書類が混在しているフォルダ構造のトップ階層のフォルダを指定すると、Markdown書類とPages書類をピックアップし、ファイル名でそれらをソートし、順次PDFに書き出すAppleScriptを書いてみました(必要は発明のマザー!)。

book1.png

ただ、MacDownには「書類をPDFに書き出す」という機能がAppleScript側に公開されていません(T_T)。

macdown_dict.png

ないものを「ないない」と嘆いても仕方がないので、さっさとGUI Scriptingで強制的にメニュー操作することにしました。

macdown_gui.png

で、どこに? どこに保存させるのでしょう??

大丈夫! そんなときには、保存ダイアログで幾つかのフォルダに強制的に移動させるキーボードショートカットが存在しており、Command-Dは「カレントディレクトリをデスクトップに移動させる」=「保存先をデスクトップにする」働きをします。

このため、保存先を操作しづらい(不可能とはいいませんけれども)GUI Scriptingにおいて保存先を指定することが、デスクトップフォルダについては可能になっています。

Pagesの方はひじょうに素直に(GUI Scriptingなんて使わずに)PDF書き出しが可能です。

そんなわけで、時間に追い詰められながらもなんとか大量のデータ処理を行っているのでありました。書き出した大量のPDFもAppleScriptでさくっと連結できるので、非常にいい感じです。あとは、本が完成すれば、、、、

AppleScript名:指定フォルダ以下にあるMDとPagesをソートしてPDFに書き出す
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”
–use spotLight : script “spotlightLib”

property searchRes : {}

load framework

set origPath to POSIX path of (choose folder with prompt “Markdown/Pages ファイルの入っているフォルダを選択”)

set aResList to (spotlightSearch(origPath, “kMDItemKind == ’Markdown’ || kMDItemKind == ’Pages 一般書類’”) of me) as list –Caution! this parameter is *localized*

–フルパスとファイル名のペアの2D Listを作成
set newList to {}
repeat with i in aResList
  set j to contents of i
  
set aStr to (current application’s NSString’s stringWithString:j)
  
set aFileName to aStr’s lastPathComponent()
  
set the end of newList to {aStr, aFileName}
end repeat

–番号順にソート
set sortIndexes to {1} –Key Item id: begin from 0, Sort by filename
set sortOrders to {true}
set sortTypes to {“compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(newList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
if resList = {} then return –No Result

–ソートした順番にMarkdownファイル/Pages書類ファイルをオープンしてデスクトップにPDF生成してクローズ
repeat with i in newList
  copy i to {fullPath, aFileName}
  
  
set apFile to (POSIX file (fullPath as string))
  
set anAlias to apFile as alias
  
set aFileName to aFileName as string
  
  
if aFileName ends with “.md” then
    exportFromMacDown(anAlias) of me –Markdown
  else if aFileName ends with “.pages” then
    exportFromPages(anAlias) of me –Pages
  end if
end repeat

–指定のPagesファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromPages(anAlias)
  tell application “Finder”
    set aName to name of anAlias
  end tell
  
  
set dtPath to (path to desktop) as string
  
set outPath to dtPath & aName & “.pdf”
  
  
tell application “Pages”
    close every document without saving
    
open anAlias
    
export document 1 to file outPath as PDF with properties {image quality:Best}
    
close every document without saving
  end tell
end exportFromPages

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  tell application “MacDown”
    open {anAlias}
  end tell
  
  
tell current application
    delay 1 –ここの時間待ちが少ないと画像抜けが発生?
  end tell
  
macDownForceSave() of me
  
  
tell application “MacDown”
    close every document without saving
  end tell
end exportFromMacDown

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application “MacDown”
  
tell application “System Events”
    tell process “MacDown”
      – File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke “d” using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Spotlight Libの内容を引っ張り出してきた
on spotlightSearch(origPOSIXpath, aCondition)
  
  
set searchRes to {} –initialize
  
  
initiateSearchForFullPath(aCondition, origPOSIXpath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while searchRes = {}
    current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
set anObj to searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
–set anAttrList to anObj’s attributes() –”mdls” attributes
  
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  –  
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/05/11 ShangriLa Anime API V1のデータを取得する

技術書典の一般向けサイトで、参加サークルの詳細な説明文が見られるようになったので、ひととおり見てみました。すると、ノーマークだったサークルが面白いことをやっていることが分かったので、ちょっと見てみました。

ブースB-11の「秋葉原IT戦略研究所」さんが「SNSデータ解析で見る2016年アニメ界の展望」という本を出されるそうで、それだけだと何のことかわからなかったのですが、Twiter上でハッシュタグをつけてつぶやいている膨大なデータを分析してみた、という話だと理解しました。

その活動の一環として、「秋葉原IT戦略研究所」さんではRESTful APIで呼べるアニメ情報データベースを公開されており、とくに認証も何もかかっていないので気楽に呼べそうです。

そこで、実際にAppleScriptから呼んでみることにしました。実行にはShane StanleyのAppleScript Libraries「Bridge Plus」のインストールを必要とします。

まずは、このデータベースが対象にしているクール(1クール13話、1年を52週と仮定したときに1年は4クール)の情報を取得してみました。各クールが何年何月何日から何年何月何日までなのか、という情報は提供してくれないため、各自でカレンダー計算を行う必要がありそうです(4/1なのに前クールの最終回を放映していたというパターンもあるので、そのあたりどうなるのかルールが少々不明)。

AppleScript名:GET method REST API_Anime API_get cours
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/cours”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSDictionary) {7:{id:7, year:2015, cours:3}, 3:{id:3, year:2014, cours:3}, 8:{id:8, year:2015, cours:4}, 4:{id:4, year:2014, cours:4}, 9:{id:9, year:2016, cours:1}, 5:{id:5, year:2015, cours:1}, 1:{id:1, year:2014, cours:1}, 6:{id:6, year:2015, cours:2}, 10:{id:10, year:2016, cours:2}, 2:{id:2, year:2014, cours:2}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–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
  
–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 ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

次に、指定した年のアニメ作品に関する情報を取得。

AppleScript名:GET method REST API_Anime API_getInfo_in_a_year
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/” & “2016″

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSArray) {{id:281, title:”機動戦士ガンダム サンダーボルト”}, {id:282, title:”プリンス・オブ・ストライド オルタナティブ”}, {id:283, title:”無彩限のファントム・ワールド”}, {id:284, title:”ハルチカ〜ハルタとチカは青春する〜”}, {id:285, title:”ノルン+ノネット”}, {id:286, title:”アクティヴレイド −機動強襲室第八係−”}, {id:287, title:”少女たちは荒野を目指す”}, {id:288, title:”僕だけがいない街”}, {id:289, title:”おじさんとマシュマロ”}, {id:290, title:”ファンタシースターオンライン2 ジ アニメーション”}, {id:291, title:”だがしかし”}, {id:292, title:”暗殺教室(第2期)”}, {id:293, title:”ディバインゲート”}, {id:294, title:”おしえて!ギャル子ちゃん”}, {id:295, title:”石膏ボーイズ”}, {id:296, title:”霊剣山 星屑たちの宴”}, {id:297, title:”GATE 自衛隊 彼の地にて、斯く戦えり(2期)”}, {id:298, title:”昭和元禄落語心中”}, {id:299, title:”紅殻のパンドラ”}, {id:300, title:”ブブキ・ブランキ”}, {id:301, title:”ラクエンロジック”}, {id:302, title:”デュラララ!!×2 結”}, {id:303, title:”ナースウィッチ小麦ちゃんR”}, {id:304, title:”虹色デイズ”}, {id:305, title:”大家さんは思春期!”}, {id:306, title:”Dimension W”}, {id:307, title:”灰と幻想のグリムガル”}, {id:308, title:”シュヴァルツェスマーケン”}, {id:309, title:”最弱無敗の神装機竜(バハムート)”}, {id:310, title:”赤髪の白雪姫(第2期)”}, {id:311, title:”てーきゅう(第7期)”}, {id:312, title:”魔法少女なんてもういいですから。”}, {id:313, title:”蒼の彼方のフォーリズム”}, {id:314, title:”この素晴らしい世界に祝福を!”}, {id:315, title:”亜人”}, {id:316, title:”FAIRY TAIL ZERO”}, {id:317, title:”ももくり”}, {id:318, title:”この男子、魔法がお仕事です。”}, {id:319, title:”SUSHI POLICE”}, {id:320, title:”血液型くん!4″}, {id:321, title:”迷家‐マヨイガ‐”}, {id:322, title:”宇宙パトロールルル子”}, {id:323, title:”機動戦士ガンダムユニコーン RE:0096″}, {id:324, title:”影鰐-KAGEWANI-承”}, {id:325, title:”ぼのぼの”}, {id:326, title:”フューチャーカード バディファイト トリプルディー”}, {id:327, title:”逆転裁判”}, {id:328, title:”学戦都市アスタリスク 2nd SEASON”}, {id:329, title:”僕のヒーローアカデミア”}, {id:330, title:”マクロス”}, {id:331, title:”コンクリート・レボルティオ〜超人幻想〜THE LAST SONG”}, {id:332, title:”くまみこ”}, {id:333, title:”怪盗ジョーカー(シーズン3)”}, {id:334, title:”ばくおん!!”}, {id:335, title:”聖戦ケルベロス 竜刻のファタリテ”}, {id:336, title:”ハンドレッド”}, {id:337, title:”薄桜鬼〜御伽草子〜”}, {id:338, title:”ジョーカー・ゲーム”}, {id:339, title:”双星の陰陽師”}, {id:340, title:”SUPER LOVERS”}, {id:341, title:”鬼斬”}, {id:342, title:”文豪ストレイドッグス”}, {id:343, title:”あんハピ♪”}, {id:344, title:”クロムクロ”}, {id:345, title:”ネトゲの嫁は女の子じゃないと思った?”}, {id:346, title:”甲鉄城のカバネリ”}, {id:347, title:”少年メイド”}, {id:348, title:”坂本ですが?”}, {id:349, title:”田中くんはいつもけだるげ”}, {id:350, title:”キズナイーバー”}, {id:351, title:”はいふり”}, {id:352, title:”ふらいんぐうぃっち”}, {id:353, title:”とんかつDJアゲ太郎”}, {id:354, title:”三者三葉”}, {id:355, title:”うさかめ”}, {id:356, title:”マギ シンドバッドの冒険”}, {id:357, title:”Re:ゼロから始める異世界生活”}, {id:358, title:”うしおととら(第3クール)”}, {id:359, title:”ワガママハイスペック”}, {id:360, title:”ジョジョの奇妙な冒険 ダイヤモンドは砕けない”}, {id:361, title:”テラフォーマーズ リベンジ”}, {id:362, title:”プリパラ(3rdシーズン)”}, {id:363, title:”エンドライド”}, {id:364, title:”ビッグオーダー”}}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–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
  
–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 ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

最後に、年およびクール(1〜4)を指定して作品情報を取得するものです。

AppleScript名:GET method REST API_Anime API_getInfo_in_a_year_and_cour
– Created 2016-05-11 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set aYear to 2016
set aCour to 1

set reqURLStr to “http://api.moemoe.tokyo/anime/v1/master/” & (aYear as string) & “/” & (aCour as string)

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
set aRESCode to responseCode of aRes
–>  200
set aRESHeader to responseHeader of aRes
–>  {Connection:”keep-alive”, Access-Control-Allow-Origin:”*”, Content-Type:”application/json; charset=utf-8″, Content-Length:”353″, Server:”nginx/1.8.0″, Date:”Wed, 11 May 2016 01:14:49 GMT”}

return aRESTres
–>  (NSArray) {{id:281, title_short3:”", sex:0, sequel:0, created_at:”2016-01-01 23:40:06.0″, public_url:”http://gundam-tb.net/”, twitter_hash_tag:”gundam_tb”, title:”機動戦士ガンダム サンダーボルト”, updated_at:”2016-01-01 23:40:06.0″, twitter_account:”gundam_tb”, title_short1:”サンダーボルト”, title_short2:”", cours_id:9}, …….

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
–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
  
–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 ASify from (dRes’s statusCode())
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

放送されている番組の情報を提供する情報発信元としては、新聞などにテレビ番組のラテ欄情報を提供している東京ニュース通信社あるいは日刊編集センターか、EPGのメタデータを提供している(株)エムデータあたりがBtoB向け専門でやっています。BtoC向けにデータを出すかどうかは不明ですが、話をしてみるといいんじゃないでしょうか?

2016/05/02 Google APIを用いてURLを短縮してより小さいQRコードを作成

Google APIを利用して指定のURLを短縮し、QRコードを作成するAppleScriptです。

各種WebサービスをAppleScriptから呼び出して、日々便利に使っています。その中でまったく興味が湧かなかったURL短縮サービスAPI(URL Shortening API)。

たまたま、WebのURLからQRコードを作成して印刷することを検討していたときに、印刷面積を減らしたいというニーズが出てきました(TEPRAで印刷しようとしていたので)。

単純に縮小して印刷する物理的な大きさを小さくすれば、印刷できないこともないのですが、ゴミなどが付着したり経年劣化で退色した場合などに、むりやり小さく印刷するとエラーに遭遇する確率が上がってしまいます。また、解像度の低いプリンターで印刷するときには、エラー発生リスクが上がります。

そこで、長くなりがちなWebのURLを、短縮URLサービスを用いて短くしたうえでQRコード化することを思いつきました(Googleで探してみると、同様の先行事例多数 ^ー^;)。

qrcodes.png

上の図で、左がオリジナルのURLをQRコード化したもの。右がURL短縮してQRコード化したものです。URL短縮がQRコードのサイズの縮小に貢献することが見て取れます。ここで用いた元URLは、

 Original URL—-http://piyocast.com/as/archives/4067
 Shorten URL—-http://goo.gl/kT372B

クラウドストレージ上にあるファイル(Dropboxなど)やGoogle Map上の位置情報を示すURLは長くなりがちですが、いったんURL短縮サービスを経由することで、QRコードに印刷するのに抵抗感のない程度の長さの文字列に圧縮でき、たいへんけっこうなことです(Google Mapsにも短縮URL変換の機能がついていますね、、、)。

実行時には、Googleアカウントを作成してGoogle APIの利用申請を行い、API Keyを取得してAppleScript中に指定してください。また、実行にあたって実行環境にShane StanleyのAppleScript Libraries「Bridge Plus」がインストールしてあることが条件となります。

AppleScript名:POST method REST API_Google Shortener URL and make QR code
– Created 2016-05-01 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

property APIKey : “XXxxXxXXXxXxX-XxXXxXXXxxxxXXXXxXxXXxXXX” –Google API Key

set aLongURL to “http://piyocast.com/as/archives/4067″ –Target Long URL
set aShortURL to shortenURL(aLongURL) of me
if aShortURL = “” then return –Error

set dtPath to POSIX path of (path to desktop) & ((current application’s NSUUID’s UUID()’s UUIDString())’s stringByAppendingString:“.png”)
set aRes to writeQRimageAsPNG(aShortURL, dtPath) of me

–指定パスにQRコードをPNG画像で出力
on writeQRimageAsPNG(aTargStr, aTargPath)
  set aStr to current application’s NSString’s stringWithString:aTargStr
  
set strData to aStr’s dataUsingEncoding:(current application’s NSISOLatin1StringEncoding)
  
set qrFilter to current application’s CIFilter’s filterWithName:“CIQRCodeGenerator”
  
qrFilter’s setValue:strData forKey:“inputMessage”
  
qrFilter’s setValue:“H” forKey:“inputCorrectionLevel”
  
set anImage to qrFilter’s outputImage()
  
saveNSImageAtPathAsPNG(anImage, aTargPath) of me
end writeQRimageAsPNG

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –成功ならtrue、失敗ならfalseが返る
end saveNSImageAtPathAsPNG

–与えられたURL文字列を短縮する
on shortenURL(aLongURL)
  set myAPIKey to my APIKey
  
set reqURLStr to “https://www.googleapis.com/urlshortener/v1/url?key=” & myAPIKey
  
set aRec to {longUrl:aLongURL}
  
set aRes to callRestPOSTAPIAndParseResults(reqURLStr, aRec) of me
  
  
set aRESTres to json of aRes
  
set aRESCode to responseCode of aRes
  
set aRESHeader to responseHeader of aRes
  
  
if aRESCode is not equal to 200 then return “”
  
set aShort to (aRESTres’s valueForKey:“id”) as string
  
return aShort
end shortenURL

–POST methodのREST APIを呼ぶ
on callRestPOSTAPIAndParseResults(aURL, aPostData)
  
  
load framework
  
  
–Request  
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
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:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
–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 ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResults

★Click Here to Open This Script 

2016/03/13 Google Translate APIでサポート言語一覧を指定言語で取得する

Google Translate API(REST API)を呼び出して、翻訳可能な言語の名称一覧を指定言語(Japanese)で取得するAppleScriptです。

実行するには、Shane StanleyのBridgePlus Script Libraryを実行するMacの~/Library/Script Librariesにインストールし、GoogleのAPI利用登録を行ってAPI Keyを取得しておく必要があります。ご自分のAPI Keyを下記リストのmyAPIKeyに代入(リスト中では伏字)してから実行してください。

実行結果から、Google Translate APIが104の言語をサポートしていることがわかります。

この程度の問い合わせなら問題なく実行できているのですが、いざ翻訳させようとするとまだクリアーできていない困難が(汗) Googleのサービス側のログとかを見られると、どこで間違っているのか分かりそうですが、、、、というか、そういうサービス自体が存在していそうな、、、、

AppleScript名:GET method REST API_Google Translate API サポート言語一覧を取得
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” –https://www.macosxautomation.com/applescript/apps/BridgePlus.html

set myAPIKey to “XXxxXxXXXxXxX-XxXXxXXXxxxxXXXXxXxXXxXXX”
set reqURLStr to “https://www.googleapis.com/language/translate/v2/languages?key=” & myAPIKey & “&target=ja”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSDictionary) {error:{message:”Bad Request”, errors:{{reason:”keyInvalid”, message:”Bad Request”, domain:”usageLimits”}}, code:400}}–エラー時

–>  (NSDictionary) {data:{languages:{{name:”アイスランド語”, language:”is”}…… {name:”英語”, language:”en”}, {name:”韓国語”, language:”ko”}, {name:”中国語(簡体)”, language:”zh”}, {name:”中国語(繁体)”, language:”zh-TW”}, {name:”日本語”, language:”ja”}}}}

–return (aRESTres’s valueForKeyPath:”data.languages.name”)’s |count|()
–>  104

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=UTF-8″, alt-svc:”quic=\”:443\”; ma=2592000; v=\”31,30,29,28,27,26,25\”", alternate-protocol:”443:quic,p=1″, Content-Encoding:”gzip”, Server:”GSE”, x-xss-protection:”1; mode=block”, Expires:”Sun, 13 Mar 2016 00:45:39 GMT”, Cache-Control:”private, max-age=0″, Date:”Sun, 13 Mar 2016 00:45:39 GMT”, Content-Length:”132″, x-content-type-options:”nosniff”, x-frame-options:”SAMEORIGIN”, Vary:”Origin, X-Origin”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
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
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

2016/03/06 REST APIに対してGET、POST、PUT、DELETEのmethodを呼び出す

AppleScriptでWeb上のREST APIを呼び出すじっけんです。Web上にあるREST APIの実験用サービスJSONPlaceholderをAppleScriptから呼び出して各種メソッド呼び出しの実証を行ってみました。

できてしまえばたいしたことはありませんが、地道に調査しておかないと今日明日でいきなりやれと言われても困ってしまいます。

実際に、NTTdocomoの形態素解析のREST APIをmethod=POSTでAppleScriptから呼び出してみたところ、

–> (NSDictionary) {request_id:”record001″, word_list:{{{”私”}, {”の”}, {”名前”}, {”は”}, {” ”}, {”長野”}, {”谷”}, {”です”}, {”。”}}}, info_filter:”form”}

となりました(words ofの実行結果と変わり映えしない、、、、)。固有名詞としてあらかじめ人名を登録しておきたいケースがほとんどなので、ちょっとこれだと使えない感じではあります。ただ、実際に稼働しているAPIを呼べることは実証できました。

AppleScript名:GET method REST API
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”

set aRes to callRestGETAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSArray) {{body:”quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto”, id:1, title:”sunt aut facere repellat provident occaecati excepturi optio reprehenderit”, userId:1},…

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:27:26 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”27520″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”6b80-uPwhAkDat3Fl5TugzmyYpQ\”", Vary:”Origin”}

–GET methodのREST APIを呼ぶ
on callRestGETAPIAndParseResults(aURL)
  load framework
  
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
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestGETAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:POST method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts”
set aPostData to {title:“foo”, body:“bar”, userId:1}

set aRes to callRestPOSTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”bar”, id:101, title:”foo”, userId:1}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–POST methodのREST APIを呼ぶ
on callRestPOSTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“POST”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
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
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPOSTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:PUT method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″
set aPostData to {title:“ぴよまるソフトウェア”, body:“PUTのじっけん”, userId:1}
–set aPostData to {title:”foo”, body:”bar”, userId:1}

set aRes to callRestPUTAPIAndParseResults(reqURLStr, aPostData) of me

set aRESTres to json of aRes
–>  (NSDictionary) {body:”PUTのじっけん”, id:1, title:”ぴよまるソフトウェア”, userId:1}
return aRESTres
set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:47:56 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”65″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”41-DL1IzEbjanDFwm6hF2BfJw\”", Vary:”Origin, X-HTTP-Method-Override”}

–PUT methodのREST APIを呼ぶ
on callRestPUTAPIAndParseResults(aURL, aPostData)
  load framework
  
set dataJson to current application’s NSJSONSerialization’s dataWithJSONObject:aPostData options:0 |error|:(missing value)
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
  
aRequest’s setHTTPMethod:“PUT”
  
aRequest’s setHTTPBody:dataJson
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Accept”
  
aRequest’s setValue:“application/json” forHTTPHeaderField:“Content-Type”
  
  
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
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestPUTAPIAndParseResults

★Click Here to Open This Script 

AppleScript名:DELETE method REST API
– Created 2016-03-06 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://jsonplaceholder.typicode.com/posts/1″

set aRes to callRestDELETEAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes
–>  (NSDictionary) {}

set aRESCode to responseCode of aRes
–>  200

set aRESHeader to responseHeader of aRes
–>  {Content-Type:”application/json; charset=utf-8″, Pragma:”no-cache”, X-Powered-By:”Express”, Via:”1.1 vegur”, Server:”Cowboy”, Expires:”-1″, Cache-Control:”no-cache”, Date:”Sun, 06 Mar 2016 07:53:51 GMT”, Access-Control-Allow-Credentials:”true”, Content-Length:”2″, Connection:”keep-alive”, X-Content-Type-Options:”nosniff”, Etag:”W/\”2-mZFLkyvTelC5g8XnyQrpOw\”", Vary:”Origin”}

–DELETE methodのREST APIを呼ぶ
on callRestDELETEAPIAndParseResults(aURL)
  load framework
  
set aRequest to current application’s NSMutableURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
set respF to false
  
  
aRequest’s setHTTPMethod:“DELETE”
  
  
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
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestDELETEAPIAndParseResults

★Click Here to Open This Script 

2016/03/06 REST APIを呼ぶじっけん v2

Web上のREST APIのサービスを呼び出すAppleScriptの改良版です。

v1ではただREST APIを呼んで、結果をNSStringで取得するだけでしたが、各種仕様を確認してJSON文字列とみなしNSJSONSerializationでparseしてNSDictionaryなりNSDictionary in NSArrayにして返します。

REST APIの返り値についてはXML/JSONのどちらを用いるかとくに規約はないようですが、一般的にはJSONとのことなのでJSONとみなしてparse。また、parseした結果をAppleScriptのオブジェクトにいったん変換してしまうと、構造上データを取り出せなくなってしまうケースがあるため、Cocoaオブジェクトの状態のままで返すようにしてみました(json of aRes)。

本ScriptはMethod=GETで呼んでいる超簡単なAPIの呼び出し例ですが、その他のケース(POST、PUT、DELETE、HEAD、OPTIONS、PATCH、COPY、SEARCH)についても呼び出し方を調べておきたいところです。

AppleScript名:REST APIを呼ぶじっけん v2
– Created 2016-03-03 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

set reqURLStr to “http://demos.xojo.com/EEWS/index.cgi/GetAllCustomers”
–set reqURLStr to “https://api.github.com/repos/mmattozzi/cocoa-rest-client/releases”

set aRes to callRestAPIAndParseResults(reqURLStr) of me

set aRESTres to json of aRes –ASのRecordに変換してしまうと、あとで値が取り出せなくなるケースがあったので、NSDictionaryのまま返している
–>  (NSDictionary) {GetAllCustomers:{10044:{Zip:”63101″, City:”St. Louis”, FirstName:”Beatrice”, LastName:”Fulton”, State:”MO”}, …

set aRESCode to responseCode of aRes
–>  404

set aRESHeader to responseHeader of aRes
–>  {Content-Encoding:”gzip”, Content-Type:”text/html; charset=UTF-8″, Content-Length:”5376″, Connection:”close”, Server:”Apache/2.2.15 (CentOS)”, Date:”Sat, 05 Mar 2016 15:37:26 GMT”}

on callRestAPIAndParseResults(aURL)
  
  
load framework
  
  
set aRequest to current application’s NSURLRequest’s requestWithURL:(current application’s |NSURL|’s URLWithString:aURL)
  
set respF to false
  
set aRes to current application’s NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value)
  
set resList to aRes as list
  
–log {length of resList}
  
–> {2}
  
  
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)
  
–>  (NSString) “{”GetAllCustomers”:{”10174″:{”FirstName”:”Abdul”,”LastName”:”Mcconnell”,”City”:”Colorado Springs”,”State”:”CO”,”Zip”:”80935″},….
  
  
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)
  
–set cRes to ASify from aJsonDict –json
  
  
–Get Response Code
  
set dRes to contents of second item of resList
  
set resCode to ASify from (dRes’s statusCode())
  
–>  404
  
  
–Get Response Header
  
set resHeaders to ASify from (dRes’s allHeaderFields())
  
–>  (NSDictionary) {Content-Encoding:”gzip”, Content-Type:”text/html; charset=UTF-8″, Content-Length:”5376″, Connection:”close”, Server:”Apache/2.2.15 (CentOS)”, Date:”Thu, 03 Mar 2016 13:44:22 GMT”}
  
  
return {json:aJsonDict, responseCode:resCode, responseHeader:resHeaders}
  
end callRestAPIAndParseResults

★Click Here to Open This Script 

2016/02/13 Myriad Tables Libによる表インタフェースの表示

Shane StanleyによるAppleScript Libraries「Myriad Tables Lib」のバージョン1.0.0を試してみました。

同ライブラリをダウンロードして、「Myriad Tables Lib.scptd」を~/Libraries/Script Libraries」フォルダに入れると利用可能になります。

以前からいろいろTable ViewをAppleScript上で表示させたりしていましたが、それのデラックス版です。同ライブラリにはAppleScript用語辞書やドキュメントもついているため、手軽に表インタフェースを表示できます。

table1.png

table3.png
▲ヘッダー行をクリックすると当該セルをキーとしたソートを行う

table4.png
▲ヘッダー行を再度クリックすると当該セルをキーとした逆順ソートを行う

table5.png
▲popup menuの表示も可能

table6.png
▲選択+入力が可能なcombo boxの表示も可能

AppleScriptによるワークフローにこうした表インタフェースをつけることの意味は、処理前のデータの確認(CSVファイルなど)、処理範囲の指定(選択範囲のみ処理するとか)、処理後の結果確認などを手軽に行えるというあたりにあります。

# テーブル内容のしぼりこみ表示が行えるとなおよいでしょう

データ処理を行う対象を指定するためだけにExcelが必要、といった話がよくありましたが、こうしたGUI部品をAppleScriptから手軽に(Xcodeを使わずに)必要な部分だけ呼び出せることにはメリットがあります。

table2.png
▲フルオプション指定時

いろいろとオプションを指定すると、行番号や選択範囲の削除/新規作成、カスタムビューの追加表示(accessory view)なども行えます。accessory viewについては、とりあえずNSImageViewを表示してみましたが、これに「飾り」以上の意味づけがあるのかどうかが気になります。

同梱のSampleを見てみたら、オプションのチェックボックスの値を取得していました。そういう使い方ができるもよう。ただし、accessory viewに指定できるビューの最大サイズに制限があるため、ここに巨大なWebViewを表示するとかMKMapViewを表示するのは苦しい。

この表インタフェース上ですべてのデータ入力を行ってもらうといった使い方は向いていないので(不可能ではないものの、途中でScriptがクラッシュしたときに途中のworkファイルが保存されていない)、あくまで内容確認、処理範囲選択などの用途に活用するとよいのではないでしょうか。

AppleScript名:かんたんなテーブルの表示
– Created 2016-02-07 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use script “Myriad Tables Lib”

set x to {a:1.4} – 未サポートクラス(record)は表示できないが、無視される
set theHeads to {“名”, “姓”, “インデックス”, “チェックボックス”, “スコア”, “日付”} –ヘッダー
set theDate to current date

set someData to {{“長野谷”, “隆昌”, 1, true, 10000, theDate, x}, ¬
  {“長野谷”, “ぴよまる”, 2, true, -13.5, theDate, x}, ¬
  {
“長野谷”, “ぴよこ”, 3, false, 9.0, theDate + 40000, x}, ¬
  {
“長野谷”, “ぴよぴよ”, 4, false, 1.23456789E+7, theDate + 50000, x}, ¬
  {
“長野谷”, “ぴよきち”, 5, true, 133.4567, theDate + 30000, x}, ¬
  {
“長野谷”, “ぴよまろ”, 6, false, 22.0, theDate + 60000, x}}

–テーブルの各列のデータ表示形式はこのテンプレートをもとにしている
set theTemplate to {“”, “”, 1, true, 1, current date, missing value}

–Step1- 必須操作。最初にデータを作成する必要がある
set myTable to make new table with data someData ¬
  with title “テーブルサンプル” column headings theHeads ¬
  
with prompt “複数行選択可。 すべてのデータは編集可能です。” editable columns {} ¬
  
row template theTemplate ¬
  
with multiple selections allowed and empty selection allowed

–Step 2-テーブルの表示オプション(ハイライト表示、枠線表示など)。本Stepは省略可
modify table myTable highlighted rows {} ¬
  grid style grid both dashed between rows ¬
  
OK button name “OK” with OK button is default

–Step 3テーブル表示を行う
set theResult to (display table myTable)
–>  {rows selected:{1}, values selected:{{”長野谷”, “隆昌”, 1, true, 10000, date “2016年2月8日月曜日 18:13:07″, {a:1.4}}}, values returned:{{”長野谷”, “隆昌”, 1, true, 10000, date “2016年2月8日月曜日 18:13:07″, {a:1.4}}, {”長野谷”, “ぴよまる”, 2, true, -13.5, date “2016年2月8日月曜日 18:13:07″, {a:1.4}}, {”長野谷”, “ぴよこ”, 3, false, 9.0, date “2016年2月9日火曜日 5:19:47″, {a:1.4}}, {”長野谷”, “ぴよぴよ”, 4, false, 1.23456789E+7, date “2016年2月9日火曜日 8:06:27″, {a:1.4}}, {”長野谷”, “ぴよきち”, 5, true, 133.4567, date “2016年2月9日火曜日 2:33:07″, {a:1.4}}, {”長野谷”, “ぴよまろ”, 6, false, 22.0, date “2016年2月9日火曜日 10:53:07″, {a:1.4}}}, button number:1, gave up:false}

★Click Here to Open This Script 

2016/02/09 指定URLのJSONをダウンロードしてRecordに変換 v2.1

指定URLのJSONデータを取得して、結果をrecordに変換するAppleScriptです。AppleScript Libraries「BridgePlus」を利用するように変更してみました。

AppleScript名:指定URLのJSONをダウンロードしてRecordに変換 v2.1
– Created 2016-02-06 by Takaaki Naganoya
– Modified 2016-02-08 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

–東京電力電力供給状況API JSON URL(指定された日時の電力使用状況を返す)
set a to downloadAndParseJSON(“http://tepco-usage-api.appspot.com/2016/2/6/19.json”) of me
–>  {forecast_peak_period:18, forecast_peak_usage:3670, capacity_updated:”2012-02-05 08:30:00″, usage:3720, forecast_peak_updated:”2012-02-05 08:30:00″, day:6, usage_updated:”2016-02-06 11:05:05″, capacity:4484, saving:false, year:2016, month:2, capacity_peak_period:18, forecast:0, hour:19, entryfor:”2016-02-06 10:00:00″}

–東京電力電力供給状況API JSON URL(指定された日の毎時の電力使用状況を、配列として返す)
set b to downloadAndParseJSON(“http://tepco-usage-api.appspot.com/2016/2/6.json”) of me
–>  {{forecast_peak_period:18, forecast_peak_usage:3670, capacity_updated:”2012-02-05 08:30:00″, usage:3081, forecast_peak_updated:”2012-02-05 08:30:00″, day:6, usage_updated:”2016-02-05 16:05:06″, capacity:4484, saving:false, year:2016, month:2, capacity_peak_period:18, forecast:0, hour:0, entryfor:”2016-02-05 15:00:00″}…….{forecast_peak_period:18, forecast_peak_usage:3670, capacity_updated:”2012-02-05 08:30:00″, usage:3645, forecast_peak_updated:”2012-02-05 08:30:00″, day:6, usage_updated:”2016-02-06 12:05:07″, capacity:4484, saving:false, year:2016, month:2, capacity_peak_period:18, forecast:0, hour:20, entryfor:”2016-02-06 11:00:00″}}

–東京電力電力供給状況API JSON URL(指定された月の毎日毎時の電力使用状況を、配列として返す)
set c to downloadAndParseJSON(“http://tepco-usage-api.appspot.com/2016/2.json”) of me
–>  {{forecast_peak_period:18, forecast_peak_usage:4260, capacity_updated:”2012-01-31 23:30:00″, usage:2901, forecast_peak_updated:”2012-01-31 23:30:00″, day:1, usage_updated:”2016-01-31 16:05:04″, capacity:4641, saving:false, year:2016, month:2, capacity_peak_period:18, forecast:0, hour:0, entryfor:”2016-01-31 15:00:00″}……{forecast_peak_period:18, forecast_peak_usage:3670, capacity_updated:”2012-02-05 08:30:00″, usage:3645, forecast_peak_updated:”2012-02-05 08:30:00″, day:6, usage_updated:”2016-02-06 12:05:07″, capacity:4484, saving:false, year:2016, month:2, capacity_peak_period:18, forecast:0, hour:20, entryfor:”2016-02-06 11:00:00″}}

on downloadAndParseJSON(aURL)
  set aRes to downloadDataFromWeb(aURL, 30) of me
  
if aRes = false then return false –download error
  
  
set jsonString to current application’s NSString’s stringWithString:aRes
  
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)
  
  
set bRes to ASify from aJsonDict
  
return bRes
end downloadAndParseJSON

on downloadDataFromWeb(aURL, timeOutSec)
  set aURL to current application’s |NSURL|’s alloc()’s initWithString:aURL
  
set aReq to current application’s NSURLRequest’s alloc()’s initWithURL:aURL cachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeOutSec
  
set urlRes to (current application’s NSURLConnection’s sendSynchronousRequest:aReq returningResponse:(missing value) |error|:(missing value))
  
  
if urlRes = missing value then
    return false –Download Error
  else
    set aVal to (current application’s NSString’s alloc()’s initWithData:urlRes encoding:(current application’s NSUTF8StringEncoding))
    
return aVal
  end if
end downloadDataFromWeb

★Click Here to Open This Script 

2015/12/08 都道府県リストから隣接都道府県を含む該当のコードを抽出する

都道府県コードを指定すると、「当該都道府県」+「隣接都道府県」のコードリストを抽出するAppleScriptです。

休みの日に、

「全国の『戦場の絆』が導入されているゲーセンの最寄駅までの距離をすべて計算し、一番辺鄙(へんぴ)な場所にあるゲーセンを算出しよう!」

と思い立ち(余計なお世話)、日本全国の駅のリスト(9,000件以上)を「駅データ.jp」から取得し、全国のゲーセンの住所(から算出した緯度、経度情報)と距離を測って最寄り駅までの距離を求めてみました。

farestgamecenter.png
▲最寄駅が最も遠いゲームセンター「サンゲームス鹿屋」。最寄駅まで23km。ただし、地元の人たちは車で移動しているのでまったく問題ないことが航空写真からもわかる

最初に組んだバージョンは、780箇所ぐらいのゲーセンと日本全国の駅(9,000件以上)の距離をすべて計算する仕様になっていたので、距離計算にCore i7 2.6GHzのMacBook Pro Retinaでも90分ぐらいかかりました(さすがに700万回距離計算したので)。手っ取り早くプログラムを組むことを重視したので、膨大な計算時間も仕方ないところですが、これではとうてい実用的とはいえません。

free_034_005.png

何も東京のド真ん中にある地点と北海道の最果てにある駅の距離を計測しなくても、そこが「最寄駅」ではないことぐらいは明白なので(ただし、用途による)、東京+隣接する都道府県だけに検索範囲をしぼってあげることで大幅な計算の高速化が行えるだろう、とあたりをつけ、47都道府県の隣接都道府県のテーブル(これ)を作ってみました。

次に、このテーブルを用いて「ゲーセンの所在する県+まわりの都道府県(隣接都道府県)」だけの距離計算を行うようにして、実際に圧倒的に演算量を減らすことに成功。わずか10分強で計算完了しました。

さらに、「毎回駅データの抽出を行わずに、県ごとにループして最初だけ抽出処理」するようにしたら、5分ぐらいで計算が完了。当初の90分から大幅な高速化を行うことができました。

最寄駅を求める、といったぐらいの演算であれば、こうして工夫することで計算量を大幅に減らせるので便利です。

目下、かんたんに複数CPUコアに処理を割り振る並列処理化AppleScriptを整備してあり、さまざまな並列処理タスクを実行した経験からいえば・・・4コア8スレッドのCore i7のMacBook Proで並列処理すれば、シングルコアでの実行時間(5分)の半分程度(2〜3分)の処理時間にはなるものと予想されます。

それよりも多いコア数(Mac Proなど)で並列処理実行した場合の性能は不明ですが、とくにノート型では複数コアで並列処理した際に、コアの温度上昇による処理能力の全体的な低下(サーマルスロットリング)が顕著であり、期待値どおりの処理性能を引き出すことは困難です(システム全体では処理能力に余裕があっても、負荷を増やしてすべてを埋め尽くすのは難しい)。

このため、デスクトップ型用のマルチコアCPUで処理を行うと、ノート型用のCPUよりも顕著な処理性能の向上が期待できます(持ってないので、やったことがないですけれども)。

用途によっては、長崎県と大分県は陸路でつながっているので、隣接県として扱ってもよいかもしれないですし、鹿児島県の一部は沖縄県と近いので隣接県として扱っておく必要があるかもしれません。そのあたりは「用途次第」でしょう。さらに、北海道新幹線の開業によって、北海道と青森県が隣接している、とみなすことも可能に。

AppleScript名:都道府県リストから隣接都道府県を含む該当のコードを抽出する
– Created 2015-12-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

property prefList : {{prefCode:1, prefName:“北海道”, neighbors:{}}, {prefCode:2, prefName:“青森県”, neighbors:{3, 5}}, {prefCode:3, prefName:“岩手県”, neighbors:{2, 4, 5}}, {prefCode:4, prefName:“宮城県”, neighbors:{3, 5, 6, 7}}, {prefCode:5, prefName:“秋田県”, neighbors:{2, 3, 4, 6}}, {prefCode:6, prefName:“山形県”, neighbors:{3, 4, 5, 7, 15}}, {prefCode:7, prefName:“福島県”, neighbors:{4, 6, 8, 9, 10, 15}}, {prefCode:8, prefName:“茨城県”, neighbors:{7, 9, 10, 11, 12}}, {prefCode:9, prefName:“栃木県”, neighbors:{7, 8, 10, 11, 12}}, {prefCode:10, prefName:“群馬県”, neighbors:{7, 9, 11, 15, 20}}, {prefCode:11, prefName:“埼玉県”, neighbors:{8, 9, 10, 12, 13, 19, 20}}, {prefCode:12, prefName:“千葉県”, neighbors:{8, 11, 13}}, {prefCode:13, prefName:“東京都”, neighbors:{11, 12, 19, 14}}, {prefCode:14, prefName:“神奈川県”, neighbors:{13, 19, 22}}, {prefCode:15, prefName:“新潟県”, neighbors:{6, 7, 10, 16, 20}}, {prefCode:16, prefName:“富山県”, neighbors:{15, 17, 20, 21}}, {prefCode:17, prefName:“石川県”, neighbors:{16, 18, 21}}, {prefCode:18, prefName:“福井県”, neighbors:{17, 21, 25, 26}}, {prefCode:19, prefName:“山梨県”, neighbors:{11, 13, 14, 20, 22}}, {prefCode:20, prefName:“長野県”, neighbors:{10, 11, 15, 16, 19, 21, 22, 23}}, {prefCode:21, prefName:“岐阜県”, neighbors:{16, 17, 18, 20, 23, 24, 25}}, {prefCode:22, prefName:“静岡県”, neighbors:{14, 19, 20, 23}}, {prefCode:23, prefName:“愛知県”, neighbors:{20, 21, 22, 24}}, {prefCode:24, prefName:“三重県”, neighbors:{21, 23, 25, 26, 29}}, {prefCode:25, prefName:“滋賀県”, neighbors:{18, 21, 24, 26}}, {prefCode:26, prefName:“京都府”, neighbors:{18, 24, 25, 27, 28, 29}}, {prefCode:27, prefName:“大阪府”, neighbors:{26, 29, 28, 30}}, {prefCode:28, prefName:“兵庫県”, neighbors:{26, 27, 31, 33}}, {prefCode:29, prefName:“奈良県”, neighbors:{24, 25, 26, 27, 30}}, {prefCode:30, prefName:“和歌山県”, neighbors:{24, 27, 29}}, {prefCode:31, prefName:“鳥取県”, neighbors:{28, 33, 32, 34}}, {prefCode:32, prefName:“島根県”, neighbors:{31, 34, 35}}, {prefCode:33, prefName:“岡山県”, neighbors:{28, 31, 34}}, {prefCode:34, prefName:“広島県”, neighbors:{33, 31, 32, 35}}, {prefCode:35, prefName:“山口県”, neighbors:{32, 34}}, {prefCode:36, prefName:“徳島県”, neighbors:{37, 39}}, {prefCode:37, prefName:“香川県”, neighbors:{36, 38, 39}}, {prefCode:38, prefName:“愛媛県”, neighbors:{37, 39}}, {prefCode:39, prefName:“高知県”, neighbors:{36, 37, 38}}, {prefCode:40, prefName:“福岡県”, neighbors:{44, 43, 41}}, {prefCode:41, prefName:“佐賀県”, neighbors:{40, 42}}, {prefCode:42, prefName:“長崎県”, neighbors:{41}}, {prefCode:43, prefName:“熊本県”, neighbors:{40, 42, 44, 45, 46}}, {prefCode:44, prefName:“大分県”, neighbors:{40, 43, 45}}, {prefCode:45, prefName:“宮崎県”, neighbors:{43, 44, 46}}, {prefCode:46, prefName:“鹿児島県”, neighbors:{43, 45}}, {prefCode:47, prefName:“沖縄県”, neighbors:{}}}

set aPref to 13 –Tokyo
set aRes to my filterRecListByLabel2(prefList, “prefCode == [c]%@”, {aPref})
set targList to neighbors of aRes & aPref
–>  {11, 12, 19, 14, 13}

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離)し、1つのアイテムだけを返す
on filterRecListByLabel2(aRecList as list, aPredicate as string, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:aParam
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
set bList to ASify from filteredArray as list
  
set cList to first item of bList
  
return cList
end filterRecListByLabel2

★Click Here to Open This Script 

2015/12/08 データリストに合致するものを抽出

レコードのリストから、指定フィールドに指定した複数の値に該当するレコードを抽出するAppleScriptです。

NSPredicateを用いて、さまざまな凝った抽出条件が指定できますが、”IN”演算子であとにリストを指定するやり方が面倒だったので、NSCompoundPredicateにOR条件で複数条件を合成して検索するようにしてみました。

AppleScript名:データリストに合致するものを抽出
– Created 2015-12-06 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use BridgePlus : script "BridgePlus"

set prefList to {{station_name_k:"", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", post:"040-0063", pref_cd:"1"}, {station_name_k:"", lon:"140.733539", add:"函館市亀田本町", station_cd:"1110102", lat:"41.803557", e_sort:"1110102", close_ymd:"", station_g_cd:"1110102", station_name:"五稜郭", e_status:"0", line_cd:"11101", open_ymd:"", station_name_r:"", post:"041-0813", pref_cd:"2"}}

set aRes to my filterDictArrayByLabel3(prefList, "pref_cd == %@", {"1"})
–>  {{post:"040-0063", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", pref_cd:"1", station_name_k:""}}

set bRes to my filterDictArrayByLabel3(prefList, "pref_cd == %@", {"1", "2"})
–>  {{post:"040-0063", lon:"140.726413", add:"北海道函館市若松町12-13", station_cd:"1110101", lat:"41.773709", e_sort:"1110101", close_ymd:"", station_g_cd:"1110101", station_name:"函館", e_status:"0", line_cd:"11101", open_ymd:"1902-12-10", station_name_r:"", pref_cd:"1", station_name_k:""}, {post:"041-0813", lon:"140.733539", add:"函館市亀田本町", station_cd:"1110102", lat:"41.803557", e_sort:"1110102", close_ymd:"", station_g_cd:"1110102", station_name:"五稜郭", e_status:"0", line_cd:"11101", open_ymd:"", station_name_r:"", pref_cd:"2", station_name_k:""}}

–リストに入れたレコードを、指定の属性ラベルの値で抽出(predicateとパラメータを分離、複数条件をORでつなぐ)
on filterDictArrayByLabel3(aList, aPredicate as string, aParam)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set predList to {}
  
repeat with i in aParam
    set j to i as text
    
set the end of predList to (current application’s NSPredicate’s predicateWithFormat:aPredicate argumentArray:{j})
  end repeat
  
set pred to current application’s NSCompoundPredicate’s orPredicateWithSubpredicates:predList
  
set filteredArray to aArray’s filteredArrayUsingPredicate:pred
  
set bList to ASify from filteredArray as list
  
return bList
end filterDictArrayByLabel3

★Click Here to Open This Script 

2015/10/28 Keynote 6.6のじっけん(2)〜放射状にラインを引く

Keynote 6.6でドキュメント上に放射状にラインを引くじっけんです。手作業ではさすがにかったるい内容ですが、AppleScriptからであれば「やってみようかな」という気になる内容です。

実行にはShane StanleyのAppleScriptライブラリ「BridgePlus」をあらかじめインストールしておく必要があります。

kn1.png

(少々ごまかしつつも)さっくりできました。拡大するとこんなかんじです。

実用性はひたすらありませんが、こういう操作ができるようになったのかと思うと感慨深いものがあります。

kn2.png

AppleScript名:Keynote 6.6で放射状にラインを引く
– Created 2015-10-27 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use bPlus : script “BridgePlus”

set aNum to 250

load framework

tell application “Keynote”
  –新規ドキュメント作成
  
set aDoc to (make new document with properties {document theme:theme “ホワイト”, width:1024, height:768})
  
–set aDoc to (make new document with properties {document theme:theme “White”, width:1024, height:768})
  
tell aDoc
    set aHeight to height
    
set aWidth to width
    
    
set aCenterW to aWidth / 2
    
set aCenterH to aHeight / 2
    
    
set aMas to master slide “空白”
    
–set aMas to master slide “Blank”
    
    
tell slide 1
      set base slide to aMas
      
      
repeat with i from 1 to 130 by 6
        set {x, y} to calcSinCos(i, aCenterW, aCenterH, aNum) of me
        
make new line with properties {start point:{aCenterW, aCenterH}, end point:{x, y}, rotation:i}
      end repeat
      
    end tell
  end tell
end tell

on calcSinCos(i, aCenterW, aCenterH, aNum)
  tell current application
    set aSinNum to (current application’s SMSForder’s sinValueOf:i)
    
set aCosNum to (current application’s SMSForder’s cosValueOf:i)
    
set x to ((aNum * (aSinNum as real))) + aCenterW
    
set y to (aNum * (aCosNum as real)) + aCenterH
    
return {x, y}
  end tell
end calcSinCos

★Click Here to Open This Script 

2015/09/29 BridgePlus 1.2のframeworkの新機能テスト(1)

Shane StanleyのASObjCExtras.frameworkがAppleScript Library「BridgePlus」に進化するにあたって、同ライブラリのバンドル内にフレームワークが内包される形式に移行しました。

「フレームワークを直接(AppleScriptObjCになじみのない)scripterに使ってもらうのは敷居が高すぎる」とShaneが判断し、ラッパーを介することで使いやすさを増したのが「BridgePlus」である、といえます。BridgePlusではCocoaの値を返すことを極力避け、listやtextなどのScripterになじみ深いデータ型にかならず変換して値を返すように細心の注意が払われています。型変換のためにオーバーヘッドが発生しても、とっつきやすさを優先させたわけです。

ただ、逆をいえばBrdgePlusの本体は内包されているフレームワークであって、Script Libraryはその入れ物(+呼び出し用のハンドラ群+AppleScript用語辞書)にすぎません。

BridgePlusに移行してから、内部のframeworkの機能についてはノーマークだったのですが、先日のIM関連情報取得や状態設定にみられるような「お得な機能」をチェックしておくべきと考え、ひととおり調べてみました(朝のランニングにしてはハードなものになりました)。

結論からいえば、ひじょうに有用性の高い機能がいくつも見つかり・・・個人的には有用な調査が行えました。量が多いので、まだすべてを確認し終わっていませんが、9割ぐらいはカバーできていると思います。

なお、返り値についてはASObjC Explorer 4のCocoaログ機能で表示されたものをそのまま記載しており、NSConcreteValueというのはログのためにASObjC Explorer 4内部で定義しているものであり、実際の内容を示すものではありません(無視してOK)。

アップデート:
Shaneから「resourceValueForKey: forURLsOrFiles:」について正しい呼び出し方について指摘があり、「NSURLを利用するように設計してあり、指定のパス(ディレクトリ)内の各ファイルについてループせずに特定の属性を取得するようになっている」とのこと。

AppleScript名:BrdgePlus 1.2のframeworkの新機能てすと1.1
– Created 2015-09-29 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus” version “1.2″

load framework

– New in v1.2.0

– House-keeping methods

set aRes to BridgePlus’s SMSForder’s frameworkDate()
–>  (NSString) “Sep 23 2015, 15:37:08″

– Regular Expression methods

– Returns the first matching string found. If none found, returns missing value.
set bRes to BridgePlus’s SMSForder’s findFirstMatch:“[98]\\d{6}” inString:“#98158084 Xxxxx Xxxxx xxxx” options:“ix”
–>  (NSString) “9815808″

–Returns the first match record found. If none found, returns missing value.
set cRes to BridgePlus’s SMSForder’s findFirstMatchRecord:“[98]\\d{6}” inString:“#98158084 Xxxxx Xxxxx xxxx” options:“ix”
–>  (NSDictionary) {​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​captureGroup:0, ​​​​​foundString:”9815808″​​​}

–Returns an array of the matching strings found. If none found, returns an empty array.
set dRes to BridgePlus’s SMSForder’s findMatches:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix”
–>  (NSArray) {​​​​​”9815808″, ​​​​​”9800000″​​​}

–Returns an array of the match records found. If none found, returns an empty array.
set eRes to BridgePlus’s SMSForder’s findMatchRecords:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix”
–>  (NSArray) {​​​​​{​​​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​​​captureGroup:0, ​​​​​​​foundString:”9815808″​​​​​}, ​​​​​{​​​​​​​foundRange:(NSConcreteValue) NSRange: {16, 7}, ​​​​​​​captureGroup:0, ​​​​​​​foundString:”9800000″​​​​​}​​​}

– Returns an array for each match, sorted in your specified capture group order. If you specify non-existent capture groups or capture groups that do not participate in a particular match, they will be represented by missing value. If no matches are found, the overall result will be missing value.
set fRes to BridgePlus’s SMSForder’s findMatches:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix” captureGroups:{0}
–>  (NSArray) {​​​​​{​​​​​​​”9815808″​​​​​}, ​​​​​{​​​​​​​”9800000″​​​​​}​​​}

– Returns an array of match records for each match, sorted in your specified capture group order. If no matches are found, the overall result will be missing value.
set gRes to BridgePlus’s SMSForder’s findMatchRecords:“[98]\\d{6}” inString:“#98158084 Xxxxx 98000000 xxxx” options:“ix” captureGroups:{0}
–>  (NSArray) {​​​​​{​​​​​​​{​​​​​​​​​foundRange:(NSConcreteValue) NSRange: {1, 7}, ​​​​​​​​​captureGroup:0, ​​​​​​​​​foundString:”9815808″​​​​​​​}​​​​​}, ​​​​​{​​​​​​​{​​​​​​​​​foundRange:(NSConcreteValue) NSRange: {16, 7}, ​​​​​​​​​captureGroup:0, ​​​​​​​​​foundString:”9800000″​​​​​​​}​​​​​}​​​}

– Substring extraction methods

set hRes to BridgePlus’s SMSForder’s charactersOfString:“abcdefghijklmnopqrstuvwxyz”
–>  (NSArray) {​​​​​”a”, ​​​​​”b”, ​​​​​”c”, ​​​​​”d”, ​​​​​”e”, ​​​​​”f”, ​​​​​”g”, ​​​​​”h”, ​​​​​”i”, ​​​​​”j”, ​​​​​”k”, ​​​​​”l”, ​​​​​”m”, ​​​​​”n”, ​​​​​”o”, ​​​​​”p”, ​​​​​”q”, ​​​​​”r”, ​​​​​”s”, ​​​​​”t”, ​​​​​”u”, ​​​​​”v”, ​​​​​”w”, ​​​​​”x”, ​​​​​”y”, ​​​​​”z”​​​}

set iRes to BridgePlus’s SMSForder’s wordsOfString:“This is a pen.” – ignore localization
–>  (NSArray) {​​​​​”This”, ​​​​​”is”, ​​​​​”a”, ​​​​​”pen”​​​}

set jRes to BridgePlus’s SMSForder’s localizedWordsOfString:“私の名前は長野谷です。” –consider localization, but not consider proper noun or person’s name
–>  (NSArray) {​​​​​”私”, ​​​​​”の”, ​​​​​”名前”, ​​​​​”は”, ​​​​​”長野”, ​​​​​”谷”, ​​​​​”です”​​​} –{”長野”,”谷”} have to be {”長野谷”} . But it is person’s name.

set kRes to BridgePlus’s SMSForder’s sentencesOfString:“Hello. Goodby. See-you again!” – ignore localization
–>  (NSArray) {​​​​​”Hello. “, ​​​​​”Goodby. “, ​​​​​”See-you again!”​​​}

set lRes to BridgePlus’s SMSForder’s localizedSentencesOfString:“こんにちは。こんばんは。さようなら。えっ?!それはたいへんだ!! 「東京には空がない」と智恵子は言った。”
–>  (NSArray) {​​​​​”こんにちは。”, ​​​​​”こんばんは。”, ​​​​​”さようなら。”, ​​​​​”えっ?!”, ​​​​​”それはたいへんだ!!「”, ​​​​​”東京には空がない」と智恵子は言った。”​​​} — 5th item seems wrong

set mRes to BridgePlus’s SMSForder’s paragraphsOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​”Hello. Goodby. See-you again!”, ​​​​​”Hello2. Goodby2. See-you again2!”​​​}

set nRes to BridgePlus’s SMSForder’s stringsOfString:“xxxx” inString:“#98158084 Xxxxx 98000000 xxxx” options:(current application’s NSCaseInsensitiveSearch)
–>  (NSArray) {​​​​​”Xxxx”, ​​​​​”xxxx”​​​}

set oRes to BridgePlus’s SMSForder’s stringsOfString:“ひよこ” inString:“ひょこっと登場したひよこだよびよこぴよこ” options:(current application’s NSCaseInsensitiveSearch) locale:(current application’s NSLocale’s localeWithLocaleIdentifier:“ja”)
–>  (NSArray) {​​​​​”ひよこ”​​​}

– Substring range methods

set pRes to BridgePlus’s SMSForder’s rangesOfCharactersOfString:“あいうえお”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 1}, ​​​​​(NSConcreteValue) NSRange: {3, 1}, ​​​​​(NSConcreteValue) NSRange: {4, 1}​​​}

set qRes to BridgePlus’s SMSForder’s rangesOfCharactersOfString:“This is a pen.”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 1}, ​​​​​(NSConcreteValue) NSRange: {3, 1}, ​​​​​(NSConcreteValue) NSRange: {4, 1}, ​​​​​(NSConcreteValue) NSRange: {5, 1}, ​​​​​(NSConcreteValue) NSRange: {6, 1}, ​​​​​(NSConcreteValue) NSRange: {7, 1}, ​​​​​(NSConcreteValue) NSRange: {8, 1}, ​​​​​(NSConcreteValue) NSRange: {9, 1}, ​​​​​(NSConcreteValue) NSRange: {10, 1}, ​​​​​(NSConcreteValue) NSRange: {11, 1}, ​​​​​(NSConcreteValue) NSRange: {12, 1}, ​​​​​(NSConcreteValue) NSRange: {13, 1}​​​}

set rRes to BridgePlus’s SMSForder’s rangesOfLocalizedWordsOfString:“私の名前は長野谷です。”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 1}, ​​​​​(NSConcreteValue) NSRange: {1, 1}, ​​​​​(NSConcreteValue) NSRange: {2, 2}, ​​​​​(NSConcreteValue) NSRange: {4, 1}, ​​​​​(NSConcreteValue) NSRange: {5, 2}, ​​​​​(NSConcreteValue) NSRange: {7, 1}, ​​​​​(NSConcreteValue) NSRange: {8, 2}​​​}

set sRes to BridgePlus’s SMSForder’s rangesOfSentencesOfString:“Hello. Goodby. See-you again!”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 7}, ​​​​​(NSConcreteValue) NSRange: {7, 8}, ​​​​​(NSConcreteValue) NSRange: {15, 14}​​​}

set tRes to BridgePlus’s SMSForder’s rangesOfLocalizedSentencesOfString:“こんにちは。こんばんは。さようなら。えっ?!それはたいへんだ!! 「東京には空がない」と智恵子は言った。”
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 6}, ​​​​​(NSConcreteValue) NSRange: {6, 6}, ​​​​​(NSConcreteValue) NSRange: {12, 6}, ​​​​​(NSConcreteValue) NSRange: {18, 4}, ​​​​​(NSConcreteValue) NSRange: {22, 11}, ​​​​​(NSConcreteValue) NSRange: {33, 19}​​​}

set uRes to BridgePlus’s SMSForder’s rangesOfParagraphsOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 29}, ​​​​​(NSConcreteValue) NSRange: {30, 32}​​​}

set vRes to BridgePlus’s SMSForder’s rangesOfLinesOfString:“Hello. Goodby. See-you again!
Hello2. Goodby2. See-you again2!”

–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {0, 29}, ​​​​​(NSConcreteValue) NSRange: {30, 32}​​​}

set wRes to BridgePlus’s SMSForder’s rangesOfString:“xxxx” inString:“#98158084 Xxxxx 98000000 xxxx” options:(current application’s NSCaseInsensitiveSearch)
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {10, 4}, ​​​​​(NSConcreteValue) NSRange: {25, 4}​​​}

set xRes to BridgePlus’s SMSForder’s rangesOfString:“ひよこ” inString:“ひょこっと登場したひよこだよびよこぴよこ” options:(current application’s NSCaseInsensitiveSearch) locale:(current application’s NSLocale’s localeWithLocaleIdentifier:“ja”)
–>  (NSArray) {​​​​​(NSConcreteValue) NSRange: {9, 3}​​​}

set yRes to BridgePlus’s SMSForder’s indexSetWithArray:{1, 2, 3, 4, 5}
–>  (NSIndexSet) [number of indexes: 5 (in 1 ranges), indexes: (1-5)]

–Miscellaneous methods

set theFiles to current application’s NSFileManager’s defaultManager()’s contentsOfDirectoryAtURL:(Cocoaify (path to desktop)) includingPropertiesForKeys:{current application’s NSURLContentModificationDateKey} options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) |error|:(missing value)
set zRes to ASify from current application’s SMSForder’s resourceValueForKey:(current application’s NSURLContentModificationDateKey) forURLsOrFiles:theFiles
–>  {​​​​​date “2015年9月24日木曜日 15:02:34″, ​​​​​date “2015年9月18日金曜日 19:19:08″, ​​​​​date “2015年7月29日水曜日 19:08:31″, ​​​​​date “2015年7月20日月曜日 10:44:34″, ​​​​​date “2015年8月21日金曜日 16:49:28″, ​​​​​date “2015年7月30日木曜日 22:43:41″, ​​​​​date “2015年9月5日土曜日 13:15:02″, ​​​​​date “2015年9月29日火曜日 12:31:32″, ​​​​​date “2015年9月5日土曜日 13:28:11″, ​​​​​date “2015年7月30日木曜日 22:29:34″, ​​​​​date “2015年9月16日水曜日 9:33:31″, ​​​​​date “2015年5月30日土曜日 18:53:29″, ​​​​​date “2015年9月16日水曜日 8:57:55″, ​​​​​date “2015年9月12日土曜日 13:40:53″, ​​​​​date “2015年8月28日金曜日 11:42:57″, ​​​​​date “2015年7月14日火曜日 18:10:27″, ​​​​​date “2015年9月28日月曜日 17:12:39″, ​​​​​date “2015年6月19日金曜日 20:18:17″, ​​​​​date “2015年7月18日土曜日 17:35:20″, ​​​​​date “2015年5月10日日曜日 0:01:01″, ​​​​​date “2015年6月22日月曜日 15:46:16″, ​​​​​date “2015年9月16日水曜日 9:08:07″, ​​​​​date “2015年9月5日土曜日 13:14:35″, ​​​​​date “2015年9月16日水曜日 9:02:31″, ​​​​​date “2015年9月29日火曜日 0:11:38″, ​​​​​date “2015年8月18日火曜日 9:58:19″, ​​​​​date “2015年8月28日金曜日 21:23:26″, ​​​​​date “2015年7月18日土曜日 17:35:34″, ​​​​​date “2015年4月23日木曜日 15:21:54″, ​​​​​date “2015年6月3日水曜日 10:05:53″, ​​​​​date “2015年4月23日木曜日 15:21:56″, ​​​​​date “2015年9月26日土曜日 21:10:07″, ​​​​​date “2015年8月31日月曜日 17:29:15″, ​​​​​date “2015年9月22日火曜日 21:19:06″, ​​​​​date “2015年2月14日土曜日 0:30:28″, ​​​​​date “2015年8月24日月曜日 11:28:05″, ​​​​​date “2015年8月22日土曜日 16:52:07″, ​​​​​date “2015年8月14日金曜日 11:27:24″, ​​​​​date “2015年8月14日金曜日 11:27:23″, ​​​​​date “2015年8月14日金曜日 11:06:33″, ​​​​​date “2015年8月14日金曜日 13:13:16″, ​​​​​date “2015年8月31日月曜日 17:08:35″, ​​​​​date “2015年9月12日土曜日 15:45:52″, ​​​​​date “2015年6月22日月曜日 15:18:06″, ​​​​​date “2015年8月18日火曜日 10:01:56″, ​​​​​date “2015年5月21日木曜日 22:15:15″, ​​​​​date “2015年8月18日火曜日 10:06:21″, ​​​​​date “2015年8月20日木曜日 12:52:31″, ​​​​​date “2015年7月10日金曜日 16:01:16″, ​​​​​date “2015年7月14日火曜日 18:18:39″, ​​​​​date “2015年7月16日木曜日 15:22:20″, ​​​​​date “2015年7月16日木曜日 15:33:31″, ​​​​​date “2015年7月16日木曜日 17:01:33″, ​​​​​date “2015年7月18日土曜日 17:28:59″, ​​​​​date “2015年8月10日月曜日 23:13:55″, ​​​​​date “2015年8月14日金曜日 18:08:45″, ​​​​​date “2015年8月25日火曜日 18:47:58″, ​​​​​date “2015年9月8日火曜日 16:22:08″, ​​​​​date “2015年9月2日水曜日 15:04:11″, ​​​​​date “2015年9月6日日曜日 19:53:32″, ​​​​​date “2015年9月15日火曜日 20:49:36″, ​​​​​date “2015年9月21日月曜日 0:44:20″, ​​​​​date “2015年9月28日月曜日 18:45:49″, ​​​​​date “2015年9月28日月曜日 18:46:30″, ​​​​​date “2015年9月29日火曜日 10:38:05″, ​​​​​date “2015年9月29日火曜日 10:40:08″, ​​​​​date “2015年9月29日火曜日 12:29:06″, ​​​​​date “2015年9月29日火曜日 12:29:12″, ​​​​​date “2015年9月29日火曜日 13:53:04″, ​​​​​date “2015年9月21日月曜日 10:20:00″, ​​​​​date “2015年9月21日月曜日 8:55:56″, ​​​​​date “2011年12月29日木曜日 14:30:32″, ​​​​​date “2015年7月12日日曜日 17:53:35″, ​​​​​date “2015年5月19日火曜日 14:43:24″, ​​​​​date “2015年9月15日火曜日 18:30:44″, ​​​​​date “2015年9月29日火曜日 12:25:24″, ​​​​​date “2015年9月29日火曜日 12:27:06″, ​​​​​date “2015年9月29日火曜日 12:28:28″, ​​​​​date “2015年5月25日月曜日 15:59:07″, ​​​​​date “2015年5月25日月曜日 15:59:07″​​​}

★Click Here to Open This Script 

2015/09/28 IMの状態を取得・設定する

Shane StanleyのBridgePlus v1.2で新たに実装された「IMまわりの制御を行う機能」を用いて、IMの状態を取得・設定するAppleScriptです。Shane、本当にありがとう! Many Thanks!!

日本語Scripterの長年の懸案事項であった「日本語入力の状態の取得および設定」を、GUI Scriptingを使用せずに行えます。

fig0.png

自分もひそかに「Cocoaの機能を呼び出してできないか?」と独自に(NSTextInputContextあたりを)調べていた最中だったので、Shaneから「こんなのできたよ」と教えてもらった時には腰を抜かしました。

さまざまなアプリケーションの自動化を行うさいに、IMの状態取得と設定は欠くべからざる機能なのですが、GUI Scripting経由で強引に設定するやり方はいまひとつ信頼性が低く(Applet内から実行したときに効かないことがあるのはなんでだろー)、とてつもなく重要な機能の割に長年ASの標準機能に実装もされてきませんでした。

もう、オーストラリアに足を向けて寝られません。カナダ(Mark Alldritt)、アメリカのバーモント州(Bill Cheeseman)にも足を向けて寝られません(クパティーノに足を向けて寝るべき)。

ただ、実験したところ機能が「強力すぎる」点も見受けられ、たとえばフランス語を使わない設定になっている環境でフランス語のIMを呼び出すと・・・呼び出せるうえに、システム環境設定の「言語と地域」にいきなりフランス語が追加されます。このことはよーーく覚えておく必要があります(調子に乗って各種IMを呼び出しまくると、「言語と地域」にいろんな言語が追加されます)。

また、com.apple.inputmethod.ironwoodは音声入力に該当するようですが、これを呼び出しても音声認識による入力状態には移行しませんでした。惜しい、おしすぎる!(^ー^;;

fig1.png

fig2.png

fig3.png

fig4.png

AppleScript名:IMの状態を取得・設定する
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePLus : script “BridgePlus” version “1.2″ –1.2より古いバージョン(1.1など)ではこの機能が存在しない

load framework

set y to current application’s SMSForder’s availableInputSourceIDs()
–>  (NSArray) {”com.apple.inputmethod.Kotoeri.Japanese”, “com.apple.inputmethod.Kotoeri.Roman”, “com.apple.inputmethod.Kotoeri.Japanese.Katakana”, “com.apple.inputmethod.Kotoeri”, “com.apple.50onPaletteIM”, “com.apple.inputmethod.ironwood”, “com.apple.CharacterPaletteIM”, “com.apple.KeyboardViewer”}

set y to current application’s SMSForder’s allAvailableInputSourceIDs()
–>  (NSArray) {”com.apple.inputmethod.Kotoeri.Japanese”, “com.apple.inputmethod.Kotoeri.Roman”, “com.apple.inputmethod.Kotoeri.Japanese.Katakana”, “com.apple.inputmethod.Kotoeri”, “com.apple.50onPaletteIM”, “com.apple.inputmethod.ironwood”, “com.apple.CharacterPaletteIM”, “com.apple.KeyboardViewer”, “com.apple.keylayout.US”, “com.apple.SyntheticRomanMode”, “com.apple.keylayout.Czech-QWERTY”, “com.apple.keylayout.Czech”, “com.apple.keylayout.Estonian”, “com.apple.keylayout.Hungarian-QWERTY”, “com.apple.keylayout.Hungarian”, “com.apple.keylayout.Latvian”, “com.apple.keylayout.Lithuanian”, “com.apple.keylayout.PolishPro”, “com.apple.keylayout.Polish”, “com.apple.keylayout.Slovak”, “com.apple.keylayout.Slovak-QWERTY”, “com.apple.keylayout.Bulgarian-Phonetic”, “com.apple.keylayout.Bulgarian”, “com.apple.keylayout.Byelorussian”, “com.apple.keylayout.Macedonian”, “com.apple.keylayout.Russian-Phonetic”, “com.apple.keylayout.Russian”, “com.apple.keylayout.RussianWin”, “com.apple.keylayout.Serbian”, “com.apple.keylayout.Ukrainian-PC”, “com.apple.keylayout.Ukrainian”, “com.apple.keylayout.Colemak”, “com.apple.keylayout.Dvorak-Left”, “com.apple.keylayout.Dvorak-Right”, “com.apple.keylayout.Dvorak”, “com.apple.keylayout.DVORAK-QWERTYCMD”, “com.apple.keylayout.KANA”, “com.apple.keylayout.Australian”, “com.apple.keylayout.Austrian”, “com.apple.keylayout.Belgian”, “com.apple.keylayout.Brazilian-ABNT2″, “com.apple.keylayout.Brazilian”, “com.apple.keylayout.British-PC”, “com.apple.keylayout.British”, “com.apple.keylayout.Canadian-CSA”, “com.apple.keylayout.Canadian”, “com.apple.keylayout.Danish”, “com.apple.keylayout.Dutch”, “com.apple.keylayout.Finnish”, “com.apple.keylayout.French-PC”, “com.apple.keylayout.French-numerical”, “com.apple.keylayout.French”, “com.apple.keylayout.German”, “com.apple.keylayout.Irish”, “com.apple.keylayout.Italian-Pro”, “com.apple.keylayout.Italian”, “com.apple.keylayout.Norwegian”, “com.apple.keylayout.Portuguese”, “com.apple.keylayout.Spanish-ISO”, “com.apple.keylayout.Spanish”, “com.apple.keylayout.Swedish-Pro”, “com.apple.keylayout.Swedish”, “com.apple.keylayout.SwissFrench”, “com.apple.keylayout.SwissGerman”, “com.apple.keylayout.USInternational-PC”, “com.apple.keylayout.2SetHangul”, “com.apple.keylayout.AfghanDari”, “com.apple.keylayout.AfghanPashto”, “com.apple.keylayout.AfghanUzbek”, “com.apple.keylayout.Anjal”, “com.apple.keylayout.Arabic-AZERTY”, “com.apple.keylayout.Arabic-NorthAfrica”, “com.apple.keylayout.Arabic-QWERTY”, “com.apple.keylayout.Arabic”, “com.apple.keylayout.ArabicPC”, “com.apple.keylayout.Armenian-HMQWERTY”, “com.apple.keylayout.Armenian-WesternQWERTY”, “com.apple.keylayout.Azeri”, “com.apple.keylayout.Bangla-QWERTY”, “com.apple.keylayout.Bangla”, “com.apple.keylayout.CangjieKeyboard”, “com.apple.keylayout.Cherokee-Nation”, “com.apple.keylayout.Cherokee-QWERTY”, “com.apple.keylayout.Croatian”, “com.apple.keylayout.Croatian-PC”, “com.apple.keylayout.Devanagari-QWERTY”, “com.apple.keylayout.Devanagari”, “com.apple.keylayout.Faroese”, “com.apple.keylayout.FinnishExtended”, “com.apple.keylayout.FinnishSami-PC”, “com.apple.keylayout.Georgian-QWERTY”, “com.apple.keylayout.Greek”, “com.apple.keylayout.GreekPolytonic”, “com.apple.keylayout.Gujarati-QWERTY”, “com.apple.keylayout.Gujarati”, “com.apple.keylayout.Gurmukhi-QWERTY”, “com.apple.keylayout.Gurmukhi”, “com.apple.keylayout.Hawaiian”, “com.apple.keylayout.Hebrew-QWERTY”, “com.apple.keylayout.Hebrew”, “com.apple.keylayout.Hebrew-PC”, “com.apple.keylayout.Icelandic”, “com.apple.keylayout.Inuktitut-Nunavut”, “com.apple.keylayout.Inuktitut-Nutaaq”, “com.apple.keylayout.Inuktitut-QWERTY”, “com.apple.keylayout.InuttitutNunavik”, “com.apple.keylayout.IrishExtended”, “com.apple.keylayout.Jawi-QWERTY”, “com.apple.keylayout.Kannada-QWERTY”, “com.apple.keylayout.Kannada”, “com.apple.keylayout.Kazakh”, “com.apple.keylayout.Khmer”, “com.apple.keylayout.Kurdish-Sorani”, “com.apple.keylayout.Malayalam-QWERTY”, “com.apple.keylayout.Malayalam”, “com.apple.keylayout.Maltese”, “com.apple.keylayout.Maori”, “com.apple.keylayout.Myanmar-QWERTY”, “com.apple.keylayout.Nepali”, “com.apple.keylayout.NorthernSami”, “com.apple.keylayout.NorwegianExtended”, “com.apple.keylayout.NorwegianSami-PC”, “com.apple.keylayout.Oriya-QWERTY”, “com.apple.keylayout.Oriya”, “com.apple.keylayout.Persian-QWERTY”, “com.apple.keylayout.Persian”, “com.apple.keylayout.Persian-ISIRI2901″, “com.apple.keylayout.Romanian-Standard”, “com.apple.keylayout.Romanian”, “com.apple.keylayout.Sami-PC”, “com.apple.keylayout.Serbian-Latin”, “com.apple.keylayout.Sinhala-QWERTY”, “com.apple.keylayout.Sinhala”, “com.apple.keylayout.Slovenian”, “com.apple.keylayout.SwedishSami-PC”, “com.apple.keylayout.Tamil99″, “com.apple.keylayout.Telugu-QWERTY”, “com.apple.keylayout.Telugu”, “com.apple.keylayout.Thai-PattaChote”, “com.apple.keylayout.Thai”, “com.apple.keylayout.TibetanOtaniUS”, “com.apple.keylayout.Tibetan-QWERTY”, “com.apple.keylayout.Tibetan-Wylie”, “com.apple.keylayout.Turkish-QWERTY-PC”, “com.apple.keylayout.Turkish-QWERTY”, “com.apple.keylayout.Turkish”, “com.apple.keylayout.USExtended”, “com.apple.keylayout.UnicodeHexInput”, “com.apple.keylayout.Urdu”, “com.apple.keylayout.Uyghur”, “com.apple.keylayout.Vietnamese”, “com.apple.keylayout.Welsh”, “com.apple.keylayout.ZhuyinBopomofo”, “com.apple.keylayout.390Hangul”, “com.apple.keylayout.3SetHangul”, “com.apple.keylayout.GJCRomaja”, “com.apple.keylayout.HNCRomaja”, “com.apple.keylayout.WubihuaKeyboard”, “com.apple.keylayout.WubixingKeyboard”, “com.apple.keylayout.ZhuyinEten”, “com.apple.inputmethod.PluginIM”, “com.apple.inputmethod.Ainu”, “com.apple.inputmethod.ink.inkserver”, “com.apple.inputmethod.Korean”, “com.apple.PressAndHold”, “com.apple.inputmethod.SCIM”, “com.apple.inputmethod.AssistiveControl”, “com.apple.inputmethod.Tamil”, “com.apple.inputmethod.TCIM”, “com.apple.inputmethod.ChineseHandwriting”, “com.apple.inputmethod.VietnameseIM”, “com.apple.inputmethod.AinuIM.Ainu”, “com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana”, “com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman”, “com.apple.inputmethod.Korean.3SetKorean”, “com.apple.inputmethod.Korean.2SetKorean”, “com.apple.inputmethod.Korean.HNCRomaja”, “com.apple.inputmethod.Korean.390Sebulshik”, “com.apple.inputmethod.Korean.GongjinCheongRomaja”, “com.apple.inputmethod.SCIM.ITABC”, “com.apple.inputmethod.SCIM.WBH”, “com.apple.inputmethod.SCIM.WBX”, “com.apple.inputmethod.Tamil.AnjalIM”, “com.apple.inputmethod.Tamil.Tamil99″, “com.apple.inputmethod.TCIM.WBH”, “com.apple.inputmethod.TCIM.Zhuyin”, “com.apple.inputmethod.TCIM.Cangjie”, “com.apple.inputmethod.TCIM.ZhuyinEten”, “com.apple.inputmethod.TCIM.Jianyi”, “com.apple.inputmethod.TCIM.Pinyin”, “com.apple.inputmethod.VietnameseIM.VietnameseSimpleTelex”, “com.apple.inputmethod.VietnameseIM.VietnameseVNI”, “com.apple.inputmethod.VietnameseIM.VietnameseVIQR”, “com.apple.inputmethod.VietnameseIM.VietnameseTelex”, “jp.monokakido.inputmethod.Kawasemi”, “com.parallels.inputmethod.ParallelsIM”, “com.visionobjects.Axiotron Quickscript.TSC”, “jp.monokakido.inputmethod.Kawasemi.Japanese”, “jp.monokakido.inputmethod.Kawasemi.Japanese.HalfWidthKana”, “jp.monokakido.inputmethod.Kawasemi.Japanese.HalfWidthRoman”, “jp.monokakido.inputmethod.Kawasemi.Roman”, “jp.monokakido.inputmethod.Kawasemi.Japanese.FullWidthRoman”, “jp.monokakido.inputmethod.Kawasemi.Japanese.Katakana”, “jp.monokakido.inputmethod.Kawasemi.Japanese.Code”}

–デモ: IMのメニューで「A」と「あ」を交互に切り替える
repeat 10 times
  set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.Kotoeri.Roman”
  
tell current application
    delay 0.5
  end tell
  
set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.Kotoeri.Japanese”
  
tell current application
    delay 0.5
  end tell
end repeat

★Click Here to Open This Script 

AppleScript名:ironwood(音声認識入力)を呼び出したが、音声入力は始まらなかった
– Created 2015-09-16 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePLus : script “BridgePlus” version “1.2″ –1.2より古いバージョン(1.1など)ではこの機能が存在しない

load framework

tell application “TextEdit” to activate
delay 3
set x to current application’s SMSForder’s changeInputSourceTo:“com.apple.inputmethod.ironwood”

★Click Here to Open This Script 

2015/09/14 地域、事業所名のリストを出現頻度カウントなどの処理を行って整形

家族から頼まれて作成したプログラムで、割と手こずったデータ処理のAppleScriptです。

Excelに入っているデータを読み取ってAppleScript側で集計処理を行ってみました。Pure AppleScript的な処理とCocoa的な処理がいろいろと入り混じっており、逆に効率が悪くなっているかもしれません(ただし、BridgePlusを活用しまくり、Cocoaの機能を利用して処理を書きまくっているので、処理速度は爆速)。

ここに掲載したScriptは、その処理の一部ですが・・・単体で実行可能なものです。

{「営業所エリア名」、「事業所名」}のペアになっているリストで頻度集計を行うというものですが、同時に「営業所エリア名」を「エリア名略称」に変換します。

Input:{{”西営業所”, “◯◯◯◯◯”}, {”四国営業所”, “●●●●”}, {”西営業所”, “◯◯◯◯◯”}}

Output:
(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

非常に些細な処理ですが、実際に組んでみるとそれなりに手間がかかりました。

NSCountedSetにNSDictionaryを突っ込んで頻度カウントができるんじゃないかと思って試行錯誤してみたのですが、なかなかうまくいきませんでした(どうやら突っ込んでカウントはできているものの、値を取り出すのに失敗)。まだ、なかなか難しいです(汗)。

AppleScript名:地域、事業署名のリストを出現頻度カウントなどの処理を行って整形v2
– Created 2015-09-13 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”

load framework

set oList to {{“西営業所”, “◯◯◯◯◯”}, {“四国営業所”, “●●●●”}, {“西営業所”, “◯◯◯◯◯”}}

set aRes to formatBranchDescriptions(oList)
–>
(*
“(XXXX西部)◯◯◯◯◯ (2)
(XXXX四国)●●●●

*)

on formatBranchDescriptions(oList)
  set labelList to {“branchName”, “elipName”}
  
set aList to BridgePlus’s sublistsIn:oList asDictionariesUsingLabels:labelList
  
  
—set aList to {{branchName:”西営業所”, elipName:”◯◯◯◯◯”}, {branchName:”四国営業所”, elipName:”●●●●”}, {branchName:”西営業所”, elipName:”◯◯◯◯◯”}}
  
  
set anArray to Cocoaify aList
  
  
set elipNameList to (anArray’s valueForKey:“elipName”) as list
  
–>  {”◯◯◯◯◯”, “●●●●”, “”◯◯◯◯◯”}
  
set eList to uniquify1DList(elipNameList) of me –ユニーク化
  
  
set outStr to “”
  
repeat with i in eList
    set j to contents of i
    
set aRes to countSpecifiedItem(elipNameList, {j}) of me
    
    
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“elipName == %@”, j)
    
set aRec to (anArray’s filteredArrayUsingPredicate:aPredicate)
    
set anItem to branchName of first item of aRec
    
set eName2 to convBranchToElip(anItem)
    
if aRes > 1 then
      set outStr to outStr & {eName2 & j & “ (” & aRes & “)”} & return
    else
      set outStr to outStr & {eName2 & j} & return
    end if
  end repeat
  
return outStr
end formatBranchDescriptions

–リスト中の指定項目の出現回数を返す
on countSpecifiedItem(aList, countItem)
  set aRes to BridgePlus’s indexesOfItems:countItem inList:aList inverting:false
  
set aCount to length of aRes
  
return aCount
end countSpecifiedItem

–1D Listをユニーク化
on uniquify1DList(theList as list)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set bList to ASify from bArray as list
  
return bList
end uniquify1DList

–営業所エリア名、エリア名略称変換
on convBranchToElip(aBranch)
  set aList to {{branchName:“東京営業所”, elipName:“(XXXX東京)”}, {branchName:“千葉営業所”, elipName:“(XXXX千葉)”}, {branchName:“神奈川営業所”, elipName:“(XXXX神奈川)”}, {branchName:“西営業所”, elipName:“(XXXX西部)”}, {branchName:“四国営業所”, elipName:“(XXXX四国)”}, {branchName:“茨城営業所”, elipName:“(XXXX茨城)”}}
  
set aDic to Cocoaify aList
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“branchName == %@”, aBranch)
  
set aRes to (aDic’s filteredArrayUsingPredicate:aPredicate)
  
try
    set anItem to contents of first item of (aRes as list)
    
set eName to (elipName of anItem)
  on error
    set eName to “”
  end try
  
return eName
end convBranchToElip

★Click Here to Open This Script 

2015/09/11 与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ

Cocoaの機能を用いて、与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げするAppleScriptです。Shane StanleyのAppleScript Libraryである「BridgePlus」(フリー)のインストールが必要です。

こういうのがやりたくて、地道に部品を揃えてきたわけで・・・やらないわけにはいきません。

ただし、思ったよりも泥沼に足を突っ込みかけました。食後のハイキングのはずが、雪山で遭難しかけたという印象です。

NSSpeechSynthesizerのavailableVoices()は「インストールされていないTTS Voice」も返してくるため、せっかくテキストの言語を特定して、TTS Voiceを取得しても・・・インストールされていないTTS Voiceが返ってくるとお手上げです(T_T)。

tts_voice_list.png

英語だとNSLinguisticTaggerによる判定で”en”が、フランス語だと”fr”が返ってきますが、”en”はあくまで英語を示すものであり、”en_GB”もあれば”en_US”もあれば、”en_IN”(インド)、”en-scotland”(スコットランド)、”en_ZA”(南アフリカ)もあるわけです(自分も物好きですが、南アフリカのTTS Voiceはインストールしていません)。

“en”をキーにして、全TTS Voiceから言語コードを取得し、最初にヒットした言語コードが”en_ZA”であれば南アフリカのTTS Voiceでsayコマンドの実行を行おうとしてしまうので、”en”を”en-US”に置き換えるような、つじつまあわせの処理が必要になってしまいました(もう少しうまいやり方もありそうですが、食後の散歩のレベルを超えてしまいますので)。

実際に実戦レベルのAppleScriptを書いてみるまで、見えてこない「落とし穴」もあるので、こうして細かいScriptを書いておくという作業はなかなかあなどれません、、、

AppleScript名:与えられたテキストの言語を自動判別して対応する言語のTTS Voiceで読み上げ
– Created 2015-09-11 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set aRes to getVoiceLanguageFromTTSVoices() –Language Code

–English, French, Japanese, Chinese
set cList to {“With the new MacBook, we set out to do the impossible.”, “Le nouveau MacBook est le résultat d’un défi presque impossible”, “新しいMacBookとともに、私たちは不可能だと思えることに挑みました。”, 于全新 MacBook,我们给自己定了一个几乎不可能实现的目}

–各言語テキストでループ
repeat with i in cList
  set j to contents of i
  
set aLang to specifyLanguageOfText(j) –テキストから言語コードを取得
  
  
–つじつまあわせ。ちょっとカッコ悪い
  
–(NSSpeechSynthesizerのavailableVoicesでインストールされていないVoiceまで返ってくることへの対策)
  
if aLang = “en” then
    set aLang to “en-US”
  else if aLang = “fr” then
    set aLang to “fr-FR”
  else if aLang = “ja” then
    set aLang to “ja-JP”
  end if
  
  
–全ボイス中から言語をサポートしているものを抽出
  
repeat with ii in aRes
    set jj to contents of ii
    
    
if (jj as text) is equal to (aLang as text) then
      
      
–最初にヒットした言語コードでPremium Voiceを検索
      
set v1fList to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
      
set v1mList to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, jj)
      
set v1List to v1fList & v1mList
      
      
if v1List = {} then
        –Premium Voiceでヒットしなかった場合に、すべてのVoiceを検索
        
set v2fList to getAllVoiceNameWithFiltering(“VoiceGenderFemale”, jj)
        
set v2mList to getAllVoiceNameWithFiltering(“VoiceGenderMale”, jj)
        
set v2List to v2fList & v2mList
        
        
if v2List = {} then
          display dialog “この言語(” & jj & “)をサポートするTTS Voiceはインストールされていません。” –Error
          
exit repeat
        else
          copy v2List to v1List
        end if
        
      end if
      
      
repeat with iii in v1List
        set aVoice to contents of iii
        
try
          –総当たりでSayコマンド実行(インストールされていないTTS Voiceも取得できてしまうため)
          
tell current application
            say j using aVoice
          end tell
          
exit repeat
        end try
      end repeat
      
      
exit repeat
    end if
  end repeat
  
end repeat

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

–All Voices (VoiceLanguageで抽出)
on getAllVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLanguage == %@ and VoiceGender == %@”, voiceLang, voiceGender)
  
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getAllVoiceNameWithFiltering

on getLocaleIdentifierFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getLocaleIdentifierFromTTSVoices

on getVoiceLanguageFromTTSVoices()
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLanguage”)
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
end getVoiceLanguageFromTTSVoices

on specifyLanguageOfText(aStr)
  set aNSstring to current application’s NSString’s stringWithString:aStr
  
set tagSchemes to current application’s NSArray’s arrayWithObjects:(current application’s NSLinguisticTagSchemeLanguage)
  
set tagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:tagSchemes options:0
  
tagger’s setString:aNSstring
  
set aLanguage to tagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return aLanguage as text
end specifyLanguageOfText

–1D Listをユニーク化してソート
on uniquifyAndSort1DList(theList as list, aBool as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:“@distinctUnionOfObjects.self”
  
set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“compare:”
  
set cArray to bArray’s sortedArrayUsingDescriptors:{aDdesc}
  
set bList to (ASify from cArray) as list
  
return bList
end uniquifyAndSort1DList

–文字置換
on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to (ASify from bString) as string
  
return cString
end repChar

★Click Here to Open This Script 

2015/08/26 ASOCで性別と言語コードを指定してTTS voiceを取得 v2

Cocoaの機能を用いて、Text To Speechの音声から性別と言語を指定して、TTS Voice名称をリストで返すAppleScriptです。最初のバージョンに対して、「Premium」voiceに限定しないvoice名取得ルーチン「getAllVoiceNameWithFiltering」を追加しています。ほかに、機能的な差異はありません。

前バージョンに対し、Shane StanleyからNSPredicateの指定について、

set aStr to current application’s NSString’s stringWithString:(”VoiceLocaleIdentifier == ‘” & voiceLang & “‘ and  VoiceGender == ‘” & voiceGender & “‘ and VoiceIdentifier ENDSWITH[c] ‘premium’”)
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aStr

って、書いてあるけど引数をNSPredicateの指定文字列から分離したほうがクォートで囲む囲まないで悩まなくていいから、分離しといたほうがいいよ(意訳)という指摘があって・・・実際、NSPredicate以外のコードはすぐに書けたものの・・・クォートの指定でけっこう試行錯誤して悩んでいたので、まったくその通り(^ー^;;;;

set aPredicate to current application’s NSPredicate’s predicateWithFormat_(”VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)

とか(ここで「predicateWithFormat_」とアンダースコアで書かないと構文確認をパスできないところにASOCの闇と、Shaneの試行錯誤を感じます、、、)、

set aPredicate to current application’s NSPredicate’s predicateWithFormat:”VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@” argumentArray:{voiceLang, voiceGender, “premium”}

みたいに書いたほうがシンプルでいいよ的な話で、まさにそのとおり。で、いろいろ書き換えておきました。

各TTS Voiceについては、性別、言語、国などのほかに「年齢」というパラメータも持っているのですが、年齢を条件にして綿密に絞り込む・・・というほどには、TTS Voiceの数が多くないので、年齢は考慮していません。

これ(↑)を掲載するまでに、「クリップボードの内容をスタイル付きテキストで取得する方法」について調べ、調べきれなかったので、do shell script使いまくりの昔作ったAppleScriptでHTMLに書き出した、というオチもありました。Cocoaのクリップボード関連、普段使わないうえに、いざ真剣に調べだしてもあんまりまとまったまともでわかりやすい情報がなくて困りものです。

AppleScript名:ASOCで性別と言語コードを指定してTTS voiceを取得 v2
– Created 2015-08-25 by Takaaki Naganoya
– Modified 2015-08-26 by Shane Stanley, Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set v1Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “ja_JP”)
–>  {​​​​​”Otoya”​​​}

set v2Res to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Samantha”​​​}

set v3Res to getAllVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Agnes”, ​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Kathy”, ​​​​​”Princess”, ​​​​​”Samantha”, ​​​​​”Vicki”, ​​​​​”Victoria”​​​}

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLocaleIdentifier == %@ and VoiceGender == %@ and VoiceIdentifier ENDSWITH[c] %@”, voiceLang, voiceGender, “premium”)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

–All Voices
on getAllVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_(“VoiceLocaleIdentifier == %@ and VoiceGender == %@”, voiceLang, voiceGender)
  
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceName”) as list
  
  
return aReList
  
end getAllVoiceNameWithFiltering

on getLaunguageCodeFromTTSVoices()
  
  
–Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDict to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDict)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
  
end getLaunguageCodeFromTTSVoices

★Click Here to Open This Script 

2015/08/25 ASOCで性別と言語コードを指定して高音質voiceを取得

Cocoaの機能を用いて、Text To Speechの音声から性別と言語を指定して、高音質のVoice名称をリストで返すAppleScriptです。

voice1.png
▲システム環境設定「音声入力と読み上げ」>「テキスト読み上げ」

voice2.png
▲OSにインストールされているVoiceリスト

男性:VoiceGenderMale
女性:VoiceGenderFemale

で性別を指定して高音質のvoice名称を取得します。

アメリカ英語(en_US)で男性の音声をどれでもいいから指定したいが、どの音声が入っているかは特定できないしデフォルトで指定されているかどうかもわからない、といった場合にある程度の見当をつけるために作ったものです。

TTS Voiceで使用可能な言語コード一覧については、「ASOCで全voiceから言語コードを抽出」のプログラムで取得できるようにしておきました。

AppleScript名:ASOCで性別と言語コードを指定して高音質voiceを取得
– Created 2015-08-25 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set v1Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “ja_JP”)
–>  {​​​​​”Otoya”​​​}

set v2Res to getPremiumVoiceNameWithFiltering(“VoiceGenderFemale”, “en_US”)
–>  {​​​​​”Allison”, ​​​​​”Ava”, ​​​​​”Jill”, ​​​​​”Samantha”​​​}

set v3Res to getPremiumVoiceNameWithFiltering(“VoiceGenderMale”, “en_GB”)
–> {”Daniel”}

–Premium Voices Only
on getPremiumVoiceNameWithFiltering(voiceGender, voiceLang)
  –Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
–Filter Voice
  
set aStr to current application’s NSString’s stringWithString:(“VoiceLocaleIdentifier == ’” & voiceLang & “’ and VoiceGender == ’” & voiceGender & “’ and VoiceIdentifier ENDSWITH[c] ’premium’”)
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aStr
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aReList to (filteredArray’s valueForKey:“VoiceNameRoot”) as list
  
  
return aReList
  
end getPremiumVoiceNameWithFiltering

★Click Here to Open This Script 

AppleScript名:ASOCで全voiceから言語コードを抽出
– Created 2015-08-25 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use BridgePlus : script “BridgePlus”

load framework

set cRes to getLaunguageCodeFromTTSVoices()
–>  {​​​​​”fr_FR”, ​​​​​”zh_TW”, ​​​​​”it_IT”, ​​​​​”en_ZA”, ​​​​​”es_AR”, ​​​​​”ko_KR”, ​​​​​”ro_RO”, ​​​​​”en_IN”, ​​​​​”fr_CA”, ​​​​​”hi_IN”, ​​​​​”da_DK”, ​​​​​”en-scotland”, ​​​​​”pt_BR”, ​​​​​”zh_CN”, ​​​​​”sv_SE”, ​​​​​”es_ES”, ​​​​​”hu_HU”, ​​​​​”ar_SA”, ​​​​​”en_GB”, ​​​​​”ja_JP”, ​​​​​”fi_FI”, ​​​​​”zh_HK”, ​​​​​”tr_TR”, ​​​​​”nb_NO”, ​​​​​”pl_PL”, ​​​​​”id_ID”, ​​​​​”cs_CZ”, ​​​​​”el_GR”, ​​​​​”he_IL”, ​​​​​”ru_RU”, ​​​​​”de_DE”, ​​​​​”en_AU”, ​​​​​”nl_BE”, ​​​​​”pt_PT”, ​​​​​”th_TH”, ​​​​​”sk_SK”, ​​​​​”en_US”, ​​​​​”en_IE”, ​​​​​”nl_NL”, ​​​​​”es_MX”​​​}

on getLaunguageCodeFromTTSVoices()
  
  
–Make Blank Array
  
set outArray to current application’s NSMutableArray’s arrayWithObject:{}
  
  
–Make Installed Voice List
  
set aList to current application’s NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (current application’s NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aResArray to (outArray’s valueForKey:“VoiceLocaleIdentifier”)
  
  
set aSet to current application’s NSMutableSet’s setWithArray:aResArray
  
set aResList to aSet’s allObjects()
  
  
set bResList to BridgePlus’s listByDeletingBlanksIn:aResList –Remove Blank Items
  
  
return bResList
  
end getLaunguageCodeFromTTSVoices

★Click Here to Open This Script 

2015/08/22 NSDataDetectorを用いてテキストから各種データを抽出

NSDataDetectorを用いて、本来は自然言語のテキスト(メールの本文とかスケジュールとか)から指定の属性の情報を抽出するAppleScriptです。

前バージョンよりも情報抽出部を短く記述できるという指摘がShaneからあったため、その点を書き換えて動作確認したものです。内容自体は前バージョンと変わりありません。

NSDataDetectorのサンプルで、すべての属性データを指定しているものが存在せず、ひととおり調べてテストするのにはそれなりに時間がかかりました(T_T)

AppleScript名:ASOCで各種文字列からdate objectを抽出 v2
– Created 2015-08-21 by Shane Stanley
– Modified 2015-08-22 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use script “BridgePlus”

set theString to “Sunny September 4
Fri., September 1
Fri. Sept. 2
Fri Sep 3
Sep. 4
Sept. 9
9/8
9/7/15
09/06/2015
9/5/2015
2015/9/5
2015年9月5日
2015年9月5日(土)
2015.9.5


set theDates to ASify from (my getDatesIn:theString)
–>  {​​​​​date “2015年9月4日金曜日 12:00:00″, ​​​​​date “2015年9月1日火曜日 12:00:00″, ​​​​​date “2015年9月2日水曜日 12:00:00″, ​​​​​date “2015年9月3日木曜日 12:00:00″, ​​​​​date “2015年9月4日金曜日 12:00:00″, ​​​​​date “2015年9月9日水曜日 12:00:00″, ​​​​​date “2009年7月15日水曜日 12:00:00″, ​​​​​date “2015年9月6日日曜日 12:00:00″, ​​​​​date “2015年9月5日土曜日 12:00:00″, ​​​​​date “2015年9月5日土曜日 12:00:00″, ​​​​​date “2015年9月5日土曜日 12:00:00″, ​​​​​date “2015年9月5日土曜日 12:00:00″​​​}

on getDatesIn:aString
  set anNSString to current application’s NSString’s stringWithString:aString
  
  set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeDate) |error|:(reference)
  
  set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“date”
  
  return theResults as list
end getDatesIn:

★Click Here to Open This Script 

AppleScript名:ASOCで各種文字列から電話番号を抽出 v2
– Created 2015-08-21 by Shane Stanley
– Modified 2015-08-21 by Takaaki Naganoya
– Modified 2015-08-22 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theString to “長野谷隆昌
(Takaaki Naganoya)
maro@piyocast.com
http://piyocast.com/as
2015年8月21日〜23日
080-1111-2222
東京都練馬区中村橋1-2-3

set theDates to (extractPhoneNumberFromNaturalText(theString))
–>  {​​​​​”080-1111-2222″​​​}

on extractPhoneNumberFromNaturalText(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypePhoneNumber) |error|:(reference)
  
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“phoneNumber”
  
  
return theResults as list
end extractPhoneNumberFromNaturalText

★Click Here to Open This Script 

AppleScript名:ASOCで各種文字列から住所を抽出 v2
– Created 2015-08-21 by Shane Stanley
– Modified 2015-08-21 by Takaaki Naganoya
– Modified 2015-08-22 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theString to “長野谷隆昌
(Takaaki Naganoya)
maro@piyocast.com
http://piyocast.com/as
2015年8月21日〜23日
東京都練馬区中村橋1-2-3

set theDates to (extractAddressFromNaturalText(theString))
–>  {{State:”東京都”, Street:”中村橋1-2-3″, City:”練馬区”}}

on extractAddressFromNaturalText(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeAddress) |error|:(reference)
  
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“addressComponents”
  
  
return theResults as list
end extractAddressFromNaturalText

★Click Here to Open This Script 

AppleScript名:ASOCで各種文字列からリンクURLを抽出 v2
– Created 2015-08-21 by Shane Stanley
– Modified 2015-08-21 by Takaaki Naganoya
– Modified 2015-08-22 by Shane Stanley
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set theString to "長野谷隆昌
(Takaaki Naganoya)
maro@piyocast.com
http://piyocast.com/as
2015年8月21日〜23日
080-1111-2222
東京都練馬区中村橋1-2-3
"

set theDates to (extractLinksFromNaturalText(theString))
–>  {​​​​​(NSURL) mailto:maro@piyocast.com, ​​​​​(NSURL) http://piyocast.com/as​​​}

on extractLinksFromNaturalText(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeLink) |error|:(reference)
  
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:"URL"
  
  
return theResults as list
end extractLinksFromNaturalText

★Click Here to Open This Script 

AppleScript名:ASOCで各種文字列からフライト情報を抽出 v2
– Created 2015-08-21 by Shane Stanley
– Modified 2015-08-21 by Takaaki Naganoya
– Modified 2015-08-22 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set theString to “UA460 SFO to YVR [Flight] 6/12/2013 United Airlines(UA) #460 dep SFO 7:57pm PDT arr YVR 10:14pm PDT; Ticket #0162360127882, Ticket #0162360127883; conf #K5XBXY; Note:, Seats:—/30A , Seats:—/30B

set theDates to (extractTransitInfoFromNaturalText(theString))
–>  {​​​​​{​​​​​​​Flight:”460″​​​​​}​​​}

on extractTransitInfoFromNaturalText(aString)
  set anNSString to current application’s NSString’s stringWithString:aString
  
  
set {theDetector, theError} to current application’s NSDataDetector’s dataDetectorWithTypes:(current application’s NSTextCheckingTypeTransitInformation) |error|:(reference)
  
  
set theMatches to theDetector’s matchesInString:anNSString options:0 range:{0, anNSString’s |length|()}
  
set theResults to theMatches’s valueForKey:“components”
  
  
return theResults as list
end extractTransitInfoFromNaturalText

★Click Here to Open This Script 

2015/08/10 使用中のMacの製品呼称を取得する v2

使用中のMacの製品呼称を取得するAppleScriptです。

@fixdotさんにTwitterで教えていただいた ここの情報 でplistから検索するように変更してみました。

ただ、やはり製品が新しくなってもマシンの世代が変わっていないものについては、違いを検出できないため、決定的なものにはなっていません。製品のシリアル番号から生産年と生産週は取得できるため、このあたりをからめて(モデルごとの生産年・週の境目がわかれば)、擬似的な判定は可能と思われます。

AppleScript名:使用中のMacの製品呼称を取得する v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgeP : script “BridgePlus”

set pListPath to “/System/Library/PrivateFrameworks/ServerInformation.framework/Versions/A/Resources/English.lproj/SIMachineAttributes.plist”
set aRec to retDictFromPlist(pListPath) of me

set hwName to (do shell script “sysctl -n hw.model”) as text
–>  ”MacBookPro10,1″

set aMachineRec to retRecordByLabel(aRec, hwName)
–>  {​​​​​”x86_64″, ​​​​​”/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/com.apple.macbookpro-15-retina-display.icns”, ​​​​​{​​​​​​​marketingModel:”MacBook Pro with Retina display, Intel Core i7, 15\” (Mid 2012)”, ​​​​​​​model:”MacBook Pro”, ​​​​​​​description:”15\” MacBook Pro with Retina display, dual-core Intel Core i7 processor and aluminum unibody, introduced mid 2012.”, ​​​​​​​processor:”Intel Core i7″​​​​​}​​​}

set macName to marketingModel of (last item of aMachineRec)
–>  ”MacBook Pro with Retina display, Intel Core i7, 15\” (Mid 2012)”

–Read plist as record
on retDictFromPlist(aPath)
  set thePath to current application’s NSString’s stringWithString:aPath
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theDict to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
return theDict as record
end retDictFromPlist

–リストに入れたレコードを、指定の属性ラベルの値で抽出
on filterRecListByLabel(aRecList as list, aPredicate as string)
  –ListからNSArrayへの型変換
  
set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  –抽出
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aPredicate
  
set filteredArray to aArray’s filteredArrayUsingPredicate:aPredicate
  
  –NSArrayからListに型変換して返す
  
set bList to (ASify from filteredArray) as list
  
return bList
end filterRecListByLabel

–指定レコードの指定ラベルの値を取得する
on retRecordByLabel(aRec as record, aKey as string)
  set aDic to current application’s NSDictionary’s dictionaryWithDictionary:aRec
  
set aVal to aDic’s valueForKey:aKey
  
set aValNum to (ASify from aVal) as list
  
return aValNum
end retRecordByLabel

★Click Here to Open This Script 

2015/07/23 ASOCでレコードのリストをユニーク化

レコードのリスト(例:{{namae:”Piyomaru”,age:16}, {namae:”Piyoko”, age:13}})で、重複データ項目があった場合にそなえ、重複項目を削除する「ユニーク化」を行うAppleScriptObjCのScriptです。

BridgePlusを用いたバージョンと、ASObjCExtras.frameworkを用いたバージョンを掲載します。現在ではBridgePlusの使用が強く推奨されています。

AppleScript名:ASOCでレコードのリストをユニーク化(BridgePlus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus”

load framework

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to Cocoaify aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to ASify from bRes as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

AppleScript名:ASOCでレコードのリストをユニーク化(ASObjCExtras.framework)
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

set msRecList to {{msName:“格 陸戦型ジム 獲得済 COST: 120″, sortieTimes:12}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}, {msName:“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, sortieTimes:6}}

set newMsList to uniquefyList(msRecList)
–>  {{sortieTimes:6, msName:”狙 ジム・スナイパーカスタム 獲得済 COST: 200″}, {sortieTimes:12, msName:”格 陸戦型ジム 獲得済 COST: 120″}}

–レコードのリストをユニーク化
on uniquefyList(aList)
  set msArray to current application’s SMSFord’s Cocoaify:aList
  
set aRes to current application’s NSSet’s setWithArray:(msArray’s allObjects())
  
set bRes to aRes’s allObjects()
  
set cRes to bRes’s ASify() as list
  
return cRes
end uniquefyList

★Click Here to Open This Script 

2015/07/13 ASOCでDict書き込み_3(Bridge Plus)

完全に趣味の内容のAppleScriptを、Shane Stanleyがアップデートして送ってくれました(汗)。趣味のScriptというのは、完全に作り捨てに近いというか、あまり後先考えずに作ってしまうことが多いですが・・・

書き換えポイントは、

 (1)ASObjcExtras.frameworkではなくBridgePlusライブラリを使用
 (2)NSFileManagerでディレクトリを作成
 (3)パスの組み立て時に「URLByAppendingPathComponent:」を使うことにより、フォルダを示すパスの末尾に「:」がついていなかったとか、「::」といった誤った表記を行ってしまった場合に備えている

とのこと。do shell scriptなんてダサいぜ、的なShaneのご意見もありましたが、使って簡潔になる箇所(dateコマンドとか)は使ってもいいんじゃないかと。いえ、意見はとってもわかるんですけど、「使えるものはなんでも使う派」なんで(^ー^;;

OS X 10.11ではaliasやfileとNSURLが正確にBridgeされるようになるので、パスの取り扱いについては機能向上(簡潔な記述)が期待ができそうです。”NSURL”を書くと予約語とぶつかる、とかいうダサダサな仕様が直ることは10.11に期待したいです。

しかし、英語圏のShaneから正確な日本語データの入ったプログラムが送られてくると(リスト内のリンクをクリックすればScript Editorに転送されるのはわかっているとはいえ)、かなりビビります(^ー^;;

AppleScript名:ASOCでDict書き込み_3(Bridge Plus)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
use script “BridgePlus” – instead of ASOBjCExtras framework

load framework – BridgePlus command to load

set a1List to {“msName”, “sortieTimes”}
set b1List to {{“近 装甲強化型ジム 獲得済 COST: 200″, 66}, {“遠 ジム・キャノン 獲得済 COST: 160″, 43}, {“近 ザクII(F2) 獲得済 COST: 160″, 42}, {“近 ジム・コマンド 獲得済 COST: 200″, 32}, {“近 ジム(WD隊) 獲得済 COST: 160″, 28}, {“近 陸戦型ガンダム 獲得済 COST: 220″, 24}, {“近 ジム改 獲得済 COST: 240″, 22}, {“遠 ガンタンク 獲得済 COST: 200″, 22}, {“格 ジム(指揮官機) 獲得済 COST: 160″, 20}, {“近 ジム 獲得済 COST: 120″, 19}, {“遠 量産型ガンタンク 獲得済 COST: 160″, 14}, {“格 陸戦型ジム 獲得済 COST: 120″, 12}, {“格 ガンダム 獲得済 COST: 280″, 11}, {“近 ジム・トレーナー 獲得済 COST: 120″, 9}, {“射 ジム・スナイパーII(WD隊) 獲得済 COST: 220″, 9}, {“射 陸戦型ガンダム(ジム頭) 獲得済 COST: 200″, 7}, {“格 ガンダムEz8 獲得済 COST: 240″, 6}, {“近 ジム・寒冷地仕様 獲得済 COST: 200″, 6}, {“狙 ジム・スナイパーカスタム 獲得済 COST: 200″, 6}, {“格 ジム・ストライカー 獲得済 COST: 180″, 4}, {“格 ガンキャノン重装型 獲得済 COST: 160″, 3}, {“近 アクア・ジム 獲得済 COST: 160″, 2}, {“射 ガンキャノン 獲得済 COST: 200″, 2}, {“近 ジム・コマンドライトアーマー 獲得済 COST: 160″, 1}, {“格 ボールK型 獲得済 COST: 120″, 0}, {“格 B.D.2号機 獲得済 COST: 260″, 0}, {“格 プロトタイプガンダム 獲得済 COST: 280″, 0}, {“近 パワード・ジム 獲得済 COST: 240″, 0}, {“射 デザート・ジム 獲得済 COST: 160″, 0}, {“遠 量産型ガンキャノン 獲得済 COST: 200″, 0}}

– BridgePlus uses SMSForder instead of SMSFord in ASOBjCExtras, but method is the same
set aArray to current application’s SMSForder’s subarraysIn:b1List asDictionariesUsingLabels:a1List |error|:(missing value)

set cRec to {msList:aArray, sortieDate:date string of (current date)}

set aName to “efsf.plist”
saveRecordToFolAsPlist(cRec, “戦場の絆”, aName) of me

on saveRecordToFolAsPlist(theRecord, folName, aName)
  
  
set myAppSupDir to POSIX path of (path to application support from user domain)
  
set folderURL to (current application’s class “NSURL”’s fileURLWithPath:myAppSupDir)’s URLByAppendingPathComponent:folName
  
  
–do shell script(mkdir -p)のかわりに、指定ディレクトリまで作成
  
current application’s NSFileManager’s defaultManager()’s createDirectoryAtURL:folderURL withIntermediateDirectories:true attributes:(missing value) |error|:(missing value)
  
  
set theDict to current application’s NSDictionary’s dictionaryWithDictionary:theRecord
  
set aRes to theDict’s writeToURL:(folderURL’s URLByAppendingPathComponent:aName) atomically:true
  
  
return aRes as boolean
  
end saveRecordToFolAsPlist

★Click Here to Open This Script 

2015/05/29 BridgePlus v1.1.0 Readme日本語翻訳版

(Written By Shane Stanley、Translated By Takaaki Naganoya〜翻訳・掲載はShaneの許諾済み)

BridgePlusはAppleScriptで記述した「Script Library」で、バージョン10.9以降のOS Xで動作します。

典型的なScripterにとって、BridgePlusは「ASObjC Runner.app」の機能をもとにしたハンドラ群を含んだライブラリです。

AppleScriptObjCユーザーにとって、AppleScriptとCocoaオブジェクトの相互変換機能を向上させたコマンド群で、(AppleScript用語辞書によるラッピングを介さず)拡張機能に直接アクセスが可能なものです。

bridge.png

BridgePlusは、いずれかのScript Librariesフォルダ(~/Library/Script Libraries/ など)にインストールしておく必要があります。また、Scriptアプリケーションのバンドル内に直接埋め込むことも可能です(後述)。

基本的な利用方法

もし、あなたがAppleScriptObjCを使うつもりがない場合、BridgePlusは1つの適切なコマンドをAppleScript用語辞書の中に持っています。それが、「open lib description」コマンドです。本コマンドを実行すると、このReadme書類(英文)を自動でオープンします。

AppleScript名:sample1
use BridgePlus : script “BridgePlus”
open lib description

★Click Here to Open This Script 

BridgePlusライブラリ内のハンドラ(サブルーチン)を呼び出すためには、ライブラリ経由で呼び出すことになります。

たとえば、ソーティング(データ並べ替え)のハンドラ(サブルーチン)、

on sortListOfNumbers:listOfNumbers

を呼び出すためには、

AppleScript名:sample2
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers:{9, 2, 7, 3, 6, 4}
–> {2, 3, 4, 6, 7, 9}

★Click Here to Open This Script 

のように書くことになります。もし、OS X 10.6.x上でのAppleScriptObjCハンドラ風に、

AppleScript名:sample3
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers_({9, 2, 7, 3, 6, 4})

★Click Here to Open This Script 

と書いたとしても、コンパイル(構文確認)すると、

AppleScript名:sample4
use AppleScript version “2.4″
use scripting additions
use BridgePlus : script “BridgePlus”
set sortedList to BridgePlus’s sortListOfNumbers:{9, 2, 7, 3, 6, 4}

★Click Here to Open This Script 

のように書き換えられます。

(注記:オリジナルではここで各種ハンドラの実例紹介。サンプル部分をすべて日本語訳していると時間がかかりすぎるので、Shaneと協議の結果、まずは日本語訳も実例以外の部分を掲載ということで話がまとまった)

基本的な利用方法

BridgePlusは2つのメインコマンドを持っており、これらはAppleScriptObjC記述時に、CocoaとAppleScriptのオブジェクトの変換をシンプルにし、機能向上させるものです。

 Cocoaify:AppleScript側の値をCocoa側の等しい型の値(例:integer→NSInteger)に変換します
 ASify from:Cocoa側の値をAppleScript側の等しい型の値(例:NSInteger→integer)に変換します

BridgePlus Script LibraryのAppleScript用語辞書を見るには、

 スクリプトエディタ:BridgePlusを~/Library/Script Libraries.フォルダにコピーしたのちに、「ファイル」>「用語説明を開く…」を実行
 Script Debugger:Optionキーを押しながらScript Debuggerアイコンにドラッグ

と操作してください。

多くのケースにおいて、AppleScriptObjCによる処理は以下の3ステップから構成されます。

 AppleScript側の値をCocoa側の等しい型に変換
 Cocoaオブジェクトのinstance methodを呼び出す
 7覯漫Cocoaの値)をAppleScript側の値に変換して返す

たとえば、以下のようなAppleScriptObjCのコードがあったとして、

AppleScript名:sample5
use framework “Foundation”

set someList to {1, 3, 2} –I added :-)
set anArray to current application’s NSArray’s arrayWithArray:someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to anArray as list

★Click Here to Open This Script 

これはBridgePlusを使ってこのように書き換えられます。

AppleScript名:sample6
use framework “Foundation”
use script “BridgePlus”

set someList to {1, 3, 2} I added :-)
set anArray to Cocoaify someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to ASify from anArray

★Click Here to Open This Script 

ほんのちょっとシンプルになりましたが、劇的なほどではありませんね。しかしながら、「Cocoaify」「ASify from」コマンドを使うことによる利点は、AppleScriptとCocoaの間の型変換をより完全に行えることにあります。

・Cocoaifyコマンド:AppleScript側の値がdateオブジェクト、もしくはlist型やRecord型でdateオブジェクトを含む場合に、dateはNSDateに変換されます。alias型やfileはNSURLに変換されます

・ASify fromコマンド:Cocoa側の値がNSDateの場合、およびNSDateを含むNSArrayやNSDictionaryの場合、AppleScript側のdateに変換されます。同様に、NSURLはfile («class furl»)に変換されます。

Cocoa側の値がFloating Pointの数値を含む場合、それらはAppleScript側のrealに変換されますし、listやrecord内に含まれるrealとして変換されます。実際に、OS X 10.10.3上でCocoaのFloating Pointの数値1.2をAppleScript側のrealに変換すると1.200000047684になるなど、AppleScriptの標準機能を使うと精度が失われてしまいます。でも、BridgePlusの「ASify from」コマンドを使えば、こんな問題は起きません。Cocoaの1.2を変換しても、AppleScript側でも1.2が得られます。

なので、たとえば上記のサンプルScriptにAppleScriptのdate型のデータをlist型データに入れても、きちんとソートされます。

AppleScript名:sample7
use framework “Foundation”
use scripting additions
use script “BridgePlus”

set someList to {current date, (current date) - (2 * days), (current date) - (1 * days)} Today, The Day before Yesterday, Yesterday
set anArray to Cocoaify someList
set anArray to anArray’s sortedArrayUsingSelector:“compare:”
set aList to ASify from anArray
–> {date “2015年5月28日木曜日 0:22:39″, date “2015年5月29日金曜日 0:22:39″, date “2015年5月30日土曜日 0:22:39″}

★Click Here to Open This Script 

BridgePlusなしでAppleScriptの標準機能を使った場合には、サブルーチンを用いてひとつひとつのdateの値をNSDateに変換してから並べ替える必要があります。さらに、並べ替えたあとに元の型に変換し直す必要も出てきます。そのさいのAppleScriptObjCのコードはより長く複雑なものになってしまいます。

高度なAppleScriptObjCでの利用方法

「BridgePlus.scptd」のバンドルは、多くのScripterにとって有用なフレームワーク(BridgePlus.framework)を含んでいます。本ライブラリBridgePlusはこの内部フレームワークが持つすべての機能をサポートする用語は持っていませんが、あなたのScriptに「use script “BridgePlus”」の1行を加えるだけで、フレームワークのmethodに直接アクセスできるようになります。

フレームワーク内のmethodのほとんどは、「SMSForder」クラスから呼び出せるmethodとして実装されており、これらは「ASObjC Runner.app」(現在では廃止)をもとに機能拡張されたものです。「SMSForder」クラスのヘッダーファイルを確認するには、「open framework header」コマンドを実行します。

AppleScript名:sample8
use script “BridgePlus”
open framework header

★Click Here to Open This Script 

BridgePlusのフレームワークは、実際のところ「ASObjCExtras.framework」のアップデート版であり、ASObjCExtras.frameworkがすでにインストールされている環境でのコンフリクトやクラッシュを避けるために名称変更されたものです。

SMSForder内のmethodを直接呼び出す前に、最低でも1つのBridgePlus.scptdライブラリ内のコマンドを実行する必要があります。それが「load framework」コマンドです(もしくは何かBridgePlusの持つコマンド・・・たとえば、Cocoaify “” を実行することでもOKです)。

かんたんなサンプル:

AppleScript名:sample9
use framework “Foundation”
use scripting additions
use script “BridgePlus”

load framework
set newList to ASify from (current application’s SMSForder’s colsToRowsIn:{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}} |error|:(missing value))
>  {{1, 4, 7, 10}, {2, 5, 8, 11}, {3, 6, 9, 12}}

★Click Here to Open This Script 

やってはいけないことがあります。たとえあなたがBridgePlusライブラリをインストールしてあって使っていても、Script中に「use framework “BridgePlus”」とは書いてはいけません。その書き方ではBridgePlusバンドル内部のフレームワークは見つかりません。「use script “BridgePlus” 」の行とBridgePlusの持つコマンドを1つ実行するとフレームワークが(自動で)読み込まれるようになっています。

SMSForderはCocoa的に0からはじまるインデックスを用います、一方でBridgePlusライブラリの「AppleScriptObjC」で直接内部機能を呼び出さないハンドラを呼び出した場合にはAppleScriptの世界で標準的な1からはじまるインデックスで値を返します。

実行環境へのインストール(Deployment)

「~/Library/Script Libraries」フォルダに「BridgePlus.scptd」ファイルを入れることで、あなたのすべてのAppleScript中でBridgePlusの機能を利用できるようになります。

BridgePlusライブラリをあなたの書いたScriptと一緒に配布する際には、BridgePlus.scptdファイルをあなたのScriptのBundle内の「/Contents/Resources/Script Libraries」フォルダに入れておけば大丈夫です(アプレットの場合でも、.scptdファイルの場合でも)。

もし、あなたがScript Bundle(AppleScriptアプレット/.scptd)をCodesignすることを検討している場合、まず最初にライブラリ中に入っているBridgePlus.frameworkをcodesignする必要があります。

BridgePlusをXcodeプロジェクト内でお使いの場合、「BridgePlus.framework」をBridgePlusライブラリのバンドル内から取り出して、他のframeworkと同様にXcodeプロジェクトに登録し、「BridgePlus.scptd」バンドル内から削除する必要があります。このようにすれば、BridgePlus.frameworkのcodesignはXcodeがめんどうを見てくれます。

BridgePlusのダウンロードはこちら!
http://www.macosxautomation.com/applescript/apps/BridgePlus.html

著名なScripterでありBlogerであるTakaaki Naganoyaによる(表現原文ママ)ASObjCExtras.frameworkの総合マニュアル(英文)はこちらです。
同マニュアルは、もしあなたがBridgePlusの上級者向けの(AppleScriptObjCの)機能を利用したい場合には有用なものです。

http://piyocast.com/as/asinyaye/free-asobjcextras-scripting-guide/

本ライブラリの利用にあたって、ライブラリ全体、内部フレームワークのみの場合の両方で自由に再配布できます(再配布時の料金は発生しません)。
利用料金は発生しませんが、動作保証もいたしません。「At your own risk」でご利用ください。