空談録

世界で5人くらいに役立ちたい

Power QueryがURLを認識できないとか言い出した時

誰もこのエラー踏んでないなと思ったらそりゃ誰も踏まないわってなった。

アズールレーンというゲームをしていると自分の手持ちがよくわからなくなったので、Excelでキャラのレベルを管理しています。
ただ、わざわざキャラ一覧とかつくりたくないのでPower QueryでWikiのページから拾ってきてそこにレベルを書き足して管理してました。

ところがどっかのタイミングでなんか見かけないエラーが出てきたという話です。

使ってたクエリ

めっちゃ普通のクエリです。途中まで使えていたのが謎。
参照してたページは キャラクターリスト - アズールレーン(アズレン)攻略 Wiki

let
    Source = Web.Page(Web.Contents("http://azurlane.wikiru.jp/index.php?%A5%AD%A5%E3%A5%E9%A5%AF%A5%BF%A1%BC%A5%EA%A5%B9%A5%C8")),
    Data = Source{2}[Data],
    #"Replaced Value" = Table.ReplaceValue(Data,"C","20",Replacer.ReplaceText,{"No."}),
    #"Changed Type" = Table.TransformColumnTypes(#"Replaced Value",{{"No.", Int64.Type}, {"名前", type text}, {"陣営", type text}, {"レア度", type text}, {"艦種", type text}, {"建造時間", type text}, {"耐久", Int64.Type}, {"装甲", type text}, {"装填", Int64.Type}, {"火力", Int64.Type}, {"雷装", Int64.Type}, {"回避", Int64.Type}, {"対空", Int64.Type}, {"航空", Int64.Type}, {"消費", Int64.Type}, {"速力", Int64.Type}, {"声優", type text}})
in
    #"Changed Type"

ほぼ未加工(どっかのタイミングでテーブルが参照できなくなったのでそこだけ触ってます)

最近こんなエラーが出てきた

ところがある日更新しようとしたらこんなのが出てくるようになりました。

f:id:fantasticswallow:20180317104627p:plain

DataFormat.Error: The supplied URL must be a valid 'http:' or 'https:' URL.
Details:
javascript:false

いやどう見てもhttpで始まってるんですが…。

ぐーぐるで同じようなの探してたんですが、ゼロ幅スペースが~的なのが出てくるだけで答えにたどり着けず。
UserAgent偽装とかやっても取れないので割とお手上げでした。

javascript:falseから考えてみる

しかし今更別サイトから拾うように~って思っても面倒だしということで何とか直してみます。

まずこの現象自体は何起因で起こってるのか切り分けます。
・Webページに接続できていない
・Webページには接続できているがデータを返してもらえない (UserAgentなどでブロックされている)
・Webページに接続できてデータが返ってきているが、テーブルの中身が存在していない。(別のjsonで返してくるなど)
・Webページに接続できてデータがもらえているがパースができない

概ねこの4パターンかと。
とりあえず一番上だった場合はもはやお手上げなのでそこから見ていきます。

Web.Contents関数がbinaryのデータを返すため、接続できているか自体はこれだけで判別できます。
本当に落ちてきてるかも見るためにBinary.ToTextで文字列に変換しておきます。

Binary.ToText(Web.Contents("http://azurlane.wikiru.jp/index.php?%A5%AD%A5%E3%A5%E9%A5%AF%A5%BF%A1%BC%A5%EA%A5%B9%A5%C8"))

f:id:fantasticswallow:20180317110232p:plain

こんな感じでひとまず何かしら落ちてきていることがわかります。つまり接続自体は問題ないということに。

残る問題を切り分けていきます。3つ目についてはブラウザの開発者ツール使えば返ってきてるか見えるのでそこで判別。
めんどいので画像は割愛。こちらも含まれています(というか入ってなかったら今までどうやってたの状態)

というわけでパースに失敗している説が濃厚になってきました。
そのため先ほどのBinaryの中身を眺めてみましょう。尻切れとかかもしれませんし。
Text.FromBinaryで文字列に展開します。

f:id:fantasticswallow:20180317110938p:plain

ペロッ…これは…文字コード…!

この結果を見る限りどうも自動文字コード判定で何か違う文字コードを選択している可能性が高いです。
ならば文字コードを正しく選択すればうまくいくのでは…!?ってことでやってみましょう。

Text.FromBinaryの第2引数に文字コードを指定することができるのでここに数字を入れます。
魔法の数字についてはMSDNから拾ってきます。
Encoding クラス (System.Text)

