OmniAICreator/Qiita-1.07M huggingface dataset:
超初心者が、3週間でAZ-900に合格してみた（１回不合格もしてみた）

#超初心者が3週間でMicrosoftのAZ-900に合格してみた勉強法をメモ程度に記載したページです。ほぼ自分用です。

**■合格者のスペック**
・国公立大学文系卒業
・業務でAzure、クラウドは全く使用しない
・クラウドって何？という感じの事前知識レベル
・Pythonの資格は２つ取得済

**■勉強時間■**
合計３週間
平日は2時間
休日は3,4時間
クラウドが全くわからない人物だったので、抵抗感をなくす、とか
体・頭になじむ、までが結構時間かかりました。

**■勉強方法■**
2回目の受検で合格。1回目は盛大に不合格をくらっています。

***ステップ１　どんなテストかググって自分なりに把握する。***
まずこれがかなり重要。「試験範囲を把握する」ということではなく
「あ、つまりAZUREでの名前と役割。セキュリティの話ね」などと
自分の言葉で「どんなことが問われる試験なのか」を話せて理解できていることが大事だと思います。

***ステップ２　おすすめの書籍を購入してざざざっと流し見する***
書籍は2冊購入しました。
「クラウドの基本」
「AZ-900試験対策本」
ざーーと流し見してノートに殴り書きしていきます。

***ステップ３　ノートにまとめる。MSLearnをざっと見る。***
まずは、「パブリッククラウドって何？」から始まっているので、
「クラウドとは」という領域が理解できるようにノートにまとめていきました。

***ステップ４　Microsoft Training Day　に参加する***
このトレーニングに参加することが大事かと思います。私が受講した際はバウチャーも発行されましたし、日本語で丁寧に解説してくださるので、仕事の合間に見ました。

***ステップ５　UDEMYの模擬試験を購入してやりまくる***
まる覚えになってしまうことが怖かったので、みなさんが勧めているUDEMYの模擬試験を購入しやりまくりました。この模擬試験は必ず購入したほうがいいと思います。
問題数が多いのと、解説も見ることができるので超役に立つ。しかも安い。

**■結果■**
MicrosoftTraining Dayを受講してバウチャー利用できるようになってから、
次の週に受検をしました（テストセンター受検）

…1回目落ちました泣

700点で合格なのですがあと一歩、という得点だったと思います。
泣きながら帰宅します。

どこが弱かったのかを確認し、その範囲を再度復習。あまり時間をあけずに受検をしにいき、2回目で合格することができました。

**■感想■**
・AZ-900は結構落ちているという人の情報も聞きます。私も落ちました。
・資格としては簡単なものだけど、クラウド初学者はちょっと時間かかると思います
・完璧に勉強・インプットするよりも、模擬試験をやることもしくはとりあえず実際に受検してみること、など実践・アウトプットを重視したほうがいいです
・勉強を通して、基本知識がついたのとMicrosoftの資格の勉強の仕方の要領を得ることができたと思います。

次の試験も頑張ります。
Githubの通知をリアルタイムでslackで受け取りたい

## Github->Slackへの通知の設定を更新する

### やりたいこと

以下全レポジトリ共通で良い。

- 即時に通知が欲しい
   - メンションされた時
   - 自分が立てたissueにコメントがついた時
   - 自分が開いたPRにレビュー/コメントがされた時
   - 自分が開いたPRにコメントがついた時
   - PRのレビュー依頼が来た時
   - 自分がレビュー/参加したPRにコメントがついた時
   - (特定のキーワードを含む発言がされた時)
- まとめて1日数回通知がほしい
   - チームメンションされた時
   - 自分が参加しているissueにコメントがついた時

### 結局どうしたか

👉 GitHubの方で、`Scheduled reminder for org_name`を設定した。

https://github.com/settings/reminders/:org_name

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/80f455fd-e2b9-d03c-6534-0b6e3928161f.png)


📝 上記を設定すれば、Slack GitHub app で `/github subscribe :org_name`の設定をしなくても大丈夫な気がする。



<details>
<summary> Slack GitHub appのスラック通知 </summary>

https://github.com/integrations/slack#readme


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/ee47ec06-f81f-b6d5-2216-bc4e2661c192.png)


GitHubの方で、`Scheduled reminder for org_name`を設定したら、以下の設定はとりあえずしなかった。

- `/github subscribe owner/repo reviews comments`
- `/github unsubscribe owner/repo reviews comments`


reviews, commentsとかを何も指定されなくても、Mention(以下)はsubscribeされる。っぽい。

> - Assignee in an Issue
> - Reviewer for a PR
> - Mentioned in a PR/Issue description/comment/discussion
> - Reviewer for a Deployment
> - Scheduled reminders for PR review requests


</details>

<details>
<summary>その他メモ</summary>

deployment review ってのを初めて知った。

</details>
地図データファイル KMLの開発者体験を向上するホットリロード

## 概要

