空談録

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

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でギリギリ活用できるかな?くらいなのがなんとも厳しい…。

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

この辺で

PowerBIでお小遣い管理をしよう

月一更新…なんのことかな…?

Office 365を契約しているとPowerBIもなんだかそこそこ使える雰囲気があるのですが、まったく使わないのももったいないと思うわけです。
というわけでBIっぽく自分のお金の状態をPowerBIに載せて分析してみましょう。

なおPowerBIの使い方はよくわかってないので、すごいざっくりとした使い方のみです。

とりあえずデータを作ろう

まずはPowerBIに載せるデータを用意しましょう。
今回はExcelを使います。というかExcelじゃないとつらい。

生のデータを直接出すとなんかよろしくない感じがあるので、ダミーデータをサクサクと生成します。
データの要素としては、
・日付
・消費金額
・累積値
・(カテゴリ)
・(項目名)
とします。()のものは必須ではないです。

おおむねこんな感じのテーブルになります。
PowerBIに載せるためにテーブルにしとく必要があるのでそこだけ注意です。

f:id:fantasticswallow:20170709110330p:plain

支出は+、収入は-で管理します。
Currently Costのところは '=E6 - D7'みたいな式で計算。
Categoryは好きにお使いください(入力が面倒なのでA,B,C...みたいな振り方している)

データについては次のような条件を基に生成しました
・30日に200000くらいお金を手に入れる
・大体毎日ごはんに =INT(RAND() * 1000 + 500)くらいの出費をする
・毎月1日にOffice 365に900円、携帯代として8000円はらう
・4/1に交通費として70000払い、以後は5000円ずつ交通費として使用
・大体3週間おきに =INT(RAND() * 2000 + 2500)くらいの本を買う
・10日おきに2900円払うと無料で10連を回すやつをやる
・月末金曜日に1,2本何か買う
・あとたまに適当な出費

みたいな感じです。私ではありません

生成し終わったらテーブルを日付順にソートします。これでひとまずテーブルは完成です。

日付ごとの残金を作成する

上のやつだけでも行ける気がしますが、日ごとの推移を追うにはちょっと不便なのでもう一つテーブルを作成します。
というのもすべての日にデータを入力してるわけではないのと、複数行に同じ日付がはいるケースがあるため、集計が面倒だからです
なので日付と残金だけ入れたテーブルを作成しましょう。
こっちはPower Queryで簡単に。