(Power Queryは中身はC#なのでSystem.Text.Encodingの値が使えます)

wikiページの文字コードは開発者ツールで見つけましょう。charset=euc-jpとのことなので20932が第2引数になります。

f:id:fantasticswallow:20180317112415p:plain

無事に読めました。これであとはWeb.PageでパースすればOKです。

= Web.Page(Text.FromBinary(Web.Contents("http://azurlane.wikiru.jp/index.php?%A5%AD%A5%E3%A5%E9%A5%AF%A5%BF%A1%BC%A5%EA%A5%B9%A5%C8"), 20932))

これで無事に取れるようになったので、Source部分を差し替えればうごきます。


というわけでいわゆるCJKな言語の問題でした。向こうはほぼ起こらないしなぁ。。。

最近Go言語的なのを触ってますがなかなか難しいですねぇ。
全部C#にしたいきもちが出てきてしまう……。
そういうのにBlazorとかいいんだろうなと思ったり思わなかったり。

この辺で

ExcelにPython載ってもVBAは不滅だと思う話

新年なのでぽえむすたーと。

さて、年末くらいにExcelPythonをのっけることを検討する話が出てましたね。
最初よくわからずそんなんないやろという気持ちでしたが、まあPythonのお勉強はしたいのでぜひ頑張ってほしいという気持ち。

ところでこの件、割とググってると「VBAがなくなるかも!?」みたいな話が出てきます。
今出てる情報的にどう考えてもVBAが死ぬ話ではないのでその辺の思考的なのをまとめたいという記事。

中の人じゃないので本当の事実は知らないです。正確な情報はないです。その辺はご承知ください。

とりあえず追っていきましょう

前提条件

VBAの代替って話ですので、現状のVBA同様の環境になることを想定します。
つまりOfficeに言語環境を組み込み、Officeのインストールだけでスクリプトの実行が行える環境です。

Pythonのっけるにしてもどのバージョン問題

いまいちこの辺詳しくないのですが、Pythonのどのバージョンをのっけるかってそう簡単に決まる話ではないと思われます。
Python 2.xと3.xでごかんせいがーてきな話は有名ですが、Python 3.x系であればすべて同じ構文が使えるんです?というところがあると思っています。
つまり3.6と3.7で言語構文的なところに変化がないかというところ。

この間で新しく新機能が~って話になるとかなりつらみが出てきます。
新しいPythonで書いたスクリプトが過去のPython環境でうごかないんですけどーって話がある場合、組み込んだPythonをむやみに上げれなくなってしまいます。

普通に上げればいいじゃん?って思う方は身の回りでOffice 2010を使ってる人がいないか思い出すとよいかと思います。

つまるところ、組み込むにしても更新が活発なPythonのどれを組み込むのか、というのはかなり難しいお話になります。
この点枯れてそうな2.7を入れるのありなのでは?ってなりますが、サポート切れの言語をMSが組み込むのかなり評判悪そうというところ。

そのため、VBAみたいに言語仕様が変わらないのはある意味ではメリットだったりします。
さっさとOffice 365に全シフトしてほしい…

(メモ):ふと思ったのですが、Office Addins (office.jsの方。旧Apps for Office)は割とにょきにょきバージョン上げてるなと思いました。
とはいえあっちはマニフェスト定義してるしまだなんとかなるんかしら。

パッケージどうするの問題

Pythonのメリットってやはり豊富なパッケージ(? ライブラリ?)にあると思います。
こっちも普段使わないので知らないのですが、pip installは大事だとおそらく思っています。
巷でDeep learningにつよいって言われててもやはりtensorflow, caffe, chainer, scikit-learnやらのライブラリに助けられているところも大きいと思います。numpy, scipyとかもありそう。

ところで、この辺のパッケージもどう管理するのか、という話が出てきます。
たぶん生のPythonしか使えなくてうれしい!神!って人はおそらくいないと思っています。
(それ以上にDeep LearningとかData Analysisとかその辺の活用見込んでるはずなのに、その手のパッケージ一切使えません!じゃお話にならない)

さて、Pythonバージョン以上にパッケージのほうはさらに面倒です。

  • パッケージをどこに持つか
  • パッケージのバージョンをどうするか

という2つの問題が出てきます。

バージョンの話はさっきのPythonのどのバージョンの話と一緒です。バージョン互換性について一切保証されていないので、下手に組み込むわけにもいきません。
組み込まれたものしか使えません!もとてもつらみあるし。

そうなるとnuget packageよろしく、依存パッケージをインストールしようぜ!が割とよさげです。
しかしこれはこれで結構インパクトでかくて、Excelファイル開くたびにpip installが走るのウゴゴーってなります。
2MBのExcelファイル開いたら10MBのPythonパッケージ落ちてきました!とか言われるとかなりつらいという。
さらに落としてきたパッケージの管理もだるくて、毎回消してると開くたびにダウンロード走るし、全部残してるとディスク領域にゴミがというところ。

じゃあExcelファイルにパッケージ埋め込もうぜ!
なんかファイル容量一気にふえたんですけお。。。
組み込みPython問題が…。

書きながら考えてたら妥当なのが毎回ダウンロード走らすだなって思いました。
オフラインで使えないのは妥協。でも絶対ユースケースにある。(何よりもプロキシブロックとかされそう)

というわけでパッケージもちょいちょいめんどいなーというところ。

VBAExcelだけじゃないんですよ

個人的に上のはどうにかしようと思えばなる類かなぁと思っています。

ところでPythonのっける話って実はExcelとしか言われてないんですよ。
最近生きてるのか知らないAccessとか、地味にVBA使われてそうなOutlook、実は載ってるWord、PowerPoint、Projectなどもいるわけです。

VBA置き換えとなると当然これらの製品にも載るわけですが、他の製品だとCOM触れればよくねっていう感じがあるんですよ。
つまり他の製品のVBAを変えるのにPythonである必要性がないというところ。

データ分析とかの面でExcelPythonの相性がいいのは自明なんですが、PowerPointとかWordと相性いいか?といわれるとはて…となります。
その段階で他のあらゆる言語を押しのけてPythonというのはかなりよくわからない回答だなとなります。
最近はほぼWindows .NET 4,6とか載ってるのでC#いいじゃん!とかVB.NETいいじゃんとかその辺を押しのける理由があんまりないなーと。

ExcelだけPythonにして~とかだと結局VBAも保守せざるを得ないんで、それはExcelに載せない理由がなくなり死なないという流れ。
というか切り替えるときにもこれ起こって不滅なのでは…?っていう感想です。
Office Addinsも言うほど流行ってないし…
(Officeのバージョンの問題な気もする。やはり早くOffice 365に全しh)

(メモ):全く関係ないですが、Office 2019の製品にAccessとPublisherとOneNoteが載ってないオーラなんですよね。
Changes to Office and Windows servicing and support – Windows for IT Pros
これも実際出ないとわからないですが、AccessとPublisherは消滅する可能性ありそう。
OneNoteは無償化なりで販売しなくていいやという流れになってそうですが、ここ数年アップデートされてないAccessとPublisherはうーーーんという気持ち。
個人用途でAccessそもそも高いのと、商用だとSQL Database方面使おうって話で頻度低そうだし、Publisherはどの程度使われてたんだろ感なので消える可能性は十分あるかなーって感じです。

Office 2019自体もLTSB的なパッケージに対する累積アップデート状態であまり面白みがないのがぐぬみ。

VBAよりもPower Queryのほうが死にそう

というわけでVBAが生き残る理由を書いていたのですが、こうなるとだいぶ載り方が決まってきます。
Power QueryよろしくPython Editorが載るという感じか、PythonのパッケージでExcel使えるようにするとか。後者は既にある気がする。

Python Editorが載ると当然役目が大体おんなじというか下位互換味があるPower Queryちゃん存続の危機となります。
データソースへのアクセス的なところで役割分担を果たしてほしい。。。

(ちなみにPython Editorは上の問題どうするの?って話については、ファイルに紐づかないPythonだからバージョン固定じゃないし、使う機会限られるから毎回ダウンロードでいいし、Excelにしか乗せなくても問題ないという感じで丸く収まります)


という感じでなんかいまいちまとまりませんでしたが、VBAは死なんよ!何度でも生き続けるさ!という話でした。
えっ? ここまで書いてPythonにやられたらどうするのって? 1年間毎日Python for Excelの話書きます。。。

今年もゆるっとブログ書きたい…。

この辺で

2017年を振り返りたい

ブログ書いてないので振り返りでも書くかーって思ったら今22:54まじ

というわけで本当に書いてないですが振り返りましょう。猛スピードで振り返りましょう。
あと60分で終わるのかが気になる。

お仕事

はたらきたくない。

というわけでついに学生生活が終わりしゃかいじんにジョブチェンジしました。
ろっぽんぎのどこかでMacBook Proを眺め続けています。Windows 10ほしい。Office 2016ほしい。

お賃金というものによって生活水準が大幅に向上したのはいいのですが、精神が疲れる→散財パターンが多すぎてあれ。
完全にダメなポタクになってしまった。

あと早く死にたい、お仕事やめたいとかしか言ってないので何やりたいかも決めないとなぁと思いつつ。
今後何になるかは未定です!って言いながら仕事してるマンです。もうだめだ。

そさげ

ぱどどら:やめた
しゃどばす:第4弾でついていけなくなって第5弾で終止符がうたれた
ゴ魔乙:ギルバトだけやってる状態になってきた。土曜スコアタは参戦してもいい気がするけどもちべーーしょんがどっかいってしまっている。

そして2月ごろに✝闇✝のゲームFGOを開始したわけです。
いろいろ書きたいところはあるかもですが時間がないので
・よかったところ
 マリーかわいい
 エリちゃんかわいい
 1章たのしかった
・わるいところ
 ガチャはわるいぶんめい
 とりあえずグロければいいんだろ的な表現
 育成がだるすぎる
こんな感じでしょうか。もはやなんで続けているのか謎い。

ガチャはもうちょっと確率上げてほしいっていうか凸ゲーなのかよくわからなくなるやつ。
宝具Lvしか上がらないからいいじゃんってことなんですかね。わかりません

育成についてもスキル上げがエンドコンテンツなのはいいとしても、再臨がいつまでたっても楽にならないのはどうなんだろうと思ったり。
他のゲームってもうちょっと進行すると進化とかが割と楽になる気がするんですがFGOまったくその兆候がない…(新しく出てきたキャラが一線級になるまでが長いと使われないままさらに新しいキャラになりかねないので早くなる気がする)
えっ? 種火とQPと再臨素材を毎日集めてれば楽になる? ごもっともだけど…ごもっともだけどううむ

あとは9月くらいにアズールレーンを開始
ストレス要素を減らした艦これって感じの印象ですね。いいところは残しつつ批判ポイントは回避している感じ(羅針盤とか羅針盤とか羅針盤とか。あとは修復システム、轟沈やら)
ロドニーかわいい
とはいえ難易度の頭打ち感が否めないのでそろそろ苦しそうかなーという印象
このペースの難易度上昇で11章とか12章とか実装できるんだろうかって感じがあります。脅威度下げるシステムで~って言っても限界あるので今後どうするのかが気になったり。

課金周りとかは本当に良い感じなので頑張ってほしい気持ちはあります。
願わくばこの世のガチャの闇を照らし出す光が遍く注がれんことを。

ちなみに現在の艦隊はこんな感じ
f:id:fantasticswallow:20171231232435p:plain
ロドニーしかケッコンしてない…。
もう一人を決めるのが困る。

お花

じゃぶじゃぶしてた。
いや月5000は守ってたはず…?

割と完成してきて総合力570000とかになってきました。
f:id:fantasticswallow:20171231232650p:plain
あと3万は上げたいけどそっから先はほぼ無理な気がする。

そろそろ虹に置き換える以外の手段が取れなくなってきていて、しかし私は金でいいからつかいたいんじゃーーーってなるのでここからアンプルゥ投入して60万狙うのがちょうどいいかなぁという感じです。
パーティ1はほぼフルアンプルゥにすることで総合力を担保状態。

最近はお花も割とイベント走れてないのでいろいろとゲーム続けるのの限界を感じる日々です。
何もかも仕事がながすぎるのがわるい…

あれげ

たしか4本くらい買っていた気がします。
そして1本もクリアしていないという悲劇。

オトメドメインはお嬢様攻略途中でなぜかフェードアウト(たぶん時間取れなくてやれてなかったらやらなくなってしまった)
バルドブリンガーは武器集めに執着しすぎてメインを進めずにぐわーっ
CM3D2は買ったばっかりでやれてない。
金色ラブリッチェはお姫様攻略完で割とよさげなので走り切りたさ。

やる暇が本当にない……
そさげ時間を減らすとかそさげしながらオートで読むとか策を講じたい

PC

デスクトップを買いました
つくものそこそこよさげなミドルタワー

ノートPCでやってるといろいろ限界が感じられてたのでちょうどよかったという感じはあります。
限界というかコスパがひどくわるい…(同じ快適さを出すのに倍くらいかかるイメージ)

とりあえずVRとかできそうなGTX 1070とか積んだ子になりましたが、大体快適に動くのでとてもいいです。
特にお花が快適

あとモニターもEV2456に変更。こっちは本当によくて、目が疲れて昼寝ということがものすごい減りました。
怪しい疲れたおっさんを見る必要もないですしノングレア最高

しゅっぴ

学生時代「ふぁぼツバメは金を持ったらめっちゃ物買いそう」と言われていました。
では社会人になってどうなったか確認してみましょう。

2017年カテゴリ別出費~

  • しょくひ :198818
  • いやしカテゴリ:294489
  • そさげ :194740
  • その他の趣味:395354

ファッ!?
その他はPC周りがでかすぎたので放置することにして他がつらい
食費と同レベルにそさげに課金する私は一体っていうかこれだけあれば椅子が…椅子が…かえる…
つらい

癒しと書いたポタク関連については、正直ある程度はしょうがないとして、ちょっと高くないですかという気持ちも
これだけ使えば抱き枕カバーが7個も増える
一回のイベント費用が4万になってる時点でお察し状態

来年の目標

まだ環境整備が終わってないのでとりあえずその辺買えるまで出費の比率変えたさががが

とりあえず
PS4
・椅子
・G9 X MarkII
あたりを買うまではそさげ課金を抑えたいなぁ……。

というわけで
FGOは福袋だけ課金に
・そさげ切りたい
・癒し予算雑に切りすぎているので必要性を再確認したい

というところを目標に平和に生存していきたさ。


うおおお文章能力の低さが…。
書いてて難ですがすっごい読みづらい気がする。
でもあと3分しかないのですし。

来年はOffice 2019が出るとかなのでその辺を足掛かりにまたOffice追っていくようにしたい。

この辺で。

PHPでSlackに現在の降雨情報を投げてみる

暑い寒い寒い寒い暑い暑い寒いみたいなの本当につらいのでやめてほしい。

たまにSlackで現在の降水情報を見たいと思うときがあります。ないですかそうですねそんな気もします。
アメッシュ見ろやって話ではあるんですが、範囲の狭さ的に「そろそろやばいな」オーラを把握するのが面倒です。

というわけで、誰が得するのかわかりませんが現在の雨情報をSlackで投げてみましょう。
LambdaとかAzure Functionとかでやればいいんでしょうが、お金がないのでサイト動かしてるサーバーで簡単に動くPHPにします。
ほかの選択肢もあるにはあるんですけど、いまいち動かし方わからんという感じなので…。

全体の流れ

今回はSlackで適当に雨って投げたら動いてくれる仕組みにします。

というわけで
・Slackに投稿 (ユーザー)

↓(Outgoing Webhook)

PHPで雨情報取得 (サーバー) ←→ どこかの降水量情報

↓(Slack API (files.upload))

Slackに投稿される

みたいな感じにしましょう。

なんかOutgoing WebhookはLegacyとか言われていますが、特に終わる気配はないはずなのでこちらで。
internal integrationsとかいまいちわからないですし…?

まあなんにせよSlackから雨情報くれというのがサーバーに渡ればいいので、slash commandあたりでも作れると思います。
サクサクと作っていきましょう。

降水量を取得しよう

Outgoing Webhookの設定は世界のどこかにあると思うので(というか面倒なので)すっ飛ばしていきます。

降水量については、できれば高解像度降水ナウキャストがほしいんですけど、使う手段がよくわからない状態です。
気象庁 | 高解像度降水ナウキャスト

そのため今回はYahooのスタティックマップを使って現在の降水情報を取得し、Slackに投げていきます。
便利なことに画像でそのままくれるので、そのまま投げればよい感じです。

developer.yahoo.co.jp

使い方は上からどうぞ。
多彩なオプションが使えるので自分だけの最強アメッシュ画像を作ろう!とかができます。

今回はこんな感じで使っていきます。

https://map.yahooapis.jp/map/V1/static?
appid={appId}&
lat=35.504193&
lon=139.473445&
z=9&
width=600&
height=450&
overlay=type:rainfall|
datelabel:off&style=base:green|
off:background,address,area_name,line_name|
on:nature,water,prefecture

appIdは自分のアプリケーションIDをいれます。
lat, lonは座標です。好きな座標を入れましょう。
zはズームレベルを示します。値が大きいほど狭い範囲の拡大画像となります。
width, heightは画像の大きさを示すので好きな値で。
overlayは地図上に出すメタ情報的なものを指定できます。
例えば富士山などの地名、東名高速道路などの道路名、山手線などの線路などを表示するかを指定できます。
雨が見たいので割とoffでざっくり消してます。県の名前くらいはさすがに欲しいのでprefectureはon。
降雨情報はtype:rainfallで表示できます。

これでこんな画像ができあがります。
f:id:fantasticswallow:20170903175414p:plain
色合いとかはstyleとかでいじってください…。

さて、この画像をPHPで取得していきます。
コードはこんな感じ。

$amedasLink = "https://map.yahooapis.jp/map/V1/static?appid=...";
$imageData = curl_get_contents($amedasLink);
$ts = strval(time());

$imageFileName = "amedas-" . $ts . ".png";
$fileRet = file_put_contents("./" . $imageFileName, $imageData);


function curl_get_contents($url)
{
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);

    $data = curl_exec($ch);
    curl_close($ch);

    return $data;
}

