OmniAICreator/Qiita-1.07M huggingface dataset:
コマンドプロンプトでtar.gz作成(power-shell)

## 1. `targz.bat`
```ruby:targz.bat
---------------------------
@echo off

rem 引数がない場合は終了する
if "%1"=="" (echo 引数がありません & pause & exit /b -1)

rem ログファイル
set LOG=if_serdata.log

rem 引数をもとにシナリオが格納させているフォルダ
set TESTDATA_PATH=C:\temp\test\%1*
rem xml格納フォルダ
set XML_PATH=E:\Temp\powershell_test\*

setlocal enableDelayedExpansion


del /S /Q %XML_PATH% > %LOG% 2>&1

rem フォルダの削除
for /D %%1 in (%XML_PATH%) do rmdir /S /Q "%%1" >> %LOG% 2>&1

rem シナリオのデータブック格納フォルダ数を取得
for /f %%A in ('dir /b /s /ad "%TESTDATA_PATH%"') do (

	rem テスト01：フルパスをそのまま出力
	echo Test01 %%A

	rem テスト02：「~n」を使って、ファイル名のみを出力（拡張子なし）
	rem echo Test02 %%~nA

	rem テスト03：「~nx」を使って、ファイル名のみを出力（拡張子あり）
	rem echo Test03 %%~nxA
	
	rem テスト04：「~d」を使って、ドライブレターのみを出力
	rem echo Test04 %%~dA
	
	rem テスト05：「~dp」を使って、ファイルパス（ファイル名は除く）を出力
	rem echo Test05 %%~dpA
	
	rem テスト06：「~tf」を使って、ファイルのタイムスタンプを出力
	rem echo Test06 %%~tA
	
	rem テスト07：「~zf」を使って、ファイルの容量（単位：byte）を出力
	rem echo Test07 %%~zA byte
)

endlocal

pause
-------------------------------
バッチファイル参考：ファイル有無判定.bat
@echo off

echo ★★★ファイル有無確認★★★
echo.

rem — 対象フォルダへ移動 —
cd c:\log

rem — ファイル有無のバッチ処理 —
set fname=”backup.log”
if exist %fname% (goto TRUE) else (goto FALSE)

:TRUE
echo ○ファイル有り
dir /B %fname%
goto END

:FALSE
echo ×ファイル無し
goto END

:END

echo.
echo.
echo.
pause



setlocal
   rem /* 今日の日付を取得(yyyymmdd) */
   set now=%date:~-10,4%%date:~-5,2%%date:~-2,2%
   
   rem /* C:\プチモンテ.txtをD:\バックアップ\プチモンテ.txtへコピー */
   copy /Y C:\プチモンテ.txt D:\バックアップ\プチモンテ.txt
 
   rem /* C:\ホームページをD:\バックアップ\ホームページ_日付へコピー */
   xcopy /Y /E C:\ホームページ D:\バックアップ\ホームページ_%now%\
      
   rem /* 一時停止 */
   pause
endlocal

@set TRUE_FALSE=TRUE

@if "%1" equ "" set TRUE_FALSE=FALSE
@if "%2" equ "" set TRUE_FALSE=FALSE
@if "%3" neq "" set TRUE_FALSE=FALSE

@if %TRUE_FALSE%==FALSE (
 echo "引数エラー：targz <tar.gzファイル名> <圧縮フォルダ>を指定してください。"
 exit /b 1
)


REM %1にパワーシェルを呼び出すバッチファイルです。
REM test.ps1 が test.bat と同じフォルダにある場合です。

@powershell -ExecutionPolicy RemoteSigned -File .\targz.ps1 -age 30 -last_name 田中 -first_name 太郎 -filename %1 -folder %2

REM 上記ファイルを圧縮(tar.gz化)しました。
@REM 実行結果を見るために一時停止させます。
PAUSE

```
## 2. `targz.ps1`
```ruby:targz.ps1
# バッチファイルから呼び出される側のパワーシェルファイルです。
Param(
    [string]$first_name
  , [string]$last_name
  , [int]$age
  , [string]$folder
  , [string]$filename
)
 
#Write-Host ('名前：' + $last_name + '　' + $first_name)
#Write-Host ('年齢：' + $age)

tar -C $folder -cvzf $filename *.*
Dockerコンテナ間で通信ができることをGitPod上でササっと確認する方法

#きっかけ
Qiitaの「[Docker の基本学習 ~ Docker のネットワーク](https://qiita.com/Arturias/items/b538e6bbf05dd3364397)」という記事で、dockerコンテナには仮想ネットワークカードとIPアドレスが付与され、お互いに通信できるということを知った。

早速試したい。でもこの前Linuxマシン片付けたばっかりだし、またLinux環境を準備するの面倒だなぁ、と思って思いついたのがGitpod上での動作確認である。
#やること概要
GitPod上でDockerをインストールし、その上に、お互いがpingで生存確認しあうだけのコンテナを立ち上げ、その結果をホストから確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/f38c7a74-ebc1-7fe1-075b-d0d9042b6468.png)
#手順
###(1) GitPodを立ち上げる。
いろいろと準備が面倒な方は以下をクリックしてみて欲しい。それだけで何にもインストールされていない素のUbuntu環境が立ち上がる。恐らく、最も高速なUbuntu環境立ち上げ手順であろう。
https://gitpod.io/#https://github.com/TakaoMori/TheSimplestGitpod
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/f5842288-a31d-83de-c311-3b0cf5549ce1.png)
###(2) Docker versionを確認する。
Gitpodのシェル上で、以下のコマンドをタイプする。すると、GitpodのUbuntu環境には最初からDocker Engineがインストールされていることがわかる。
```docker version```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/cd3c8d4a-6a21-16df-60a2-9eb1fe8d9972.png)

###(3) docker0のアドレスを確認する
実はこの「素のままのGitpod」には、ifconfigすらインストールされていない。そこで、sudo apt install net-toolsでインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/9d862b9d-818b-ccd8-cd01-c787e462fc67.png)
その後、ifconfigによってdocker0のIPアドレスが172.20.0.1であることを確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/a4370392-4556-ddfd-e128-3d1133766f7b.png)

###(4) １台目のDockerコンテナを立ち上げる。
```docker run -d ubuntu:14.04 /bin/sh -c "while true; do ping -b -c 1 -W 4 172.17.0.3; sleep 1; done"```
シェルスクリプトを読める方は想像がつくと思うが、要は１秒ごとにpingを打つのをひたすら続けるコンテナである。

無事コンテナ起動後、docker psコマンドで立ち上がったコンテナ名がわかるので、そこでdocker logs <コンテナ名>とタイプして、pingのログを見てみる。すると、相手方の172.17.0.3君は立ち上がっていないので、pingはことごとく失敗しているはずである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/1e6fc614-c1be-5aab-22bb-2331ed96ccb2.png)

###(5) ２台目のDockerコンテナを立ち上げる。
```docker run -d ubuntu:14.04 /bin/sh -c "while true; do ping -b -c 1 -W 4 172.17.0.2; sleep 1; done"```
この状態で新たに立ち上がったコンテナのログを見ると、pingはことごとく成功しているはずである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/1cfe9360-893f-6762-74fa-0488f1c1285f.png)
また、この状態であれば、最初に立ち上げたコンテナからのpingも成功するはずである。最初の図に示した状態が完成しているからである。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/8d7e4bba-321c-f298-da0e-194b51098d70.png)

###(6) Gitpodのお掃除
これ以外と大事。終わったら、https://gitpod.io/workspaces にアクセスし、Workspaceを削除しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1563669/6bffcaac-f461-2a0b-76c5-8debe8377a20.png)
[MySQL] too many placeholders の解消

##php/Laravelでのクエリ発行はEloquent/Query Builderを使用してDB処理を行うことが多い
と思います。

それを使用するとクエリで使用する文字列をインジェクションができないようにクリーンにすることなくSQLにバインドすることができるので、非常に便利。シンプルなSQLであれば直感的に記述しやすいですね。
__使いやすい反面、SQLデータにバインドするデータの量は注意が必要__ です。

##MySQLのプリペアドSQLで使用できるplaceholderは65,535 (2^16-1)個までしか使えない
という制約があります。制約の数を超えてSQLは実行エラー`Prepared statement contains too many placeholder`になります。

リストをフィルタしたとの結果に対して全チェックして処理をしたい場合に、対象データの数、更新対象カラムが多いとMySQLを利用したアプリケーションだと対応が必要になります。
***
今回の事象は一括更新処理にて発生しました。
記述されている処理の内容、対象データとしては下記のような状態でした。

・Modelの中の総カラム数が70で更新対象レコードが3,000件
++弊社の環境ではModelに拡張してbulkUpdatesのメソッドを追加しています。

##問題のコード
```php:Laravel
      $getRecords = Model::whereIn('id', $posted->lists('model_id'))
        ->lockForUpdate()
        ->get(); // ・・・ (A)

      $bulkUpdateRecords = collect(); // ・・・ (B)
      foreach ($getRecords as $getRecord) {
        $getRecord->target_column_a = 'aaa';
        $getRecord->target_column_b = 'bbb';
        $getRecord->target_column_c = 'ccc';
        $bulkUpdateRecords->push($getRecord); // ・・・ (C)
      }
      Model::bulkUpdates($bulkUpdateRecords); // ・・・ (D)

