OmniAICreator/Qiita-1.07M huggingface dataset:
WSL2でファイルシステムのアクセス速度がどの程度変わったか

なんか速度比べばかりしているような気がしますが・・・
今回はWSL2のファイルアクセス速度についてです。

WSL1から比べるとWSL2ではLinuxファイルシステムのアクセスは高速になったが、Windowsファイルシステム(/mnt/ドライブ名)のアクセスは遅くなったと言われます。
実際例えばJavaのソースリポジトリを `/mnt/c/` 以下のディレクトリにcloneしてきてビルドしたりすると極端に遅いので体感的にもわかります。

それでも意外とWSL2のほうが最新だしWSL2使おうというような感じで使われてて、かつ `/mnt/ドライブ名` 以下にワークディレクトリがあってビルドが遅くても比較対象がないので気にしていないなんてことが散見されるので、今更ながら記事にしてみました。

# 検証環境

* OS
    * Windows 10 Pro
* バージョン
    * 21H1
* CPU
    * Core i7-10700
* ディスク
    * Crucial P2 SSD 1TB

# 速度計測方法

WSL1,WSL2でそれぞれ以下のように計測しました。

1. Ubuntu20.04の環境を作成。
1. /dev/urandomを使い1kBの中身がランダムなファイルを10000個、Linux,Windows両ファイルシステムに作成。
1. 上記10000個のファイルをcpコマンドでそれぞれ同一ファイルシステムにコピーする時間をtimeコマンドで計測。
1. コピー元のディスクキャッシュの影響があるため何度か計測して安定してきた値を計測値とする。

ディスクキャッシュの関係から読込側というよりは書き込み側の速度テストになっています。
また、本来のディスクベンチマークであれば大きいサイズのファイルコピーなども行うのでしょうが、簡易計測ということで割愛しています。

# 計測結果

以下にファイルコピーの時間をそれぞれ記載しますが、時間の絶対値は環境によって変わるので、あくまで相対的に見るようにしてください。

## WSL1
Linuxファイルシステム

```
real    0m4.789s
user    0m0.094s
sys     0m4.625s
```

Windowsファイルシステム

```
real    0m6.413s
user    0m0.109s
sys     0m6.297s
```


## WSL2
Linuxファイルシステム

```
real    0m0.197s
user    0m0.044s
sys     0m0.154s
```

Windowsファイルシステム

```
real    0m56.177s
user    0m0.625s
sys     0m8.962s
```

# 結論

* WSL1の場合はWindowsファイルシステムのほうが多少遅いものの、大差ない。（どちらもそこそこ遅い？）

* WSL2は言われている通りLinuxファイルシステムは高速。だが、Windowsファイルシステムは致命的に遅い。

例えばWSL2でJavaサーバーの開発をする場合を想定すると、ワークディレクトリをWindowsファイルシステム側に置くのは非常に効率が悪いので避けるべきでしょう。逆にLinuxファイルシステム側に置くことでビルド等は速くなりそうです。（ビルドはファイルの読み書きが大量に発生するため）

IntelliJもWSLに対応して、WSL側でプロジェクトを開けるようになりましたし（かつJDKもWSL側のを使える）、VSCodeもWSL側から開けるので今後はそういったツールを活用していくのがよさそうです。

どうしても利用ツールの問題でWindowsファイルシステム側にワークディレクトリを置く必要がある場合はWSL1を利用しましょう。
powerautomate error:Principal user is missing prvAppendcr324_table_name privilege

Principal user (Id=), is missing prvAppendcr324_table_name privilege (Id=) on OTC=10316 for entity 'table_name' (LocalizedName='table_name'). context.Caller=b99f62ab-e23c-ec11-8c62-000d3ace1a5e. Or identityUser.SystemUserId=1111, identityUser.Privileges.Count=3162, identityUser.Roles.Count=0 is missing prvAppendcr324_table_name privilege (Id=5b0) on OTC=10316 for entity 'cr324_table_name' (LocalizedName='table_name').


接続参照はシステム管理者に変更したら、エラー解消
Azure Automation アカウントでのマネージドIDでの認証

# はじめに

久しぶりに Azure で Automation アカウントを使って処理の自動化をやろうとした時にハマったので備忘録を兼ねて記載します。

# 以前の Automation アカウントの作成

Automation を使って Azure 上の処理を行なう際に結構ハマるのは「誰の権限で行うか」というところだと思います。
以前は Automation アカウントを作成する際に「Azure 実行アカウントの作成」オプションがあって、ここで専用アカウントを作成しました。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/19e6252a-47b6-6b7d-5fff-fcf716095f4b.png" width="250" align="LEFT"/>👈以前の画面

Azure Active Directory の「アプリ登録」で、そのアカウントにロールを振ったりして、権限コントロールをしていたかと思います。

# 今の Automation アカウントの作成

今、Automation を作ろうとすると「Azure 実行アカウントの作成」はなくなっていて、詳細設定に「マネージドID」の項目が追加されています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/816beb45-ab38-80a5-e54d-5c66fe4bbfd8.png" width="600"/>

これまでの実行アカウントを使うのではなく、このマネージドIDを使って認証と権限のコントロールをする方法がデフォルトだそうです。

# マネージドID を使った Runbook での認証方法

mRunbook 上で実行時アカウントを使って認証するには、次のように記載していました。

```powershell:OldRunbookScript
$servicePrincipalConnection=Get-AutomationConnection -Name 'AzureRunAsConnection'

Connect-AzAccount `
        -ServicePrincipal `
        -TenantId $servicePrincipalConnection.TenantId `
        -ApplicationId $servicePrincipalConnection.ApplicationId `
        -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 
```

見慣れましたが、今考えると冗長ですよね。。。

マネージドID を使った場合は、次のようになります。

```powershell:NewRunbookScript
Connect-AzAccount -Identity
```

ワンライナーです！
ちなみに、新規に Automation アカウントを作ると、サンプル runbook がいくつかついてきますが、そのうち「AzureAutomationTutorialWithIdentity」もご参照ください。

# 旧来の「実行時アカウント」の使用方法

もし、旧来の「Azure 実行アカウントの作成」と同等のやり方が必要な場合は、Automation アカウントの「共有リソース」の「接続」を使えば実現できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177616/398e3d53-a8a4-e92a-c78d-792737bca2e2.png)

# まとめ

Azure リソースでマネージドID 対応が進んでいますが、Automation アカウントも同様にマネージドIDが使えるようになっていました。マネージドIDを使うと以前の「実行時アカウント」が不要になり、runbook もすっきりするのでお勧めです。
BERTの事前学習　Next sentence prediction の実装

```python
from transformers import BertForNextSentencePrediction

nsp_bert = BertForNextSentencePrediction.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
nsp_bert.eval()
```
```python
prompt = '私の家族は5人家族です。'
next_sentence = '家族は、父、母、兄、私、妹です。'  

input_tensor = bert_tokenizer(prompt, next_sentence, return_tensors='pt')
print(input_tensor)
```
{'input_ids': tensor([[   2, 1325,    5, 2283,    9,   76,   53, 2283, 2992,    8,    3, 2283,
            9,    6,  800,    6,  968,    6, 1456,    6, 1325,    6, 4522, 2992,
            8,    3]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1]])}

```python
list(bert_tokenizer.get_vocab().items())[:5]
```
[('[PAD]', 0), ('[UNK]', 1), ('[CLS]', 2), ('[SEP]', 3), ('[MASK]', 4)]

```python
output = nsp_bert(**input_tensor)
print(output)
```
NextSentencePredictorOutput(loss=None, logits=tensor([[14.3159,  2.9107]], grad_fn=<AddmmBackward>), hidden_states=None, attentions=None)

```python
torch.argmax(output.logits)
```
tensor(0)

うまく予測出来ました。
ここまでが簡単な Next sentence prediction の実装です。
最短で画像判別アプリを作成する(Swift)

# 雲を判別したい

素人が見た目から何の雲かを判別するのは難しい。
雲が分かれば現在の天気や今後の天気がわかる。
気象学的に雲は10種類に分けることができます。これを十種雲形と言います。

今回最小限の労力・最短での画像判別アプリ作成を目標とします。


>![スクリーンショット 2022-01-11 18.24.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261100/cf79623b-2ae7-09fc-3a4a-095e9b487332.png)
日本気協会HPより

# CoreMLで雲画像の機械学習モデルを作成

CoreMLの詳細は下記の記事を参照。
>https://qiita.com/PearlEarringMinion/items/a703eb3525e4aec6c46d

以下の分類でモデルを作成しました。
googleや気象研究者のtwitterから画像を収集し、１分類につき画像は１５〜２０枚学習させました。
![スクリーンショット 2022-01-11 18.27.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261100/fc54e71c-8cf9-9fdb-ec35-fcb4eba3f1ac.png)

# CoreML apple公式サンプルコードをDL