もともとはamedasLinkをfile_get_contentsでとっていたのですが、なんかうまくいかず…。
助けてStackOverflowをしたらこんな記事が見つかったのでcurl_get_contentsをそのままもらいましょう。
stackoverflow.com

これでひとまずyahooの降雨情報をサーバー上で取得して保存まではできました。
保存した画像をSlackに投稿していきます。

files.uploadをたたこう

ところでOutgoing Webhookではファイルアップロードはできません。
attachmentsのimage_urlを指定すればいいじゃん!っていう話ではありますが、諸事情であんまりドメインとか見られたくないとかあったりあったりで。
まあなんにせよSlackへのファイルアップロード自体はできません。参照はできます。

というわけでアップロードしていきましょう。
アップロード自体はSlack APIのリファレンス読めばまあ何とかなるかなぁと。
files.upload method | Slack

POSTで投げるパラメータ自体はある程度理解できそうなのでこちらもcurl_execで投げつけていきます。

$slackUpload = 'https://slack.com/api/files.upload';
$ch = curl_init($slackUpload);

if(array_key_exists("channel_id", $_POST) == true)
{
    $channel_id = $_POST["channel_id"];
}

// Create a CURLFile object
$cfile = curl_file_create($imageFileName);

// Assign POST data
$postParams = array(
    'token' => '{your token}',
    'channels' => $channel_id,
    'filename' => $imageFileName,
    'file' => $cfile
);
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data;charset=utf-8', 'User-Agent: curl']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postParams);