```

(A) getRecordsにidでデータ取得とともにレコードロックをかけておきます。
(B) 一括更新用データ格納のための変数をcollection()を格納し、初期化します。
(C) 取得したレコードのカラムを更新する値をセットします。今回は三つのカラムに値をセットしています。
(D) 一括更新用データ(bulkUpdateRecords)をモデルに拡張されたメソッドに引き渡し一括更新を行っています。
**bulkUpdates内で更新対象データを1,000件ごとにchunkして一括更新を行なっています。**
***
この処理の流れで(D)の段階で`Prepared statement contains too many placeholder`のエラーがログに出力されました。

取得したレコードはテーブルに存在するカラム全ての値を保持した状態なので1レコード70カラム、更新対象を1,000件毎にchunkして更新ということで70 * １000 = 70,000のplaceholderを使用したSQLが実行されたことになります。

##対応方法
下記2点が考えられると思います。

1. chunk数を500などに減らす
1. キーカラム＋更新対象をbulk処理へ引き渡す

1.の対応方法はchunkの設定を変えるだけなのでお手軽に対応ができます。ただテーブルカラムが増えていくとエラーは再現してしまいます。
2.の対応方法はコードの量は断然増えてしまいますが、テーブルカラム増ではエラーが起きないので、いつかのマイグレーションでここの処理がエラーすることはなくなります。

今回は2.の対応で修正しました。

##修正後のコード
```php:Laravel
      $getRecords = Model::whereIn('id', $posted->lists('model_id'))
        ->lockForUpdate()
        ->get();

      $bulkUpdateRecords = collect();
      foreach ($getRecords as $getRecord) {
        $update = new Model(); // ・・・ (a)
        $update->id = $getRecord->id; // ・・・ (b)
        $update->target_column_a = 'aaa';
        $update->target_column_b = 'bbb';
        $update->target_column_c = 'ccc'; // ・・・ (c)
        $bulkUpdateRecords->push($update);
      }
      Model::bulkUpdates($bulkUpdateRecords);

```

(a) 更新テーブルのクラスで新しいインスタンスを作成します。
(b) 今回はupdate処理なので新しく作成したインスタンスにidをセットする必要があります。
(c) 新しく作成したインスタンスの更新カラムに値をセットします。

記述は特に難しくなく新しいモデルインスタンスを作成しidをはじめとした必要カラムをセットしてbukl処理にのせるだけです。これで`Prepared statement contains too many placeholder`の対応は完了です。

##`too many placeholder`が起きる可能性は至る所にあります。
今回のコードの中でもロックの際にidをwhere inで取得しているところもidが65,535を超えない保証はなかったり、bulkupdateでなく、bulkinsertの処理では基本的には全カラムを指定してクエリを作成しないといけない場面などなど。

selectの場合はBuilderをクローンしてchunkで処理、レコードをマージする、bulkinsertではchunkを調整する、などケースバイケースで対応していくしかないと思います。

**MySQL環境でクエリを作成・実行する際にはplaceholderで指定する動的データ数を常に意識して楽しいMySQLライフを送りましょう。**
php-fpmのliveness/readiness設定

php-fpmをkubernetesで運用していて、ヘルスチェックで少し失敗したこと

# php-fpmの子プロセス

php-fpmのプロセスを軽くおさらい

子プロセスの数を設定してチューニングをします
staticの場合は子プロセス数固定で、`pm.max_children`で設定します
dynamicの場合は負荷に応じてプロセス数が変動しますが、最大値はstaticと同じく`pm.max_children`での設定です

同時に処理するリクエストが増えて、子プロセスの最大値に達すると、当然それ以上子プロセスは増えないので、それ以降のリクエストは待機状態になり、処理中のものが終わってプロセスが空き次第処理されていきます

# liveness/readiness

kubernetesでアプリケーションを運用する場合、基本的にはlivenessProbe/readinessProbeを設定しているはずです

https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

方法はいくつかありますが、アプリケーションにヘルスチェックエンドポイントを作成してそこにhttpGetを設定するのがよくやるパターンかと思います
私もこの方法でphp-fpmのアプリケーションを運用していました

# 失敗

このアプリケーションが動作するクラスタに急激なリクエスト増加があり、HPAによるスケールが間に合わずレスポンスの遅延が起きました
この時、php-fpmは子プロセス上限数を使い切っていました
遅延はありつつも地道にリクエストを処理していたのですが、livenessProbeが失敗したことによりコンテナに再起動がかかってしまいました

ヘルスチェックの処理もひとつのリクエストの扱いになり、詰まったリクエストの中に紛れ込んで一緒に遅延してタイムアウトになったのです
ヘルスチェックがどれだけ軽い処理だったとしても、ボトルネックがプロセス数の上限で順番待ちになってしまえば関係なかったという
遅延しながらも処理はできていたのでコンテナ再起動はやめてほしかったんですけどね、設定に忠実に従った結果なので仕方ない

（処理によって必要なリソースは違うので、プロセス数の上限で管理するのがそもそも難しい…）

# 対応案

cpuやメモリのリソースではなくプロセス数の上限でボトルネックになるのが地味に面倒

- プロセス数の上限を増やす
    - 詰まれば結局同じこと
    - リソースとのバランスが崩れるとそれはそれで死ぬ
- 割り当てリソースを増やす
    - これはもちろん意味がない
- プロセス数関係ないヘルスチェックにしたい
    - ちなみにphp-fpmのステータスページを表示するのも子プロセスが必要です

# 対応

### 1. tcpSocketでprobeを設定

```yaml
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
```

TCPソケットのコネクション確立で判定します
livenessはこれでもよいかも
readinessには足りないのではないかなと

### 2. ヘルスチェック用のプールを作成する

```ini:php-fpm.conf
[global]
; グローバル設定

[www]
; アプリケーション用のプール設定
listen = [::]:9000

[health]
; ヘルスチェック用のプール設定
listen = [::]:9001
```

こんな感じでプールを設定して、ヘルスチェック用ものを確保します
proxy側でヘルスチェックのpathを専用プール側に振り分ける設定も必要です
そこを分けるのは果たしてアプリケーションのヘルスチェックとしてどうなのという疑問もあります
あとはメモリ消費量がほんの少し増えます

# :confused: 

kubernetesでphp-fpmするの運用難しいです
Cloudflare の Zone Resource を Pulumi (TypeScript) で管理する


## はじめに

Cloudflare で管理している Zone Resource を Pulumi で管理する方法を記載します。
この記事では、すでに Cloudflare で作成済みのリソースを import します。

Pulumi の Project で Cloudflare のリソースを管理できるように設定されていることを前提に記載します。
設定が終わっていない方は以下記事を参考に設定を行ってください

https://qiita.com/mziyut/items/84212b8847eb5702dc60

## リソースの import を行う

今回は Cloudflare 上で管理する `mziyut.com` の Zone Resource を import します。
Pulumi も terraform などと同様に `pulumi import` コマンドを実行し state ファイルにリソース情報を追記します。

xxxxxxxxxxxxxxxxxxxxxxxx は Zone ID を指定してください。
Zone ID は Cloudflare の Console か API などで確認できます

```zsh
pulumi import cloudflare:index/zone:Zone mziyut_com xxxxxxxxxxxxxxxxxxxxxxxx
```

^ の import コマンドを実行すると `pulumi up` など実行したときと同様に
state の変更状況を出力した後、`index.ts` などに追記すべきサンプルコードを出力してくれます。

```zsh
Please copy the following code into your Pulumi application. Not doing so
will cause Pulumi to report that an update will happen on the next update command.

Please note that the imported resources are marked as protected. To destroy them
you will need to remove the `protect` option and run `pulumi update` *before*
the destroy will take effect.

import * as pulumi from "@pulumi/pulumi";
import * as cloudflare from "@pulumi/cloudflare";

const mziyut_com = new cloudflare.Zone("mziyut_com", {zone: ""}, {
    protect: true,
});
```

Terraform の時とちがい、先に `tf` ファイルにリソースを定義し取り込みを実行する必要がないため
少しだけ Terraform よりも簡単にリソースの取り込みを行うことができます。

#### protect リソースについて

Pulumi は import したリソースに対して `protect` という状態を付与します。
`protect` されたリソースは `pulumi destroy` などを直接実行し削除することはできません。

もし import したリソースを操作したくなった場合は `opts` に `protect: false` を指定し `pulumi up` を実行してするか、`pulumi state unprotect` を実行してからリソースを操作するようにしてください

## ファイルにリソース情報を追記する

先ほど import 時に出力されたコードを `index.ts` に追記します。
出力されているコードはあくまでサンプルと考え、適切なオプションを付与してください。

```ts:index.ts
import * as pulumi from "@pulumi/pulumi";
import * as cloudflare from "@pulumi/cloudflare";