以下のappleデベロッパーサイトから画像判別アプリのサンプルコードをダウンロードします。
>https://developer.apple.com/documentation/vision/classifying_images_with_vision_and_core_ml

# サンプルコードに作成したモデルを組み込む
ダウンロードしたサンプルファイルをXcodeで開きます。
"Models"ファイルに"MobileNet"というCoreMLモデルがありますので、こちらを削除して、自身で作成した
モデルをドラッグします。

![スクリーンショット 2022-01-11 18.33.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261100/313d3a02-edc2-5d2b-03f8-91ee25451769.png)

"Image Predictor"フォルダに"ImagePredictor"ファイルがありますのでこちらのコードを一部変更します。　 ImagePredictorクラス内の３行目のモデル名を自身で作成したモデル名称に変更します。

```swift:ImagePredictor.swift
    class ImagePredictor {
    /// - Tag: name
    static func createImageClassifier() -> VNCoreMLModel {
        // Use a default model configuration.
        let defaultConfig = MLModelConfiguration()

        // Create an instance of the image classifier's wrapper class.
        /*let imageClassifierWrapper = try? MobileNet(configuration: defaultConfig)*/

        //ここのコードのモデル名を変更する！！！！！！！！！！
        let imageClassifierWrapper = try? CloudMyImageClassifier2(configuration: defaultConfig)

        guard let imageClassifier = imageClassifierWrapper else {
            fatalError("App failed to create an image classifier model instance.")
        }

        // Get the underlying model instance.
        let imageClassifierModel = imageClassifier.model

        // Create a Vision instance using the image classifier's model instance.
        guard let imageClassifierVisionModel = try? VNCoreMLModel(for: imageClassifierModel) else {
            fatalError("App failed to create a `VNCoreMLModel` instance.")
        }

        return imageClassifierVisionModel
    } 
```

# 実機で試す
bundle IDを自分のものに変更して実機を繋いでテストしてみます。
まあまあ精度良く判別することに成功しました。
雲が存在しない場合は快晴と判別してくれます。
高積雲と巻積雲、高層雲と巻層雲は高度が違うだけで形が同じなので判別することが難しいみたいです

![スクリーンショット 2022-01-11 18.50.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261100/1ff9963d-8e3c-90a0-2a8d-24cc2445b0cb.png)

# 疑問
公式サンプルコードをベースとしているので、これをアレンジしてappストアにリリースしても、
ガイドラインの"アプリの模倣"にあたりリジェクトされてしまうでしょうか？
デベロッパー登録料払っているので、そろそろストアにリリースしたいです。