// Execute the handle
curl_exec($ch);
curl_close($ch);

試行錯誤の成果みたいな状態…。

Outgoing WebhookではPOSTメソッドでtrigger_textやらを投げてくるので、POSTパラメータから投稿されたメッセージのchannel_idを取得します。
その後curlで投げるPOSTパラメータにchannelsという形で指定します。これで投稿されたチャンネルに画像が投稿されます。

curlでPOST投げるコードについては次の記事あたりを参考にしました。
masabloggers.blogspot.jp

curl周りは良しなに設定してなげつけます。
これで基本的にはうまくいくはず…?

最終的なコード

大体こんな感じです。

function amedas()
{
	$amedasLink = "https://map.yahooapis.jp/map/V1/static?appid=...";
	$imageData = curl_get_contents($amedasLink);
	$ts = strval(time());

	if(array_key_exists("channel_id", $_POST) == true)
	{
		$channel_id = $_POST["channel_id"];
	}

	$imageFileName = "tiger-amedas-" . $ts . ".png";
	$fileRet = file_put_contents("./" . $imageFileName, $imageData);

	if ($fileRet > 0)
	{
		$slackUpload = 'https://slack.com/api/files.upload';
		$ch = curl_init($slackUpload);

		// Create a CURLFile object
		$cfile = curl_file_create($imageFileName);

		// Assign POST data
		$postParams = array(
			'token' => '{your token}',
			'channels' => $channel_id,
			'filename' => $imageFileName,
			'file' => $cfile
		);
		curl_setopt($ch, CURLOPT_POST,TRUE);
		curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);
		curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: multipart/form-data;charset=utf-8', 'User-Agent: curl']);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $postParams);

		// Execute the handle
		curl_exec($ch);
		curl_close($ch);
	}
}