const mziyut_com = new cloudflare.Zone("mziyut_com", {zone: ""}, {
    protect: true,
});
```

Pulumi には terraform の state show に値する コマンドはありません。

import されているリソースの状態を確認したい場合
`pulumi stack export` を実行すると JSON を出力できるため該当のリソースを検索し参照してください

## Reference

https://www.pulumi.com/registry/packages/cloudflare/api-docs/zone/

https://www.pulumi.com/docs/intro/concepts/resources/#protect
NervesでUTCでなくローカルタイムで時間を表示したい


ローカルタイムで時間を表示するためには、まず[タイムゾーンデータベース]が必要となるようです。
[Nerves]ファームウエア開発の上では[Nerves]コアチームの開発した[nerves_time_zones]というElixirパッケージが便利です。

![Screen Shot 2021-12-21 at 12.12.38 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82804/fbd9d5e4-f08d-a9fa-d800-63d3bd5c120d.png)

[Nerves]: https://hexdocs.pm/nerves/getting-started.html
[タイムゾーンデータベース]: https://ja.wikipedia.org/wiki/Tz_database
[nerves_time_zones]: https://hexdocs.pm/nerves_time_zones

https://qiita.com/advent-calendar/2021/nervesjp

## インストール

まず[nerves_time_zones]を依存関係リストに追加し、`mix deps.get`コマンドを実行します。

```elixir:mix.exs
def deps do
  [
    {:nerves_time_zones, "~> 0.1.2"}
  ]
end
```

## 設定

[nerves_time_zones]の設定はコンパイル時もしくは実行時に設定可能です。

**コンパイル時**

- `config/config.exs`でタイムゾーンの初期値を設定。
- 何も指定しない場合は`UTC`が使用されます。

```elixir:config/config.exs
config :nerves_time_zones, default_time_zone: "Asia/Tokyo"
```

**実行時**

- IExやコードでタイムゾーンを変更したい場合。

```elixir
# 現在使用されているタイムゾーンを表示
iex> NervesTimeZones.get_time_zone()

# タイムゾーンを変更
iex> NervesTimeZones.set_time_zone("America/New_York")