# 全ソースコードとモデル
GitHub
[https://github.com/PearlEarringMinion/CloudImageClassfyingApp](https://github.com/PearlEarringMinion/CloudImageClassfyingApp)

# 追記(2022年3月10日）
Apple Storeにリリースしました。
ダウンロードしてもらえたら嬉しいです。

[ダウンロード](https://apps.apple.com/jp/app/%E3%82%AF%E3%83%A2%E3%82%B7%E3%83%AB/id1610525474)

![BE0BDF95-B791-47BA-9B54-586CBC74B055.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261100/265d9091-ce3d-80cf-c782-20481806fd96.jpeg)
Sublime Text 自作Plugin Scripts API TIPS 個人用メモ

## 選択範囲が無い場合、行を取得する。
``` py
        for region in self.view.sel():
            if region.empty():
                region = self.view.line(region)
```

## region
選択範囲を示すregionは、開始地点と終了地点の数字の入ったオブジェクト

``` py
region = sublime.Region(0, 10)##Region自作。0文字目から10文字目
print(region)
##regionの開始地点と終了地点の数字取得
reg1 = str(region.begin())
reg2 = str(region.end())

```

## 自作誤字脱字選択肢表示機能
いちいちlinterとかインストールするのとか面倒くさいので自分で作りたいとき。

``` py
        i=0
##選択範囲を数字で取得
            reg1 = str(region.begin())
            reg2 = str(region.end())
##置き換え文字列
            replace = "テスト"
##置き換え文字列用minihtml
            links = '<a href=\'subl:testtest {"reg1":%s,"reg2":%s,"rep":"%s"}\'>te2d</a>' % (reg1,reg2,replace)
            # links = '<a href=\'subl:testtest {"reg1":0}\'>tesdt2</a>'
##検索文字列
        for region in view.find_all('　'):
            i =i+1
##viewへ範囲表示と選択肢部分構築。同じ文字列を建築しても、それぞれ別々でキー設定しないといけないので、forループで構築する。
            view.add_regions('HighlightTest'+str(i),
                         [region], 
                         "region.redish",
                         "dot",
                         sublime.DRAW_NO_FILL+sublime.DRAW_STIPPLED_UNDERLINE+sublime.DRAW_NO_OUTLINE,
                         [links]
                         )
```
CSVファイルの解析を row.split(',') で済まさない

JavaScriptでCSVファイルを読み込むコードを書きたいので`js csv 読み込み`でググったら、検索結果上位のほぼ全てのサイトが自力で`row.split(',')`したりする感じの書き方をしていたので書く記事。

# CSVファイルには標準規格がある
CSVとはComma-Separated Valuesの略で、その名の通りコンマで区切られた複数のフィールドで構成された1レコードを1行としてそれが複数行連なったテキストファイルのことである、というのは皆さんご存知でしょうがCSVファイルはそれが全てではありません。

CSVファイルとは慣習的になんとなく使われているファイルフォーマットではなく、標準規格があるフォーマットです。その規格を満たしていなければCSVファイルではない、とまでは言いませんが標準があるならそれに従うべきです。

「いや、そんなこと言ったって標準規格は超難解で使いもしない機能盛りだくさんだからだからみんな簡易的な実装してんじゃないの？」と思うかもしれませんが、そんなことはありません。

その規格は[RFC4180](https://datatracker.ietf.org/doc/html/rfc4180)で定められています。規格といっても全部でたった8ページしかなく、ファイルフォーマットを定めているのはそのうち2ページくらいです。英語でも5分あれば読めるレベルです。

# フィールドをダブルクオートでくくれる
単にコンマでフィールドを区切って改行でレコードを区切っただけだと、フィールドにコンマと改行を入れられません。よって標準規格ではダブルクオートでフィールドを囲むことでそれらの文字を入れられるようにしています。ダブルクオートで囲まれたフィールドの中でダブルクオートを使う場合はダブルクオートを2回書くことでエスケープします。

```
普通のフィールド,"カンマ:,","ダブルクオート:""","改行:
"
```

コンマや改行を一切含まないフィールドをダブルクオートで囲っても問題ありません。私はCSVを書き出すときは常にダブルクオートで囲ってます。それらの文字が入ってるかどうかで処理を分けるのが面倒ですからね。

# 閑話 - その他標準規格の細部
- 最終レコードの後の改行はあってもなくてもよい
- コンマの前後にホワイトスペースを入れてはいけない
  - そのままホワイトスペースを含んだフィールドとして扱われる
- 各レコードの最後にコンマを置いてはいけない
  - レコードの最後に空文字のフィールドがあるものとして扱われる
- 各レコードのフィールド数は同じであるべき
- (改行コードは`CR LF`)
  - あくまで規格上は。実際にはUnix系の改行コード`LF`でもいい。

# 解析するときはライブラリを使おう
このようにCSVファイルにはダブルクオートで囲むことができる仕様があります。そしてこの仕様はExcelなど多くのソフトで対応している仕様です。単純に`row.split(',')`すればいいだけではないです。

この仕様は単純で自分でも書けるでしょうが、ライブラリを使うのが無難でしょう。CSVを解析するときはライブラリを使いましょう。

# (ガチ閑話)
`js csv 読み込み`で出てきた某テック○カデミーの記事、タイトル「CSVファイルを読み込む方法」で改行で区切るとかコンマで区切るとか一切せずにファイルの内容文字列としてそのままコンソールに出して終わっててワロタ。
キーボードの表示、非表示を切り替える方法

#はじめに
キーボードの表示、非表示に関しては、ほぼ全てのアプリで実装が必要になると言っても過言ではない要件ですよね。
いつも調べて思い出すことが多いので、簡単にまとめておこうと思います。

#キーボードの表示
表示する際の実装は以下

```ViewExtensions.kt
fun View.showKeyboard(
    flag: Int = 0
) {
    (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
        .showSoftInput(this, flag)
}
```

#キーボードの非表示
非表示にする際の実装は以下

```ViewExtensions.kt
fun View.hideKeyboard(
    flag: Int = 0
) {
    clearFocus()
    context.getSystemService<InputMethodManager>()?.hideSoftInputFromWindow(windowToken, 0)
}
```

#さいごに
こういった検索頻度の多い実装は、なるべくコストを抑えて実装していきたいところですよね。
コストをかけずに実装していくために、今後も実装粒度に限らず残しておこうと思います。
BERTの事前学習　Mask language modelの実装

##環境の準備

```Python
!pip install -q transformers==4.9.0
!pip install -q fugashi
!pip install -q ipadic
# 必要なモジュールのインストール
import torch
import transformers
```
##BERT モデルと Tokenizer の準備

```Python
from transformers import BertJapaneseTokenizer, BertForMaskedLM
```
今回は東北大学で開発されたモデルである cl-tohoku/bert-base-japanese-whole-word-masking を使用します。

```Python
# 分かち書きをするtokenizer
bert_tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
lm_bert = BertForMaskedLM.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
```
##BERTの入力の準備
BertJapaneseTokenizer を使用して BERT の入力を用意します。
今回は、「彼は[MASK]として働いている。」という文章の [MASK] を予測させてみます。

```python
# 分かち書きの実行

text = '彼は * として働いている。'

tokenized_text = bert_tokenizer(text)
print(tokenized_text)
```
MLM を実装するために予測したい単語（今回は *）を [MASK] トークンに置換します。

```python
masked_idx = 2

# 予測させる単語をマスクする
tokenized_text[masked_idx] = '[MASK]'
print(tokenized_text)
```
[MASK] した単語を含んだ文字列を convert_tokens_to_ids() を使用して id に変換します。

```python
indexed_tokens = bert_tokenizer.convert_tokens_to_ids(tokenized_text)

print(indexed_tokens)
```
BERT モデルに使用できるように id 化した文字列を Tensor 型に変換します。

```python
tokens_tensor = torch.tensor([indexed_tokens])

print(tokens_tensor)
```
tensor([[1, 1, 1, 1]])

##Mask language model の実行
BertForMaskedLM を使用して Mask language model を実行します。

モデルの構造を確認してみましょう。
.eval() で推論モードに切り替えることができ、こちらのコードを実行するとモデル構造が出力されます。

```python
lm_bert.eval()
```
モデルが用意できたのでMLMの推論を行います。

```python
outputs = lm_bert(tokens_tensor)
type(outputs)
```
```python
len(outputs[0][0][0])
```
32000

今回の[MASK]した単語の予測値を取得します。

```python
pred = outputs[0][0, masked_idx]
pred
```
tensor([-4.7888, 20.6928, -3.8209,  ..., -8.5616, -4.3609, -0.3407],
       grad_fn=<SelectBackward0>)

この予測値に .topk() を用いることで予測値の内上位 ◯ に当てはまる予測値の値を取得することが出来ます。

```python
pred.topk(2)
```
torch.return_types.topk(values=tensor([20.6928,  9.1267], grad_fn=<TopkBackward>), indices=tensor([  1, 466]))

[MASK] した単語を上位 5 を出力する関数を定義します。

```python
def predict_mlm(model, input, masked_idx):
    with torch.no_grad():
        outputs = model(input)
        # 予測結果の上位5件を抽出
        predictions = outputs[0][0, masked_idx].topk(5) 

    for i, idx in enumerate(predictions.indices):
        index = idx.item()
        print(index)
        token = bert_tokenizer.convert_ids_to_tokens([index])[0]
        print(f'順位:{i + 1}\n単語:{token}')
        print('----')
```

```python
predict_mlm(lm_bert, tokens_tensor, masked_idx)
```
1
順位:1
単語:[UNK]
----
466
順位:2
単語:学校
----
20770
順位:3
単語:物事
----
959
順位:4
単語:諸
----
2633
順位:5
単語:男子
----
Power Automate で動かす Office スクリプト(1)

Power Automate はローコード／ノーコードで簡単にワークフローを作れますが、手の込んだループを作ろうとしたり複雑な処理を行おうとするとどうしても入り組んだ（？）構成になってしまいます。

Azure Functions や Azure Automation といったスクリプトに処理を渡してしまうのも一つの手ですが、これだと有償ライセンスが必要になります（プレミアムコネクタのため）。

そこで、ちょっとした処理を渡すのに便利な Office スクリプトをご紹介したいと思います。
これは Excel ファイルに含まれるスクリプトで、 VBScript とは異なるもので、 JavaScript (TypeScript) で作成します。
Office スクリプトそのものについては詳しく紹介されているエントリーがたくさんあると思いますので、ここでは「Power Automateから呼び出すスクリプト」としての Office スクリプトについて触れようと思います。

## Excel ファイルを SharePoint ライブラリまたは OneDrive に用意する

Office スクリプトは Excel on the Web 上で動作します。従って「Excel ファイル(.xlsx)」を「SharePoint ライブラリ」または OneDrive に置きます。チーム内で利用するワークフローを想定しますので、ここでは SharePoint ライブラリとします。

![20220112-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/c7bc8bf6-9780-7c2c-6e44-cc98dffb92fe.png)


## Excel ファイルを開き、[自動化]-[新しいスクリプト]をクリックする

作成されたファイルをクリックするとそのままファイルが Excel on the Web （ブラウザ上）で開きます。
自動化メニューをクリックし、新しいスクリプトをクリックします。

![20220112-003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/b6dda230-729d-9fc1-b8df-f3fa9e0e9281.png)


## ある程度のルールに従って記述する

自動で main 関数を含む処理が表示されます。これの構造は（Power Automate から利用する場合としては）以下のようになります。

![20220112-004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/dbd5ceb9-174b-b77e-741c-b3f415fdc6b3.png)


要するに
 - Power Automate から受け取る値（引数）
 - 行いたい処理
 - ログとして出力したい値、結果として返したい値
を書けばいいわけです。

例えば、以下のように書いておきます。

```typescript:引数で渡された文字列の文字数をカウント
function main(workbook: ExcelScript.Workbook,
inputString: string
)
{
  let numberOfStrings: number = inputString.length;

  console.log(`元の値:${inputString}`);
  return numberOfStrings;
}
```

## クラウドフローから呼び出す

スクリプトを保存してから、Power Automateにてクラウドフローの作成に移ります。
Excel Online (Business) のアクションから「スクリプトの実行」を選択します。
![20220112-005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/03206e82-dad9-7c07-7749-c5249bd37204.png)


先ほどのファイルを指定するとスクリプトが選べるようになっています。選べなかった場合、スクリプトの保存を実行していない可能性がありますので、再度ご確認ください。

![20220112-006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/e40dfb33-5022-0592-06ee-9a0d62392bdb.png)


スクリプトがちゃんと書けていれば、以下のように引数を指定できるようになっています。

![20220112-007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/b808800f-42f5-0e9d-6870-6ee338060fd6.png)


フローを保存してテスト実行してみます。うまく行くと、以下のように result と log がそれぞれ出力されます。

![20220112-008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/433fd498-4276-301c-13e9-5b6d5f696c0f.png)


もちろん以下のように、以下のフローで結果を利用することができます。

![20220111-009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/d23d2398-d564-1517-aee6-2b4135b3675b.png)

ここまでわかっていれば以下のサンプルを読み解いて Power Automate で動かすための Office スクリプトが作成できるようになると思います。
https://docs.microsoft.com/ja-jp/office/dev/scripts/resources/samples/samples-overview

## まとめ
Power Automate は大変便利なのですが、細かい処理をしようとしたときにフローが複雑になることがあります。そのような場合に利用するとよいと思います。ちなみに、今回のように Excel ファイルの中身（シートやテーブル）が空でも利用できます。
Power Automate はシートがテーブル化されていないと読み取ることはできませんが、Office スクリプトを経由すればシートの位置を指定して(A1とか)直接読み取ることができます。


次回以降で掘り下げていこうと思います。
「他の人はこちらも検索」をCSSで非表示にする(備忘)

#概要
Googleの検索結果画面で日々誤クリックを誘発しては舌打ちを買っている「他の人はこちらも検索(PASF)」を非表示にする方法の備忘録。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1334198/246a8589-dbc2-4fbb-9c7b-09264b388fea.png)
以前はNo Search Forという神ツールが使えていたのだが、いつの頃からかPASFではなくサマリーが消えるようになってしまっているので、CSSをいじって消す方法を残しておく。

#やり方
Stylus等のユーザースタイルシートツールを利用し、以下のスタイルを適用するだけ。
<font color="Silver">実はCSSには不慣れなので、突っ込みどころがあったら優しく教えてください。</font>

```css
div[id^="eob_"] {display: none;}
div.tF2Cxc {height:auto;}
```
SSHポートフォワードによるアクセス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493279/53ece6a9-ee53-9b27-ac50-b1e23e172c76.png)

RemoteからアクセスできるTargetのJavaScriptなWEBページにLocalからアクセスしたい。
Localポートの8081を割り当てるとすると、Localで下記のsshコマンドを実行する。

```
$ ssh -NL 8081:192.168.1.120:80 192.168.32.55
```

Localのブラウザから、http://localhost:8081 でTargetのWEBページにアクセスできるようになる。

## 参考

- [sshポートフォワーディング](https://qiita.com/mechamogera/items/b1bb9130273deb9426f5)
SageMakerAutopilot

SageMakerAutopilotを実行したのでメモ

### データ：
使用したデータは、sklearnのBoston住宅価格データセット

### 所感：
- 回帰問題の評価指標がMSEしかない点が気になりました。オプションで様々な指標を選択できると便利
- 506行のデータに対して、特徴量エンジニアリング、モデル訓練、チューニングのステップを実行しなくても、AUTOPILOT JOBがかなり速く処理された（Creation time 10 minutes ago）
Pascal VOC形式のアノテーションデータをTFRecordとして出力する

# はじめに
VoTTでTFRecordを出力できるのですが、画像ごとにTFRecordファイルが出力される仕様となっているようです。使おうとしているモデルの学習インタフェースが単独のTFRecordのみ対応しているようだったので、一旦VoTTからPascal VOC形式でエクスポートし、それを単独のTFRecordファイルに変換しました。
この記事では、TensorFlow Object Detectionに入っているスクリプト(create_pascal_tf_record.py)を使って、Pascal VOC形式のアノテーションファイルから単一のTFRecordファイルを生成する方法について説明します。

# 事前準備

以下のバージョンで動作を確認しています。

- Ubuntu v.20.04
- Python v.3.9.7
- TensorFlow v.2.7.0
- Protocol Buffers v.3.14.0

上記バージョンのモジュールをインストールし、TensorFlow Model Gardenをチェックアウトした環境を構築します。この環境を構築する手順は、以前に投稿した**[YOLO形式のアノテーションファイルをTFRecordに変換する](https://qiita.com/highitoh/items/e8a583fe74bc24f13286)**の事前準備で書いたものと同じです。

変換するPascal VOC形式のアノテーションデータを用意します。ディレクトリ構造は下のとおりです。'/home/hoge'の部分は任意で構いません。VoTTからエクスポートしたものを利用する場合、出力されたルートディレクトリの名前を'VOC2012'に変更して、任意のディレクトリに置けば大丈夫です。

```
/home/hoge/VOC2012/
|-- Annotations
|   `-- traffic_light_0001.xml    # For Example
|-- ImageSets
|   `-- Main
|       |-- signal_blue_train.txt # For Example
|       `-- signal_blue_val.txt   # For Example
|-- JPEGImages
|   `-- traffic_light_0001.jpg    # For Example
`-- pascal_label_map.pbtxt
```

アノテーションデータを変換するにあたって、いくつか事前処理が必要です。まず、VoTTから出力されるアノテーションデータを利用する場合、XMLファイルのfolderタグの内容を'VOC2012'に変更しておく必要があります。'VOC2012'になっていないと変換時にエラーが発生するので、VoTTを使っていなくても事前に確認しておくと安心です。下のコマンドを実行することで、すべてのXMLファイルのタグを置換できます。


```
$ cd /home/hoge/VOC2012/Annotations
$ find *.xml | xargs sed -i 's/<folder>.*<\/folder>/<folder>VOC2012<\/folder>/g'
```

次に、ImageSets/Mainディレクトリに'aeroplane_train.txt'を新しく作成し、訓練データの画像ファイル名(拡張子なし)を行ごとに指定します。なお、'aeroplane'はスクリプトで名前が固定されてしまっていますので変えられません。
これらのファイルは、VoTTから出力されるtxtファイルから作ることができます。VoTTから出力されるtxtファイルは、画像ファイル名の後ろに存在有無を示すフラグが入っているので、これを下のようなコマンドで削除します。

```
$ cd /home/hoge/VOC2012/ImageSets/Main
$ cp -p signal_blue_train.txt aeroplane_train.txt
$ sed -i 's/\.jpg .*$//g' aeroplane_train.txt
$ cat aeroplane_train.txt
traffic_light_0001
traffic_light_0002
...
```

必要に応じて、'aeroplane_val.txt'を作成し、検証データの画像ファイル名を同様に指定しておきます。

# 使い方

下のコマンドを実行すると、単一のTFRecordファイルが生成されます。

```
$ cd ~/models/research
$ python3 object_detection/dataset_tools/create_pascal_tf_record.py \
--data_dir=/home/hoge --year=VOC2012 \
--label_map_path=/home/hoge/VOC2012/pascal_label_map.pbtxt --set=train \
--output_path=/home/hoge/train.tfrecord
```

また、setをvalに変更すれば'aeroplane_val.txt'が読み込まれ、検証データのTFRecordファイルを生成することができます。

```
$ python3 object_detection/dataset_tools/create_pascal_tf_record.py \
--data_dir=/home/hoge --year=VOC2012 \
--label_map_path=/home/hoge/VOC2012/pascal_label_map.pbtxt --set=val \
--output_path=/home/hoge/val.tfrecord
```

# オプション

- data_dir：変換対象のデータセット(VOC2012)が置かれたディレクトリを指定します。
- year：VOC2007かVOC2012が指定できます。ここでは、VOC2012を設定します。ディレクトリ構造に登場するフォルダ名に対応している模様。
- label_map_path：ラベルデータを記述したファイルを指定します。形式は、models/research/object_detection/data/pascal_label_map.pbtxtと同じです。VoTTからエクスポートした場合は、そのルートディレクトリに出力されています。
- set：変換するデータセットの種類を指定します。trainは訓練データ、valは検証データを意味します。他にもtrainvalやtestを設定できるようです。
- output_path：出力されるTFRecordファイルのパスを指定します。
Eclipseでプロジェクトの外部の依存ライブラリのjarを抽出する

gradleでパッケージ依存関係を管理しているのですが、すべてのjarをクラスパスに読み込ませる昔ながらのバッチのためにそれらのjarを取得したいのです。
F/Wのバージョンアップに伴い、一新したい。

~~実行可能jar使えとかSpring使っているならBootにすればいいのにとかあーあー聞こえない。~~

## 雑な手順
1. Eclipseのプロジェクトエクスプローラー（いっぱいプロジェクト並んでいるやつ）から対象のプロジェクトを右クリックする。
- 出てきたコンテキストメニューから`エクスポート`を選択する。
- エクスポート対象として`実行可能JAR`を選ぶ。
- ダイアログが出てくるので適当にぽちぽちする。
- 依存JARをサブフォルダーに出力、みたいなラジオボタンがあるので設定して完了する。
Python: 画像データからサムネイルを作成する

```python
import imghdr
from io import BytesIO

from PIL import Image

def create_square_thumb(
    binary_of_fullimage: bytes,
    thumb_size: tuple,  # サムネイルのサイズ(x, y)を指定する。 例:(100, 100)
    thumb_quality: int,  # サムネイルの品質を指定する。↓をご参照
) -> bytes:
    """
    画像データのbytesから、指定したサイズと品質でサムネイルを作ってbytesで返す。
    [thumb_quality: int]
        フィルタ	    ダウンスケーリング品質	アップスケーリング品質	パフォーマンス
        PIL.Image.NEAREST			                          ⭐⭐⭐⭐⭐
        PIL.Image.BOX	    ⭐		                            ⭐⭐⭐⭐
        PIL.Image.BILINEAR	⭐	             ⭐	              ⭐⭐⭐
        PIL.Image.HAMMING	⭐⭐		                          ⭐⭐⭐
        PIL.Image.BICUBIC	⭐⭐⭐	       ⭐⭐⭐	            ⭐⭐
        PIL.Image.LANCZOS	⭐⭐⭐⭐	    ⭐⭐⭐⭐	           ⭐
    """

    # 画像データかどうか及び画像の場合その情報
    image_type = imghdr.what(None, h=binary_of_fullimage)

    if image_type is None:
        return None  # 画像データじゃない

    with Image.open(BytesIO(binary_of_fullimage)) as im:
        # 画像の中央をできるだけ大きいサイズで切り抜く
        image_square_cropped = get_square_cropped_pillow_image(im.copy())

        # 切り抜きを、指定したサイズと品質でリサイズする
        image_square_cropped.thumbnail(thumb_size, thumb_quality)

        dummy_thumb: BytesIO = BytesIO()  # サムネイルを一時保存するための入れ物

        # 一時的に保存（スマホ写真データなど、exif情報がある場合にも対応）
        image_square_cropped.save(
            dummy_thumb, format=image_type
        ) if "exif" not in im.info else image_square_cropped.save(
            dummy_thumb, exif=im.info["exif"], format=image_type
        )

        # サムネイルのバイナリデータを返す
        return dummy_thumb.getbuffer().tobytes()


def get_square_cropped_pillow_image(im: Image) -> Image:
    """
    画像の縦・横 短い方に合わせて、画像の中央を正方形に切り抜いて返す。
    """
    square_size = min(im.size)
    width, height = im.size
    box = None

    if width > height:
        top = 0
        bottom = square_size
        left = (width - square_size) / 2
        right = left + square_size
        box = (left, top, right, bottom)
    else:
        left = 0
        right = square_size
        top = (height - square_size) / 2
        bottom = top + square_size
        box = (left, top, right, bottom)

    return im.crop(box)

```

サムネイルを長方形にしたい場合は改造の必要がありんす:joy:
pythonの処理に制限時間を設定する

探索アルゴリズムを実装した際に、「10秒で探索！」など制限時間をつけて探索したいなと思うことがあり、色々探していると`timeout-decorator`という良さげなライブラリがありました。

#### timeout-decorator
https://github.com/pnpnpn/timeout-decorator

pipインストールすれば使えます。

制限時間が来ると強制終了するような使い方は[参考になる記事](https://qiita.com/toshitanian/items/133b42355b7867f5c458)があったのですが、探索アルゴリズムで使う際には制限時間がきたら最新の結果を返すような挙動をさせたいです。

ということで以下のようなコードになりました。

```python:test
import time
import timeout_decorator


def long_function(i):
    time.sleep(4)
    return i


class time_limit_class:
    def __init__(self):
        self.result = 0

    @timeout_decorator.timeout(5)
    def main(self):
        for i in range(1000):
            self.result = long_function(i)
        return self.result

    def timeout(self):
        return self.result


if __name__ == "__main__":
    time_limit = time_limit_class()
    try:
        print(time_limit.main())
    except:
        print(time_limit.timeout())

```

やってることとしては、とりあえずインスタンス作ってそこに値を保持しておき、timeoutの際にはそれを返すといったものになっています。

正直雑な実装だなとは思っているので、もっと良い方法があれば教えてください〜。
[iOS]UIパーツの領域変更

・UILabelなどパーツの領域変更

~~~
UILabel *valueText;
CGFloat x = 0;    //  左上座標X
CGFloat y = 0;    //  左上座標Y
CGFloat width = 100;　// 幅
CGFloat height= 100;　// 高さ

valueText.frame =  CGRectMake( x,y,width,height );
~~~

[https://developer.apple.com CGRectMake](https://developer.apple.com/documentation/coregraphics/1455245-cgrectmake)
Gradle公式ドキュメントを読む-Structuring Individual Builds(4)

#この記事について
Gradleをより理解するために公式ドキュメントを読んでメモ代わりとして私なりに訳していきます。
読んでいるドキュメントのバージョンは7.3.3です。
この記事で訳している章はAuthoring Gradle Builds>Structuring Individual Builds>Fine Tuning the Project LayoutFine Tuning the Project Layoutとなります。
必要な章から読んでいるため、一連の記事は公式ドキュメントの章立て通りの順にはなっていません。

##Structuring Individual Builds>[Fine Tuning the Project Layout](https://docs.gradle.org/current/userguide/fine_tuning_project_layout.html)
マルチプロジェクトビルドは、Gradleの1回の実行で複数のプロジェクトをビルドすることです。マルチプロジェクトビルドに参加するプロジェクトは設定ファイルに宣言する必要があります。

###[Project locations](https://docs.gradle.org/current/userguide/fine_tuning_project_layout.html#sub:project_locations)
マルチプロジェクトビルとは常に単一のルートからのツリー構造で表されます。ツリーの各要素がそれぞれのプロジェクトを表現します。プロジェクトはマルチプロジェクトビルドツリー内の位置を表すプロジェクトパスを持ちます。ほとんどの場合、プロジェクトパスはファイルシステム上の物理的な位置と一致します。しかし、この挙動は構成可能です。プロジェクトツリーは`settings.gradle`ファイルによって作成されます。設定ファイルの場所はルートプロジェクトの位置でもあります。

###[Building the tree](https://docs.gradle.org/current/userguide/fine_tuning_project_layout.html#sub:building_the_tree)
設定ファイルでは`include`メソッドを使ってプロジェクトツリーを構築できます。
####プロジェクトレイアウトの例
```gradle:settings.gradle
include 'project1', 'project2:child', 'project3:child1'
```
`include`メソッドはプロジェクトパスを引数に取ります。プロジェクトパスは物理ファイルシステムのパスと等しいと仮定されます。たとえば、`services:api`というパスは既定では`service/api`というフォルダ（プロジェクトからの相対位置）にマップされます。ツリーからの葉のみを指定するだけです。これは`services:hotels:api`というパスを含めると、`services`,`sevices:hotels`,'services:hotels:api'の3つのプロジェクトを作成することになります。プロジェクトパスの作業法に関する他の例はDLSドキュメントの[`Settings.include(java.lang.String[])`](https://docs.gradle.org/current/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]))を参照してください。

###[Modifying elements of the project tree](https://docs.gradle.org/current/userguide/fine_tuning_project_layout.html#sub:modifying_element_of_the_project_tree)
設定ファイルから作成されたマルチプロジェクトツリーはいわゆる`project descriptots`で構成されています。設定ファイルにあるこれらの記述子はいつでも変更することができます。記述子にアクセスは下記のように行います。

####プロジェクトツリー内の要素を走査する
```gradle:settings.gradle
include('project-a')
println rootProject.name
println project(':project-a').name
```
記述子を利用すると、名前やプロジェクトのディレクトリ、プロジェクトのビルドファイルを変更することができます。

####プロジェクトツリー内の要素の変更
```gradle:settings.gradle
rootProject.name = 'main'
include('project-a')
project(':project-a').projectDir = file('my-project-a')
project(':project-a').buildFileName = 'project-a.gradle'
```
より詳しい情報はAPIドキュメントの[ProjectDescriptor](https://docs.gradle.org/current/javadoc/org/gradle/api/initialization/ProjectDescriptor.html)クラスを参照してください。
flutter(Dart)の基礎知識(型)

Flutterの基礎部分(型)を自分なりにまとめて見ました。Flutterはオブジェクト指向であるDart言語を用いてアプリ開発ができるクロスプラットフォームになります。Widgetを重ねてアプリのUIを構築していきます。クラスのインスタンスであるプリミティブ・関数・NULLはオブジェクトであり、Objectクラスから継承されます。Dartの処理はmain関数から始まり{ }の中に定義していきます。関数の中にログ`print`や`変数`を書いて行きます。Dartの型推論は強力で、ある程度は型無しで`var`で値を入れることが可能です。またprint関数の中に`$`の値を使って変数値を入れることができます。

##int・double型(数値)

```dart
int hogeA = 5; //整数
double hogeB = 20.8; //少数   
```
##String型(文字列)

```dart
String hogeC = 'Hello!!';
```
##Boolean型(真偽値)

```dart
bool hogeD = true;
bool hogeE = false;
```
##List型(配列)&メソッド

```dart
List hogeF = [1, 4, 3, 5, 2];
hogeF.add(6);  //配列の中に6を追加
hogeF.indexOf(5); //配列の値を検索してその配列番号を取得 => 3
hogeF.insert(6, 10) //配列の６番目の要素に10を挿入する => [1, 4, 3, 5, 2, 6, 10] 
hogeF.length; //配列の長さを取得 => 6
hogeF.removeAt(6); //配列の６番目の値を削除して、その値を返す => 10
List<String> hogeG = ['ABC', 'DEF', 'GHI'];
hogeG.forEach((hogeHoge) => print(hogeHoge)); // リスト内の各要素に対して関数を実行 => ABC DEF GHI
var mappedHoges = hogeG.map((hogeHoge) => 'アルファベットの $hogeG').toList();
print(mappedHoges); // 与えられた各要素に処理をし、その要素群に対して新しいリストを作成 => ['アルファベットの ABC', 'アルファベットの DEF', 'アルファベットの GHI']
print(hogeF.contains(7)); // 与えられた要素がリストにあるか確認 => false
print(hogeG.contains('ABC')); // => true
print(hogeF.sort((hoge1, hoge2) => hoge1 - hoge2)); // 与えられた順序付け関数に基づいてリスト内の要素を順序付けする。 => [1, 2, 3, 4, 5, 6]
print(hogeG.sort((a, b) => a.length.compareTo(b.length))); // .compareToは値や文字列が等しいか、どちらが大きいかを判断 => [ABC, DEF, GHI]
var sumHoge = hogeG.reduce((hoge, nextHoge) => hoge + nextHoge);
print(sumHoge); // 与えられた関数を使って要素を単一に圧縮 => 21
const initialValue = 9;
var sumHogeHoge = hogeG.fold(initialValue, (hoge, nextHoge) => hoge + nextHoge)); // initialValueは初期値を入れるプロパティ
print(sumHogeHoge); // 与えられた関数を使って要素を単一に圧縮 => 30
```
##Set型(重複させない配列)

```dart
Set<String> hogeH = {'A', 'B', 'C'};
hogeH.add('D'); // => ['A', 'B', 'C', 'D']
hogeH.add('D'); // => false
print('$hogeH, ${hogeH.length}'); // => 'A, 'B', 'C', 'D', ['1', '1', '1', '1']
print('$hogeH.toList()[1]'); // 配列に変換 => ['A', ['B'], 'C', 'D']
```
##Map型(keyの型, value型)

```dart
Map<String, int> ageHoge = {'father': '40', 'mother': '35', 'son': '5'};
ageHoge.addAll({'daugher': '1'}); // => 配列に全ての要素を加える => ['father': '40', 'mother': '35', 'son': '5', 'daugher': '1']
[iOS]デバイスの画面解像度取得


●画面解像度をポイント単位で取得

~~~
CGRect rect = [[UIScreen mainScreen] bounds];
~~~

●画面スケール取得

~~~
CGFloat scale = [[UIScreen mainScreen]scale];
~~~

●iOSデバイス画面の解像度はポイント×スケールにより取得できます。
さくらでcronの実行

![スクリーンショット 2022-01-11 17.43.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/417196/f9fa2258-6bf0-da6c-73fc-8dc74652005f.png)


## さくらサーバーでcronの設定を行いました。
- 実行コマンドについて
    - cd /home/アカウント/www/app/batch;
        - これで、対象のディレクトリまで移動します
        - 今回はphpで実行したいので、　/usr/local/bin/phpがプログラムのコマンドパスになります([参照1](https://help.sakura.ad.jp/206206041/))
        - 黒塗り.phpが実行するファイルになります
- 実行日時は上記の設定だと2分毎に実行されます

参考
https://usortblog.com/sakuracron/
【Rails】同じscopeはmoduleに共通化しよう



複数のクラスに共通するscopeがあるとき、moduleとして共通化する。

##before

```author.rb
class Author < ApplicationRecord
  scope :active, -> { where(state: 'active') }
end
```


```book.rb
class Book < ApplicationRecord
  scope :active, -> { where(state: 'active') }
end
```

##after

```models/concerns/active_scope.rb
module ActiveScope
  extend ActiveSupport::Concern
 
  included do
    scope :active, -> { where(state: 'active') }
  end
end
```

```author.rb
class Author < ApplicationRecord
  include ActiveScope
end
```
```book.rb
class Book < ApplicationRecord
  include ActiveScope
end
```

##RSpec
モデルと同じようにテストする

```spec/models/active_scope_spec.rb
require 'rails_helper'

RSpec.describe ActiveScope, type: :model do
  describe '#active' do
    it '' do
       ...
    end
  end
end
```

##参考

https://qiita.com/hypermkt/items/0f6d759e03e2f6d527c0
別の配列から重複しない値の配列を作り直したい

配列A と配列Bにそれぞれ値があるが、配列Aに
含まれる値を
取り除いて配列Bを作り直したい

```js
const list = [
  {
    id: 'f7747f80-9386-11eb-89b0',
  },
  {
    id: 'r7747f80-9386-11eb-89b0',
  }
];

const list2 = [
  {
     id: 'f7747f80-9386-11eb-89b0',
  },
  {
     id: 'a7747f80-9386-11eb-89b0',
  }
];


const targetList = list2.filter(o => !list.find(o => o.id === list2.id));

console.debug(1, targetList);

const allList = [...list, ...targetList]

console.debug(2, allList);

```
文系出身のド初心者が1年ちょっとスクラム開発をやってみて-スクラム、私に合っているかも？-


#はじめに
こちらの記事に興味を持ってくださってありがとうございます！
文系出身で未経験入社→スクラム開発に1年ちょっとだけ携わっている、しがないIT系OLの私がスクラムをやってみての記録になります。
個人的にスクラム開発はスッと自分になじんだように感じているのでその理由や、ここはちょっと…というような内容を書いています。
※この記事はあくまで個人的な意見をつらつらと記載したものになります。一個人の考えとして捉えて頂ければと思います。

#私のステータス
まずは私自身について少しだけ。
・社会人2年目
・大学は4年制大学の文系出身
・「プログラミングは入社してからで大丈夫！」という人事の方の言葉を振りかざし、入社までIT系の勉強は一切せず
・軽度のゲームオタクなのでそこまでIT等々への嫌悪感や苦手意識はなし
・入社後半年間の研修を経て、現チームに配属
・**飽きっぽい**(集中力のキープ時間はウル〇ラマンもびっくりの短さ)

#やってみて感じたこと
###①スクラムやアジャイルの考え方は意外と受け入れやすかった
ウォーターフォール等スクラム以外に触れていないということもあるかと思いますが、個人的にはそこまで抵抗なくアジャイルの考え方を受け入れられていると思います。以下2点が個人的に好作用しているのでは…？と、個人的には分析しています。
####1.1つのタスクが最小限になっている
個人的にはこれが一番自分に合っていると思います。**ゴールがある程度明確になっている**ことも相まって、飽きずに開発業務を進めることができているのではと思います。(社会人として飽きないのは当たり前というのは別件)
####2.細かい単位でのリリースを実施している
チームやプロダクトによって異なるかと思いますが、この**小さい単位でのリリース**という考えも個人的に受け入れやすかったです。ソシャゲ等のアプデで、徐々に新機能が追加されたり、既存機能がアップデートされたり…ということに慣れていることもあり、その形態自体はスッと理解できたように思います。また、もしバグがあったとしてもすぐにhotfixしてリカバリーできる点も、心理的安全性の面から自分には合っていると思います。

###②とはいえ受け入れがたい部分もある
自分にめちゃ合っている…！かのように書きましたが、もちろん相容れない点もあります。
個人的には**”答えがない”**という考えに対してはやや苦手意識があります。勉強会等々に参加してみても**”ケースバイケース”**という一言で結局片付けられてしまうような気がして、その**絶対的な正解のなさ**に対してはつかみどころがない…と思ってしまいます。
が、**絶対的な正解がない＝工夫次第でもっと良くできる**と最近捉えることができるようになってきています。チームの一員として、みんなで一緒によりよいチームを作っていきたいと今は思えています。

#最後に
ここまで読んでいただきありがとうございました。
今後さらにスクラム開発に携わっていく中で、この意見や考え方が変わっていくことももちろんあると思うので、また適度なタイミングで自分の言葉で表現できるようにしていきたいと思います。
scikit-learnやlightgbmなどのONNX形式モデルがブラウザで動作しないとき


# やりたいこと
Pythonで訓練したモデルをONNX形式でブラウザ上（JavaScript）で推論させたい。

# 使用ライブラリ
- Python
    - scikit-learn/lightgbm
    - onnxmltools
- JavaScript
    - onnxruntime-web

# 問題
[JSのサンプルコード](https://github.com/microsoft/onnxruntime-inference-examples/blob/main/js/quick-start_onnxruntime-web-bundler/main.js)通りに書いても`session.run()`部分で`412398８`のようなエラーが出て動かない。

# 解決法
PythonでモデルをONNX形式に変換する際、`zipmap`オプションを無効にしないとブラウザでは動かないらしい。
例えば、LightGBMモデルだとこんな感じ。

```py
import onnxmltools
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_types, zipmap=False)
```

# 参考
https://github.com/microsoft/onnxruntime/discussions/9688
SvelteKit+Prismaで作成したアプリをVercelにデプロイするとエンドポイントが404になる件への対応

## やりたかったこと

タイトル通りですが、[SvelteKit](<(https://kit.svelte.dev/)>)と[Prisma](https://www.prisma.io/)で構成したアプリを [Vercel](https://vercel.com/) にデプロイして動かしてみたところ、ページから API への通信が 404(Not Found) エラーで失敗していました。
SvelteKit はいわば、Next.js の Svelte 版で`/routes`以下に`.(js|ts)`ファイルを配置するとエンドポイントとして利用することができますが、ここへの通信が失敗していました。

## 解決方法

以下のように`postbuild`コマンドを package.json に追加する。

```json:package.json
{
  "scripts": {
      ...
    "build": "svelte-kit build",
    + "postbuild": "cp prisma/schema.prisma .vercel_build_output/functions/node/render/ && cp node_modules/@prisma/engines/*query* .vercel_build_output/functions/node/render/",
      ...
  }
}

```

## 参考

<https://github.com/sveltejs/kit/issues/1547>
<https://github.com/prisma/prisma/issues/7019>
UbuntuServer 20.04 LTS を立てる

# 用意するもの
* PC
* 中身が消えてもいいUSBメモリ(大体4Gで足りる)

# インストールメディアの作成

### ubuntuのISOイメージをダウンロードする。

下記のサイトからUbuntu Server 20.04 LTSをダウンロードします。

> ubuntu: https://jp.ubuntu.com/download

### liveイメージ作成ツールをダウンロードする。
今回はrufusというツールを使用します。

> https://forest.watch.impress.co.jp/library/software/rufus/

### USBメモリを指し、rufusを起動します。

![Rufus 3.17.1846  2022_01_10 11_48_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691581/f44376c2-8791-b0c0-bb5f-2f7391d8b3ed.png)

* デバイス
    * 作成するUSBメモリを選択する

* 選択
    * 先ほどダウンロードしたUbuntuのISOイメージを選択してください。

スタートを押して作成します。
もう一度スタートが押せるようになったら終了です。

※ 作成の際にイメージモードを選択する画面が出てくるかもしれませんがそのままokを押してください。
※ その他何回か警告が出ることがありますが必要なファイルのダウンロードだったりUSBメモリのデータの削除だったらokを押してください。

# Ubuntuのインストール

1. 作成したUSBを指しPCを起動します。
1. 起動後delキーを連打してBIOS画面に入ります。
1. usbメモリで起動するように設定してください。
1. 再起動

### インストーラー
1. install ubuntu server を選択する(自動で選択されます)
1. 言語の選択
1. installerのアップデート。continueを選択
1. キーボードのレイアウトを選択します。通常はjapaneseです。
1. ネットワークの設定。通常はそのままdone
1. プロキシの設定。そのままdone
1. ubuntu archiveの設定。そのまま
1. diskの選択。そのまま
1. disk詳細。そのまま
1. 確認。yes
1. 上から名前、ホスト名、ユーザー名、パスワード、パスワードの確認
1. 今回はopen ssh serverを選択します。
1. ほかのソフト。そのまま
1. 少し待ちます。
1. reboot nowを選択して再起動。
1. USBメモリを抜く。
1. エンター

# 最後に
以上でインストール完了です。
ユーザー名を入力して、パスワードも入力すればコンソールが開きます。
その他sshサーバーの設定をすることをお勧めします。
スクリプトエディタ拡張した際に[script]を復活させる

#初めに
スクリプトエディタ拡張すると[script]が消えてしまいます。
シングルクリックで、Projectウィンドウでスクリプトの位置を教えてくれたり
ダブルクリップで、指定のエディタが開いてくれる。
これが便利でよく使うのですが、エディタ拡張するとこれが消えてしまうので
復活させたい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/fb3bb87c-0074-4ecf-389e-6889f6427cb7.png)


##Unity Answersに答えが！
調べてみると、Unity Answersに同じように質問して答えてもらっている！

https://answers.unity.com/questions/550829/how-to-add-a-script-field-in-custom-inspector.html


###スクリプト

```c#
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour((MonoBehaviour)target) , typeof(MonoScript), false);
EditorGUI.EndDisabledGroup();

```

お！復活した！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/622a5f3b-f031-1e59-21bf-228aa30196f9.png)

自分で復活させているので、好きな文字を表示できる！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37068/dc7a4006-e2bb-442c-54d4-33873262040a.png)


####最後に
スクリプトの位置がわかったり、ダブルクリップするとエディタが開く動作は開発していると
結構使うので、らくちんになったは！
[TEX] itemboxをページを跨いで使いたい(M1 Mac)

itemboxはデフォルトではページを跨いで使いことができません。
例えばitembox環境内に長い数式を書き込むと、ページの境界はこんな感じになります。
![ex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2092159/78662d39-0cb0-97d8-4a4e-abc44173ede0.png)

このような場合にはbreakitembox環境を使うと良いようです。
詳しくは下記の記事に記載されています。
https://muscle-keisuke.hatenablog.com/?page=1467446525
styファイルの置き場所に関しては、texコマンドを実行した際の

```ruby:qiita.rb
(/usr/local/texlive/2021/texmf-dist/tex/latex/jknapltx/mathrsfs.sty)
(/usr/local/texlive/2021/texmf-dist/tex/latex/carlisle/slashed.sty)
(/usr/local/texlive/2021/texmf-dist/tex/latex/here/here.sty)
```
を参考にすれば、パソコンのどこにstyファイルがあるかが分かる思います。
私の場合は
/usr/local/texlive/2021/texmf/tex/latex/
ディレクトリに集められているようです。

しかし、この記事通りにインストールしても、下記のようなエラーが出るかと思います。

```ruby:qiita.rb
! LaTeX Error: File `eclbkbox.sty' not found.
```

つまり、eclbkboxが存在しないようです。
こちらの記事
https://konoyonohana.blog.fc2.com/blog-entry-314.html
の「1.1インストール」にあるリンクからeclbkbox.styを取得できます。
あとは、先程のemathのstyフォルダに入れて、

```ruby:qiita.rb
sudo mktexlsr
```
を実行すれば、breakitembox環境が使えるかと思います。


ちなみに、Macの場合はファイルの移動などはターミナル上でやっても良いのですが、Finderの「移動」タブからコンピュータフォルダへ移動し、「command+shift+.」で隠しフォルダを表示させれば、Finderアプリ上からもファイルの移動ができます。
npm run startでエラーが出たときの対処法

今回は、npm run startでエラーが出たときの対処法について書きます。
*npm = Node Package Manager  

```
npm run start
```

上記コマンドを入力すると、このようなエラーが出ました。

```
sh: webpack-cli: command not found
```

webpack-cliが上手く機能していないと思い、グローバルにインストールし直すため次のコマンドを実行しました。

```
npm install -g webpack
npm install -g webpack-cli
```

ちなみに、「（-g）又は（--global）」をつけないとコマンドを実行した場所に「node_modules」というフォルダができ、その中（ローカル領域）にパッケージがインストールされます。

そのため、頻繁に使うパッケージなどはローカル領域に度々インストールするのではなく、グローバル領域にインストールした方が良い場合があります。

先ほどのコマンドを実行してから、node_modulesでエラーが出ました。

```
You need to install 'webpack-dev-server' for running 'webpack serve'.
Error: Cannot find module 'webpack-dev-server'
```

参考にした記事では、以下のコマンドでnode_modulesを削除していました。

```
rm -r node_modules
```

それから、

```
npm i
```


(i = installの略)


そして、再度 [npm run install] するとまたまたまたエラーが出ました。

```
Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.
 - options has an unknown property 'contentBase'. These properties are valid:
   object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }
```

Invalid options object. Dev Serverとエラー文に書かれていることから、webpack.config.jsのdevserverの記述がおかしいと思い調べると、、、

webpack-dev-serverのオプションからcontentBaseがなくなっており、元の記述のcontentBaseからstaticに変更して再度　 [npm run install]　を実行すると解決しました。

v３からv４の間で変更がありcontentBaseがお亡くなりになられておりました。

めでたし、めでたし、、、
JSDocの書き方

JSDocの書き方について簡単にメモしておく。

# JSDocとは
**JSDoc**とは、JavaScript用のAPIドキュメントジェネレータのこと。```jsdoc```コマンドを実行することでHTML形式のドキュメントを作成することができる。

# インストール方法
**JSDoc**は、**npm**から以下のようにインストールできる。

```
// グローバルインストールの場合
npm install -g jsdoc

// ローカルインストールの場合
npm install --save-dev jsdoc
```

# JSDocの主なタグ
**JSDoc**で使用されるタグの一部を紹介する。

■ `@param : 引数`

```javascript:
/**
 * @param {<型>} <引数名> - <説明>
 *
 * @param {string} name - 名前
 */
```

■ `@return : 返り値`

```javascript:
/**
 * @return {<型>} <説明>
 *
 * @param {boolean} 真偽値を返す
 */
```

■  `@throws : 例外`

```javascript:
/**
 * @throws {<型>} <説明>
 */
```

■  `@classdesc : クラスの説明`

```javascript:
/**
 * @classdesc <クラスの説明>
 */
```

■  `@constructor : コンストラクタ`

```javascript:
/**
 * <コンストラクタの説明>
 * @constructor 
 */
```

## ドキュメントの作成
HTML形式のドキュメントを作成するには、```jsdoc```をグローバルインストールした場合次のコマンドを実行する。

```
jsdoc <ソースフォルダ・ファイル名>
```
[フロント]quillで生成されたHTMLからHTMLタグを抜き、aタグ内のリンクをpタグ、hタグで出来た段落の後ろに付ける[TypeScript]

quillのHTML WYSIWYGエディタを使用してメールエディタを作成しようとしたが
仕事でHTMLメール本文をテキストに変換する必要が出たため、休日に方法を検討して文章でまとめました。
ここにその方法を記録します。

- 行いたいこと
    - pタグとhタグの段落に挟まれたaタグが持つリンクを段落の後ろに配置したい
    - quill出力されるhtmlタグをテキストメール用で使用するためHTMLタグを外したい

1. pタグ、H1~9タグの中にあるaタグが持つhrefの中身をまとめて外に出す
    - 今回は段落の後ろにリンクをまとめて配置したかったため苦労しました。 

```typescript
const convertUrl = (text: string): string => {
  // pタグとhタグを識別する正規表現
  const paragraph = /<p>.+?<\/p>|\<h[0-9]\>.+?<\/h[0-9]\>/g;
  // aタグ内のhrefを識別する正規表現
  const href = /href="(.+?)"/g;
  //   matchしたhrefの中身からhrefと""を除去するための正規表現
  const removeHref = /href=|"/g;
  return text.replace(paragraph, (matchedParagraph) => {
    const convertedParagraph = matchedParagraph
      .match(href)
      ?.map((matchedHref) => {
        return "<p>" + matchedHref.replace(removeHref, "") + "</p>"; // リンクは一旦pタグで囲う
      });
    if (convertedParagraph) {
      return matchedParagraph + convertedParagraph.join(""); // 配列として外に出されたリンクを繋げる
    } else return matchedParagraph;
  });
};

```

2. HTMLのタグを除去する 
    - 少し泥臭いですがquillで使用するタグを全て正規表現でテキストに変換します
        - 今回はpタグとhタグ、liタグを改行に変換しています
        - 他にもquillのタグを使用したい場合は置き換える内容を増やしてください
        

```typescript
    const convertHtmlToText= (mailHtml: string): string => {
      const convertedHtml = convertUrl(mailHtml);
      return convertedHtml
        .replace(/<p><br><\/p>/g, '\n')
        .replace(/<\/p>/g, '\n')
        .replace(/<\/h1>/g, '\n')
        .replace(/<\/h2>/g, '\n')
        .replace(/<\/h3>/g, '\n')
        .replace(/<\/h4>/g, '\n')
        .replace(/<\/h5>/g, '\n')
        .replace(/<\/h6>/g, '\n')
        .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '');
    };
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390039/8355a469-4b70-ca5c-b4de-315ef0eb8ae1.png)
上記のスクリーンショットのようなQuillのリンクを含む文字列を
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390039/fbf3c3c5-3fe5-b86b-3368-50d31137fa0d.png)
のようなテキストに変換ができます
Flask でフォームを保持する方法