file_put_contentsの戻り値はファイルサイズを返すので、0だったらどちらにしろダメという感じで抜けます。
あとは戻り値を取るとファイル書き込みの完了を明示的にできるというあれそれ。



説明っぽい説明がすっ飛んでいる記事になってしまった。
そもそもPHPが詳しくないというのが問題な気がする。

最近いろいろ思うところもあるので、またOfficeとか追っていきたいなぁという感情はありつつ。
しかし会社でOffice使わないのがスーパーモチベーション下がる…。
うーんうーん。

この辺で

Microsoft.AspNetCore.Razor.Language + Roslyn + .NET Core でファイル生成をしたい (その2)

読みやすいブログが分からないのでひとまず分けてみるやつ。

artfulplace.hatenablog.com

↑の続きです。C#のコードが作られたのでRoslynで実行するだけの簡単なお仕事。

ではやっていきましょう。

Razorのコードを整形する

まあさすがにそのまま投げてもこけるのが目に見えてるっていうところです。
生成されたコードで呼ばれている"Write"と"WriteLiteral"の呼ぶ先を用意しないとそこで落ちてしまいます。

usingの設定とかをざっくり行ってくれるメソッドを作りましょう。
大体こんな感じでしょうか?

internal class RazorCodeFormatter
{
    internal static string Format(string generatedCode)
    {
        var code = "using System;" + NewLine() + "using System.IO;" + NewLine() + NewLine();
        foreach (var line in generatedCode.Split(new string[] { NewLine() }, StringSplitOptions.RemoveEmptyEntries))
        {
            var trimLine = line.Trim();

            if (trimLine.StartsWith("#"))
            {
                continue;
            }

            if (trimLine.StartsWith("public async override"))
            {
                code += "        public static Action<string> WriteLiteral { get; set; }" + NewLine();
                code += "        public static Action<object> Write { get; set; }" + NewLine();

                code += NewLine();

                code += "        public static void TemplateMain()" + NewLine();
            }
            else
            {
                code += line + NewLine();
            }
        }

        return code;
    }

    private static string NewLine()
    {
        return Environment.NewLine;
    }
}

#pragmaとか#lineを消しつつ、コードを再構築します。
基本は普通につないでます。

違うところとしてはExecuteAsyncの宣言部分です。
この部分についてはこちらで呼ぶ分にはawaitableである必要がないこと、ほぼ確実に一回しか呼ばれないことから別の宣言を作るには適しているという特徴があります。

まあとりあえずstatic voidにしておきます。
名前は何でもいいです。TemplateMainにしておきました。

またWrite, WriteLiteralの値を受け取りつつ実行できるようにするために宣言を加えています。
static ActionでWriteLiteral, Writeを表現している理由は .NET Core固有の問題なんですが、ビルドするときに参照しているライブラリと、実行するときに参照しているライブラリが違うため、Roslynで生成するライブラリから呼び出しているアプリケーションの参照を行えないためです。
(アプリケーション側ではSystem.Runtimeなどを参照していますが、実行時に参照しているのは System.Private.CoreLib.ni.dllです。)