# タイムゾーンを初期値に戻す
iex> NervesTimeZones.reset_time_zone()
```

## その他の便利な関数

```elixir
# 使用可能なタイムゾーン名リストを表示
iex> NervesTimeZones.time_zones()
["Africa/Abidjan", "Africa/Accra", "Africa/Addis_Ababa", "Africa/Algiers", ...

# タイムゾーン名が使用可能か確認
iex> NervesTimeZones.valid_time_zone?("Autoracex")
false

iex> NervesTimeZones.valid_time_zone?("Asia/Tokyo")
true

# nerves_time_zonesが使用するタイムゾーン関連の環境変数を表示。
iex> NervesTimeZones.tz_environment()
%{
  "TZ" => "/srv/erlang/lib/nerves_time_zones-0.1.10/priv/zoneinfo/America/New_York",
  "TZDIR" => "/srv/erlang/lib/nerves_time_zones-0.1.10/priv/zoneinfo"
}
```

:tada::tada::tada:
APサーバとは

# 初めに
APサーバ（Application Server）に関して、基本的な情報をまとめた。

# APサーバ
アプリケーションサーバ（Application Server）は、アプリケーションソフトウェアを実行することを専門とするミドルウェアのこと。
web3階層構造におけるAPサーバは、webクライアントからのHTTPのレスポンス要求を処理するwbサーバとバックエンドのDBサーバへの橋渡しを担う。
APサーバの基本的な機能とされるものは、以下である。※機能についての厳密な定義は存在しない。

 * プログラム実行環境の提供
 * データベースへの接続機能
 * 複数の処理を連結するトランザクションの管理機能
 * 処理の流れを制御するビジネスロジックの実装

基本的に大規模なアクセス（処理）が発生する場合に管理・効率化などを行う目的が主となっている。
そのため、webアプリケーションを作成する際に利用するフレームワーク内で開発用サーバが設けられているケースもある。
特に小規模なものであれば、独立したAPサーバが存在しなくともアプリケーションとしては使用可能であるケースも多い。

# APサーバの例

ソフトフェアに関するAPサーバは言語ごとに使用されているものがある。
特にPHPではPHP内部（webサーバであるapach内部）にAPサーバとして運用する仕組みがあり、APサーバを別途導入しなくてもよい。
前述の通り、フレームワーク内にAPサーバの役割を有するものもあり、物理的なAPサーバの不在だけでなく、コーディングにおける明示的な指定をしないケースもある。

|APサーバー|言語|
|---|---|
|Tomcat|Java|
|Glass Fish|Java|
|apache|PHP|
|Unicorn|Ruby
|Thin|Ruby
|Rainbows|Ruby
|Puma|Ruby
|NGINX Unit|python|
|uWSGI|python|
|Gunicorn|python

# 最後に
APサーバに関して簡単にまとめた。
自身の関与する範囲では、まだあまり深入りしないと思われる。

# 参考・引用

https://qiita.com/yCroma/items/e46476e2ac7c372bb2a3
Qualtrics(ウェブサイトフィードバック)を触ってみた学習成果

# モチベーション
この記事では、Qualtricsを用いてウェブサイトフィードバックを実現したい方へ向けた記事となっています。
業務の中でQualtricsを学習する必要があったものの、なかなか役に立つリソースや事例が見つからず、理解を進めるのに時間がかかりました。今回学習した中で、参考になったコードやQualtricsの設定等を共有しようと思いますので、少しでも参考になれば幸いです:sunny:

※あくまでも実装する中での、気づきや考えをまとめておりますので、情報が最新でない場合や正しい表現ではない場合もございます。あらかじめご了承いただき、情報等は公開されているドキュメントを必ずご覧ください。

# Qualtricsとは ([公式](https://www.qualtrics.com/jp/))
**アンケート分析と作成が可能なオンラインアンケートツール** です。
👉 メールでアンケート送信をしたり、ウェブサイトの中で出現させたりすることができます。
👉 アンケート配信→収集→分析までを一気通貫で実施できることが強みです。

# Qualtricsを使用する目的例
1. 実際の満足度やご意見等を定期的に把握できる仕組みを導入する。
2. 1.を実現するために、ユーザーの皆様が負荷なく、手軽にアンケートに回答できる。

# 実現したいこと　
今回の記事では、以下の3つの要件をどのように実現するかについて記述します。
1. ユーザーの情報に基づいて配信するか否かを制御できる。
2. 任意のタイミングでインターセプト(※アンケートとの違いは後述)を表示する。
3. ユーザーのアンケート回答時に”認証”を行う。

##1. どのように配信対象者を制御するのか？

- Qualtricsのウェブサイトフィードバックの設定で`「対象ロジック > XM Directory」`から配信対象のセグメントを指定する。
- コードスニペット読み込み前、もしくは手動でインターセプトの表示を制御する(`QSI.API.load()`)前に`QSI.config.externalReference`を設定する。
    - Qulatricsはload時に、`QSI.config.externalReference`の値をQualtrics側に送信して、配信対象のセグメントに含まれている連絡先の`外部データ参照(External data reference)`と一致しているかどうかを判定しています。
    - QSIはQualtricsのコードスニペットの読み込みが完了すると、実体を持ったグローバル変数(`window.QSI`)として存在します。

##2. 配信タイミングをどのように制御するのか？ 
参考：[APIドキュメント](https://api.qualtrics.com/api-reference/ZG9jOjgyMTA5NTM-website-feedback-java-script-api)

- Qualtricsのウェブサイトフィードバックの設定で`「連絡頻度およびXM Directoryを表示 > インターセプトを表示 > カスタムJavaScriptコードで表示する（手動）」` を選択する。
- Qualtricsのインターセプトの表示を任意のタイミングで実行したい場合は以下のようなコードを準備します。

```typescript
// sample
window.QSI.API.unload(); // load前にunloadする必要。
window.QSI.API.load(() => window.QSI.API.run()); // 非同期処理のload()にcallback関数を渡す。
```
##3. アンケートでの、「認証」をどのように行うのか？
### 前提
- [認証について（公式）](https://www.qualtrics.com/jp/support/research-core/survey-tab/survey-flow/advanced-elements/authenticator/authenticators/)

:::note info
認証を行うことで、Qualtricsに登録されている連絡先の情報と紐付いた形で回答結果の分析が可能になります。
:::

### 方法
- 「`アンケート > アンケートフロー > 認証機能`」より、認証の設定が可能です。
- 認証では「`ExternalDataReference`」か「`RecipientEmail`」の埋め込みデータに限って利用が可能です。

:::note info
認証に使用できるデータはメールか外部データ参照のみです
:::

- `ExternalDataReference`は、ウェブサイトフィードバック側の設定を操作せずとも、`QSI.config.externalReference` の値を参照する仕組みになっています。

# まとめ 💬
色々とQuatlricsを触る中で気づいたことや、理解したことを記述してきました。まだまだリソースが少ない中ですが、少しずつこうした知見や学びが増えていくことを願っています。:pray:

# おまけ：Qualtrics内で使用されている用語の解釈🙈
Qualtrics内で使用されている用語についての理解が難しかったので、これまで触ってきた中での、解釈を記載しておきます。

| 用語 | 意味 | 
|:-----------:|:----------:|
| インターセプト | WEBサイトを訪れたユーザーに対して、アンケートを表示する仕組み。クリエイティブとの組み合わせで、どのようなインターセプトを表示させるのかを決定できる。|
| アンケート | 実際にユーザーが回答する質問。設問の設計のほか、認証や設問の分岐などのフロー制御も行える。|
| ディレクトリ | 例えると、アドレス帳みたいなもの。アンケートの配信対象の情報が管理されている。基本的に分ける必要はないが、プロダクトごとに別管理したい場合等は分けるほうが良さそう。| 
| セグメンテーション | ディレクトリに存在する動的に抽出された連絡先情報の集合。ウェブサイトフィードバックの配信対象指定に利用できる。|
| リスト |静的な連絡先情報の集合。メールでの配信や、アンケートの認証ブロックに使用できる。 | 
|連絡先|アンケート配信対象者の情報。|
Dynamics 365 Finance and Operations ~ Power Appsを活用する② ~

[前回](https://qiita.com/TaichiKayama/items/fff2b69ebef66f67fb62)はデータからいきなり画面を作ってしまう機能を使いました。
環境 > Dataverse > 接続を見てみると、「Fin & Ops アプリ」という FO のコネクタが接続されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/f090dd97-776e-d186-bec7-23874ef48521.png)
今日は、一から画面を作ってみます。

① ホーム > キャンバスアプリを一から作成
② アプリ名：販売注文、形式：電話
真っ白なキャンバスが出てきます。とりあえず画面上部を作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/860e7c60-87a0-29fb-e0c7-d68b48e5bb3b.png)
③ 画面左の + ボタン > 四角形
④ 画面左の + ボタン > テキストラベル("販売注文")
そうするとこんな感じで作成できます。次は画面中央部を作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/b666a664-a4fa-ef73-82a3-306f6ae8d126.png)
そういやテキスト編集しているときに思ったんですが、フォントってもっと種類ないんですかね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/af5a9d3c-eb2e-e739-91b0-c3e4eb242be3.png)
⑤ 画面上部のツールバー > 挿入 > ギャラリー > 縦
⑥ 画面全体を選択 > データソース(Fin & Ops アプリコネクタ) > SalesOrderHeaderV2
⑦ 各フィールドで表示したい項目を追加（以下では、販売注文番号と顧客名を追加。ThisItem."FOのテーブルフィールド名"）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/d9c5dcd0-a58d-8d78-5596-6b2e66acc34c.png)
これで画面作成は以上っちゃ以上です。が、これだけだと芸がないので、今回はこの画面をFOの補助画面として用いることに応用したいと思います。
そのシナリオとしては、顧客マスタ画面を閲覧した際に、「この顧客に紐づく販売注文ってどれくらいあるだろう？」という営業担当・与信担当のケースがあるかなと思います。では早速作ります。

⑧ ツリービュー > App > 右プロパティの詳細設定 > アクション
If(!IsBlank(Param("EntityId")), Set(FinOpsInput, Param("EntityId")), Set(FinOpsInput, ""));
詳細: https://docs.microsoft.com/ja-jp/dynamics365/fin-ops-core/fin-ops/get-started/embed-power-apps#building-a-canvas-app-that-uses-data-that-is-sent-from-finance-and-operations-apps
⑨ ツリービュー > Gallery1 > 画面中央部選択 > 右プロパティの詳細設定 > データ > Items
If(!IsBlank(FinOpsInput),SortByColumns(Filter(SalesOrderHeadersV2,'顧客 ID'=FinOpsInput),"SalesOrderNumber",Ascending),SortByColumns(SalesOrderHeadersV2,"SalesOrderNumber",Ascending))
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/5ffb79ae-8ba5-b93f-ee73-301b7788f4b7.png)
これで Power Apps の画面作りは終わりです。あとはこれを FO に埋め込みます。

⑩ Power Apps を保存 > 共有 > アプリIDをコピペ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/900b9a5e-e60e-7296-e101-f256198dc7d0.png)
⑪ FO > 顧客マスタ > Power Apps ボタン で追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/e99dd5cc-dcfc-d38e-45dc-11efc1b154d3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/1e30f178-529f-d050-33b7-3377c1dcaea3.png)
追加されたので、見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/e35fe0fd-7bd4-4dbd-c14a-d0c37fd52997.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2375815/95ff09dd-54b9-a049-6b67-1011b24cfdca.png)
clusterのUnity UI周りの開発紹介

# はじめに

この記事は [クラスター Advent Calendar 2021](https://qiita.com/advent-calendar/2021/cluster) の22日目の記事です。

昨日は @compile3 さんの「[仕事で振り返るclusterの3年間](https://note.com/compile3/n/n79c08fca85e3)」でした。私の知らない時代のclusterについて色々と書いてあって楽しくclusterの歴史を振り返れました。

今年の1月から[クラスター株式会社](https://corp.cluster.mu/)に所属して[cluster](https://cluster.mu/)のUnity Clientの開発をしている [sansuke](https://twitter.com/sansuke05_vr) です。

さてclusterのクライアントアプリについて、[iOS](https://qiita.com/ulaph/items/09f6f46516f5cf3dde9a)や[Android](https://qiita.com/kyokomi/items/ac2fc48be902de830a2c)の設計や機能開発の話については他のメンバーが書いていますが、Unity部分の開発全般について紹介している人はそんなにいませんでした。（私調べ）

そこで、今回は現在のclusterのUnityクライアント部分、特にGUI周りの設計思想や使われている技術について、大まかに紹介していこうと思います。

# Unityクライアントの全体構成

Unityクライアントの設計は（ほぼ）以下の図ような構成になっています。

![スクリーンショット 2021-12-19 21.43.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123264/5c897f22-991f-1cee-35df-c46d971ec02f.png)

その中でも、GUIは (基本的には) [MVP](https://virtualcast.jp/blog/2019/11/mvp_pattern_on_unity/)をアーキテクチャとして採用しています。

# MVP vs MVVM

MVPとよく比較されるアーキテクチャに[MVVM](https://ja.wikipedia.org/wiki/Model_View_ViewModel)があると思います。

clusterのUnityクライアントがMVVMではなくMVPを採用している背景としては

1. DIコンテナに[Zenject](https://github.com/modesttree/Zenject)を採用しており、View(MonoBehavior)へのInjectをできるだけしたくない

2. Viewから他のクラスへの依存をできるだけ減らし、Viewを含むPrefab単体での動作確認を行いやすくしたい

という理由があります。

1の、View(MonoBehavior)へのInjectをできるだけしたくない理由としては、前提としてZenjectは[コンストラクタへのInjectionを推奨](https://github.com/Mathijs-Bakker/Extenject#injection)しています。しかし、MonoBehaviorはコンストラクタへのInjectをすることができないため必然的にメソッドInjectionを使うことになり、循環依存が発生しやすくなるといったことがあります。

# UniRx, UniTask

Model, ViewからPresenterへのデータバインディングには [UniRx](https://github.com/neuecc/UniRx) を使っています。[^1]

UniRxはModelとPresenterだけではなく、依存関係を逆転させたい場所や、連続的なデータの加工をしたい場合に使われる事が多いです。

UniRxを使用している箇所が多くなるとコードベースが複雑になりがちですが、上記で紹介した設計などのおかげで比較的綺麗に保たれていると感じています。[^2]

また、Presenterなどの依存元から依存先に対しての非同期処理の呼び出しは [UniTask](https://github.com/Cysharp/UniTask) を使って行うことが多いです。

# 複数プラットフォーム対応

clusterは Desktop (Windows, Mac) / Mobile (iOS, Android) / VR (PCVR, Oculus Quest 2) に対応しています。

これら全てのプラットフォームに別々の画面を提供するのはコストが非常に高いため、以下のようにNested Prefabの機能を使って、ある程度画面の共通化を行っています。

![スクリーンショット 2021-12-21 23.01.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123264/0b95b5d5-a188-9f4f-a8d9-fb8b865858bf.png)

コードの方もDesktopとMobileに関しては、UI上の操作や表示方法などが違わない限りは、基本的に同じコードベースを使用できるように作っているため、DesktopとMobile両対応のためのコストは比較的少なくなっています。

また、Mobileでは [ピクチャー イン ピクチャー](https://qiita.com/ulaph/items/09f6f46516f5cf3dde9a#%E6%A9%9F%E8%83%BD%E9%96%8B%E7%99%BA%E3%81%AE%E8%A9%B1) の機能を使い、Unity側からネイティブアプリ側の機能に切り替えを行うようにすることで、モバイル部分の開発コストを下げたりもしています。

最近はこれと合わせて、VRとそれ以外のプラットフォームのコードをできるだけ共通化するようにして、さらに開発のスピードが上がるような取り組みも行われています。

# さいごに

ざっくりとですが、UnityのGUI周りの開発の紹介をしてみました。

clusterのUnity開発ではこの他にも様々な機能開発を行なっています。その辺りは、他のメンバーが[アドベントカレンダーの記事]((https://qiita.com/advent-calendar/2021/cluster))にしているので、ぜひ見てみてください！

そして、clusterでは一緒に開発に携わってくれるメンバーを大募集中です！もし興味を持たれた方は[採用ページ](https://recruit.cluster.mu/engineer/)までどうぞ！

明日は、[@anoriqq](https://qiita.com/anoriqq)さんの「ジュニアなエンジニアを助けたクラスターのメンタリング」です！

[^1]: [MVRP](https://neue.cc/2015/04/13_510.html)とも言われます

[^2]: 個人の感想です
Jest のSnapshotTest で生成されるファイルのディレクトリ構成をいい感じに変更する

## 始めに

Next.js のボイラーテンプレートで Jest を使うと、SnapshotTest が設定されているものをちらほら見かけます。
それに乗っかってページを増やしていって、SnapshotTest を行うようにテストコードを書いていたときに、少し気になることがありました。

```txt:テスト実行後のディレクトリ(説明に不要なものは省略)
├── __tests__
│   ├── __snapshots__
│   │   └── snapshot.js.snap
│   ├── pages // Snapshot がそれぞれに生成される...
│   │   ├── __snapshots__
│   │   │   └── index.test.jsx.snap
│   │   ├── index.test.jsx
│   │   └── users
│   │       ├── __snapshots__
│   │       │   └── index.test.jsx.snap
│   │       └── index.test.jsx
│   ├── snapshot.js
│   └── snapshot.js.snap
├── jest.config.js
├── jest.setup.js
├── jsconfig.json
├── package.json
├── pages
│   ├── _app.js
│   ├── index.js
│   └── users // 追加したディレクトリ
│       └── index.js
└── yarn.lock
```

なんというか、ディレクトリ構成が少し見づらいなと感じました。

- `__snapshots__` がそれぞれに作られて、バラバラに`.snap` ファイルが置かれている

file-system based なnext.js、snapshot のファイルもrouting のようにまとめたいと思い、どうすればできるのかをTips としてまとめました。

## 環境

- ベースリポジトリは[こちら](https://github.com/vercel/next.js/tree/canary/examples/with-jest)
  - ボイラーテンプレートを利用してプロジェクトを作って、`pages`配下に ページファイルを追加しています。

## どうやるか

Jest の[snapshotResolver](https://jestjs.io/ja/docs/configuration#snapshotresolver-string) のオプションを設定します。
こちらでスナップショットファイルのパスを設定できるようになります。

- スナップショットファイルパスの設定ファイルの追加

```js:snapshotResolver.js
module.exports = {
  resolveSnapshotPath: (testPath, snapshotExtension) =>
    testPath.replace("__tests__/pages", "__tests__/__snapshots__") + snapshotExtension,

  resolveTestPath: (snapshotFilePath, snapshotExtension) =>
    snapshotFilePath.replace("__tests__/", "").replace(snapshotExtension, ""),

  testPathForConsistencyCheck: "test/pages/example.test.tsx"
}
```

`__tests__/pages` 配下に書かれたsnapshot テストのファイルの保存先を`__tests__/snapshots__` 配下にするように設定しています。
`testPathforConsistencyCheck` はテスト実行前にパスの解決が正しくされるかの整合性チェックのために必要になります。

- Jest の設定ファイルに`snapshotResolver` を設定する

```js:jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  // こちらを追加
  snapshotResolver: "./snapshotResolver.js",
}