Flask でフォームを作っていて、
送信ボタンを押したときにフォームの中身が全部消えるのが嫌だったので、
対策方法をメモっておきます。

## できること

Flaskでフォームを保持する方法がわかる

## 辞書でフォームのデータを引っ張っておく

`request.form`に辞書の形でフォームのデータが内包されているため、
これを次のレンダー時に引っ張ってくるようにしましょう。

```py
from Flask import request


@app.route('/', methods=["GET", "POST"])
def index():
    if request.method == 'POST':
        # POSTの場合
        return render_template('index.html', form_data=request.form)
    else:
        # GETの場合
        return render_template('index.html')
```

セッションに保管しておいて、次来たときでも呼び出せるようにしておくのもいいですね。

```py
from Flask import request,session


@app.route('/', methods=["GET", "POST"])
def index():
    for request.form in key:
        session[key]

    if request.method == 'POST':
        return render_template('index.html', form_data=session)
    else:
        return render_template('index.html')

```

## HTML側で細工しておく

力技ですが、HTMLに`{% if %}`とかを入れて対処しましょう。

```html
{% if form_data %}
<input type={{type}} class="form-control" id={{id}} name={{id}} value="{{session[id]}}">
{% else %}
<input type={{type}} class="form-control" id={{id}} name={{id}}>
{% endif%}
```