整形メソッドを呼ぶと前回のコードがこんな感じになります。

using System;
using System.IO;

namespace Razor
{
    public class Template
    {
        public static Action<string> WriteLiteral { get; set; }
        public static Action<object> Write { get; set; }

        public static void TemplateMain()
        {

    var test = "hogehogefugafuga";
            WriteLiteral("\r\n\r\na ");
Write(DateTime.Now.ToString());
            WriteLiteral(" Eeeeeeee\r\n\r\n");
Write(false);
            WriteLiteral("\r\n\r\nname = ");
  Write(test);
            WriteLiteral("\r\n");
        }
    }
}

インデントが崩れるのは仕様です…。

ここまで出来たらRoslynで実行していきましょう。

Roslynで実行しよう!

正直Roslynわからないというのでググったら一瞬で答えが出たのでコピペします。

.NET Core - Roslyn と .NET Core によるクロスプラットフォーム コードの生成
↑の図7にある "Emit API とリフレクションによるコードのコンパイルと実行" のコードを丸パク参考にさせていただいてコードを書くと次の通り。

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;

// ~~~~~~

internal class CodeBuilder
{
    internal static void Build(string code, string filePath)
    {
        var tree = SyntaxFactory.ParseSyntaxTree(code);
        string fileName = "mylib.dll";

        var systemRefLocation = typeof(object).GetTypeInfo().Assembly.Location;
        var systemReference = MetadataReference.CreateFromFile(systemRefLocation);
        
        var compilation = CSharpCompilation.Create(fileName)
          .WithOptions(
            new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
          .AddReferences(systemReference)
          .AddSyntaxTrees(tree);
        string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
        EmitResult compilationResult = compilation.Emit(path);
        if (compilationResult.Success)
        {
            Assembly asm =
              AssemblyLoadContext.Default.LoadFromAssemblyPath(path);

            RazorViewReceiver.InitializeReceiver(filePath);

            var type = asm.GetType("Razor.Template");
            type.GetProperty("WriteLiteral").SetValue(null, new Action<string>(x => RazorViewReceiver.WriteLiteral(x)));
            type.GetProperty("Write").SetValue(null, new Action<object>(x => RazorViewReceiver.Write(x)));

            type.GetMethod("TemplateMain").Invoke(null, null);

            RazorViewReceiver.FinishReceive();
        }
        else
        {
            foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
            {
                string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}, Location: { codeIssue.Location.GetLineSpan()}, Severity: {codeIssue.Severity}";
                Console.WriteLine(issue);
            }
        }
    }
}

自分で書いたコード探す方が手間というくらいコピペ…。
Reflectionしか書いてないので解説についてはさっきのページを読むとよいかと

System.Runtimeらへんに参照を追加してコンパイルして作成したDLLを読み込んでReflectionで実行という流れです。
WriteとWriteLiteralにはラムダ式で参照していきます。実行時参照は共通化されるので、SetValueは普通に実行できます。

最後にRazorViewReceiverについて書いていきましょう。

Razorの出力をファイルに書き出す

普通にWriteとWriteLiteral受け取ってStreamWriterで書くだけです。簡単ですね。

public class RazorViewReceiver
{
    private static FileStream file { get; set; }
    private static StreamWriter writer { get; set; }

    public static void InitializeReceiver(string fileName)
    {
        file = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite);
        writer = new StreamWriter(file);
    }

    public static void FinishReceive()
    {
        writer.Flush();
        writer.Dispose();
        file.Dispose();
    }

    public static void WriteLiteral(string content)
    {
        writer.Write(content);
    }

    public static void Write(object content)
    {
        writer.Write(content.ToString());
    }
}

改行文字は勝手にやってくれるので、WriteLineは使わないでおきます。
あとはStreamWriterを開くのと閉じるのを明示的に行うためにInitializeとFinishを用意してTemplateMainを挟むとOKです。

ここまでやったらFormatとBuildをGenerateCodeに対して実行します。
実行した結果が下の画像です。

f:id:fantasticswallow:20170811161918p:plain

すると…なんということでしょう! みすぼらしいcshtmlからこれまたよくわからないファイルが生まれているではありませんか!

まあこれだけだと使いにくい気もします。C#のコードをもっと呼べるようにしたいとかできるはずなのですが今回は面倒なのでしません。


というわけでRazorを使ってファイル生成しました!という話でした。
指定したフォルダのcshtmlを全部一括でやるとかまではやりたいんですけどシンプルにやるならこんくらいかなぁと。

最近会社がMacで家がWindowsで文字を入力するのがカオスになっています。
キーボードの配列くらい統一してくれませんかね…………。

東方の新作が出ている音がするけど辞書ファイルを作るモチベーションが足りていない……。

この辺で。

Microsoft.AspNetCore.Razor.Language + Roslyn + .NET Core でファイル生成をしたい (その1)

夏休みの自由研究は4時間かからなかったようです…。
Razorが優秀すぎる。

というわけで、Macとかで使えるコード生成の仕組みがほしいなーと思ったわけですが、固定ファイルだけだとさすがに汎用性なさすぎるし、かといってテンプレートエンジンの仕組みを作るのはだるいわけですよ。
なので既存のテンプレートエンジン使ってコード生成できる仕組みを作ろう!という話です。

既存のテンプレートエンジンに乗っかってファイルを作れると
・テンプレートエンジン作るコスト、メンテナンスコストがなくなる
・構文考えなくていい
・ドキュメント作らなくていい
…最高ですね!