module.exports = createJestConfig(customJestConfig)
```

その後テストを実行すると下記のようになります。

```txt:修正後のテスト実行後のディレクトリ
├── __tests__
│   ├── __snapshots__
│   │   ├── index.test.jsx.snap
│   │   ├── snapshot.js.snap
│   │   └── users
│   │       └── index.test.jsx.snap
│   ├── pages
│   │   ├── index.test.jsx
│   │   └── users
│   │       └── index.test.jsx
│   ├── snapshot.js
│   └── snapshot.js.snap
├── jest.config.js
├── jest.setup.js
├── package.json
├── pages
│   ├── _app.js
│   ├── index.js
│   ├── index.module.css
│   └── users
│       ├── [userId].js
│       └── index.js
├── snapshotResolver.js
└── yarn.lock
```

`__tests__/__snapshots__`配下にまとまりました。なんとなくスッキリした気がするので満足です。

snapshotResolver を使うことで、簡単にまとめることができました。
Go 言語で GitLab Bot の作成


# 準備

以下の Github を参考にして、Bot を作成します。

https://github.com/xanzy/go-gitlab

```
go get github.com/xanzy/go-gitlab
```

# ユーザー名の取得

以下でユーザー名を取得することができます。

```
package main

import (
	"log"

	"github.com/xanzy/go-gitlab"
)

func main() {
	git, err := gitlab.NewClient("-eo1D7ZVkyczkvTt4G7V",gitlab.WithBaseURL("http://192.168.202.129:9010/api/v4"))
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	users,_,err := git.Users.ListUsers(nil)
	for _,i := range users {
		fmt.Println(i.Name)
	}
}
```

出力結果

```
 >> go run git-bot-test.go
Golang
Administrator
```

# 新しい Issue を作成する

基本的には、option で詳しい内容などを設定してから create してあげる流れとなっています。

```
	newTask := &gitlab.CreateIssueOptions{
		Title: gitlab.String("CreateTaskTest"),
		Description: gitlab.String("This is test from git-bot-test.go"),
	}
	_, _, err = git.Issues.CreateIssue(34,newTask)
	if err != nil {
		log.Fatal(err)
	}