フォームが一つとかだけならいいんですが、同じようなフォームを複数扱う場合に大変なので、
マクロ機能を使うと大変便利です。
~~え？違う内容のフォームがたくさんある？…力技でどうにかしましょう~~

```html
{% macro form(id) -%}
    {% if form_data %}
    <input type={{type}} class="form-control" id={{id}} name={{id}} value="{{session[id]}}">
    {% else %}
    <input type={{type}} class="form-control" id={{id}} name={{id}}>
    {% endif%}
{%- endmacro %}
```
【SQL】テーブル定義を取得

# 【SQL】テーブル定義を取得
````
WITH BASE as(		
	  SELECT CONCAT (TABLE_SCHEMA, '.', TABLE_NAME)as TABLE_NAME		
	    , COLUMN_NAME		
		, DATA_TYPE	
		, CHARACTER_MAXIMUM_LENGTH	
		, CHARACTER_OCTET_LENGTH	
		, NUMERIC_PRECISION	
		, NUMERIC_PRECISION_RADIX	
		, DATETIME_PRECISION	
		, IIF(IS_NULLABLE = 'YES', ',', 'NotNull') AS NN	
		, ORDINAL_POSITION	
	  FROM [INFORMATION_SCHEMA].[COLUMNS]		
	  WHERE TABLE_NAME IN ('{ テーブル物理名 }')		
	)		
	SELECT TABLE_NAME AS 'テーブル名'		
	  , COLUMN_NAME AS 'カラム名'		
	  , DATA_TYPE AS '型'		
	  , CASE		
	      WHEN DATA_TYPE in ('char', 'nchar', 'nvarchar', 'varchar') THEN		
		    IIF(CHARACTER_MAXIMUM_LENGTH > 0, CHARACTER_MAXIMUM_LENGTH, '')	
	      WHEN DATA_TYPE in ('bigint', 'int', 'tinyint') THEN		
		    IIF(NUMERIC_PRECISION > 0, NUMERIC_PRECISION, '')	
		  WHEN DATA_TYPE in ('numeric', 'decimal') THEN	
		    IIF(NUMERIC_PRECISION > 0, NUMERIC_PRECISION, '')	
		    --IIF(NUMERIC_PRECISION > 0, CONCAT(NUMERIC_PRECISION, NUMERIC_SCALE) , '')	
			
		  WHEN DATA_TYPE in ('bit', 'varbinary', 'date', 'datetime') THEN ''	
			
			ELSE ''
		END AS 桁	
	  , NN AS NotNull		
	FROM BASE		
	ORDER BY TABLE_NAME, ORDINAL_POSITION		
	;