[GoogleEarth](https://earth.google.com/web/?hl=ja)上で表示するプレースマークや線、視点などを記述するテキストファイル KMLを作成するときにちょっと便利な方法です。
Web版のGoogleEarth上で、作成しているKMLファイルの表示を確認をするときに、修正のたびに新しいプロジェクトとしてファイルをインポートするのは少し面倒じゃないですか？

そこで **KMLファイルを変更したら自動でファイルをリロード** する方法を考えました。

![kml-server.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2591762/4d1c16d5-2694-2dc2-fe6d-0bb0657f8d2b.gif)

GIF動画では、\<LookAt>要素を変更して、カメラの視点を変えています。
\<LookAt>要素の内容は、そのアイテムをクリックするとレンダリングされるため、クリック後に視点が変わります。
\<Point>要素の位置などは常時レンダリング処理されているため、KMLファイル保存後すぐに反映されます。

## 方法

Web版のGoogleEarthはローカルファイルを自動では読み込めません(セキュリティ保護のため)。ですが、サーバーと通信してアイテムを読み込む機能があるため、作成中のKMLファイルをローカルサーバーからサーブすることでホットリロードを実現しています。紹介する方法では、簡易サーバーを立てるのにnpmのライブラリをつかっています。

### ローカルサーバーの準備

ローカルサーバーを準備しますが、https通信じゃないとGoogleEarth内部で通信エラーがおきます。そのため、npmのライブラリ [live-server-https](https://www.npmjs.com/package/live-server-https)を使います。このライブラリは簡易サーバーを立てる[live-server](http://tapiov.net/live-server/)用に、自作のssl証明書を用意してくれています。
まず、サーバー用にフォルダを用意し、必要なライブラリをインストールします。

```shell
mkdir kml-server
cd kml-server
npm install live-server live-server-https
```

package.jsonのscriptsを以下のようにしておきます。LOCAL_IPとPORTは各自で調べて記入します。

```json
  "scripts": {
    "start": "live-server --https=./node_modules/live-server-https --cors --host=YOUR_LOCAL_IP --port=PORT"
  },
```

その後、

```shell
npm run start
```

とコマンドを打つと、ローカルサーバーが立ち上がります。

### GoogleEarthにインポートするファイルの準備

GoogleEarthのプロジェクトにインポートするファイルは、ローカルサーバーとの通信用ファイルです。以下のようなファイルを準備します。\<href>要素で作成中のKMLファイルを指定しますが、ローカルサーバー起点のパスを記述します。また\<refreshInterval>要素でリロード間隔を指定します。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <NetworkLink>
    <name>Server</name>
    <open>1</open>
    <Link>
      <href>https://LOCAL_IP_ADDRESS:PORT/EDITED_FILE.kml</href>
      <refreshMode>onInterval</refreshMode>
      <refreshInterval>1</refreshInterval>
    </Link>
  </NetworkLink>
</kml>
```

### 作成するKMLファイルの準備

作成するKMLファイルは、通信用のKMLファイルに記述したパスと、ローカルサーバー上のパスが一致しなければいけません。今回はプロジェクトディレクトリ直下でサーバーを立ち上げ、またそこに作成するKMLファイルを置きます。

```shell
touch EDITED_FILE.kml
```

このファイルのkml要素の子要素として、Folder要素を置きました。このFolder内部に地図表示用の記述を追加していきます。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Folder>
  </Folder>
</kml>
```

ここでKMLファイル編集の際、2つ注意点があります。

* 不正なKMLを記述したまま保存しない
* kml要素の子として2つ以上要素を置かない

この2点を守らないと、ホットリロードが停止し、以降変更が適用されなくなります。その際は、KMLファイルを修正し、再度通信用のKMLファイルをインポートするか、GoogleEarthを再読み込みすることでホットリロード機能が復活します。

### 編集を始める

以上の準備を行い、以下の手順で行います。

1. ローカルサーバーを立ち上げる
1. GoogleEarthのプロジェクトに、通信用のKMLファイルをインポートする
1. 作成中のKMLファイルを編集する

最初にローカルサーバーとhttps通信をする際、この通信の証明書は信頼されている機関発行のものではないとの警告がでると思います。そのときは、安全でない方法で続行するか、例外を追加する必要があります。どちらもプロンプトの詳細オプションの下にありますので、探して続行してください。
これでKMLファイルを編集して保存すると、変更内容が適用されているはずです。

## 最後に

少しでも便利だと感じてくれる人がいると嬉しいです。
ABC242のA, B, C問題 を Elixir で解く

https://qiita.com/obelisk68/items/4b71b70d04b6ee6710d7

これに続いて、Elixir 超初心者が AtCoder に挑戦してみました。

https://atcoder.jp/contests/abc242

クソコードを御指導下さい。

# A問題: T-shirt

```elixir 
defmodule Main do
  def main do
    IO.read(:line) |> integer_list() |> do_solve() |> IO.puts()
  end
  
  defp integer_list(str) do
    str
    |> String.trim()
    |> String.split(" ")
    |> Enum.map(&String.to_integer/1)
  end
  
  defp do_solve([a, _, _, x]) when 1 <= x and x <= a, do: 1.0
  defp do_solve([a, b, c, x]) when a < x and x <= b, do: c / (b - a)
  defp do_solve(_), do: 0
end
```

ガード節の中では`&&` `||`が使えず、`and` `or`を使うというのがハマリポイントでした。それから Elixir では整数どうしの`/`でも結果が`Float`になるんですね。

# B問題: Minimize Ordering

```elixir 
defmodule Main do
  def main do
    IO.read(:line)
    |> String.trim()
    |> String.to_charlist()
    |> Enum.sort()
    |> List.to_string()
    |> IO.puts()
  end
end
```

これは文字列をコードポイントに直してソートして、また文字列に直すと。

# C問題: 1111gal password

これは動的計画法なんですが、はて、関数型言語で動的計画法とは…となりました。ぐぐってみると、`reduce`を使うと。Ruby なら`inject`で（`reduce`もありますが）、Elixir なら`Enum.reduce`ですね。なるほど、と。

で、リストを使って実装したところ、

```elixir 
defmodule Main do
  def main do
    IO.read(:line)
    |> String.trim() |> String.to_integer()
    |> do_solve([1, 1, 1, 1, 1, 1, 1, 1, 1])
    |> IO.puts()
  end
  
  defp do_solve(n, list) do
    1..n - 1
    |> Enum.reduce(list, &get_next_list(&1, &2))
    |> Enum.sum() |> r()
  end
  
  defp get_next_list(_, list) do
    0..8 |> Enum.map(&dp(&1, list))
  end
  
  defp dp(0, enum), do: Enum.at(enum, 0) + Enum.at(enum, 1) |> r()
  defp dp(8, enum), do: Enum.at(enum, 7) + Enum.at(enum, 8) |> r()
  defp dp(i, enum) do
    Enum.at(enum, i - 1) + Enum.at(enum, i) + Enum.at(enum, i + 1)
    |> r()
  end
  
  defp r(n), do: rem(n, 998244353)
end
```

TLEをくらいました。どうも、`Enum.at`が遅いのではと考え、マップを使って再挑戦。マップは Ruby だとハッシュって感じですね。いわゆる連想配列。

```elixir 
defmodule Main do
  def main do
    IO.read(:line) |> String.trim() |> String.to_integer()
    |> do_solve(%{1=>1, 2=>1, 3=>1, 4=>1, 5=>1, 6=>1, 7=>1, 8=>1, 9=>1})
    |> IO.puts()
  end
  
  defp do_solve(n, map) do
    1..n - 1
    |> Enum.reduce(map, &get_next_map(&1, &2))
    |> Map.values()
    |> Enum.sum() |> r()
  end
  
  defp get_next_map(_, map) do
    1..9
    |> Enum.reduce(%{}, fn i, acc -> Map.put(acc, i, dp(i, map)) end)
  end
  
  defp dp(1, map), do: map[1] + map[2] |> r()
  defp dp(9, map), do: map[8] + map[9] |> r()
  defp dp(i, map), do: map[i - 1] + map[i] + map[i + 1] |> r()
  
  defp r(n), do: rem(n, 998244353)
end
```

なんとか AC しました。1652ms ですけれどね。なかなかむずかしい。

# 最後に

もっといい書き方があったら教えて下さいね。
ViewPager2でスワイプで画面切り替えを無効にする方法

今回はViewPager2で作ってみようと思った時に、スワイプで画面を切り替えるのではなく、自分で用意したナビゲーション処理で切り替えたかった為、調べたメモです。


# setUserInputEnabled
ViewPager2にはsetUserInputEnabledが用意されてます。

```
binding.viewPager2.isUserInputEnabled = false
```

ViewPager2への移行をしていないので、これから使用していきたいと思います。

# 公式リファレンス

https://developer.android.com/reference/kotlin/androidx/viewpager2/widget/ViewPager2?hl=ja#setuserinputenabled
macのPythonはコマンドによってバージョンが違う

# 環境

- macOS Big Sur 11.6.4

# macOSにはPythonが2種類インストールされている

```
% python -V
Python 2.7.16
% python3 -V
Python 3.9.7
```

# 気づいた経緯

### 処理がコケる

Windowsでやっているようにvenvの構築をしようとしたら途中でコケる、何故？？

```
% python -m venv hoge
/System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python: No module named venv
```

### そもそもvenvはいつ追加されたのか

下記の通り、バージョン3.3から追加されている。

引用元：[venv --- 仮想環境の作成 — Python 3.10.0b2 ドキュメント](https://docs.python.org/ja/3/library/venv.html)
![スクリーンショット 2022-03-20 16.41.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526492/ebb0229d-407f-6a94-41ef-24df035ee66e.png)

### pythonコマンドで呼び出されるバージョンを確認

バージョン2系が呼び出されていることを確認。

```
% python -V
Python 2.7.16
```

### Pythonバージョン3は呼び出せないのか？と調べる

python3で呼び出せることがわかる。

```
% python3 -V
Python 3.9.7
```

### Pythonバージョン3で作成、有効化を確認

※ここだけわかりやすいように「%」の前も記載

```
xxx % python3 -m venv hoge
xxx % source hoge/bin/activate
(hoge) xxxx % 
```

# 雑感

別プラットフォームでノリで書くとこういうのを踏むので、ちゃんと調べようという話。

# おまけ：macOS Monterey(12.4)クリーンインストール時の挙動

```
% python -V 
zsh: command not found: python
% python3 -V
Python 3.8.9
```

***pythonは死んだ！この人でなし！***
GitHubのPersonal Access Tokenが期限切れした場合の対処

## なぜ記事を書こうと思ったか
 ターミナル下でGitHubにpushをしようとした際に以下のようなエラーが確認された。
 ```
 remote: Invalid username or password.
 fatal: Authentication failed for 'https://github.com/{ユーザー名}/{リポジトリ名}/'
 ```
 認証に失敗して、pushができない、、
 このエラーはGitHubでPersonal Access Tokenを設定して、期限が切れた場合に発生するエラーになります。
 エラー対処を何回かしていて、その度に少し調べていたので、備忘録として記事に残します。

## エラー対処
 1. まず、最初にブラウザでGitHubにログインをしてください。
 2. ログイン後、右上のプロフィール画像をクリックし、[settings]を選択後、Developper settingsの欄を開いてください。
 3. [Personal access tokens]の欄を開き、有効期限切れになったトークンの「Expires on [日時]」を選択して下さい。
 <img width="742" alt="スクリーンショット 2022-03-20 16 33 25" src="https://user-images.githubusercontent.com/75520329/159152777-9f07e724-533e-4f70-ace3-336153cf5d0e.png">
 
 4.パスワードの入力を求められるので、アカウントを作成した際のパスワードを入力してください。
 5. 「Expiration」で有効期限を設定できます。無制限も設定できますが、脆弱性につながる恐れがある為、有効期限を設定する事をおすすめします。(私の場合は、3ヶ月で設定しています。)
 6. 「Regenerate token」をクリックし、新しいトークンを生成して下さい。(**※トークン生成後のみでしかトークンの値が確認できない為、必ずお使いのPCのメモ帳などに残しておくようにして下さい。**)
 7. ターミナルに移動し、`git push`をして下さい。
 8. ユーザー名とパスワードの入力を求められるので、GitHubのユーザー名と新しく生成したトークンの値を入力して下さい。

 これで私の場合は、問題なくpushすることができました！フゥ〜...

## 最後に
 今回は、何回か対処したことがあったので、何のエラーか直ぐに理解できたのですが、最初は「pushできないんだけどぉぉ〜！！何でだよぉ〜！」と焦ってました笑
 この記事を見て、参考にして頂けたら嬉しいです。
 今回の手順以外の方法もあるので、今回の記事で対処できなかった場合は、他の記事も活用してみて下さい。
 ここまで読んで頂き有難うございました〜。

## 参考記事
・[GitHubパーソナルアクセストークンの期限切れと更新作業](https://qiita.com/gin_teacher/items/d52a25fb5ca9fed3108a)

・[【使い方】githubのアクセストークンの有効期限を更新【VsCode】](https://ebookbrain.net/create-github-token/)
Dockerでエラーが出た

## 行っていたこと
Cloud Runにデプロイをする前にDockerにあげて確認をしようとしていた。
しかし、Dockerfileに記載してある`RUN yarn install`から先に進まない...

Dockerfile

    FROM node:16.13.1-alpine
    WORKDIR .
    COPY package.json yarn.lock ./
    COPY . .
    RUN yarn install
    RUN yarn cache clean --force && rm -rf node_modules/.cache
    RUN yarn build
    EXPOSE 8111
    CMD ["yarn", "start"]

## エラー内容

    "ENOSPC: no space left on device, write"

## 対応

`docker system prune`を実行したら、なんと46GB以上のいらないものが削除された！


## 最後に

Dockerを使っていると、こんなこともあるのかと勉強になったので記事にまとめておきます。

※自分では解決できませんでしたが後日、参考になるサイトを教えて頂いたので参考文献に入れておきます。

## 参考文献

https://qiita.com/ponsuke0531/items/edf2eee638202aa7f61f

https://qiita.com/yikegaya/items/08933c1cbd0ea990ced3

https://qiita.com/zembutsu/items/f577ea8dad6dc64d70b6

https://stackoverflow.com/questions/50142049/enospc-no-space-left-on-device-nodejs

https://qiita.com/hyt-sasaki/items/e2cbc4cfb5ae698de498
ゼロ幅スペースの罠が凶悪すぎた話

※本記事では実際のコードを抜き出し C#インタラクティブ で実行した結果を記載しています。
環境は `Microsoft (R) Visual C# インタラクティブ コンパイラ バージョン 4.1.0-5.22109.6 ()`

## 発端

文字列処理をしているときに遭遇した理解できないバグ
なんじゃこれ

```cs
> TimeSpan.Parse("0:00:00​")
System.FormatException: 文字列は有効な TimeSpan として認識されませんでした。
  + System.Globalization.TimeSpanParse.TimeSpanResult.SetFailure(System.Globalization.TimeSpanParse.ParseFailureKind, string, object, string)
  + System.Globalization.TimeSpanParse.ProcessTerminal_HM_S_D(ref System.Globalization.TimeSpanParse.TimeSpanRawInfo, System.Globalization.TimeSpanParse.TimeSpanStandardStyles, ref System.Globalization.TimeSpanParse.TimeSpanResult)
  + System.Globalization.TimeSpanParse.ProcessTerminalState(ref System.Globalization.TimeSpanParse.TimeSpanRawInfo, System.Globalization.TimeSpanParse.TimeSpanStandardStyles, ref System.Globalization.TimeSpanParse.TimeSpanResult)
  + System.Globalization.TimeSpanParse.TryParseTimeSpan(string, System.Globalization.TimeSpanParse.TimeSpanStandardStyles, System.IFormatProvider, ref System.Globalization.TimeSpanParse.TimeSpanResult)
  + System.Globalization.TimeSpanParse.Parse(string, System.IFormatProvider)
```

## 検証してみる

0:00:00 が `TimeSpan` として無効なのではと思ったので実験

```cs
> TimeSpan.Parse("0:00:00")
[00:00:00]
```

普通にパースされます。じゃあ違いはなんだ、ということで比べてみると

```cs
> "0:00:00​" == "0:00:00"
false
```

WHY C# Compiler!!
冷静に考えると `:` が怪しいなあと思ったので、それぞれコピペして調べてみます

```cs
> (int)':'
58
> (int)':'
58
```

同じです。こうなったら全文字疑ってみましょう

```cs
> "0:00:00​".Select(x => (int)x).ToArray()
int[8] { 48, 58, 48, 48, 58, 48, 48, 8203 }
> "0:00:00".Select(x => (int)x).ToArray()
int[7] { 48, 58, 48, 48, 58, 48, 48 }
```

`8203` ってなんじゃ??とりま16進数にして

```cs
> 8203.ToString("x")
"200b"
```

`\u200b` で検索

## ゼロ幅スペース

`\u200b` はゼロ幅スペースという文字らしいです。見えないけど存在している、そんな文字。
例えばHTMLなので改行できない位置を明示するのに使うらしい。確かに今回の処理対象テキストはWeb由来でした。

## `Trim()` では消えない

今回のコードでは前後に空白文字がある可能性は想定されていて、与えられた文字列を`Trim()` はしていました。ということで `string.Trim()` のドキュメントを参照すると(いつもどおりのガバガバ機械翻訳だったので英語版を参照します)

> Notes to Callers
The .NET Framework 3.5 SP1 and earlier versions maintain an internal list of white-space characters that this method trims. Starting with the .NET Framework 4, the method trims all Unicode white-space characters (that is, characters that produce a true return value when they are passed to the IsWhiteSpace(Char) method). Because of this change, the Trim() method in the .NET Framework 3.5 SP1 and earlier versions removes two characters, ZERO WIDTH SPACE (U+200B) and ZERO WIDTH NO-BREAK SPACE (U+FEFF), that the Trim() method in the .NET Framework 4and later versions does not remove. In addition, the Trim() method in the .NET Framework 3.5 SP1 and earlier versions does not trim three Unicode white-space characters: MONGOLIAN VOWEL SEPARATOR (U+180E), NARROW NO-BREAK SPACE (U+202F), and MEDIUM MATHEMATICAL SPACE (U+205F).

つまり、.NET framework 4以降ではゼロ幅スペースは `Trim()` では消えないということのようです。

## 解決策

解決策というほどのことでも無いですが `string.Replace("\u200b","")` 。
~~これだから最近のunicodeは嫌いだ~~
ChromeのDeveloper ToolsでWebAPI / RestAPIを実行してJavaScriptでゴニョりたい！

# ChromeのDeveloper ToolsでWebAPI / RestAPIを実行してJavaScriptでゴニョりたい！

「ローカルにNodeが入っていない環境で、とりあえずWebAPIを実行して結果をJavaScriptで利用したい！」と思うことありませんか？
今回、会社の都合でローカルにNodeが入っていなかったり、Windowsメインの営業さんの環境でもおそらく入っているChromeで実行できるresuest用のfunctionを作成しました。

## まずは動くもの

```javascript
const apiRequest = ({method, url, headers, params}) => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest()
  const jsonParse = (data) => {
    try {
      return JSON.parse(data)
    } catch (error) {
      return data
    }
  }
  xhr.open(method || 'GET', url)
  if (headers) {
    Object.entries(headers).forEach(([key, value]) => xhr.setRequestHeader(key, value))
  }
  xhr.onload = () => resolve({status: xhr.status, response: jsonParse(xhr.response), xhr})
  xhr.onerror = () => reject({status: xhr.status, response: jsonParse(xhr.response), xhr})
  if (params) {
    xhr.send(JSON.stringify(params))
  } else {
    xhr.send()
  }
})
const main = async () => {
  const response = await apiRequest({method: 'GET', url: 'https://api.github.com/users/defunkt'})
  console.log(response)
}
main()

```

## 使い方

```javascript
apiRequest({
  method: 'GET',
  url: 'http://example.com',
  headers: {
    'Authorization': 'base xxxxxx',
    'Content-Type': 'application/json'
  }
})
```

promiseが返ってくるので、必要に応じてawaitしてレスポンスを使って後続処理を実行してください。

## こんな使い方

TypeScriptに変換する必要はありますが、昨年爆誕したWeb版Excelで稼働する
Officeスクリプトに組み込んでボタンポチポチで、SaaSで提供されているAPIを起動して
画面に返したり、他のAPIにつなげたりすることもできます！

spreadsheet＋GASを使えば良い説もあるのですが、簡単に利用できない不自由な環境の人も少なくないはず。
（私は不自由な環境の人です）
【Vuex】action 内で，別の module の action を呼び出すときの注意点

Vuex の action 内で，別の module の action を呼び出したい場合に，思いがけず詰まったので共有させていただきます！

# エラー発生
stockModule 内の createStock アクション内から， quizModule の fetchQuizList アクションを呼び出すために，以下のようなコードを書きました．


```javascript

const stockModule = {
  namespaced: true,
  actions: {
    createStock(context, payload) {
     // 以下略
     context.dispatch("quiz/fetchQuizList", { root: true }); // 注目
    }
  }
};

const quizModule = {
  namespaced: true,
  actions: {
    fetchQuizList(context) {
      // 以下略
    }
  }
};

const store = new Vuex.Store({
  modules: {
    stock: stockModule,
    quiz: quizModule,
  },
});

export default store;

```

すると，このようなエラーが出てしまいました！！ `{root: true}` でグローバル空間を指定しているはずなのに，なぜ `unknow local action type` と言われるのか，2 時間くらい悩んでしまいました 涙
```
unknown local action type: quiz/fetchQuizList, global type: stock/quiz/fetchQuizList
```

# 原因
dispatch は ，
第一引数：__アクション名__ 
第二引数：__payload__
を指定しなければいけないという基本的なことがわかっていませんでした．
```javascript
context.dispatch("quiz/fetchQuizList", { root: true }); // 注目
```
の引数の指定の仕方が間違っていました．dispatch の第二引数には payload が入るので，
このままだと，`stock/quiz/fetchQuizList` というアクションに，`{ root: true }` というpayload を渡してしまいます！

# 解決
今回は，`stock/quiz/fetchQuizList`に渡したい payload は無いので，下のように第二引数に `null`, 第三引数に `{root: true}` を指定すると上手く呼び出せるようになりました！
```javascript
context.dispatch("quiz/fetchQuizList", null, { root: true }); // 注目
```

# まとめ

dispatch は ，
第一引数：__アクション名__ 
第二引数：__payload__ 
を必ず指定し， `{root: true}` は第三引数で指定するということを学びました！！

# 参考記事
https://vuex.vuejs.org/ja/guide/modules.html
【備忘録】kail Linuxのapt-get updateで起こったHash Sum mismatch問題を解決した件

## 概要
- セキュリティ関連のテストを学ぶために､[ハッキング・ラボのつくりかた 仮想環境におけるハッカー体験学習（IPUSIRON）｜翔泳社の本](https://www.shoeisha.co.jp/book/detail/9784798155302)をやってみようとしている
- `Kali Linux`の環境構築で､`apt-get update`できなくて困った
- 対処策がいくつかある用なので､見つけたものを共有する
- 原因までは調べられてないので､詳しい方にコメントいただけると嬉しいです


## 現象
- Virtual Box 内のKali Linuxで､`sudo apt-get update`すると`Hash Sum mismatch`になる
    -  違うのは`SHA256`だけ
    -  `SHA1`と`MD5Sum`は一致している
      ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418947/6e276f26-23c7-7fe4-2e8b-a61feefa2987.png)
-  環境情報
    -  Windows 10 Pro (21H2)
    -  Virtual Box (6.1.14)
    -  Kali Linux (2022.1,amd64)
## 解決策
- 手順
    -  Kail LinuxのVirtual Boxをシャットダウンする
    -  Windows Power Shell を管理者モードで立ち上げて､以下のコードを実行する
        -  `bcdedit /set hypervisorlaunchtype off`
    -  正常に実行された旨のメッセージを確認した上で､コンピュータを再起動する
    -  出典
        -  [how to fix: hash sum mismatch error/package has no installation candidate kali linux - YouTube](https://www.youtube.com/watch?v=mjc4PZSumts)
        -  https://unix.stackexchange.com/questions/574760/kali-linux-apt-update-returns-hash-sum-mismatch-error
-  結果
    -  `Hash Sum mismatch`が出なくなり､無事`apt-get update`できた
## 備考
- 上記解決策を見つけるまで2つの対処策を試して失敗している
- 環境や原因によっては有効な可能性があるので､上でダメならこちらを試せば解決する可能性もあり


## 失敗した対処策
### (1)`/var/lib/apt/lists`　のファイルを移動(または削除)する
-  出典
    -  [Ubuntuのapt-get updateで「ハッシュサムが適合しません」と出た時の対処法](https://www.nemotos.net/?p=1428)
    - [apt updateがインデックスファイルのダウンロードに失敗する場合の対処法 - Ubuntu入門](https://kaworu.jpn.org/ubuntu/apt_update%E3%81%8C%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%AB%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AE%E5%AF%BE%E5%87%A6%E6%B3%95)
- 結果
    - 変化なし(試す前と同じく､SHA256だけ不一致状態)
### (2)`gcrypto`の設定をいじる
- 出典
    - [apt-get時にgcrypto20が原因でHash Sum mismatchになる話 - Qiita](https://qiita.com/4utum00n/items/3882586a62a738b53833)
    - [apt - “Hash sum mismatch” error due to identical SHA1 and MD5 but different SHA256 - Ask Ubuntu](https://askubuntu.com/questions/1235914/hash-sum-mismatch-error-due-to-identical-sha1-and-md5-but-different-sha256/1242739#1242739)
- 対処
    - `$ sudo bash`
    - `# mkdir /etc/gcrypt`
    - `echo all >> /etc/gcrypt/hwf.deny`
- 結果
    - 解決せず
## 終わりに
[ハッキング・ラボのつくりかた 仮想環境におけるハッカー体験学習（IPUSIRON）｜翔泳社の本](https://www.shoeisha.co.jp/book/detail/9784798155302)､すごく良い本だが､本が書かれてから`Kali Linux`のほうの仕様が結構変わっているため､ちょいちょい読み替えていくところが発生している
時間があれば､どう読み替えればいいかも書いていきたい｡
[Ubiquant Market Prediction]investment_idごとのtime_id数に着目したEDA

こんにちは、tmrtjと申します。

私は現在株式会社音圧爆上げくんという会社に所属しており、プロKagglerとして活動しています。


私は現在プロKagglerとして、業務の一環で、現在Kaggleで行われているコンペの一つである[Ubiquant Market Prediction](https://www.kaggle.com/c/ubiquant-market-prediction)に参加しています。



また、Kaggle上にCodeとして同じ内容の記事を英語で投稿しています。
リンクは[こちら](https://www.kaggle.com/code/tmrtj9999/number-of-time-ids-by-investment-id)です。







<br>
<br>

# はじめに


与えられるinvestment_idのセットは各time_idごとに異なっています。その中には頻繁に出現するinvestment_idもあればあまり登場しないものもあります。

今回は各investment_idごとの出現回数を調べ、EDAをしてみました。




# 実装

まずはデータを読み込んでDataFrameにします。

```
import os
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
%matplotlib inline

import warnings
warnings.filterwarnings('ignore')

import lightgbm as lgbm
from lightgbm import *

df = pd.read_parquet('../input/ubiquant-parquet/train_low_mem.parquet')

```
<br>
<br>
各investment_idごとにtime_id数を集計し、それを降順に並べてグラフ化します。

```
from matplotlib import pyplot as plt
import pandas as pd
import matplotlib.pyplot as plt


time_count = df['investment_id'].value_counts()
time_count = time_count.reset_index()

time_ids = time_count['investment_id']

plt.plot(time_ids)
plt.show()
```

<br>
<br>

**結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2465043/10a7786f-882a-6ce4-1046-7b1fd7a8c944.png)



<br>
<br>

次に、降順に、100銘柄ごとのtargetの平均値をとり、それをグラフ化します。
```


from matplotlib import pyplot as plt
import pandas as pd
import matplotlib.pyplot as plt

inv_list = time_count['index'].values.tolist()
mean_list = []

for i in range(0, 36):
    df['flag'] = 0
    df.loc[df['investment_id'].isin(inv_list[100*i:100*(i+1)]), 'flag'] = 1

    df_2 = df[df['flag'] == 1]
    target_mean = df_2['target'].mean()

    mean_list.append(target_mean)




plt.plot(mean_list)
plt.show()

```
<br>
<br>

**結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2465043/9a4828cf-2e46-80db-cb1c-47f15ff7a211.png)


明らかにtargetの平均値が高くなっている部分と、ガクッと値が落ちている部分があることがわかります。

<br>





<br>
さらに、降順に、100銘柄ごとのtargetの標準偏差をとり、それをグラフ化します。

```
from matplotlib import pyplot as plt
import pandas as pd
import matplotlib.pyplot as plt

inv_list = time_count['index'].values.tolist()
std_list = []

for i in range(0, 36):
    df['flag'] = 0
    df.loc[df['investment_id'].isin(inv_list[100*i:100*(i+1)]), 'flag'] = 1

    df_2 = df[df['flag'] == 1]
    target_std = df_2['target'].std()

    std_list.append(target_std)



plt.plot(std_list)
plt.show()

```

**結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2465043/1b30d5be-9a9c-3e2e-637b-c669e417ab24.png)


右に行くほど、（つまり、time_id数が少ないinvestment_idほど）、targetの標準偏差が大きくなっていることがわかります。


分析は以上です。
スコア向上の何らかの参考になれば幸いです。



<br>

# さいごに
株式会社音圧爆上げくんではプロKagglerを募集しています。
ご興味のある方はぜひ以下のリンクをご覧ください。
[Wantedlyリンク](https://www.wantedly.com/projects/748285)
Ubuntu Server 20.04.3 LTS 構築#22 Docker構築（MYSQLのテーブル作成～データの追加）

実施日:2022/03/11～

【今回の内容】
MySQLのテーブル作成からデータの追加迄

# テーブル作成
1.テーブル作成

```
構文
CREATE TABLES テーブル名 (カラム名1 データタイプ , カラム名2 データタイプ) ; 
```
※カラムのデータを連番で入力したい場合
AUTO_INCREMENTを追加したいカラム名の後に追加する。
※行を一意に識別できるようにする場合
PRIMARY KEYを追加したいカラム名の後に追加する。

# データの追加
2.データの追加
```
構文
INSERT INTO テーブル名 VALUSE (各カラムのデータ) ;
```

# 実践
上記の構文を元に下記のようなデータを作成しました。
+--------+--------+
|id     　　 +result　|
+--------|--------|
|5900 　   |123    　 |
|5901  　  |234    　 |
|5902 　   |345    　 |
|5903  　  |456    　 |
|5904 　   |567    　 |
|5905   　 |678    　 |
|5906 　   |781   　  |
+--------+--------+ 

# 関連項目
[インストールから基本操作迄](https://qiita.com/art22/items/3ba46d56fa7aac70c409)
[MYSQLのコンテナ作成～データベース作成](https://qiita.com/art22/items/bd007969c58869ba3561)

本日はここまで
データをビット列に押し込む

## 概要
データをビット列に押し込めることで、データサイズを小さくする事ができる  

## 問題
データ数: 最大 10^7 （10,000,000）  
データ: 10^7 より小さい整数 （0 〜 9,999,999）  

### 問題の特徴
- 入力の範囲が限られている
- 同じ整数は出現しない
- 考える対象は整数のデータのみ

## 解決案
1. 一つのデータ（7桁の整数）を7バイトで表す 
2. 一つのデータ（7桁の整数）を32bitで表す   
3. ビット列で表す（各データを1bitにする）

```
   一つのデータサイズ| 全データサイズ
1.  7byte * 8 = 56bit| 56 * 10,000,000 = 560,000,000bit 
2.              32bit| 32 * 10,000,000 = 320,000,000bit 
3.               1bit|  1 * 10,000,000 =  10,000,000bit 
```

## 結論
案3.のビット列にする事で、全データを 10,000,000 ビットで表すことができる  
上記案の 1/56、1/32にデータサイズを小さくすることが可能  

ビット列で表すのに有効となるデータセットの条件
- 限られた範囲内にある
- 密
- 重複がなく、付随する情報がない


### ビット列の例

データ
```
行番| data   
   1| 0000001
   2| 0000003
   3| 0000002
   4| 0000004
...
```

bit列
```
行番| データ | データのbit表記|| [bit列]
   1| 0000001| 0000010        || 0000010
   2| 0000003| 0001000        || 0001010
   3| 0000002| 0000100        || 0001110
   4| 0000004| 0010000        || 0011110
...
```

## 実装
https://github.com/qoncyu/book.syugyoku/blob/main/col1/a.c

## 参考書籍
珠玉のプログラミング 本質を見抜いたアルゴリズムとデータ構造  
https://amzn.to/3N5mMTI
DockerでPythonをcronで自動実行するまで(Debian)

# はじめに
Linuxでコマンドを定期的に自動実行するにはcronというものを使うとよいらしい。
しかし、Dockerコンテナ内でPythonをcronをしようとすると意外と大変だったのでメモ。

# Dockerコンテナの立ち上げ
Dockerの解説は、[さくらのナレッジの解説](https://knowledge.sakura.ad.jp/13265/)がわかりやすくておすすめです。

さて、まずは適当な場所で以下のようなディレクトリを作ります。
```
test/
　 ├ docker-compose.yml
　 └ Dockerfile
```
ファイルの中身はそれぞれ、

``` docker-compose.yml
version: '3'
services:
  main:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./:/workspace
    stdin_open: true
    tty: true
    ports:
    - "127.0.0.1:8000:8888"
```
``` Dockerfile:Dockerfile
FROM python:3.8
RUN apt update
```
です。基本的なことしか書いていません。
testフォルダの中で以下のコマンドを打ち、コンテナを作って中に入りましょう。
```
docker-compose up -d
docker-compose exec main bash
```
用意したファイルはworkspaceフォルダにあります。

# 順番に試す
まず、このままだとcronを設定するために必要なコマンド`crontab -e`が動きません。
```
root@34cf5a4ef1fd:/# crontab -e
bash: crontab: command not found
```
cronをインストールしてやる必要があるようなので以下のコマンドでインストールします。

```
apt-get install -y cron
```
もう一回`crontab -e`を打つと、今度はエディターがないと怒られます。
```
root@34cf5a4ef1fd:/# crontab -e
no crontab for root - using an empty one
update-alternatives: error: no alternatives for editor
/usr/bin/sensible-editor: 25: editor: not found
/usr/bin/sensible-editor: 28: nano: not found
/usr/bin/sensible-editor: 31: nano-tiny: not found
/usr/bin/sensible-editor: 34: vi: not found
Couldn't find an editor!
Set the $EDITOR environment variable to your desired editor.
crontab: "/usr/bin/sensible-editor" exited with status 1
```
以下のコマンドでとりあえずvimをインストールします。
```
apt-get install -y vim
```
これで`crontab -e`が動くようになったので、とりあえずシェルスクリプトを1分ごとに動かしてみます。
テスト用に、/workspaceに以下のシェルスクリプトを用意します。
```/workspace/test.sh
#!/bin/bash
echo "hello" >> /workspace/hello.txt
```
helloという文字列をテキストファイルに追記していくスクリプトです。

次に、`crontab -e`コマンドで設定ファイルを編集して、以下の分を追記します。
これがcronの設定になります。
```:crontab -e
*/1 * * * * /workspace/test.sh
```
1分ごとにシェルスクリプトを実行してくださいという設定です。
パスはフルパスで設定します。

そして、以下のコマンドでcronの定期実行をスタートさせます。
```
/etc/init.d/cron start
```
うまく動いていれば、hello.txtが作成され、1分ごとにhelloという文字列が追加されていきます。
止めるときは、
```
/etc/init.d/cron stop
```
です。

次にPythonコマンドを動かしてみたいので、/workspaceに.pyファイルを作成します。

```/workspace/test.py
with open('/workspace/hello.txt', 'a') as f:
    print('python', file=f)
```
同じようにpythonという文字列をhello.txtに追記していくプログラムです。
同じように`crontab -e`でcronの設定をしたいのですが、以下の記述だと動きません。

```:crontab -e
*/1 * * * * python /workspace/test.py
```
cronがPythonのパスを認識してくれないことが原因のようです。
そこで、`which python`コマンドでpythonファイルの場所を見つけ出して、そこのパスをコマンドとして、以下のように書き直します。

```:crontab -e
*/1 * * * * /usr/local/bin/python /workspace/test.py
```

これで動くようになります。

# 参考にした記事
- https://haitech-blog.com/cron-path/
- https://qastack.jp/ubuntu/262063/how-to-find-python-installation-directory-on-ubuntu
- https://qiita.com/sey323/items/407cfa303f9ec217c677
- https://teratail.com/questions/281244
- https://qiita.com/yasuaki0206/items/306c859f0854dd0cfabf
- https://qiita.com/nagimaruxxx/items/b7308644b5d2c9efbeea
- https://qiita.com/houtarou/items/cad584e7d10adf22e417
- https://ytyaru.hatenablog.com/entry/2020/05/25/000000
ランニングコスト0の書籍管理webアプリ！　メイン

# ランニングコスト0の書籍管理webアプリを開発！(機能拡張中)

## 開発背景

本が大好き！
↓
本買いすぎて持ってる本が把握できなくなってきた。。。
↓
管理アプリ作るか！

てことで個人の所蔵の管理を目的に開発したものです。

## 利用技術・利用サービス

* [google driveのホスティング](https://qiita.com/keisuke_suzuki_engineer/items/f9d92ef7722aae2af120)
* [GAS(Google App Script)](https://qiita.com/keisuke_suzuki_engineer/items/83ad64b97179af749d1a)
* [Vue.js](https://qiita.com/keisuke_suzuki_engineer/items/16694d050fdaea98f29c)
* [QuaggaJS](https://qiita.com/keisuke_suzuki_engineer/items/7e336a80e0ff4439ea34)
* openDB(coming soon)

## システム構成

![システム構成図-概略図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601154/fc6a9107-1539-34e2-6939-f5bc8958e7ad.jpeg)


基本構成はSPA(single page application)とAPIサーバです。
詳細な構成の紹介は各機能実装時にするとして、ここでは基本構成を紹介します。

* クライアントサイド
  * Vueを利用したCSR(client side rendering)です。
  * 機能ごとにSPAとして実装します。

* サーバサイド
  * GASをAPIサーバに見立て運用します。
  * スプレッドシートをDBに見立てて運用します。



## 機能

* 機能共通の基盤(coming soon)
* 書籍登録(coming soon)
* 貸出(coming soon)
* 返却(coming soon)
* 棚移動(coming soon)
* 棚卸(coming soon)
Laravel新規プロジェクト作成方法

## 最新のLaravelバージョンで作成
```
// composerを使用した方法
$ composer create-project laravel/laravel {プロジェクト名} --prefer-dist

// 例
$ composer create-project laravel/laravel sample --prefer-dist


// Laravelインストーラを使用した方法
$ laravel new {プロジェクト名}

// 例
$ laravel new sample
```
## 指定のLaravelバージョンで作成
```
// composerを使用した方法
$ composer create-project laravel/laravel {プロジェクト名} "{バージョン}" --prefer-dist

// 例
$ composer create-project laravel/laravel sample "8.*" --prefer-dist
```

## Laravel Sailを使用する場合
```
$ curl -s https://laravel.build/{プロジェクト名} | bash

// 例
$ curl -s https://laravel.build/sample | bash
```
Python: 具象クラスのインスタンスを返す抽象メソッドの型ヒント

## 概要
抽象クラスのメソッドが具象クラスのインスタンスを返す場合、その抽象基底クラスはジェネリック型にする必要がある。また、その型変数は抽象規定クラスを上界に指定する必要がある。


## 具体例
足し算と符号反転を定義すれば、引き算を自動で定義してくれる抽象クラスを考える。

```py
from abc import ABC, abstractmethod

class AdditiveGroup(ABC):
    @abstractmethod
    def __add__(self, _):
        pass

    def __sub__(self, other):
        return self.__add__(other.__neg__())
    
    @abstractmethod
    def __neg__(self):
        pass
```

これを使って、整数を３で割った余りで分類する次のようなクラスを定義するとmod 3の世界で足し算、引き算、符号の反転が行える。

```py
class FG3(AdditiveGroup):
    def __init__(self, v):
        self.v = v % 3

    def __add__(self, other):
        return FG3(self.v + other.v)

    def __neg__(self):
        return FG3(-self.v)

    def __repr__(self):
        return f"FG3({self.v})"


a = FG3(0)
b = FG3(1)
c = FG3(2)

print(a + b + c)  # -> FG(0)
print(-c)         # -> FG(1)
print(b - c)      # -> FG(2)
```

ところで、当然ながらFG3のインスタンスにFG3のインスタンス以外を足したり引いたりしようとするとエラーになる。

```py
print(a + 1)  # -> AttributeError: 'int' object has no attribute 'v'
```

型ヒントを追加して、このバグを静的解析にて発見したい。

## 型ヒントの追加
具象クラスFG3の型ヒントは次の通り。`__add__`と`__neg__`で`FG3`が必要だが、まだ定義されていないので文字列`"FG3"`で代用する必要がある。

```py
class FG3(AdditiveGroup):
    v: int

    def __init__(self, v: int) -> None:
        self.v = v % 3

    def __add__(self, other: "FG3") -> "FG3":
        return FG3(self.v + other.v)

    def __neg__(self) -> "FG3":
        return FG3(-self.v)

    def __repr__(self) -> str:
        return f"FG3({self.v})"
```

あるいは、`__future__.annotations`をインポートすれば`FG3`を使用できるようになる。

```py
from __future__ import annotations

class FG3(AdditiveGroup):
    v: int

    def __init__(self, v: int) -> None:
        self.v = v % 3

    def __add__(self, other: FG3) -> FG3:
        return FG3(self.v + other.v)

    def __neg__(self) -> FG3:
        return FG3(-self.v)

    def __repr__(self) -> str:
        return f"FG3({self.v})"
```


ややこしいのは抽象クラスの方で、 `__add__`、`__sub__`そして`__neg__`の引数及び戻り値の型は具象クラスでなければならない。しかし、抽象クラスの定義時には将来どのクラスによって継承されるか分からない。そこで、ジェネリック型を利用する。

ところが、次のように単純にジェネリック型にしただけではエラーとなる。

```py
T = TypeVar("T")

class AdditiveGroup(ABC, Generic[T]):
    @abstractmethod
    def add(self, _: T) -> T:
        pass

    def __sub__(self, other: T) -> T:
        return self.__add__(other.__neg__())

    @abstractmethod
    def __neg__(self) -> T:
        pass
```


原因は、`__sub__`メソッドの`self.__add__(other.__neg__())`で、`other`が`__neg__`を定義しているか、この時点では分からないためである。

実際のところ、`T`型は抽象クラス`AdditiveGroup`を継承しているはずで、`other`は`__neg__`を定義している。この条件がコードに表れていないのでエラーとなっている。

型変数`T`に「`AdditiveGroup`を継承していること」という条件を付けると、次のようになる。

```py
T = TypeVar(“T”, bound="AdditiveGroup")

class AdditiveGroup(ABC, Generic[T]):
    @abstractmethod
    def add(self, _: T) -> T:
        pass

    def __sub__(self, other: T) -> T:
        return self.__add__(other.__neg__())

    @abstractmethod
    def __neg__(self) -> T:
        pass
```

ここでも、 型変数`T`の定義時点では`AdditiveGroup`は未定義なので、`bound="AdditiveGroup"`と文字列を使っている。この部分は、`__future__.annotations`をインポートしていても文字列でなければならない。
vCPUとは

## 勉強前イメージ

virtualな感じ？
だけど、CPUにバーチャルってあるんかね

## 調査

### vCPU とは

virtual CPU の略で、
物理的なCPUの能力を分割し、仮想マシンのCPUとして構成したものを指します。
そのため、vCPUは仮想サーバでのお話になります。

仮想マシン上で起動されたOSやアプリケーションからは普通のCPUに見えています。
仮想マシンは1台以上の仮想CPUを持っており、複数のCPUで並列処理も可能です。

## 勉強後イメージ

最近短くてあれやねんけど、
コレに関しては結構納得してしまったので・・・・

## 参考

- [仮想CPU 【virtual CPU】 vCPU / 仮想プロセッサ](https://e-words.jp/w/%E4%BB%AE%E6%83%B3CPU.html)
- [仮想サーバー](https://www.onamae-cloud.com/faq/details/32)
Laravel8でHTMLの共通部分をパーツにして使い回す方法(レイアウト共通化)｜ビュー、コントローラー、ルーティング

# 前提
Laravel6や7の記事は多かったのですが、Laravel8からルーティングの書き方が変わったのに気づかず進めてうまくいかなかったので、解決策を備忘録として残します。

Laravel 8.83.4
Composer version 2.2.7

# コントローラーを作る
プロジェクトファイルに移動した状態で以下のコマンドを打つとコントローラーファイルが生成される

```
$ php artisan make:controller LayoutController
```

`app/Controllers`の中に、`LayoutController.php`ができているので開く。

```PHP
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class LayoutController extends Controller
{
    //
}
```

LayoutControllerクラスの中(`//`があるとこ)に以下を挿入

```PHP
class LayoutController extends Controller
{
    public function index() {
		return view('layouts.index');
    }
}
```

layoutsフォルダの中のindex.blade.phpを返す、という内容にする。


# 親ビューを作る
`resources/view/layouts`の中に`layout.blade.php`ファイルを作って開く。

```HTML
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>共通レイアウト</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">

    </head>
    <body class="vh-100 d-flex justify-content-center text-center">
        <div class="w-75 mt-3">
            <div class="text-black-50 text-left border-bottom mt-5">
                <h1>ここは親ビューだよん</h1>
            </div>

            <div>
                @yield('content')
            </div>
        </div>

    </body>
</html>
```

# 挿入するパーツ部分(子ビュー)を作る

 次に、同じように`resources/view/layouts`の中に`index.blade.php`ファイルを作って開く。

```HTML
<!-- layoutフォルダ配下の layout.blade.php を親にして継承 -->
@extends('layouts.layout')

@section('content')

<h2>ここは子ビュー、パーツ部分です</h2>

@endsection
```

# web.phpにルーティングを書く

すでにweb.phpにはいくつかコードが入っていると思いますが、最終行に以下を追加すればOKです。

```PHP
Route::get('/layout', [App\Http\Controllers\LayoutController::class, 'index'])->name('layouts.index');
```

これで、/layoutにアクセスすると、"親ビュー"やら、"子ビュー"などの文字が出ていると思います。


# 表示されなかったら・・・

ターミナルでプロジェクトフォルダのところまで行き、以下のコマンドを打つと、今使われているルーティングが表示されます。
```
$ php artisan route:list
```

URIの列に/layoutがあるか探してみてください。

なければ、ルートが追加されていないので、以下のコマンドで手動で追加しましょう。

```
$ php artisan route:clear
```
Go1.18からtext/templateのand, orが短絡評価されるようになった

表題の通りです。

`and`, `or` の右辺値で副作用を起こすテンプレートを書いていた場合、Go1.17と1.18で挙動が変わります。Go本体と同じ仕様になったので分かりやすいですね。

```:テンプレート
{{$d := dict}}
{{or 1 (set $d "msg" "right value is evaluated!")}}
{{$d}}
```

go1.17: `or` の右辺値も評価されるので、 `$d` にキーが追加されている

```:go1.17

1
map[msg:right value is evaluated!]
```

go1.18: `or` の左辺値がtruthyなので短絡評価され、 `$d` にキーが追加されない

```:go1.18

1
map[]
```

# 挙動は何によって変わる？

Go本体のバージョンで決まります。 `go.mod` ファイルのGoディレクティブは関係ありませんでした[^1]。

そのため、`go install` で同じバージョンのバイナリを落としてきても、Go本体のバージョンによって挙動が変わります。

```bash:go1.18
$ go version
go version go1.18 linux/amd64
$ echo | go run github.com/syuparn/tmplscript@v0.6.0 '{{$d := dict}}{{$_ := or 1 (set $d "msg" "right value is evaluated!")}}{{$d}}'
map[]
```

```bash:go1.17
$ go version
go version go1.17.6 linux/amd64
$  echo | go run github.com/syuparn/tmplscript@v0.6.0 '{{$d := dict}}{{$_ := or 1 (set $d "msg" "right value is evaluated!")}}{{$d}}'
map[msg:right value is evaluated!]
```

(`kubectl` や `helm` 等、ビルドに使ったGoのバージョン情報も表示するツールはこのような挙動の違いを意識しているのでしょうか？)

とはいえ、Goは標準パッケージ含めv1系の[後方互換を保証している](https://go.dev/doc/go1compat)ので、実装の穴をついて変なことをしない限りバージョンを意識する必要は無さそうです[^2]。

# おわりに

変な挙動やバグに依存したコードは書かないようにしよう！ :innocent:

[^1]: [公式リファレンス](https://go.dev/ref/mod#go-mod-file-go)では「ディレクティブより新しいバージョンの言語仕様が使われたらコンパイルエラーにする」と書かれていますが、標準パッケージについての記述はありませんでした。また、標準パッケージは `go.mod` でもバージョン管理されないので、純粋に本体バージョンに紐づくと考えてよさそうです。
[^2]: 短絡評価が非互換の変更に当たるかどうかは[issueでも長い間議論されていた](https://github.com/golang/go/issues/31103)ようです。最終的に、右辺値を評価することはgodocに仕様として書かれていないので、nil pointer dereferenceを回避できる利点のほうが大きいと判断され採用されました。
「型 'string' を型 'never' に割り当てることはできません。」というエラーを解決するにあたって

# はじめに
TypeScriptでTodoアプリを作って途中で、タイトルのようなエラーに遭遇しました。

# 現状

```typescript
  const [inCompleteTodos, setInCompleteTodos] = useState([]);

---- 中略 ----

  const onClickAdd = () => {
    if (inputTodo === "") return;
    const newTodos = [...inCompleteTodos, inputTodo];
    setInCompleteTodos(newTodos);  //ここでエラー発生
    setInputTodo("");
  };
```
もともと、`const [inCompleteTodos, setInCompleteTodos] = useState(["hoge"]);` このように`"hoge"`
という値を仮で入れていたのですが、削除したところエラーが出るようになりました。

![Screenshot 2022-03-20 14.08.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1472874/7e934572-ec5d-0919-9b78-4b9bf08dd425.png)


useState([""])のように何かしら値を入れてみるとエラーは消えるのですが、上記写真のように余計な`完了・削除`ボタンが表示されてしまいます。

# 結論

単純に、配列の中にどういう型の値が入ってくるか示されていなかったために起きたエラーで `useState<string[]>([]);`のように明示的に`<string[]>`を記述してあげたら望みの挙動を実現できました。

# 補足： neverとvoid
https://qiita.com/irico/items/e1f96c642ed0a5a53742#:~:text=%E3%81%AF%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%A7%E3%81%99%22)%0A%7D-,void%E3%81%A8never%E3%81%AE%E9%81%95%E3%81%84%E3%81%AF%EF%BC%9F,-%E3%81%93%E3%82%8C%E3%81%AF%E5%AE%9F%E9%9A%9B

上記記事がわかりやすかったです。

fooやbarは`void`、bazは`never`型になります。

voidは`returnでの戻り値がない`、というものに対して、neverは`そもそもreturnしない`、というものになります。

ちなみに、fooのように`return`を書かなかった場合は`undefined`が返ってきます。
```typescript
const foo = () => {
}

const bar = () => {
    return 
}

const baz = () => {
    while(true){}
}


let fooVar:never = foo() // error
let barVar:never = bar() // error
let bazVar:never = baz() //ok!
```
ブラウザバックはダメ絶対

# 初めに
業務の中でブラウザバックを禁止したい場面は多々あると思います。
今日はブラウザバックを禁止する方法について書いてみたいと思います。

# 試したコード
## 作成コード
back.html
 ⇒ ブラウザバックを禁止するページのhtml
error.html
 ⇒ ブラウザバックを押下した場合に遷移させるhtml
moto.html
 ⇒ back.htmlにブラウザバック用の履歴を積むための遷移元ページのhtml
stop-brawser-back.js
 ⇒ ブラウザバックを防ぐためのコードが書いてあるjs

## 詳細
```html:back.html
<html>
  <head>
    <script
  src="https://code.jquery.com/jquery-3.6.0.min.js"
  integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
  crossorigin="anonymous"></script>
    <script src="stop-brawser-back.js"></script>
  </head>
test
</html>
```

```html:error.html
<html>
  ブラウザバックを押したな！
</html>
```

```html:moto.html
<html>
  <p>遷移元</p>
  <a href="back.html">遷移先</a>
</html>
```

```js:stop-brawser-back.js
$(() => {
  history.pushState(null, null);
  $(window).on("popstate", (e) => {
    location.href = "error.html"
  });
}); 
```

## 動作検証
### 遷移元画面のhtml(moto.html)を開いて遷移先リンクを押下
|![元画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/8120131a-ff74-ee65-6d59-45ec0b33a6d6.png)|
|:--|

### 遷移先の画面でブラウザbackを実施
|![先.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/2d00a5a7-02ca-ef00-bcad-415ab9abfcd6.png)|
|:--|
※Goocle Chromeの場合はこの手順で一度画面をクリックする必要があります…この制約が少しつらいですが、ブラウザの仕様のためどうすることもできないです。良い方法があれば教えてほしいです。

### ブラウザback時に別のページに遷移する
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/c4f8ef94-5228-2b2a-78ee-cb23bc93b42c.png)|
|:--|


## 終わりに
本当に使用するとなったらerror.htmlにも工夫が必要になります。そのまま同じページに遷移させるとか工夫はいろいろできると思います。
Chromeの場合にフォーカスしないとpopstateのイベントが動かないのが気になりますね…
解決方法を知っている方がいたら教えてください！！
[RxSwift]cell側のボタンtapをVCで検知する

## 前提

例えば、
Rxを使用するプロジェクトでCell内部のbuttonタップ検知を行いたい場合は多くあると思います。
しかし、Rxを採用しているのでCell側のbuttonでaddTargetしてDelegateを使って。。。のようなことはあまり使うべきではないです。

そもそもRxはそれらの課題解決を目的としても作られた背景もあります。

ではどのように実装しましょうか。

実装方法は**いくつかある**と思うのですが、今回はそのうちの一つを紹介していきたいと思います。

## 

RxSwiftを採用しているということはおそらくMVVMアーキテクチャを合わせて採用していると思います。
MVVMアーキテクチャではViewController側でViewModelの参照を持つことがあります。しかしカスタムセルを作っている場合cell側でもViewModelの参照を持たせて。。といった感じになります。
大きなプロジェクトでは話は別ですが、タップ検知しかしないcell側でViewModelの参照を持たせて、DIさせてと毎回書くのは面倒だし（この行為自体は悪いことではない）タップ検知をしたいだけなのに他に書くコードが多すぎて冗長にも感じられるかと思います。

そのような時はViewController側でコードを書いてあげることも一つの手でしょう。

```swift
     // cell内のお気に入りボタンタップを検知する
    cell.favoriteButton.rx.tap
        .subscribe(onNext: { _ in
            viewModel.input.favoriteButtonTapped.onNext(())// ViewModelにイベントを送る
         }).disposed(by: cell.disposeBag)// ←cell側のdisposeBagを使用する
```
Railsチュートリアル7.3.4でハマった（ArgumentError）

# 内容
タイトルの通りです。ArgumentErrorのエラーが発生しました。rails7系でrailsチュートリアルに取り組んだ際の内容です。
## 開発環境
ruby 3.1.1
rails 7.0.0

## テストコード
```users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
  end
end
```
## 実行結果

```
$ rails t
Running 18 tests in a single process (parallelization threshold is 50)
Started with run options --seed 3639

ERROR["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x000000010c979b60 @name="UsersSignupTest">, 0.13537400000495836]
 test_invalid_signup_information#UsersSignupTest (0.14s)
ArgumentError:         ArgumentError: wrong number of arguments (given 2, expected 1)
            test/integration/users_signup_test.rb:8:in `block (2 levels) in <class:UsersSignupTest>'
            test/integration/users_signup_test.rb:7:in `block in <class:UsersSignupTest>'

  18/18: [================================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.17766s
18 tests, 31 assertions, 0 failures, 1 errors, 0 skips
```

# 解決策
どうやらバージョンの違いによるエラーでした。
調べたところ困っている方も多かったので解決策を書いておきます。
post users_path,〜の「,」を消せば通ります。
## 改善済みコード
```users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
  end
end
【Microsoft365】Vivaインサイトで用いる「Insights管理者」の割り当て方法

# はじめに
以前、以下の記事にてMicrosoft365のサービスである**Vivaインサイト**のライセンスの割り当て方について紹介させて頂きました。

https://qiita.com/satton6987/items/dc1353133a9c068e6472

今回は**Vivaインサイト**のライセンスを割り当てたユーザーを管理するための「Insights管理者」の権限の割り当てについてご紹介します。
※本情報については、Microsoftサポートに確認した内容になります。

# 前提
- Microsoft365 E3(もしくはE5)のライセンスを取得済み
- Vivaインサイトのオプションライセンスを購入済み(分析対象のユーザー分)
- 「Insights管理者」を付与するためのアカウントを作成済み
- 全体管理者アカウントにて本作業を実施(onmicrosoft.com)

# Insights管理者とは？
チームやグループの働き方を分析したい人に付与する権限になります。
例)リーダー、課長、部長

Insights管理者の権限は、全体管理者(onmicrosoft.com)が対象のユーザーに付与する形になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/4ae513ec-db12-6372-d674-efbaa0cf3247.png)

※メンバーへのライセンス付与の考え方については、「はじめに」に記載しているリンクをご確認下さい。

# AzureADを用いた権限の割り当て手順
※Vivaインサイトのライセンスは保有していないため、あくまでも権限を割り当てるだけの手順になります。

**1.全体管理者ユーザーで、Azure Active Directory 管理センター (https://aad.portal.azure.com/) にアクセスします。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/b9bd6db2-817e-297d-e5da-a554d2c44c3c.png)

**2.画面左側メニュー内 [ユーザー] をクリックします。**
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/ae243839-b4e8-3292-7cc8-fac1311e3fd9.png)


**3."すべてのユーザー" 画面が表示されますので、画面上部 "ユーザーの検索" より、該当ユーザー情報を入力し、検索します。**

対象のユーザーが表示されたら、クリックします。
![2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/f9caf76a-13b1-a5de-d55b-cf90cd6483b2.png)

**4."管理" セクション内の [割り当てられたロール] をクリック後に[割り当ての追加] をクリックします。**
![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/a5c931a8-aec9-9b17-b709-a395cdbdb4a5.png)

**5."Insights 管理者" にチェックを入れ、[追加] をクリックします。**
![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/e7da5f79-6e8a-9a15-5f1e-8c630197e28f.png)
![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/cedfa47d-3e64-0817-f8ae-3f28c2ae4c5d.png)

**6."Insights 管理者" が割り当たっていることを確認する。**
![7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/966e93d5-1a66-695f-bd0c-2f5316d9f047.png)

# Insights管理者付与後にできること
Vivaインサイト内の**MyAnalytics**にてチームの分析が可能です。
※分析するチーム内のユーザーに有料のVivaインサイトのアドオンが付与されていることが条件になります。

![config-settings-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636476/b7be0747-62a9-b930-89b3-8a0c924caa1d.png)

# さいごに
ざっくりにはなりましたが、Insights管理者権限の割り当て方について記載させて頂きました。
このMicrosoft365内の**Vivaインサイト**というサービスになりますが、公式ドキュメント等を拝見しても全然情報がなかったので、仕様を理解することに苦労しました。
最終的には、Microsoftサポートの方にひたすら確認を行い、方法を把握することができました。
インターネット上になかなか無い情報なので、**Vivaインサイト**の整備をこれから行う方に参考になればと思います。

# 参考情報
[MyAnalytics ダッシュボード](https://docs.microsoft.com/ja-jp/viva/insights/personal/use/dashboard-2#config-settings)
[Microsoft Viva インサイト](https://www.microsoft.com/ja-jp/microsoft-viva/buy-insights?activetab=pivot%3aoverviewtab)
Draw.ioの導入方法と日本語化

## draw.ioとは？

フローチャートやオフィスのレイアウト図、ネットワーク図を作成できる無料の作図ツールです。これを使ってAWSのインフラ構成図を作成します。

## ①draw.ioへアクセス

まず下記のリンクから**draw.io**にアクセスします。

https://www.draw.io/

## ②draw.ioの日本語化

「Decide later」をクリックします。
![7168b67882cab3796c8dd6c3be8ba2de.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/4f47e133-fa37-2f28-f634-372190dbb2da.png)

右上の地球のマークをクリックします。

![b9668b381d313621860de0c10e5372c8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/34650f0f-7da3-886b-e1ba-ca1776800e20.png)

**日本語**を選択します。

![9ea91040bfea79c128feee75eaf538f0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/d644c487-9df4-aedb-f14b-2071d223a316.png)

ページを更新します。

![142cc9cf2c1e73286666a1bfe25eb0ce.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/031654e8-8629-fa0b-beae-a24307d05214.png)

日本語になっていればOKです。

![5fd608263b8e7499abf6f7e2fe182b6f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/78f5182e-fb77-e3d7-447e-0179e3ff26a9.png)

ファイルの保存先は任意でOKです。

## ③インフラ構成図を作成

左上の**ファイル→新規作成**をクリックします。

![47fc526f4e85224e973b8a5d6cb9b737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/0de3b595-6e80-dbd3-5e9f-da234e2b887f.png)

**Cloud**を選択してAWSのアイコンがあるものを選択します。

![3163920235b99e409c784655377c86c8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/4b23d8d1-992e-d783-8b3d-98db87f9848e.png)


左のタブから選択したりテキストを挿入したりして

![5eb0a7ee2b06ee87e76944e1bc3dbdbf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584500/f0b6a656-0173-bd0b-4960-50a0db98daa9.png)

このように作成することができます。