```

CreateIssueOptions は以下が指定できます。

```
type CreateIssueOptions struct {
	IID                                *int       
	Title                              *string   
	Description                        *string    
	Confidential                       *bool      
	AssigneeIDs                        []int      
	MilestoneID                        *int       
	Labels                             Labels     
	CreatedAt                          *time.Time 
	DueDate                            *ISOTime   
	MergeRequestToResolveDiscussionsOf *int       
	DiscussionToResolve                *string    
	Weight                             *int       
	IssueType                          *string    
}
```

今回は、```Title```  と、```Description```を編集しました。

また、CreateIssue 関数を用いることで、Issue の作成が行えます。
ここには、前半にプロジェクトの ID である PID と、先ほど設定した CreateIssueOptions を引数に持ちます。

以下のように、Issue が作成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1773603/b11cd38b-8c93-6939-2a41-ef05ccca7cd1.png)
Qiitaにいっぱい記事を投稿して、プログラムへの意欲をアピールしようという話

#この記事の目的
・自分がQiitaに記事を投稿する目的の記録と、自分への戒め。
・転職の際にポートフォリオの代わりになったらいいなという思い。
<br>
#投稿者の現状
私はプログラムを勉強してエンジニアになりたいと思っている初学者です。
これまでのプログラム学習歴は3か月ほどで、すべて独学になります。
3か月の中身は、ほぼ毎日[ツイッター](https://twitter.com/koharu2739)で学習進捗を呟いているのでそちらに。
<br>
私の今まで学習してきた言語（フレームワーク等含む）は以下の通り。
<br>

| 言語など | 進捗 |
|:-:|:--|
| HTML5&CSS3 | LPやコーポレートページなどを作成できる |
| Sass | LPやコーポレートページなどを作成できる |
| JavaScript | 一般的な動的サイトを作成できる |
| jQuery | プラグインを用いて様々な効果を挿入できる |
| Vue.js | 学習中。簡単なSPAであれば作成できる |
| Firebase | 学習中。Vue.jsで作ったサイトを公開できる。<br>データベースの構築やグーグル認証などの基本的なことはできる |

<br>
#Qiitaで記事を投稿する理由
これまでは実践的なプログラミング言語の学習を中心にやってきました。
ですが最近は、言語の使い方やライブラリ、フレームワークよりも、もっと基礎的な部分を学習したほうがいいのではと思い、**学習内容をコンピューターサイエンスや、JavaScriptのなかでも設計や思想や構築などといったところに焦点を当てています。**
<br>
ですが、そういった部分はど基礎もど基礎。
そこを学んだところで見栄えのいいポートフォリオなど作れるはずがありません（たぶん）。
<br>
昨今、未経験からエンジニアになるためには企業にアピールするためのポートフォリオ（制作物）は必須と言われています。自分の技術力をアピールする為ですね。
<br>
**しかし、転職エージェントや現役エンジニアの方たちに話を聞いてみると、未経験採用に限っては、どうやらポートフォリオは技術力を見る為ではなく、プログラムに対する意欲を見る為のものということ。**
<br>
どういうことかというと、現場未経験の人は例え頑張っても、現場で通用するレベルの技術力を身に付けるのはまず無理（少なくとも企業側はそう認識している）。だからこそ、入社後も意欲的に技術を学べるかというところを重視しているという事のようです。
<br>
だとすれば、例えポートフォリオ（制作物）じゃなくても、別でプログラムに対する意欲がアピールできればいいんじゃないかという目論見。
<br>
「そんなことをしなくとも、基礎を学んだあと実践的なものも学んでポートフォリオを作ればいいじゃないか」という意見も最もなのですが、正直そこまでやろうとすると転職まで何か月かかるのか見当がつきません。
<br>
今すぐに！　という訳でもありません。
**しかしあくまでも私見ですが、独学で何か月もやっていくより、その時間を仕事として現場で働く方がよっぽどスキルアップに繋がると考えています。**
<br>
**この試みが実際にうまくいくかは分かりませんが、やってみる価値はあると思っています。**
<br>
#どういうふうに記事を投稿していくか
###1.投稿頻度
**できれば毎日一本以上。
しかし、実際の記事の文量であったり、記事の作成時間など。
それが大きくなりすぎてインプットの時間が減ってしまっては本末転倒でもあります。**

なので、結局は記事を書きつつ調整していくことになるでしょう。
でも、できるだけ毎日一本を目標にやっていく予定です。
<br>
###2.記事の内容
他の人がすでに書いている内容かどうかは気にしません。
これはアウトプットの為でもありますし、真面目に学習出来ていることさえ分かればいいのです。

**ですが、記事の内容はできるだけ理解しやすいようにかみ砕いていくようにします。
勉強した内容を自分なりに説明・表現することで理解を深める為です。**
<br>
#最後に
上手くいくかは分かりません。
しかし自分にもいい挑戦として、そして基礎分野というアウトプットしにくい部分をアウトプットできる良手として、やっていきたいと思います。
<br>
何か進捗があれば、また関連した記事を書くつもりです。
<br>
<br>
19日間 & 2教材だけでAWS SAAに合格しました

# 背景
- 19日間、2つのUdemy教材（[ハンズオン教材](https://www.udemy.com/course/aws-associate), [問題集](https://www.udemy.com/course/aws-knan)）のみ利用して合格しました。
点数は736点なので、合格ライン720点ギリギリです。
- CLFすら持っていないので、AWS認定は初めて受験しました。
- 同棲中なので、ずっと勉強するわけにはいかない（それでもかなり理解してくれて、勉強に時間を取れたので感謝しています。）
- プログラミングの学習と並行、自分としては慣れていない技術のNext.js/Amplify/TS等を学習。普段業務で使う技術はRuby on Railsが殆どです。
- S3/CloudFront/RDS/EC2/ELB/ALB等、かなり浅くですが業務で触れていました。
- エンジニアとしてのキャリアは3年目、Web系の開発がメインでインフラエンジニアではありません。

# ポイント
- 一旦計画は立てて、このままやるのは無理だな感があったら、方法を変える（プログラミングの実装でハマったら、設計を疑って変えるみたいな） **←最重要、これだけでも良い位です**
- アウトプット重視、具体的には問題を解く、解説を読んだ時要約や感想をメモに書く等。（人間は入力情報は99%忘れてしまうという説もあります。）
- 出来ることをやる、出来ないことはやらない。
- 基準値を上げる。私の場合は身近な方に「2週間で合格できるよ」と身近で言われたことが大きいです。
- 資格試験の勉強はあくまで、合格。演習を繰り返して、単語の反射で解くでOK。
- 資格取得の目的は「聞いたことある知識」をまず増やす。取得してしまってから自分で触ったり会話、業務の中で使える知識に転化させるだと思います。

# 流れ
## 学習開始前
教材リサーチして色々使おうと考えていました。
使おうと思っていた教材は以下です。
・[これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)
・[【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)
・[徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書 徹底攻略シリーズ](https://www.amazon.co.jp/dp/B07M7S9GDL/ref=dp-kindle-redirect?encoding=UTF8&btkr=1) 
・ブラックベルト
・[CloudTech](https://kws-cloud-tech.com/)さんの無料問題集

## 〜１週間
### スタート時点
- React関連のプログラミングと平行して学習
- アウトプットを重視しようと考え、[【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)の問題を2回解きました。正答率は45%程度。

### 学習計画のダイエット
- 問題を2回解いた時点で「時間かかるので教材は2つに絞ろう。落ちる前提でまず受験しよう」と決めました。その時参考にしたのはこちらのサイトです→[SyachikuLOG：【合格体験記】AWS ソリューションアーキテクト(アソシエイト)に2週間で合格した話](https://syachiku.net/aws-saa/)

#### 教材
- [これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)
- [【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)

### まだスローペース
- この時期、問題を解いた後の復習は不正解の問題のみ回答を読み、感想や要約をPCのメモ帳に書いていました。
- [これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)のハンズオンを〜30程度まで進めていましたが、開発の勉強に多く時間をとって、あまり資格の学習はしていませんでした。

## 〜２週間

### インプット終了
- [これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)のハンズオンを、1~200は全部通しました。時間が足りず金曜日の夜〜の徹夜で170個程度進めました。
- ハンズオンを全部やっていると間に合わなそうなので、概要の動画視聴だけorググるだけ+一言アウトプット要約or感想をメモに書いていました。

### 演習開始
- [【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)を最後まで通しました。
- [これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)教材のテストを全部解きました。

## 〜３週間
### 演習2週目
- 2週目の演習を開始しました。
- 解いた順番は[【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)の1~6回を通す、[これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)の付属問題の1~3回を通す。という流れです。
- 前者の問題集の第1回、付属問題の1~2回だけ合格点を超えました。
- この時点では復習でメモを書いていると間に合わないので、間違った問題の解説を読むだけにしていました。

### 試験直前〜試験
- この時点でも焦らず、出来ることをやるという事で、毎日1回分程度の問題を解いて回答を読むという流れでした。
- 2週目最後である、[これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座（SAA-C02試験対応版）](https://www.udemy.com/course/aws-associate/)の3回目のテストがギリギリ完了しませんでした。35問くらいまで解きました。
PleasanterからPowerShellを使ってCSVデータをエクスポートする

# 概要

プリザンターにはレコードの内容をCSVファイルでエクスポートするという、すてきな機能が搭載されています。
しかもなんと、API経由でも出力することができるのです。
早速PowerShellを使ってプログラムを書いてみましょう。

# エクスポートの設定

あらかじめ出力したいテーブルにエクスポートの設定を行いましょう。

![2021-12-21_18h48_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167316/183805f5-b468-e934-6b2b-b4a9e52f80d1.png)

・公式マニュアル
https://pleasanter.org/manual/table-management-export

左上の ID の部分は後で使うので覚えておきましょう。

# サンプルコード

またまた公式マニュアルを参考にPowerShellでプログラムを作ってみました。
https://pleasanter.org/manual/api-export

``` expport.ps1
Add-Type -AssemblyName "System.Web"
$error.Clear()

$requestUrl = "http(s)://[プリザンターのアドレス]/api/items/[サイトID]/export"
$apiKey = "[プリザンターのAPIKEY]"

$json = @{
    ApiVersion = 1.1
    ApiKey = $apiKey
    ExportId = 1 
}

trap [Net.WebException] { continue; }
try {   
    $requestBody = $json | ConvertTo-Json 
    $res = Invoke-RestMethod -Uri $requestUrl -ContentType "application/json" -Method POST -Body ${requestBody}
} catch {   
    Write-Output $_.Exception
}

# ファイル出力
[System.Text.Encoding]::UTF8.GetString([System.Text.Encoding]::GetEncoding("ISO-8859-1").GetBytes($res.Response.Content)) | Out-File -NoNewline test.csv
```

jsonデータの `ExportId` はあらかじめ作っておいたプリザンターのエクスポート設定の `ID` と同じ値にします。

最後に文字化け対策を施してファイル出力をしています
`test.csv`の部分が保存される実際のファイル名になります。

文字化け対策はこちらを参考にさせていただきました
https://qiita.com/zaki-lknr/items/1ae3258d7b77c5e2a2ba

# CSVファイルの中身

![2021-12-21_18h43_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167316/dfb076f2-434f-42db-de23-8eaab34c452a.png)
プリザンターから直接エクスポートしたものと同様のファイルになればOK


# （自動化したい場合）タスクスケジューラで自動化

たとえば毎日ある時刻にCSVファイルを生成するといった処理を行いたい場合は
作成した.ps1ファイルをタスクスケジューラで動かすとよいです。

タスクスケジューラへの登録は以前書いたこの記事に記載してあります。
https://qiita.com/kTsuji/items/ab2aed5682e1406dd413#%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%A9%E3%81%A7%E7%9B%A3%E8%A6%96

# さいごに

作成されたCSVを・・・

・指定されたメールアドレスに自動送信
・プリザンターと連携するシステムにCSVデータを自動送信

メールAPIなどを活用すれば上記のような応用ができるのでは？と思います。

プリザンターのちょっとした、ほんのちょっとした小技の紹介でした。
PPAP歌ってみた

主にPV（プロモーションビデオ）系、あるいはMV（ミュージックビデオ）系の作品を作るときに役立つテクニックです。

まず全体のテンポを管理する指揮者役のスプライトを用意します。これは専用のスプライトを用意しても良いですし、背景をその役に当てても良いです。このオブジェクトは定期的（例えば８小節ごと）にイベントを発生させます。
そして、このイベントごとに実際に演奏を行ったりアニメーションを行ったりするスプライトを用意しますl。このスプライトでは、イベントごとに演奏をしたりアニメーションを行ったり、何かしらの演出を行います。
こうすることで、それぞれのスプライトが同期して演奏をしたりアニメーションを行ったりできるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/cb8c9eae-f99d-6f0f-7ff7-bec3e9443fa7.png)

指揮者役のスプライトで、イベントを発生させるプログラムです。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/7cd81b0d-ffe0-3d61-03ef-badd3a566ebc.png">

それぞれのスプライトで、演奏やアニメーションを行うプログラムです。

<img width="50%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75307/0b4316ef-5667-50af-902a-0d6ed399caa2.png">

サンプルプログラム
https://scratch.mit.edu/projects/619131119

![スクリーンショット](https://cdn2.scratch.mit.edu/get_image/project/619131119_480x320.png)
F#で湯婆婆を実装してみる

諸兄姉がお読みになる前に申し上げます。
本稿は徹頭徹尾**おもんない出オチ**であることを覚悟してください。

# ま、みっともないコーダーが来たもんだね。
ここは [湯婆婆 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/yubaba) 22日目だよ！
ネタでもカレンダーを埋め合わせるんだ！
ぐずぐずするんじゃないよ！

# なんだいその汚いF#は

いいかい、おまえの初仕事だ。
[湯婆婆 言語まとめ](https://qiita.com/sengoku/items/9dba838d94d5120f8a73)からF#版を検索するんだよ。

F#なんかの気配がなかったのかい？
ううーん…おかしいね。
なかったものは仕方がない。40秒で実装しな！
こうなったら出来るだけはやくコーディングするしかないよ！

できない！？それがおまえの答えかい？
あたしが**関数型言語なんて知る訳ない**だろ。
おーいやだ。
さ、[C#で湯婆婆を実装してみる](https://qiita.com/everylittle/items/d4df566a9c5f4bb58d5f)の改変でいきな！

#そいつの正体はC#もどきだよ。そう、 . N e t ！

欲にかられてとんでもないコードを組んじまったもんだよ。

```fsharp:Fsbaba.fs
open System