````
無償キャンペーンを使ってゼロ円で Oracle Cloud Infrastructure 2021 Architect Associate を取得した話

2021年12月7日～2022年3月31日の間、Oracle Cloud 認定資格の受検料が無料になるキャンペーンが行われています。
無料、いい言葉ですね。

https://blogs.oracle.com/oraclemaster/post/oci-free-cert

日本語版では以下の試験が無償で提供されています。（なんで2021の位置がバラバラなんだろう...）

- Oracle Cloud Infrastructure 2021 Architect Professional
- Oracle Cloud Infrastructure 2021 Architect Associate
- Oracle Cloud Infrastructure 2021 Cloud Operations Associate
- Oracle Cloud Infrastructure Developer 2021 Associate
- Oracle Cloud Infrastructure Foundations 2021 Associate

ということで 2021 Architect Associate 試験を受検・合格したので受検記録を投稿します。

合格時のスコアレポートは以下の通り。

![Screen Shot 2022-01-11 at 4.05.59 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66988/83eda176-7cbf-b038-4fb4-2c0129660d9f.png)

# 受検前の知識レベル

昨年から OCI を使った開発業務をしており、主に以下のサービスを利用していました。

- Compute
- Functions
- Notifications
- Object Storage Service
- MySQL Database Service
- Vault
- Email Delivery
- Resource Manager