それでひとまず思いつくのはT4かRazorというところなんですが、T4は普通の.NET Frameworkを超えた世界にいける見込みがあんまりないです。
Mono.TextTemplatingとかあったけど.Net 4.5.1とか見えてみなかったことに。
しかし!Razorは! ASP.NET Coreに対応!している!!!
つまりMacとかでも動かせそうな雰囲気です。Razorに乗っかっていきましょう。

そもそもMicrosoft.AspNetCore.Razor.Languageって何?

AspNetCore.RazorってRazorとRazor.Runtimeしかなくない?っていうところですね。
ASP.NET Coreのバージョン1.x系では、RazorとRazor.Runtimeしかなかったわけですが、2.xでリファクタリングする際に、パーサー、コード生成部分を新しいアセンブリに分割することになっています。
(AspNetCore.Razor.Evolutionとかいう名前だったやつがRazor.Languageです。)

この辺の話は下のissueでご確認ください。
github.com

つまるところRazorEngineとかがRazor.Languageに分離された形です。
逆にAspNetCore.Razorに何が残ってるのかよくわからない感じですがまあその辺はリリースされたらきっとわかるはず…。
(今のところenumが3つ入ってるだけでよくわからない…。)

以下の話は2.0.0-preview2を用いてやっていきましょう。

プロジェクトの準備をしよう

.NET Coreのコンソールアプリケーションで作っていきます。
NuGetから次のパッケージを入れておきます。

Install-Package Microsoft.AspNetCore.Razor.Language
Install-Package Microsoft.CodeAnalysis.CSharp
Install-Package System.Runtime.Loader

こんな感じに入っていればOKです。

追記(8/14) : 気が付いたら Microsoft.AspNetCore.Razor.Language の2.0がリリースされていました。
が.NET Standard 2.0以上対応です。.NETCoreApp 1.1でやると入らないので注意してください。
(2.0.0-preview2は.NET Standard 1.6なので入る)

f:id:fantasticswallow:20170811153724p:plain

RazorでC#のコードを作成する

全体の流れとしては次の通りです。

cshtml(インテリセンス効くし…) → (Razor) → C#コード → (Roslyn) → 生成結果

ここで注意すべきはRazor単体ではファイルが生成できないという点です。
Razorはビューエンジンですが、ビューの最終的な生成はASP.NET側に任せているといえます。

とまあRazorの基本をおさえたところで、実際にC#のコードを作ってみましょう。
RazorEngineに投げてRazorCSharpDocumentを作るだけの簡単なお仕事です。

var engine = RazorEngine.Create();
var project = RazorProject.Create(@"D:\razorlanguage");

var templateEngine = new RazorTemplateEngine(engine, project);
var csDocument = templateEngine.GenerateCode("_ViewStart.cshtml");
var codeDocument = templateEngine.CreateCodeDocument("_ViewStart.cshtml");

Console.WriteLine(csDocument.GeneratedCode);

ファイル名とかは適当。
ひとまず生成できればあとは使い方次第という形なので、決め打ちでまずは動かしています。

なんか特にドキュメントっぽいものがなくて泣きそうでしたが、大半はabstruct classであること、RazorEngineとかいうそれっぽいやつがいることからまあそこそこ推測はできます。

RazorEngineとRazorProjectを作ったらRazorTemplateEngineを作成します。
RazorTemplateEngine自体はただの便利クラスでしかないのですが(RazorEngine、RazorProjectを扱いやすくしているだけ)、まあ使えるものは使いましょう。

RazorTemplateEngine作ったらあとはcshtmlを投げてコード生成するだけです。
GenerateCodeでRazorCSharpDocumentをくれるのでわーい楽ちーんという。
RazorCodeDocumentは必須ではないです。ただ元のファイルパスが手に入らないのでひとまず作成しています。

ここまで書いたらいったん次のcshtmlで動かしてみましょう。

@{ 
    var test = "hogehogefugafuga";
}


a @DateTime.Now.ToString() Eeeeeeee

@false

name = @test

Razor初心者です!よろしくお願いします!

上のcshtmlを変換すると次のC#のコードが出てきます。

#pragma checksum "D:\razorlanguage\_ViewStart.cshtml" "{ff1816xx-xxxx-...}" "d8bf95..."
namespace Razor
{
    #line hidden
    public class Template
    {
        #pragma warning disable 1998
        public async override global::System.Threading.Tasks.Task ExecuteAsync()
        {
#line 1 "D:\razorlanguage\_ViewStart.cshtml"

    var test = "hogehogefugafuga";

#line default
#line hidden
            WriteLiteral("\r\n\r\na ");
#line 6 "D:\razorlanguage\_ViewStart.cshtml"
Write(DateTime.Now.ToString());

#line default
#line hidden
            WriteLiteral(" Eeeeeeee\r\n\r\n");
#line 8 "D:\razorlanguage\_ViewStart.cshtml"
Write(false);

#line default
#line hidden
            WriteLiteral("\r\n\r\nname = ");
#line 10 "D:\razorlanguage\_ViewStart.cshtml"
  Write(test);

#line default
#line hidden
            WriteLiteral("\r\n");
        }
        #pragma warning restore 1998
    }
}

自動生成感あふれる温かみのないコードですね

このC#のコードを実行するとうまい感じにビューが作られたり、ファイル出力ができるわけです。
とはいえ結構長めになってきたのと、Razorっぽい話がここで終わるので、Roslynで実行するのは次の記事に分けます。


というわけでRazorでC#のコードを吐くところまで行いました。
Razorがどういうやつなのか分かったのはよかったです。てっきりファイル出力までやってくれると思っていたらそんなことはなかった…。

つづき:
artfulplace.hatenablog.com


この辺で。

PMXモデルを3Dモデル読み込みでPowerPointに読み込ませる

なぜか買ったMetasequoiaが輝く!