Advanced Editorで見るとこんな感じです。

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Category", type text}, {"Date", type datetime}, {"Name", type text}, {"Price", Int64.Type}, {"Currently Costs", Int64.Type}}),
    Custom1 = Table.FromColumns({List.DateTimes(#"Changed Type"{0}[Date],Duration.Days(DateTime.LocalNow() - #"Changed Type"{0}[Date]) + 1, #duration(1,0,0,0))}),
    Custom2 = Table.AddColumn (Custom1, "test", (x) => Table.Last(Table.SelectRows(#"Changed Type", each x[Column1] = [Date]))[Currently Costs]),
    Custom3 = Table.FillDown(Table.ReplaceErrorValues(Custom2, {"test", null}), {"test"}),
    #"Changed Type1" = Table.TransformColumnTypes(Custom3,{{"Column1", type date}})
in
    #"Changed Type1"

Changed Typeまでは自動生成なので(Excelのテーブルから作成するとそこまで入る)、下3行について説明します。
Custom1はテーブルの最初の日付から、現在の日付まで1日おきのリストを作成します。
DateTimesの第2引数にはリストの項目数を指定するのでDuration.Daysで差分の日数を入れている感じです。

Custom2は残金の列を作成しています。特定の日付の中でも最後のデータをその日の最終残金として取得して列を生成します。

f:id:fantasticswallow:20170709113501p:plain

上のような感じで、日付と対応する金額が入るのですが、支出が記録されていない日についてはErrorとなります。
ここは前の日のデータを引き継いでほしいので、次のCustom3で対応します。

Custom3では、Errorをいったんnullに置き換え、その後FillDownでnullを一つ上の行のデータで埋めるようにしています。
これで連続したデータの完成です。

最後にDateTimeになっているのをDateにしてExcelに吐き出して完了です。

PowerBIで読み込もう

というわけでExcelのデータが完成しました。
こだわる場合はもっといれるといいと思いますが、まあ今回はこれで。

作成したExcelファイルはOneDrive上に保存します。
(どこでもいいですが、OneDriveとかに載せると自動更新がかけやすいです)

ここまでやったらようやくPowerBIを開きましょう。
Power BI | 対話型のデータ視覚化 BI ツール あたりからサインインします。

PowerBIの左下の「データの取得」から「ファイル」っぽいところの「取得」を押します

f:id:fantasticswallow:20170709115038p:plain

そしたらどこのファイルを参照するか聞かれるので、先ほど保存したファイルの保存先を選択します。
私の場合はOneDrive - 個人用となります。(適宜選んでください)

接続し終わるとファイル一覧が表示されるので、先ほど作成したExcelファイルを選択します。

f:id:fantasticswallow:20170709115344p:plain

選択するとまっさらな……なんて呼ぶのかわかりませんがシートっぽいものが表示されると思います。
表示されたらデータを選択していきます。
ひとまず残金グラフを作りましょう。

f:id:fantasticswallow:20170709115932p:plain

フィールドからテーブルQuery1のColumn1とtestを選択し(名前は適宜合わせてください)、グラフの種類を折れ線にすると上のような感じでグラフが出ます。
これだけだと手元のExcelで出すのと一緒じゃん!ってなるので予測値を出してみましょう。

f:id:fantasticswallow:20170709144330p:plain

右の視覚化ペインの中のタブを「分析」に切り替え、「予測」のグラフを追加します。
「予測の長さ」には予測したいデータ数を入力します。今回は60日分予測するために60ポイントとしています。
「信頼区間」は指定した確率でこの辺通るだろみたいな補助線を算出するのに使用されます。確率が高いほど上限値、下限値は広く取られます(全部取れば再現率は100%みたいなあれ)。95%とかでいい気はします。
「季節性」は一定の周期でグラフが変動している場合に使用します。今回のケースであればおおよそ30日置きに給料が増えてるので30とか指定します。(自動で勝手に計算してくれるので任せてもいいです。)
(内部的な予測アルゴリズムはSeasonal ETSだと思います。Excelの関数と一緒っぽいのでExcelで予測しても同じようになるはず)

なるほどこれで予測値が見れる!のはいいんですけど、今のところExcelでよくね感を避けれません。
しかしPowerBIだと、Excelでグラフを管理するのと違い、自動で更新してくれるというメリットがあります。
特に予測については自動で範囲を変えるのは結構だるいのでありがたいです。
なので自動更新の設定をしましょう。

いったんレポートを保存してレポート編集画面を抜けます。
「マイ ワークスペース」の「データセット」を選んで「更新のスケジュール設定」を選びます。
f:id:fantasticswallow:20170709151138p:plain

すると設定画面に飛ぶので、「OneDriveの資格情報」を開いて、「資格情報を編集」を押します。
なんか意味深な「~~の構成」ってダイアログが出るので「サインイン」を押しましょう。

f:id:fantasticswallow:20170709151404p:plain

サインインすると一瞬でリダイレクトされますが、これでひとまず自動更新はできるようになります。
(この操作してないとデータセットの更新ができなくなる?雰囲気がある。)

これでちまちまと最初のExcelを編集し、気になったらPowerBIを見ることで最近やばい!とかが見られるようになりました。いえーい。
グラフ一個で寂しい場合はいろいろ追加するとよいと思います。

f:id:fantasticswallow:20170709152340p:plain

特に意味のないドーナツグラフ。


というわけでいろいろ自動化しながらデータを眺めるって話でした。
たまに眺めてはまだだ…まだいける…!とかいっています。

最近はおしごとやだぁ…みたいな状態です。いつまで続くんだろう…。
しかしおかねほしい。

この辺で。

workbench.colorCustomizationsを用いた痛Visual Studio Code化

コードを書きたい、気力がない…。
創造力の減少を感じる。

かなり昔にTextMate Themeを用いてVisual Studio Codeを痛くしたものがあったと思うのですが、Visual Studio Codeが1.12になったタイミングで大きく変わったのでその対応です。
artfulplace.hatenablog.com

1.12の変更についてはリリースノートでどうぞ。
code.visualstudio.com

workbench.colorCustomizationsで設定から色を設定可能になったのはいいんですが、その結果としてまた変える対象が変わったのでぐぬぬという感じです。

注意:いつも通り壊れてるとか出たりサポート対象外になりますがそれでもよい方向けです。
公式にサポートしてほしい…。
また基本的にはlightテーマで行っています。darkなテーマでそのまま使うとおかしくなる可能性は否定できないのでご了承ください。

目指す状態

f:id:fantasticswallow:20170520215450j:plain
大体こんな感じになるようにしましょう。

cssを書き換えよう

いつも通りCSSを書き換えます。
とはいえかなり減ってきた感じです

.monaco-workbench .part.editor {
	 background: url("vsc-bg.png") no-repeat;
	 background-size: cover;
}
.monaco-editor, .monaco-editor .inputarea {
        background: none;
}
.monaco-editor.vs-dark {
        background: none;
}

まずは画像を置くのは必須です。
あとはいわゆる基本テーマの背景色を削除する形になります。
他の部分については配色テーマの置き換えで消せるのですが、基本テーマ部分(vs, vs-darkなどのuiThemeが適用される部分) については配色テーマでは操作ができないのでこの部分だけCSSで消します。

vs-darkについてはあとからCSSが指定される形になるので(light < darkという順で、vs-dark適用時もvs-lightなテーマは存在する)、無理やり消します。
ハイコントラストも同様だと思いますが探すのが面倒だった…。

残りはworkbench.colorCustomizationsで対応します。

colorCustomizationsでいじろう

というわけで画像が後ろに出てくるようにしたところで、新機能であるworkbench.colorCustomizationsをいじっていきます。
これは現在のテーマの色よりも優先される設定となります。なのでAbyssにしてるので変わらないなどなどはないわけです。

またテーマの一部だけいじりたい、という場合テーマを直接いじるのは割と大変ですしプレビューも面倒です。
そんなときにworkbench.colorCustomizationsが役立ちます。ユーザー設定保存するだけで反映されるので見た目どうかを試すのにも便利です。
微妙だったら消せばいいわけですし。

どこが設定できるかはTheme Color Referenceを見ていただくとして本題にいきましょう。
code.visualstudio.com

エディタ部分の背景色を透過させればいいので次の二つを変更します。
・editor.background
・editorGroup.background

editor.backgroundは割と範囲が広いのですが(workbench.part.editor、one-editor-silo、monaco-editor-background)、さすがにこれいじらないとつらいのでいじってしまいます。
変になる可能性も高いですが手間も増えるので難しい。

editorGroup.backgroundはworkbench.part.editor直下のcontentに該当します。これだけなので影響自体はそこまで大きくないです。

それぞれを変更したときの挙動を確認しておきます。
次の画像は左が editor.background: #FF00DD44、右が editorGroup.background: #FF00DD66 です。(それぞれもう片方は#FFFFFF00で透過させています。)

f:id:fantasticswallow:20170521114143p:plain

こうしてみると左側のほうが何層にも重なっているため右よりも濃くなっています。
なのでそのあたりを意識しつつ透明度を設定します。

workbench.colorCustomizationsでいじるときにカラーコードを8文字入れるとrgbaで反映されるのは確認済み(少なくとも今回の二つは)なので、最初の画像のような見た目であれば
・editor.background: "#EFEFEF66"
・editorGroup.background: "#FFFFFF77"
とかやると画像みたいな感じにはなります。

darkなテーマとかの場合はそれに合わせて色を変更するとよいと思います(さすがにdarkテーマでの検証まではしてない)


というわけで便利な機能が増えてましたという話でした。
issueにたびたび背景画像設定できるようにみたいなのは流れてますがどうなったのかはよくわからないのでなんとも。
しかしどういう挙動にするかもわからないので難しい…。

コードを書こうとVisual Studioを開いても何もする気がおきないので何かこうやる気を取り戻したい…。
触らない期間があるとどうしてもやる気がどっかとんで行ってしまう。ぐぬぬ

この辺で

東方天空璋 体験版用のMS-IME向けテキスト辞書

いーつーもーのー
今回は動画がすぐに上がってきたからすぐに完成したぜ。

ということで東方天空璋の体験版のスペルカード名とか曲名とかキャラ名が今すぐ変換したい方向けのやつです。
最近はGoogle 日本語入力とか流行ってる気がするし、こういう辞書ファイルの需要があるのかよくわからない…。
といいつつ私はMS-IMEをずっと使っているわけですが。


ダウンロードは下のDropBoxのリンク開いて右上にあるダウンロードからどうぞ。
ネタバレ対策はないのでネタバレ怖いという方は開かないでください。
www.dropbox.com

利用方法はIMEのオプションの「ユーザー辞書ツール」から「ツール」メニュー →「テキストファイルから登録」 でどうぞ。
よくわからなければググってください。きっとわかりやすい説明がいくらでも見つかります。

ちなみに今回もgistでバージョンを管理しています。だいぶ雑に管理しています。
https://gist.github.com/fantasticswallow/e7f1c574b46a7c79ccc9f18180547a1c
ぼくのかんがえたさいきょうのじしょふぁいる とかミスを直したいとかいろいろ勝手にどうぞ。forkとかはご自由に。

利用する上での注意点は次の通りです。
・利用は自己責任でお願いします。変な変換が紙に出たとかは責任取れません。
・編集はご自由にどうぞ。ミス修正とかもご自由に
DropBoxで公開しているファイルと同じファイルの再配布は不許可。
・辞書への登録部分で何らかの変更点があるものを「配布者の名のもとに」配布するのは構いません。

以上です。
こんなん再配布するやつおらんやろ~って気もしますがまあ一応…。


そんなわけでいつものテンプレ記事でした。
製品版は買いに行きたい…でも暑そう……。

ゲームをあまりしていないので製品版に向けて東方も練習していかないとなぁと毎回変わらない感想で終わります。
いやでも音ゲー最近したらめっちゃきつかったしだいぶ衰えてしまったのでは…。

この辺で

OneNoteのページをC#のコードでコピーする

私は知っている…誰も書いてない = 需要がないことをなぁ…!

というわけで、OneNoteのページを移動させるのがめんどくさいのでなんかアプリケーション作ってコピーしようと思ったのですが、OneNoteのApplicationClassにはCopyPageContentみたいな気の利くメソッドがありません。どういうことだ。

ApplicationClassをオブジェクトブラウザで見てるとこんなメソッドがあることはわかります。
・GetPageContent(string pageId)
・UpdatePageContent(string pageXml)
・DeletePageContent(string pageId, string objectID)
…なんかDeletePageContentは違う気がする(DeleteHierarchyの気がしてきた)。

まあ確かに世の中取得と追加ができればコピーはできるでしょう。コピーして元のページを消せば移動もOK!
なんとありがたい!!今すぐ悔い改めてほしい!!!

とりあえずページのコピーができれば移動も達成できるのでコピーの仕方について書いていきます。

コピーしたXMLのdiffを確認しよう

Office界では同じ処理がすでにある場合、既存の処理によって得られる結果を確認するというのは定石ですね。どうかしてるぜ!
Visual Studio CodeのCompareで比較してみます。

f:id:fantasticswallow:20170326111707p:plain

まあ雰囲気だけ感じ取ってもらえばいいのですが、変化しているのは
・Page.@ID
・OE.@objectID
・CallbackID.@callbackID
の3つのIDです。

PageのIDは存在するページのIDを指定する必要があります。このIDを基にUpdatePageContentはページの書き換えを行います。そのためコピー先のページIDに書き換えます。ここはReplace(元のID, 新しいページのID)で終わるので楽です。
OEのobjectIDはOE要素を追加した際に自動で振られるIDです。競合してはいけないため、IDを残してしまうとUpdatePageContentに失敗します。しかし特にこちら側で割り当てる必要もないので(勝手に追加される)、すべてのobjectIDを消せばこちらはOKです。
CallbackIDも同じく勝手に振られるのですが、画像のデータについてはこちら側で指定する必要があります。最も面倒な作業。

まあ実際やっていきましょう。

続きを読む