# 受検に向けた勉強

試験に向けた勉強期間としては約1日（！）で、主に利用したのは以下。
実は試験だけでなく、公式に提供されているラーニングパスもまた無償提供されているので、完全に無料で合格までの道を進むことができます。

https://blogs.oracle.com/oraclemaster/post/free-oracle-cloud-infrastructure-training-and-certification-ja

## 公式の資格試験準備セミナー

Architect Associate 試験については、日本語の対策セミナーも提供されています。
これを試験前日〜当日にかけて倍速で見ました。

https://learn.oracle.com/ols/module/prepare-for-oci-architect-2021-associate-certification-jp/35644/101424


## 公式の模擬試験

模擬試験もあります。本番同様、60問のフルサイズ模擬試験が無料で受けられます。
上述のセミナーを見る前に1回、見た後に1回受けました。問題の内容は変わりませんが、順序はランダムです。

https://learn.oracle.com/ols/module/practice-exam-for-oracle-cloud-infrastructure-2021-architect-associate-certification-jp/35644/102299


# 試験内容と感想

- 試験会場
    - ちょうどワーケーションをしていたので、滞在しているホテルの部屋で受検しました。
    - 試験は Lockdown Browser というアプリケーション上のブラウザで行われます。
        - このアプリケーションが起動していると他のアプリケーションを触ることができません。
        - 試験のために起動中のアプリケーションを落としておくとかをしなくてよいので、少し便利。
    - Zoomを介して試験監督にカメラ映像を共有します。複数の受検者が同一のZoomセッションに入るので、他の受検者のカメラ映像も見えてしまうのはちょっと微妙な仕様な気がする。
- 試験の内容
    - コンピュート、ストレージ、データベース、ネットワークなどの基本的なサービスについて理解できていれば問題のないレベルかと思います。OCI はまだ（AWS ほど）サービスが多くないので、知識を整理しやすいです。
    - Functions 等のサーバレス系のサービスに関する問題は範囲外となります。
- 試験時間
    - 試験時間は90分で、個人的には十分すぎるほど時間があったように感じられました。

# おわりに

というわけで、ゼロ円で OCI 認定資格を取得できました。
キャンペーン終了までまだ余裕があるので、Developer Associate も受検してみようかなーと思っています。

興味のある方はぜひ無償提供されている今のうちにトライしてみることをオススメします。
~~ちなみに不合格の場合でも年5回まで受検できるので、今なら「受かるまで受ける」というパワープレイも可能だと思います。~~