というわけでPowerPointというよりはMS Office全体に3Dモデル読み込みが付くようです。
Office Insiderではすでに利用できます。
フォーラムの通知は以下の通り
https://answers.microsoft.com/en-us/msoffice/forum/all/new-3d-in-office-now-available-to-office-insiders/cac671a4-56de-4cbd-9d48-ba7efdf0eea2

というわけで早速試しましょうって話です。
しかしモデルなんて作る才能がないのでダウンロードしたPMXを読み込ませてみます。
もちろん直接は読み込めないのでちょいちょい作業をしましょう。

目指すゴール

よくわからない規約が存在して面倒把握できない界隈なので、なるべく規約に触れないモデルを読み込ませましょう。
(改造がおおむね必須なので。なしでもいける場合はたぶんない)
(フォーマット変更も改造っちゃ改造ですし?)

今回はいつものようにプロ生ちゃんを使わせてもらいましょう。
改変可というのが分かりやすくて大変良いですね。

pronama.azurewebsites.net

↑のMMD向けのモデルを使用します。

f:id:fantasticswallow:20170806215830p:plain

こんな感じで読み込めたらゴールとしましょう。

Officeで読み込める3Dモデルの形式について

読み込みましょう!といっても読み込める形式が分からないと話になりませんね。
というわけでOfficeを見てみると、次のモデルが読み込めることが分かります。

  • fbx (Autodesk Filmbox)
  • obj (Wavefront)
  • 3mf (3D Manufacturing format)
  • ply (Stanford Polygon format)
  • stl (STereoLithography)
  • glb (binary GL Transmission Format)

割と標準的な面々…?がそろっている雰囲気です。
(正直この時点で察せる部分がある方はいると思いますが、機能的にPMXを読み込んでもしょうがない感じではあります)

ひとまずPMX→(変換)→読み込める形式→Officeとやるのが妥当に見えますのでこの方向で行きましょう。

PMXを変換しよう

そんなわけで変換します。
読み込むにあたって以下のものを使用します。

これだけあれば十分読み込めるはずです。

さくっと作業しましょう。
まず読み込みたいPMXをPMXエディタで開きます。

保存する前に、 "材質" タブを開き、材質のテクスチャファイルに日本語が含まれていないことを確認してください。

f:id:fantasticswallow:20170806222158p:plain

この中で日本語のファイル名のテクスチャが指定されていると、後で読み込めなくなる可能性があります。適宜リネームして半角文字列だけで表現してください。

日本語文字列などがない状態になったら、"ファイル" → "エクスポート" で "mqoファイル"に書き出しましょう。
(一覧にない場合はプラグインが入ってないのでいれてください)

f:id:fantasticswallow:20170806222415p:plain

えっ? obj形式が選べるじゃん!!って感じですが、たいていの場合でうまくいかない気がするのでここは耐えてmqoにします。
mqoにしたらあとはMetasequoiaで作業します。

mqo → objの変換

先ほどつくったmqoファイルをMetasequoiaで開きます。

そしたら特に変更せずに "ファイル" → "名前を付けて保存" を選択します。
この時の保存形式は "Wavefront (*.obj)" にします。 (Standardでテクスチャ反映しながら出せるのがこれしかない…)
また、この時の保存するファイル名には日本語をいれてはいけません。

f:id:fantasticswallow:20170806223105p:plain

先ほどは "プロ生ちゃんSD"でしたが、テクスチャが読み込めなくなるので "pronama_SD_mqo.obj"に変更しています。
保存を押すとOBJ形式のエクスポートの設定ダイアログが出るので、大体デフォルトで出力します。
(MTLは出してください)

f:id:fantasticswallow:20170806223307p:plain

こうすることでobj形式が無事にうまれます。やったぜ!

PowerPointでobjファイルを読み込む

objファイルが書き出せたらPowerPointで読み込んでみます。
"挿入"タブ → "描画? (Illustration)"グループの "3Dモデル"をファイルから読み込みましょう。

すると最初の画像のような感じで読み込めるはずです。 (画像のは正面向いてないけど)

なんかおかしいとき

せっかくなので適切に読み込めない場合の例を挙げておきます。

f:id:fantasticswallow:20170806223951j:plain

ファイル名が日本語になっている場合が左です。おそらく.mtlファイルが発見できずに読み込みに失敗しています。

また真ん中はPMXエディタで出力したときです。右の正常な状態と比べて、テクスチャが明らかにやばい感じです。
おそらくUV座標が適切に吐き出されていない気がします。
(PMXは割とテクスチャ画像を正方形にしない感じがあるので、そこで壊れてる可能性あり。)

こんな感じでテクスチャまで読み込もうとするとはまりやすいポイントがあるので注意はいります。

ところで読み込んで何するの?

モデルが見れます。おしまい

……えっ? 何々? そんなんならやらなくてよくないって?
まあ実際今のところ(というか今後通して)そんな気はします。

というのもPMXにあるようなモーフやらボーンやら剛体やらという機能はすべて使えず、ただのモデルだけが存在する状態です。Y字?モデルのままなのでうーーーんという。
ポーズ変えてモデルとして書き出すとかやるなら画像貼り付けでよくない?っていうところもありますし。
(3mfとかplyとかstlはそういうボーンとか持たない感じあるのでまあしょうがないんですけど……)

あくまで「3Dモデルを直接読み込んで、適切な角度から眺められる」機能でしかないです。GUNUNU。
ただPowerPointのMorphは効くので、モデルを回して眺めるみたいなことをスライドに入れることはできます。
むしろExcelとかは読み込んでも全く使い道がわからない………



というわけで新しい機能をPMXで試すという話でした。
PowerPointでギリギリ活用できるかな?くらいなのがなんとも厳しい…。

最近ただのゲームのスクショを張りまくるマンになっているのでなんかもう少しなんとかしたさがある。
でもおしごとでプヨグラミングするとつかれる。きゅうじつはゲームしたい…。

この辺で