"契約書だよ。そこに名前を書きな。" |> stdout.WriteLine
let name = stdin.ReadLine()
printfn "フン。%sというのかい。贅沢な名だねぇ。" name
let SayName s = $"今からお前の名前は{s}だ。いいかい、{s}だよ。分かったら返事をするんだ、{s}!!"
(name.Length |> Random().Next, 1) |>name.Substring |> SayName |> Console.WriteLine
```

**馬鹿な最終行のコード**はやめとくれ。
そんなひょろひょろなパイプライン連打に何が出来るのさ。
![2021-12-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81851/a41887b0-5697-8285-0fa4-7875bb8c0e6f.png)

…あぁー、コンソールを汚しちまって。
おまえたち、せめて出力方法を統一しな！

この`stdout.WriteLine`と`printfn`と`Console.WriteLine`の中からおまえのお父さんとお母さんを見つけな。
チャンスは一回だ。ピタリと当てられたらおまえたちゃ自由だよ。

いない！？
…
……
………大ー当たりー！
行きな！おまえの勝ちだ！
**早く次のアドカレにいっちまいな！**

# いやもう、何のデモ？

♪呼んでいる～　VSのどこか奥で～
♪いつも高階関数する　夢を見たい
♪かなしいコードは数えきれないけれど**以下略。終わり！閉廷！！**
サイトの収益について

この記事は [驚異のFANZA女優検索](https://fanza-actress-bust-search.azurewebsites.net/) Advent Calendar 2021 の 22日目の記事です。

#収益について
驚異のFANZA女優検索は最初から1つだけ収益化を考えていた。それはアフィリエイトリンクだ。収益化を考えていたというより女優APIを使った時点で既にアフィリエイトリンクが組み込まれている。つまり女優APIで取得出来る情報の中に、アフィリエイトID付きリストページURLがある。なので自分のサイトからDMMへのリンクを作ればそれでもう収益化を見越した施策となる。

#広告リンク
あとはあまりベタベタリンクを貼るのが嫌だったので同じくDMMの広告も少しだけ置いてみた。ここはGoogleアフィリエイトとかの方が良いのかもしれないが、アダルトサイトな時点でDMMに統一しておいた方がトラブルが少ないかなと思った。一応邪魔にならず、サイトになじむようにレイアウトは気をつけたつもりだ。ミスクリックを誘導するような場所には置かず、テーマに沿わないような内容は出ないようにしている。年齢認証前はアイドル、認証後はアダルトの広告が出る。

#DMMからの報酬
自分のサイトからDMMへ移動してそこで商品を買ってくれる人がいれば収益が入る。作品のページから直接その商品を買うのが一番報酬がいいダイレクト報酬で購入の金額の35%、違う商品でも同じカテゴリーならダイレクトよりは悪いがカテゴリ報酬がもらえる。購入の金額の10%だ。1000円の作品を買えば100円だ。ただ、自分のサービスのPVが上がったからといってそのままリンクをクリックする人が増えるかは別の話だ。一応クリック数は見えるが、自分のサイトのPVとの関係性はまだよく分かっていない。

#実際に得られた収益
まず、[【個人開発】DMM.comの女優APIを使って約5万人の女優を検索できる驚異のサービスを作った](https://qiita.com/fabsabsfabsabs/items/e8e3b717dc311ac2176d)を公開した時にQiitaでバズって、サービス自体のPVが上がり始めて収益があった。その時が8月でクリック数が約700回で収益が約1000円だった。9月は今の所一番多いクリック数で1400回で約1500円だった。10月はクリック数がガクッと減って400回で100円。11月は理由は良く分からないが、8月以上のクリック数約1000回あったにも関わらず収益は40円だった。クリック数が上がれば収益が上がるとは限らないとは思っていたが、11月の結果を見るとまあ読めないと思った。

#結局手にした金額は未だ0円
驚異のFANZA女優検索はサービスを開始してから合計で4000円ぐらいの収益を得ている。ただDMM.comは5000円以下の収益では払い戻しはないため実際にこのサービスで得ている利益は今のところ0円だ。正直アダルトサイトを作ればすぐにでも結構な収益が出るかもなーとなんとなく思っていたので、実際に作って何ヶ月かで4000円はちょっとしょっぱい数字だ。サーバー代など考えると勿論マイナスだ。今のところドメインは無料で済ませているが、せめてサーバー台ぐらいは広告収入で賄いたい所ではある。まあとは言えこればっかりは焦ってもしょうがない。
[NodeJs]Expressのrouterを整理する

NodeJsのアドベントカレンダー23日目の記事です

#はじめに
ExpressサーバーでRouteを定義していくとどんどん行数増えていってしまいますよね。
私も今までは処理をapp.jsに書かずfunctionの呼び出しで対応していたのですが、つい先日もっと上手に書く方法を知ったので共有したいと思います。
もしかしたら、当たり前なのかもしれませんが…。

#課題
参考書とかWeb見ながらExpress書くとこんな感じで、1つのファイルに全部のRoute書いてしまいがちかと思います。 はい、私がそうでした。

```js:app.js
const express = require("express");
const app = express();

app.get('/user', (req, res) => {
...
})

app.post('/user', ...
app.patch('/user', ...
app.delete('/user', ...
app.get('task', ...
app.post'task', ...
```
※ ...はJavaScriptだと違った意味になってしまいますが省略の意味で見てください

サーバーに機能が増えていくと、五月雨式に行数が増えてしまい、メンテナンスが大変に。
チームで開発していたらたくさんの人が同時に同じファイルいじることにもなり、思わぬ事件になったり…。これをどうにかしたいと思います。

#解決方法
まずは機能のまとまりごとにファイルを作成します。
上記の場合、/user 系のためにuserRouter.js, /task系のためにtaskRouter.jsというファイルを作ります。

そして、それぞれのファイルの先頭でこんな感じの宣言をします。

``` js:userRouter.js
const express = require("express");
const router = new express.Router();
```
そして、元のapp.jsから処理を移動してきます。※app.xxx はrouter.xxxに変えます。

``` js:userRouter.js
router.get('/user', (req, res) => {
...
})

router.post('/user', ...
router.patch('/user', ...
router.delete('/user', ...
```
同じことをtaskRouter.jsにも実施します。

最後は元のapp.jsの修正です。
作成したrouterをrequireして、useするだけ。

```js:app.js 
const express = require("express");
const app = express();

app.use(express.json());

const userRouter = require('./userRouter')
const taskRouter = require('./taskRouter')

app.use(userRouter)
app.use(taskRouter)

```
無事、処理のほとんどが別ファイルに移動され、app.jsはすっきりしました。
こうすることで、新しいパスを作る時にもapp.jsの変更は2行だけ。メンテも簡単です。

#まとめ
個人的にはこれを知ってからapp.jsには処理をほとんど書かなくなりました。
コードも読みやすくなりますし、後からメンバー入っても影響範囲は最低限に抑えられます。よかったら試してみてください。 

詳細を知りたい方はここの最後に書かれているexpress.routerを参照してください。

https://expressjs.com/ja/guide/routing.html
Game of PODs で遊んだ

こちらは[ITRC Advent Calendar 2021](https://adventar.org/calendars/6368)の21日目の記事です。
前の記事: [@koseiinfratop](https://qiita.com/koseiinfratop)さんの[ArchLinuxインストール方法](https://qiita.com/koseiinfratop/items/e9bf7e7b84b8f04abce4)
次の記事: Kさんの[【Java入門】JavaFXを使って物体を動かす【FXML＆CSS】](https://note.com/k_tott/n/n8ad9989ba7c7)

※ ネタバレ防止の為、実際の問題や解き方は記述しませんでした

## きっかけ

CKAやトラブルシューティングコンテストのように問題が解きたく調べていく過程で知った

## 難易度

問題によるが基礎的な事が理解出来いると解けるかなと感じた
分からない箇所もドキュメントに頼るとちゃんと解ける

## 実際にやってみる

kodekloudの[Game of PODs](https://kodekloud.com/courses/game-of-pods/)にアクセスし、ユーザ登録をしてスタート

まずはどのように遊ぶのかという動画があるのでルールを理解してからいざゲームへ

### ステージ

現在(2021年12月21日)でのステージ状況は以下になっております

- 遊べるステージは4つ
  - Bravo
  - Pento
  - Radis Islands
  - Tyro
- 環境の移行で遊べないステージは2つ
  - Iron Gallery of Braavos
  - Voting Applocation
- 実装待ちステージは3つ
  - Datalake
  - Ingress
  - Services

最初はBravoから解いていきましょう

### ルール

Let's fix it!で問題を解き始めます
制限時間以内に問題を解き入手したMagic Chantを入力しSubmitする事でクリアとなります

### 問題について

Katacodaを利用しターミナルを実際に叩いて解いています
環境の準備に5分程かかります
少し癖があるのでKatacodaの使い方を調べてから解き始めた方がいいと思いました
やり直しは出来ますが環境が初期化されるので注意が必要です

基本的にTerminal, Quiz Portalのタブがあります
+をクリックすることで増やしたいタブを選択出来ます
その他タブが必要だったりする場合は動画に使い方がありますのでご覧下さい

- Terminal
  - 現在はホスト名が変わっている
- Quiz Portal
  - 仕様や何処まで解けたが分かります
  - checkをクリックし、正常な状態と判断されるとMagic Chantを入手出来ます
  - アイコンをクリックすると仕様が表示されます
  - 矢印をクリックすると関連が表示されます

## 感想

その章だけで使う技術の説明はあるので初学者でも解きやすかったです
youtubeにも動画があるのでまずは雰囲気が知りたいって人は拝聴をオススメします
※ ネタバレ注意

<iframe width="560" height="315" src="https://www.youtube.com/embed/rWkR0ldRsig" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## 参考

- [Game of PODs - kodekloud](https://kodekloud.com/courses/game-of-pods/)
- [Kubernetes Documentation - kubernetes.io](https://kubernetes.io/docs/home)
- [KodeKloud youtube channel - YouTube](https://www.youtube.com/channel/UCSWj8mqQCcrcBlXPi4ThRDQ)
Python + Exiftool でファイルメタデータ取得

# この記事は？

Exiftool (及びそのラッパー) を用いて、Python でファイルのメタデータを取得する方法です。

Exiftool というは、様々なファイルのメタデータの取得や書き込みができるコマンドラインツールです。
この記事では、読み込み方法にだけ触れており、書き込み方法には触れておりません。

# `pyexiftool`で取得

```sh
pip install pyexiftool
```

```py
from exiftool import ExifTool

with ExifTool() as et:
  data = et.get_metadata("<FileName>")
  # data: dict型

# 必要に応じて dict型のメソッドを用いてデータ取得
print(data.get("File:FileName"))
```

複数ファイルを一度に指定することもできます。

```py
from exiftool import ExifTool

with ExifTool() as et:
  data = et.get_metadata_batch(["<File1>", "<File2>"])
  # data   : list型
  # data[n]: dict型
```

特定のタグのみを取得することもできます。

```py
from exiftool import ExifTool

with ExifTool() as et:
  data = et.get_tag("File:FileName", "<FileName>")
  # data   : str型
  data = et.get_tags(["File:FileName", "File:FileType"], "<FileName>")
  # data   : dict型

# get_tag_batch, get_tags_batch で上記同様に複数ファイル指定も可能
```

参考:

https://sylikc.github.io/pyexiftool/

# `pyexifinfo`で取得

```sh
pip install pyexifinfo
```

```py
from pyexifinfo import information

data = information("<FileName>")
# data: dict型

# 必要に応じて dict型のメソッドを用いてデータ取得
print(data.get("File:FileName"))
```

CSV や XML での取得も可能。

```py
import pyexifinfo

data = pyexifinfo.get_csv("<FileName>")
# data: str型
data = pyexifinfo.get_json("<FileName>")[0]
# data: dict型
data = pyexifinfo.get_xml("<FileName>")
# data: str型
```

参考:

https://github.com/guinslym/pyexifinfo

# 外部コマンド実行で取得

`exiftool`コマンドを外部コマンド呼び出しで実行し、標準出力を取得するという無理やりな方法です。
当然ながら、この方法では`exiftool`コマンドがインストールされている必要があります。

```sh
# On APT (Debian, Ubuntu...)
sudo apt install exiftool
```

```py
from subprocess import run, PIPE

buf = run("exiftool -s3 -FileName 'FileName'", shell=True, stdout=PIPE)
# buf: bytes型
data = buf.stdout.decode("UTF-8").strip("\n")
# data: str型
```

`-FileName`オプションは、ファイル名のみを表示させるオプション。
その他メタデータを表示させるには、別のタグを記述、あるいは記述無しで全表示。
全メタデータを取得してやりくりするには、文字列処理を自力で書かなければならない。

(地味に大掛かりなことをやっているので、この方法は非推奨です。)
Next2Dでゲームを作ってみる(前半)

# ゲームを作ってみる
今日から、Next2D Frameworkを利用してナンバーパズルゲームを作ってみようと思います。

## 仕様を考える

- パズルのピースを四色とする。同じ色同士のピースが結合でき、ナンバーは加算される
- ピースが結合するとランダムで新しいピースが生成される
- ナンバーはユーザーが指定した2から9の倍数になると得点に加算され、タイムゲージが回復する
- タイムゲージが0になるとゲーム終了

## シーンのURL設計を考える

* オープニング

```text
https://example.com/top
```

* ホーム画面

```text
https://example.com/home
```

* ゲーム画面

```text 
https://example.com/game/play
```

* ゲーム終了画面
 
```text 
https://example.com/game/result
```

# 素材を作る

- タイトルロゴ
- 四色のピースとテキストエリア
- タイムバー
 
必要な素材を[NoCode Tool](https://tool.next2d.app/)で制作してみようと思います。
まずは素材なので、デフォルトの機能だけで作っていきます。また、見た目を整えるのは最終日に行います。

## タイトルロゴ
タイトルロゴをテキスト機能で設置します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80502/d6f9dc61-042b-006b-c04c-84b3f3b283a3.png" width="300">

## 四色のピースとテキストエリア
角丸矩形の雛形にBevelフィルターを薄く適用して、それっぽいボタンを4つ作ります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80502/b32f9df6-b8ba-a9f8-c2d1-4d17d8a13c2c.png" width="300">

## タイムバー
マスクを適用して、中のShapeのx座標を移動してバーの減算を表現します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80502/6be35445-bcf2-0b68-9895-5ea97804b488.png" width="300">

# ViewとViewModelを作成する

URL設計したパスを`routing.json`に追加します。

```json:routing.json
{
  "top": {
    "requests": [
      {
        "type": "content",
        "path": "{{ content.endPoint }}content/opening.json",
        "name": "OpeningContent",
        "cache": true
      }
    ]
  },
  "home": null,
  "game/play": {
    "requests": [
      {
        "type": "content",
        "path": "{{ content.endPoint }}content/material.json",
        "name": "MaterialContent",
        "cache": true
      }
    ]
  },
  "game/result": null
}
```

## 自動生成
デフォルトで設定されてるディレクトリは不要なので`src/view`の中を空にします。
`routing.json`に設定すれば、後は以下のコマンドを実行するだけで自動でViewとViewModel、そして必要なディレクトの全てを自動生成します。

```linux
npm run generate
```

これで、素材の準備はできたので、明日からはFrameworkでロジック部分の制作を進めていければと思います。
どうしてもチケットもぎもぎしたい！！！もぎもぎもぎもぎ！！！


:::note
この記事は2021年12月21日(火) Flutterアドベントカレンダーです
:::

# こんにちはいるべさんです。

先日、今年入社した会社の最終出社日を終えました。
思えば今年はFlutterエンジニアとしての生を受けたり、転職する決意をしたり、良い方々との出会いがあったり濃ゆい一年でした。

私は労働本体よりも人間関係の方が仕事のパフォーマンスへの影響度が高いと思っています。それも何倍も。

辞めてはしまいましたが、とてもとてもありがたいことに楽しいことが多く、いい人たちに囲まれたなと思える日々でした。

### だがしかし

しかし何より、地元に帰省する際に利用した「もげる航空券」を「もいだ」ときがこの一年で一番快感でした。最高でした。
労働からの解放。満員電車からの解放。一人暮らしの虚しさからの解放。沢山の意味合いがこもったチケットもぎでした。

_**あの感動をもう一度!!!**_


# これがもげるチケットだ！！！

### 縦バージョン

<img width=250 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601990/232c2a43-444d-40ba-8b54-e0c54e49d144.png">

### なんと横バージョン

<img width=250 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601990/fa27e01b-8e8d-dde7-554e-c5c4dde15c4b.png">


そして...
... ...
## もげろ！！！！

ばちーん！
<img width=250 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601990/4a2df355-ec03-994d-456e-94b88e65d1d4.png">

ばちーん！
<img width=250 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601990/70fa9e79-37b6-0c84-0e07-ccb0b60c5e66.png">

# 最高だ！！！

- CustomPainterで書いてます
- 言うまでもなくFlutterです
- 解説はコメント書いてあるしコード見てください🌟

https://github.com/airy-swift/flutter_ticket

# おわりに

Flutter, swift, kotlinを扱っていきます！
是非[Twitter](https://twitter.com/airy_jingo)と[ブログ](https://airy-56405.web.app/#/)見ていってください！


明日は@sugitlab さんです！
アドベントカレンダーも残り4日ですね！楽しんでいきましょう！🌟